blob: f7587ad9669008fc521a66d84211ddfa4e09a181 [file] [log] [blame]
#include <u.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <netdb.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "ndbhf.h"
AUTOLIB(resolv)
static void nstrcpy(char*, char*, int);
static void mkptrname(char*, char*, int);
static Ndbtuple *doquery(char*, char*);
/*
* Run a DNS lookup for val/type on net.
*/
Ndbtuple*
dnsquery(char *net, char *val, char *type)
{
static int init;
char rip[128];
Ndbtuple *t;
USED(net);
if(!init){
init = 1;
fmtinstall('I', eipfmt);
}
/* give up early on stupid questions - vwhois */
if(strcmp(val, "::") == 0 || strcmp(val, "0.0.0.0") == 0)
return nil;
/* zero out the error string */
werrstr("");
/* if this is a reverse lookup, first look up the domain name */
if(strcmp(type, "ptr") == 0){
mkptrname(val, rip, sizeof rip);
t = doquery(rip, "ptr");
}else
t = doquery(val, type);
return t;
}
/*
* convert address into a reverse lookup address
*/
static void
mkptrname(char *ip, char *rip, int rlen)
{
char buf[128];
char *p, *np;
int len;
if(strstr(ip, "in-addr.arpa") || strstr(ip, "IN-ADDR.ARPA")){
nstrcpy(rip, ip, rlen);
return;
}
nstrcpy(buf, ip, sizeof buf);
for(p = buf; *p; p++)
;
*p = '.';
np = rip;
len = 0;
while(p >= buf){
len++;
p--;
if(*p == '.'){
memmove(np, p+1, len);
np += len;
len = 0;
}
}
memmove(np, p+1, len);
np += len;
strcpy(np, "in-addr.arpa");
}
static void
nstrcpy(char *to, char *from, int len)
{
strncpy(to, from, len);
to[len-1] = 0;
}
/*
* Disgusting, ugly interface to libresolv,
* which everyone seems to have.
*/
enum
{
MAXRR = 100,
MAXDNS = 4096,
/* RR types */
Ta= 1,
Tns= 2,
Tmd= 3,
Tmf= 4,
Tcname= 5,
Tsoa= 6,
Tmb= 7,
Tmg= 8,
Tmr= 9,
Tnull= 10,
Twks= 11,
Tptr= 12,
Thinfo= 13,
Tminfo= 14,
Tmx= 15,
Ttxt= 16,
Trp= 17,
Tsig= 24,
Tkey= 25,
Taaaa= 28,
Tcert= 37,
/* query types (all RR types are also queries) */
Tixfr= 251, /* incremental zone transfer */
Taxfr= 252, /* zone transfer */
Tmailb= 253, /* { Tmb, Tmg, Tmr } */
Tall= 255, /* all records */
/* classes */
Csym= 0, /* internal symbols */
Cin= 1, /* internet */
Ccs, /* CSNET (obsolete) */
Cch, /* Chaos net */
Chs, /* Hesiod (?) */
/* class queries (all class types are also queries) */
Call= 255 /* all classes */
};
static int name2type(char*);
static uchar *skipquestion(uchar*, uchar*, uchar*, int);
static uchar *unpack(uchar*, uchar*, uchar*, Ndbtuple**, int);
static uchar *rrnext(uchar*, uchar*, uchar*, Ndbtuple**);
static Ndbtuple *rrunpack(uchar*, uchar*, uchar**, char*, ...);
static Ndbtuple*
doquery(char *name, char *type)
{
int n, nstype;
uchar *buf, *p;
Ndbtuple *t;
int qdcount, ancount;
if((nstype = name2type(type)) < 0){
werrstr("unknown dns type %s", type);
return nil;
}
buf = malloc(MAXDNS);
if(buf == nil)
return nil;
if((n = res_search(name, Cin, nstype, buf, MAXDNS)) < 0){
free(buf);
return nil;
}
if(n >= MAXDNS){
free(buf);
werrstr("too much dns information");
return nil;
}
qdcount = (buf[4]<<8)|buf[5];
ancount = (buf[6]<<8)|buf[7];
p = buf+12;
p = skipquestion(buf, buf+n, p, qdcount);
p = unpack(buf, buf+n, p, &t, ancount);
USED(p);
return t;
}
static struct {
char *s;
int t;
} dnsnames[] =
{
"ip", Ta,
"ns", Tns,
"md", Tmd,
"mf", Tmf,
"cname", Tcname,
"soa", Tsoa,
"mb", Tmb,
"mg", Tmg,
"mr", Tmr,
"null", Tnull,
"ptr", Tptr,
"hinfo", Thinfo,
"minfo", Tminfo,
"mx", Tmx,
"txt", Ttxt,
"rp", Trp,
"key", Tkey,
"cert", Tcert,
"sig", Tsig,
"aaaa", Taaaa,
"ixfr", Tixfr,
"axfr", Taxfr,
"all", Call,
};
static char*
type2name(int t)
{
int i;
for(i=0; i<nelem(dnsnames); i++)
if(dnsnames[i].t == t)
return dnsnames[i].s;
return nil;
}
static int
name2type(char *name)
{
int i;
for(i=0; i<nelem(dnsnames); i++)
if(strcmp(name, dnsnames[i].s) == 0)
return dnsnames[i].t;
return -1;
}
static uchar*
skipquestion(uchar *buf, uchar *ebuf, uchar *p, int n)
{
int i, len;
char tmp[100];
for(i=0; i<n; i++){
if((len = dn_expand(buf, ebuf, p, tmp, sizeof tmp)) <= 0)
return nil;
p += 4+len;
}
return p;
}
static uchar*
unpack(uchar *buf, uchar *ebuf, uchar *p, Ndbtuple **tt, int n)
{
int i;
Ndbtuple *first, *last, *t;
*tt = nil;
first = nil;
last = nil;
for(i=0; i<n; i++){
if((p = rrnext(buf, ebuf, p, &t)) == nil){
if(first)
ndbfree(first);
return nil;
}
if(t == nil) /* unimplemented rr type */
continue;
if(last)
last->entry = t;
else
first = t;
for(last=t; last->entry; last=last->entry)
last->line = last->entry;
last->line = t;
}
*tt = first;
return p;
}
#define G2(p) nhgets(p)
#define G4(p) nhgetl(p)
static uchar*
rrnext(uchar *buf, uchar *ebuf, uchar *p, Ndbtuple **tt)
{
char tmp[Ndbvlen];
char b[MAXRR];
uchar ip[IPaddrlen];
int len;
Ndbtuple *first, *t;
int rrtype;
int rrlen;
first = nil;
t = nil;
*tt = nil;
if(p == nil)
return nil;
if((len = dn_expand(buf, ebuf, p, b, sizeof b)) < 0){
corrupt:
werrstr("corrupt dns packet");
if(first)
ndbfree(first);
return nil;
}
p += len;
rrtype = G2(p);
rrlen = G2(p+8);
p += 10;
if(rrtype == Tptr)
first = ndbnew("ptr", b);
else
first = ndbnew("dom", b);
switch(rrtype){
default:
goto end;
case Thinfo:
t = rrunpack(buf, ebuf, &p, "YY", "cpu", "os");
break;
case Tminfo:
t = rrunpack(buf, ebuf, &p, "NN", "mbox", "mbox");
break;
case Tmx:
t = rrunpack(buf, ebuf, &p, "SN", "pref", "mx");
break;
case Tcname:
case Tmd:
case Tmf:
case Tmg:
case Tmr:
case Tmb:
case Tns:
case Tptr:
case Trp:
t = rrunpack(buf, ebuf, &p, "N", type2name(rrtype));
break;
case Ta:
if(rrlen != IPv4addrlen)
goto corrupt;
memmove(ip, v4prefix, IPaddrlen);
memmove(ip+IPv4off, p, IPv4addrlen);
snprint(tmp, sizeof tmp, "%I", ip);
t = ndbnew("ip", tmp);
p += rrlen;
break;
case Taaaa:
if(rrlen != IPaddrlen)
goto corrupt;
snprint(tmp, sizeof tmp, "%I", ip);
t = ndbnew("ip", tmp);
p += rrlen;
break;
case Tnull:
snprint(tmp, sizeof tmp, "%.*H", rrlen, p);
t = ndbnew("null", tmp);
p += rrlen;
break;
case Ttxt:
t = rrunpack(buf, ebuf, &p, "Y", "txt");
break;
case Tsoa:
t = rrunpack(buf, ebuf, &p, "NNLLLLL", "ns", "mbox",
"serial", "refresh", "retry", "expire", "ttl");
break;
case Tkey:
t = rrunpack(buf, ebuf, &p, "SCCY", "flags", "proto", "alg", "key");
break;
case Tsig:
t = rrunpack(buf, ebuf, &p, "SCCLLLSNY", "type", "alg", "labels",
"ttl", "exp", "incep", "tag", "signer", "sig");
break;
case Tcert:
t = rrunpack(buf, ebuf, &p, "SSCY", "type", "tag", "alg", "cert");
break;
}
if(t == nil)
goto corrupt;
end:
first->entry = t;
*tt = first;
return p;
}
static Ndbtuple*
rrunpack(uchar *buf, uchar *ebuf, uchar **pp, char *fmt, ...)
{
char *name;
int len, n;
uchar *p;
va_list arg;
Ndbtuple *t, *first, *last;
char tmp[Ndbvlen];
p = *pp;
va_start(arg, fmt);
first = nil;
last = nil;
for(; *fmt; fmt++){
name = va_arg(arg, char*);
switch(*fmt){
default:
return nil;
case 'C':
snprint(tmp, sizeof tmp, "%d", *p++);
break;
case 'S':
snprint(tmp, sizeof tmp, "%d", G2(p));
p += 2;
break;
case 'L':
snprint(tmp, sizeof tmp, "%d", G4(p));
p += 4;
break;
case 'N':
if((len = dn_expand(buf, ebuf, p, tmp, sizeof tmp)) < 0)
return nil;
p += len;
break;
case 'Y':
len = *p++;
n = len;
if(n >= sizeof tmp)
n = sizeof tmp-1;
memmove(tmp, p, n);
p += len;
tmp[n] = 0;
break;
}
t = ndbnew(name, tmp);
if(last)
last->entry = t;
else
first = t;
last = t;
}
*pp = p;
return first;
}