| #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" | 
 |  | 
 | 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; | 
 | } |