446 lines · 7.5 KB · C
RawDownload
#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 voidmkptrname(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 voidnstrcpy(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 intname2type(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;}