plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

sysdnsquery.c (7661B)


      1 #include <u.h>
      2 #include <netinet/in.h>
      3 #include <arpa/nameser.h>
      4 #include <resolv.h>
      5 #include <netdb.h>
      6 #include <libc.h>
      7 #include <ip.h>
      8 #include <bio.h>
      9 #include <ndb.h>
     10 #include "ndbhf.h"
     11 
     12 static void nstrcpy(char*, char*, int);
     13 static void mkptrname(char*, char*, int);
     14 static Ndbtuple *doquery(char*, char*);
     15 
     16 /*
     17  * Run a DNS lookup for val/type on net.
     18  */
     19 Ndbtuple*
     20 dnsquery(char *net, char *val, char *type)
     21 {
     22 	static int init;
     23 	char rip[128];
     24 	Ndbtuple *t;
     25 
     26 	USED(net);
     27 
     28 	if(!init){
     29 		init = 1;
     30 		fmtinstall('I', eipfmt);
     31 	}
     32 	/* give up early on stupid questions - vwhois */
     33 	if(strcmp(val, "::") == 0 || strcmp(val, "0.0.0.0") == 0)
     34 		return nil;
     35 
     36 	/* zero out the error string */
     37 	werrstr("");
     38 
     39 	/* if this is a reverse lookup, first look up the domain name */
     40 	if(strcmp(type, "ptr") == 0){
     41 		mkptrname(val, rip, sizeof rip);
     42 		t = doquery(rip, "ptr");
     43 	}else
     44 		t = doquery(val, type);
     45 
     46 	return t;
     47 }
     48 
     49 /*
     50  *  convert address into a reverse lookup address
     51  */
     52 static void
     53 mkptrname(char *ip, char *rip, int rlen)
     54 {
     55 	char buf[128];
     56 	char *p, *np;
     57 	int len;
     58 
     59 	if(strstr(ip, "in-addr.arpa") || strstr(ip, "IN-ADDR.ARPA")){
     60 		nstrcpy(rip, ip, rlen);
     61 		return;
     62 	}
     63 
     64 	nstrcpy(buf, ip, sizeof buf);
     65 	for(p = buf; *p; p++)
     66 		;
     67 	*p = '.';
     68 	np = rip;
     69 	len = 0;
     70 	while(p >= buf){
     71 		len++;
     72 		p--;
     73 		if(*p == '.'){
     74 			memmove(np, p+1, len);
     75 			np += len;
     76 			len = 0;
     77 		}
     78 	}
     79 	memmove(np, p+1, len);
     80 	np += len;
     81 	strcpy(np, "in-addr.arpa");
     82 }
     83 
     84 static void
     85 nstrcpy(char *to, char *from, int len)
     86 {
     87 	strncpy(to, from, len-1);
     88 	to[len-1] = 0;
     89 }
     90 
     91 /*
     92  * Disgusting, ugly interface to libresolv,
     93  * which everyone seems to have.
     94  */
     95 enum
     96 {
     97 	MAXRR = 100,
     98 	MAXDNS = 4096,
     99 
    100 	/* RR types */
    101 	Ta=	1,
    102 	Tns=	2,
    103 	Tmd=	3,
    104 	Tmf=	4,
    105 	Tcname=	5,
    106 	Tsoa=	6,
    107 	Tmb=	7,
    108 	Tmg=	8,
    109 	Tmr=	9,
    110 	Tnull=	10,
    111 	Twks=	11,
    112 	Tptr=	12,
    113 	Thinfo=	13,
    114 	Tminfo=	14,
    115 	Tmx=	15,
    116 	Ttxt=	16,
    117 	Trp=	17,
    118 	Tsig=	24,
    119 	Tkey=	25,
    120 	Taaaa=	28,
    121 	Tcert=	37,
    122 
    123 	/* query types (all RR types are also queries) */
    124 	Tixfr=	251,	/* incremental zone transfer */
    125 	Taxfr=	252,	/* zone transfer */
    126 	Tmailb=	253,	/* { Tmb, Tmg, Tmr } */
    127 	Tall=	255,	/* all records */
    128 
    129 	/* classes */
    130 	Csym=	0,	/* internal symbols */
    131 	Cin=	1,	/* internet */
    132 	Ccs,		/* CSNET (obsolete) */
    133 	Cch,		/* Chaos net */
    134 	Chs,		/* Hesiod (?) */
    135 
    136 	/* class queries (all class types are also queries) */
    137 	Call=	255	/* all classes */
    138 };
    139 
    140 
    141 static int name2type(char*);
    142 static uchar *skipquestion(uchar*, uchar*, uchar*, int);
    143 static uchar *unpack(uchar*, uchar*, uchar*, Ndbtuple**, int);
    144 static uchar *rrnext(uchar*, uchar*, uchar*, Ndbtuple**);
    145 static Ndbtuple *rrunpack(uchar*, uchar*, uchar**, char*, ...);
    146 
    147 static Ndbtuple*
    148 doquery(char *name, char *type)
    149 {
    150 	int n, nstype;
    151 	uchar *buf, *p;
    152 	Ndbtuple *t;
    153 	int qdcount, ancount;
    154 
    155 	if((nstype = name2type(type)) < 0){
    156 		werrstr("unknown dns type %s", type);
    157 		return nil;
    158 	}
    159 
    160 	buf = malloc(MAXDNS);
    161 	if(buf == nil)
    162 		return nil;
    163 
    164 	if((n = res_search(name, Cin, nstype, buf, MAXDNS)) < 0){
    165 		free(buf);
    166 		return nil;
    167 	}
    168 	if(n >= MAXDNS){
    169 		free(buf);
    170 		werrstr("too much dns information");
    171 		return nil;
    172 	}
    173 
    174 	qdcount = (buf[4]<<8)|buf[5];
    175 	ancount = (buf[6]<<8)|buf[7];
    176 
    177 	p = buf+12;
    178 	p = skipquestion(buf, buf+n, p, qdcount);
    179 	p = unpack(buf, buf+n, p, &t, ancount);
    180 	USED(p);
    181 	return t;
    182 }
    183 
    184 static struct {
    185 	char *s;
    186 	int t;
    187 } dnsnames[] =
    188 {
    189 	"ip",		Ta,
    190 	"ns",	Tns,
    191 	"md",	Tmd,
    192 	"mf",	Tmf,
    193 	"cname",	Tcname,
    194 	"soa",	Tsoa,
    195 	"mb",	Tmb,
    196 	"mg",	Tmg,
    197 	"mr",	Tmr,
    198 	"null",	Tnull,
    199 	"ptr",	Tptr,
    200 	"hinfo",	Thinfo,
    201 	"minfo",	Tminfo,
    202 	"mx",	Tmx,
    203 	"txt",	Ttxt,
    204 	"rp",	Trp,
    205 	"key",	Tkey,
    206 	"cert",	Tcert,
    207 	"sig",	Tsig,
    208 	"aaaa",	Taaaa,
    209 	"ixfr",	Tixfr,
    210 	"axfr",	Taxfr,
    211 	"all",	Call,
    212 };
    213 
    214 static char*
    215 type2name(int t)
    216 {
    217 	int i;
    218 
    219 	for(i=0; i<nelem(dnsnames); i++)
    220 		if(dnsnames[i].t == t)
    221 			return dnsnames[i].s;
    222 	return nil;
    223 }
    224 
    225 static int
    226 name2type(char *name)
    227 {
    228 	int i;
    229 
    230 	for(i=0; i<nelem(dnsnames); i++)
    231 		if(strcmp(name, dnsnames[i].s) == 0)
    232 			return dnsnames[i].t;
    233 	return -1;
    234 }
    235 
    236 static uchar*
    237 skipquestion(uchar *buf, uchar *ebuf, uchar *p, int n)
    238 {
    239 	int i, len;
    240 	char tmp[100];
    241 
    242 	for(i=0; i<n; i++){
    243 		if((len = dn_expand(buf, ebuf, p, tmp, sizeof tmp)) <= 0)
    244 			return nil;
    245 		p += 4+len;
    246 	}
    247 	return p;
    248 }
    249 
    250 static uchar*
    251 unpack(uchar *buf, uchar *ebuf, uchar *p, Ndbtuple **tt, int n)
    252 {
    253 	int i;
    254 	Ndbtuple *first, *last, *t;
    255 
    256 	*tt = nil;
    257 	first = nil;
    258 	last = nil;
    259 	for(i=0; i<n; i++){
    260 		if((p = rrnext(buf, ebuf, p, &t)) == nil){
    261 			if(first)
    262 				ndbfree(first);
    263 			return nil;
    264 		}
    265 		if(t == nil)	/* unimplemented rr type */
    266 			continue;
    267 		if(last)
    268 			last->entry = t;
    269 		else
    270 			first = t;
    271 		for(last=t; last->entry; last=last->entry)
    272 			last->line = last->entry;
    273 		last->line = t;
    274 	}
    275 	*tt = first;
    276 	return p;
    277 }
    278 
    279 #define G2(p) nhgets(p)
    280 #define G4(p) nhgetl(p)
    281 
    282 static uchar*
    283 rrnext(uchar *buf, uchar *ebuf, uchar *p, Ndbtuple **tt)
    284 {
    285 	char tmp[Ndbvlen];
    286 	char b[MAXRR];
    287 	uchar ip[IPaddrlen];
    288 	int len;
    289 	Ndbtuple *first, *t;
    290 	int rrtype;
    291 	int rrlen;
    292 
    293 	first = nil;
    294 	t = nil;
    295 	*tt = nil;
    296 	if(p == nil)
    297 		return nil;
    298 
    299 	if((len = dn_expand(buf, ebuf, p, b, sizeof b)) < 0){
    300 	corrupt:
    301 		werrstr("corrupt dns packet");
    302 		if(first)
    303 			ndbfree(first);
    304 		return nil;
    305 	}
    306 	p += len;
    307 
    308 	rrtype = G2(p);
    309 	rrlen = G2(p+8);
    310 	p += 10;
    311 
    312 	if(rrtype == Tptr)
    313 		first = ndbnew("ptr", b);
    314 	else
    315 		first = ndbnew("dom", b);
    316 
    317 	switch(rrtype){
    318 	default:
    319 		goto end;
    320 	case Thinfo:
    321 		t = rrunpack(buf, ebuf, &p, "YY", "cpu", "os");
    322 		break;
    323 	case Tminfo:
    324 		t = rrunpack(buf, ebuf, &p, "NN", "mbox", "mbox");
    325 		break;
    326 	case Tmx:
    327 		t = rrunpack(buf, ebuf, &p, "SN", "pref", "mx");
    328 		break;
    329 	case Tcname:
    330 	case Tmd:
    331 	case Tmf:
    332 	case Tmg:
    333 	case Tmr:
    334 	case Tmb:
    335 	case Tns:
    336 	case Tptr:
    337 	case Trp:
    338 		t = rrunpack(buf, ebuf, &p, "N", type2name(rrtype));
    339 		break;
    340 	case Ta:
    341 		if(rrlen != IPv4addrlen)
    342 			goto corrupt;
    343 		memmove(ip, v4prefix, IPaddrlen);
    344 		memmove(ip+IPv4off, p, IPv4addrlen);
    345 		snprint(tmp, sizeof tmp, "%I", ip);
    346 		t = ndbnew("ip", tmp);
    347 		p += rrlen;
    348 		break;
    349 	case Taaaa:
    350 		if(rrlen != IPaddrlen)
    351 			goto corrupt;
    352 		snprint(tmp, sizeof tmp, "%I", ip);
    353 		t = ndbnew("ip", tmp);
    354 		p += rrlen;
    355 		break;
    356 	case Tnull:
    357 		snprint(tmp, sizeof tmp, "%.*H", rrlen, p);
    358 		t = ndbnew("null", tmp);
    359 		p += rrlen;
    360 		break;
    361 	case Ttxt:
    362 		t = rrunpack(buf, ebuf, &p, "Y", "txt");
    363 		break;
    364 
    365 	case Tsoa:
    366 		t = rrunpack(buf, ebuf, &p, "NNLLLLL", "ns", "mbox",
    367 			"serial", "refresh", "retry", "expire", "ttl");
    368 		break;
    369 
    370 	case Tkey:
    371 		t = rrunpack(buf, ebuf, &p, "SCCY", "flags", "proto", "alg", "key");
    372 		break;
    373 
    374 	case Tsig:
    375 		t = rrunpack(buf, ebuf, &p, "SCCLLLSNY", "type", "alg", "labels",
    376 			"ttl", "exp", "incep", "tag", "signer", "sig");
    377 		break;
    378 
    379 	case Tcert:
    380 		t = rrunpack(buf, ebuf, &p, "SSCY", "type", "tag", "alg", "cert");
    381 		break;
    382 	}
    383 	if(t == nil)
    384 		goto corrupt;
    385 
    386 end:
    387 	first->entry = t;
    388 	*tt = first;
    389 	return p;
    390 }
    391 
    392 static Ndbtuple*
    393 rrunpack(uchar *buf, uchar *ebuf, uchar **pp, char *fmt, ...)
    394 {
    395 	char *name;
    396 	int len, n;
    397 	uchar *p;
    398 	va_list arg;
    399 	Ndbtuple *t, *first, *last;
    400 	char tmp[Ndbvlen];
    401 
    402 	p = *pp;
    403 	va_start(arg, fmt);
    404 	first = nil;
    405 	last = nil;
    406 	for(; *fmt; fmt++){
    407 		name = va_arg(arg, char*);
    408 		switch(*fmt){
    409 		default:
    410 			return nil;
    411 		case 'C':
    412 			snprint(tmp, sizeof tmp, "%d", *p++);
    413 			break;
    414 		case 'S':
    415 			snprint(tmp, sizeof tmp, "%d", G2(p));
    416 			p += 2;
    417 			break;
    418 		case 'L':
    419 			snprint(tmp, sizeof tmp, "%d", G4(p));
    420 			p += 4;
    421 			break;
    422 		case 'N':
    423 			if((len = dn_expand(buf, ebuf, p, tmp, sizeof tmp)) < 0)
    424 				return nil;
    425 			p += len;
    426 			break;
    427 		case 'Y':
    428 			len = *p++;
    429 			n = len;
    430 			if(n >= sizeof tmp)
    431 				n = sizeof tmp-1;
    432 			memmove(tmp, p, n);
    433 			p += len;
    434 			tmp[n] = 0;
    435 			break;
    436 		}
    437 		t = ndbnew(name, tmp);
    438 		if(last)
    439 			last->entry = t;
    440 		else
    441 			first = t;
    442 		last = t;
    443 	}
    444 	*pp = p;
    445 	return first;
    446 }