1258 lines · 25.3 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <bio.h>#include <ndb.h>#include <ip.h>#include "dns.h" enum {	Nibwidth = 4,	Nibmask = (1<<Nibwidth) - 1,	V6maxrevdomdepth = 128 / Nibwidth,	/* bits / bits-per-nibble */ 	/*	 * ttl for generated ptr records.  it was zero, which might seem	 * like a good idea, but some dns implementations seem to be	 * confused by a zero ttl, and instead of using the data and then	 * discarding the RR, they conclude that they don't have valid data.	 */	Ptrttl = 120,}; static Ndb *db;static Lock	dblock; static RR*	addrrr(Ndbtuple*, Ndbtuple*);static RR*	cnamerr(Ndbtuple*, Ndbtuple*);static void	createptrs(void);static RR*	dblookup1(char*, int, int, int);static RR*	doaxfr(Ndb*, char*);static Ndbtuple*look(Ndbtuple*, Ndbtuple*, char*);static RR*	mxrr(Ndbtuple*, Ndbtuple*);static RR*	nsrr(Ndbtuple*, Ndbtuple*);static RR*	nullrr(Ndbtuple*, Ndbtuple*);static RR*	ptrrr(Ndbtuple*, Ndbtuple*);static RR*	soarr(Ndbtuple*, Ndbtuple*);static RR*	srvrr(Ndbtuple*, Ndbtuple*);static RR*	txtrr(Ndbtuple*, Ndbtuple*); static int	implemented[Tall] ={	[Ta]		1,	[Taaaa]		1,	[Tcname]	1,	[Tmx]		1,	[Tns]		1,	[Tnull]		1,	[Tptr]		1,	[Tsoa]		1,	[Tsrv]		1,	[Ttxt]		1,}; /* straddle server configuration */static Ndbtuple *indoms, *innmsrvs, *outnmsrvs; static voidnstrcpy(char *to, char *from, int len){	strncpy(to, from, len);	to[len-1] = 0;} intopendatabase(void){	char netdbnm[256];	Ndb *xdb, *netdb; 	if (db)		return 0; 	xdb = ndbopen(dbfile);		/* /lib/ndb */ 	snprint(netdbnm, sizeof netdbnm, "%s/ndb", mntpt);	netdb = ndbopen(netdbnm);	/* /net/ndb */	if(netdb)		netdb->nohash = 1; 	db = ndbcat(netdb, xdb);	/* both */	return db? 0: -1;} /* *  lookup an RR in the network database, look for matches *  against both the domain name and the wildcarded domain name. * *  the lock makes sure only one process can be accessing the data *  base at a time.  This is important since there's a lot of *  shared state there. * *  e.g. for x.research.bell-labs.com, first look for a match against *       the x.research.bell-labs.com.  If nothing matches, *	 try *.research.bell-labs.com. */RR*dblookup(char *name, int class, int type, int auth, int ttl){	int err;	char *wild, *cp;	char buf[256];	RR *rp, *tp;	DN *dp, *ndp; 	/* so far only internet lookups are implemented */	if(class != Cin)		return 0; 	err = Rname;	rp = nil; 	if(type == Tall){		for (type = Ta; type < Tall; type++)			if(implemented[type]) {				tp = dblookup(name, class, type, auth, ttl);				lock(&dnlock);				rrcat(&rp, tp);				unlock(&dnlock);			}		return rp;	} 	lock(&dblock);	dp = dnlookup(name, class, 1); 	if(opendatabase() < 0)		goto out;	if(dp->rr)		err = 0; 	/* first try the given name */	if(cfg.cachedb)		rp = rrlookup(dp, type, NOneg);	else		rp = dblookup1(name, type, auth, ttl);	if(rp)		goto out; 	/* try lower case version */	for(cp = name; *cp; cp++)		*cp = tolower(*cp);	if(cfg.cachedb)		rp = rrlookup(dp, type, NOneg);	else		rp = dblookup1(name, type, auth, ttl);	if(rp)		goto out; 	/* walk the domain name trying the wildcard '*' at each position */	for(wild = strchr(name, '.'); wild; wild = strchr(wild+1, '.')){		snprint(buf, sizeof buf, "*%s", wild);		ndp = dnlookup(buf, class, 1);		if(ndp->rr)			err = 0;		if(cfg.cachedb)			rp = rrlookup(ndp, type, NOneg);		else			rp = dblookup1(buf, type, auth, ttl);		if(rp)			break;	}out:	/* add owner to uncached records */	if(rp)		for(tp = rp; tp; tp = tp->next)			tp->owner = dp;	else {		/*		 * don't call it non-existent if it's not ours		 * (unless we're a resolver).		 */		if(err == Rname && (!inmyarea(name) || cfg.resolver))			err = Rserver;		dp->respcode = err;	} 	unlock(&dblock);	return rp;} static ulongintval(Ndbtuple *entry, Ndbtuple *pair, char *attr, ulong def){	Ndbtuple *t = look(entry, pair, attr); 	return (t? strtoul(t->val, 0, 10): def);} /* *  lookup an RR in the network database */static RR*dblookup1(char *name, int type, int auth, int ttl){	Ndbtuple *t, *nt;	RR *rp, *list, **l;	Ndbs s;	char dname[Domlen];	char *attr;	DN *dp;	RR *(*f)(Ndbtuple*, Ndbtuple*);	int found, x; 	dp = nil;	switch(type){	case Tptr:		attr = "ptr";		f = ptrrr;		break;	case Ta:		attr = "ip";		f = addrrr;		break;	case Taaaa:		attr = "ipv6";		f = addrrr;		break;	case Tnull:		attr = "nullrr";		f = nullrr;		break;	case Tns:		attr = "ns";		f = nsrr;		break;	case Tsoa:		attr = "soa";		f = soarr;		break;	case Tsrv:		attr = "srv";		f = srvrr;		break;	case Tmx:		attr = "mx";		f = mxrr;		break;	case Tcname:		attr = "cname";		f = cnamerr;		break;	case Taxfr:	case Tixfr:		return doaxfr(db, name);	default://		dnslog("dnlookup1(%s) bad type", name);		return nil;	} 	/*	 *  find a matching entry in the database	 */	t = nil;	free(ndbgetvalue(db, &s, "dom", name, attr, &t)); 	/*	 *  hack for local names	 */	if(t == nil && strchr(name, '.') == nil)		free(ndbgetvalue(db, &s, "sys", name, attr, &t));	if(t == nil) {//		dnslog("dnlookup1(%s) name not found", name);		return nil;	} 	/* search whole entry for default domain name */	strncpy(dname, name, sizeof dname);	for(nt = t; nt; nt = nt->entry)		if(strcmp(nt->attr, "dom") == 0){			nstrcpy(dname, nt->val, sizeof dname);			break;		} 	/* ttl is maximum of soa minttl and entry's ttl ala rfc883 */	x = intval(t, s.t, "ttl", 0);	if(x > ttl)		ttl = x; 	/* default ttl is one day */	if(ttl < 0)		ttl = DEFTTL; 	/*	 *  The database has 2 levels of precedence; line and entry.	 *  Pairs on the same line bind tighter than pairs in the	 *  same entry, so we search the line first.	 */	found = 0;	list = 0;	l = &list;	for(nt = s.t;; ){		if(found == 0 && strcmp(nt->attr, "dom") == 0){			nstrcpy(dname, nt->val, sizeof dname);			found = 1;		}		if(cistrcmp(attr, nt->attr) == 0){			rp = (*f)(t, nt);			rp->auth = auth;			rp->db = 1;			if(ttl)				rp->ttl = ttl;			if(dp == nil)				dp = dnlookup(dname, Cin, 1);			rp->owner = dp;			*l = rp;			l = &rp->next;			nt->ptr = 1;		}		nt = nt->line;		if(nt == s.t)			break;	} 	/* search whole entry */	for(nt = t; nt; nt = nt->entry)		if(nt->ptr == 0 && cistrcmp(attr, nt->attr) == 0){			rp = (*f)(t, nt);			rp->db = 1;			if(ttl)				rp->ttl = ttl;			rp->auth = auth;			if(dp == nil)				dp = dnlookup(dname, Cin, 1);			rp->owner = dp;			*l = rp;			l = &rp->next;		}	ndbfree(t); //	dnslog("dnlookup1(%s) -> %#p", name, list);	return list;} /* *  make various types of resource records from a database entry */static RR*addrrr(Ndbtuple *entry, Ndbtuple *pair){	RR *rp;	uchar addr[IPaddrlen]; 	USED(entry);	parseip(addr, pair->val);	if(isv4(addr))		rp = rralloc(Ta);	else		rp = rralloc(Taaaa);	rp->ip = dnlookup(pair->val, Cin, 1);	return rp;}static RR*nullrr(Ndbtuple *entry, Ndbtuple *pair){	RR *rp; 	USED(entry);	rp = rralloc(Tnull);	rp->null->data = (uchar*)estrdup(pair->val);	rp->null->dlen = strlen((char*)rp->null->data);	return rp;}/* *  txt rr strings are at most 255 bytes long.  one *  can represent longer strings by multiple concatenated *  <= 255 byte ones. */static RR*txtrr(Ndbtuple *entry, Ndbtuple *pair){	RR *rp;	Txt *t, **l;	int i, len, sofar; 	USED(entry);	rp = rralloc(Ttxt);	l = &rp->txt;	rp->txt = nil;	len = strlen(pair->val);	sofar = 0;	while(len > sofar){		t = emalloc(sizeof(*t));		t->next = nil; 		i = len-sofar;		if(i > 255)			i = 255; 		t->p = emalloc(i+1);		memmove(t->p, pair->val+sofar, i);		t->p[i] = 0;		sofar += i; 		*l = t;		l = &t->next;	}	return rp;}static RR*cnamerr(Ndbtuple *entry, Ndbtuple *pair){	RR *rp; 	USED(entry);	rp = rralloc(Tcname);	rp->host = dnlookup(pair->val, Cin, 1);	return rp;}static RR*mxrr(Ndbtuple *entry, Ndbtuple *pair){	RR *rp; 	rp = rralloc(Tmx);	rp->host = dnlookup(pair->val, Cin, 1);	rp->pref = intval(entry, pair, "pref", 1);	return rp;}static RR*nsrr(Ndbtuple *entry, Ndbtuple *pair){	RR *rp;	Ndbtuple *t; 	rp = rralloc(Tns);	rp->host = dnlookup(pair->val, Cin, 1);	t = look(entry, pair, "soa");	if(t && t->val[0] == 0)		rp->local = 1;	return rp;}static RR*ptrrr(Ndbtuple *entry, Ndbtuple *pair){	RR *rp; 	USED(entry);	rp = rralloc(Tns);	rp->ptr = dnlookup(pair->val, Cin, 1);	return rp;}static RR*soarr(Ndbtuple *entry, Ndbtuple *pair){	RR *rp;	Ndbtuple *ns, *mb, *t;	char mailbox[Domlen];	Ndb *ndb;	char *p; 	rp = rralloc(Tsoa);	rp->soa->serial = 1;	for(ndb = db; ndb; ndb = ndb->next)		if(ndb->mtime > rp->soa->serial)			rp->soa->serial = ndb->mtime; 	rp->soa->retry  = intval(entry, pair, "retry", Hour);	rp->soa->expire = intval(entry, pair, "expire", Day);	rp->soa->minttl = intval(entry, pair, "ttl", Day);	rp->soa->refresh = intval(entry, pair, "refresh", Day);	rp->soa->serial = intval(entry, pair, "serial", rp->soa->serial); 	ns = look(entry, pair, "ns");	if(ns == nil)		ns = look(entry, pair, "dom");	rp->host = dnlookup(ns->val, Cin, 1); 	/* accept all of:	 *  mbox=person	 *  mbox=person@machine.dom	 *  mbox=person.machine.dom	 */	mb = look(entry, pair, "mbox");	if(mb == nil)		mb = look(entry, pair, "mb");	if(mb)		if(strchr(mb->val, '.')) {			p = strchr(mb->val, '@');			if(p != nil)				*p = '.';			rp->rmb = dnlookup(mb->val, Cin, 1);		} else {			snprint(mailbox, sizeof mailbox, "%s.%s",				mb->val, ns->val);			rp->rmb = dnlookup(mailbox, Cin, 1);		}	else {		snprint(mailbox, sizeof mailbox, "postmaster.%s", ns->val);		rp->rmb = dnlookup(mailbox, Cin, 1);	} 	/*

Lines 1–500 of 1258.