⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dnresolve.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include <u.h>#include <libc.h>#include "dns.h"#include "ip.h"enum{	Maxdest=	24,	/* maximum destinations for a request message */	Maxtrans=	3,	/* maximum transmissions to a server */};static int	netquery(DN*, int, RR*, Request*, int);static RR*	dnresolve1(char*, int, int, Request*, int, int);char *LOG = "dns";/* *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try *  looking it up as a canonical name. */RR*dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth, int recurse, int rooted, int *status){	RR *rp, *nrp, *drp;	DN *dp;	int loops;	char nname[Domlen];	if(status)		*status = 0;	/*	 *  hack for systems that don't have resolve search	 *  lists.  Just look up the simple name in the database.	 */	if(!rooted && strchr(name, '.') == 0){		rp = nil;		drp = domainlist(class);		for(nrp = drp; nrp != nil; nrp = nrp->next){			snprint(nname, sizeof(nname), "%s.%s", name, nrp->ptr->name);			rp = dnresolve(nname, class, type, req,cn, depth, recurse, rooted, status);			rrfreelist(rrremneg(&rp));			if(rp != nil)				break;		}		if(drp != nil)			rrfree(drp);		return rp;	}	/*	 *  try the name directly	 */	rp = dnresolve1(name, class, type, req, depth, recurse);	if(rp)		return randomize(rp);	/* try it as a canonical name if we weren't told the name didn't exist */	dp = dnlookup(name, class, 0);	if(type != Tptr && dp->nonexistent != Rname){		for(loops=0; rp == nil && loops < 32; loops++){			rp = dnresolve1(name, class, Tcname, req, depth, recurse);			if(rp == nil)				break;			if(rp->negative){				rrfreelist(rp);				rp = nil;				break;			}				name = rp->host->name;			if(cn)				rrcat(cn, rp);			else				rrfreelist(rp);				rp = dnresolve1(name, class, type, req, depth, recurse);		}	}	/* distinction between not found and not good */	if(rp == 0 && status != 0 && dp->nonexistent != 0)		*status = dp->nonexistent;	return randomize(rp);}static RR*dnresolve1(char *name, int class, int type, Request *req, int depth, int recurse){	DN *dp, *nsdp;	RR *rp, *nsrp, *dbnsrp;	char *cp;	if(debug)		syslog(0, LOG, "[%d] dnresolve1 %s %d %d", getpid(), name, type, class);	/* only class Cin implemented so far */	if(class != Cin)		return 0;	dp = dnlookup(name, class, 1);	/*	 *  Try the cache first	 */	rp = rrlookup(dp, type, OKneg);	if(rp){		if(rp->db){			/* unauthenticated db entries are hints */			if(rp->auth)				return rp;		} else {			/* cached entry must still be valid */			if(rp->ttl > now){				/* but Tall entries are special */				if(type != Tall || rp->query == Tall)					return rp;			}		}	}	rrfreelist(rp);	/*	 * try the cache for a canonical name. if found punt 	 * since we'll find it during the canonical name search	 * in dnresolve().	 */	if(type != Tcname){		rp = rrlookup(dp, Tcname, NOneg);		rrfreelist(rp);		if(rp)			return 0;	}	/*	 *  if we're running as just a resolver, go to our	 *  designated name servers	 */	if(resolver){		nsrp = randomize(getdnsservers(class));		if(nsrp != nil) {			if(netquery(dp, type, nsrp, req, depth+1)){				rrfreelist(nsrp);				return rrlookup(dp, type, OKneg);			}			rrfreelist(nsrp);		}	}	/* 	 *  walk up the domain name looking for	 *  a name server for the domain.	 */	for(cp = name; cp; cp = walkup(cp)){		/*		 *  if this is a local (served by us) domain,		 *  return answer		 */		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));		if(dbnsrp && dbnsrp->local){			rp = dblookup(name, class, type, 1, dbnsrp->ttl);			rrfreelist(dbnsrp);			return rp;		}		/*		 *  if recursion isn't set, just accept local		 *  entries		 */		if(recurse == Dontrecurse){			if(dbnsrp)				rrfreelist(dbnsrp);			continue;		}		/* look for ns in cache */		nsdp = dnlookup(cp, class, 0);		nsrp = nil;		if(nsdp)			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));		/* if the entry timed out, ignore it */		if(nsrp && nsrp->ttl < now){			rrfreelist(nsrp);			nsrp = nil;		}		if(nsrp){			rrfreelist(dbnsrp);			/* try the name servers found in cache */			if(netquery(dp, type, nsrp, req, depth+1)){				rrfreelist(nsrp);				return rrlookup(dp, type, OKneg);			}			rrfreelist(nsrp);			continue;		}		/* use ns from db */		if(dbnsrp){			/* try the name servers found in db */			if(netquery(dp, type, dbnsrp, req, depth+1)){				/* we got an answer */				rrfreelist(dbnsrp);				return rrlookup(dp, type, NOneg);			}			rrfreelist(dbnsrp);		}	}	/* settle for a non-authoritative answer */	rp = rrlookup(dp, type, OKneg);	if(rp)		return rp;	/* noone answered.  try the database, we might have a chance. */	return dblookup(name, class, type, 0, 0);}/* *  walk a domain name one element to the right.  return a pointer to that element. *  in other words, return a pointer to the parent domain name. */char*walkup(char *name){	char *cp;	cp = strchr(name, '.');	if(cp)		return cp+1;	else if(*name)		return "";	else		return 0;}/* *  Get a udpport for requests and replies.  Put the port *  into "headers" mode. */static char *hmsg = "headers";static char *ohmsg = "oldheaders";intudpport(void){	int fd, ctl;	char ds[64];	char adir[64];	/* get a udp port */	snprint(ds, sizeof(ds), "%s/udp!*!0", mntpt);	ctl = announce(ds, adir);	if(ctl < 0){		/* warning("can't get udp port"); */		return -1;	}	/* turn on header style interface */	if(write(ctl, hmsg, strlen(hmsg)) , 0){		close(ctl);		warning(hmsg);		return -1;	}	write(ctl, ohmsg, strlen(ohmsg));	/* grab the data file */	snprint(ds, sizeof(ds), "%s/data", adir);	fd = open(ds, ORDWR);	close(ctl);	if(fd < 0){		warning("can't open udp port: %r");		return -1;	}	return fd;}intmkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno){	DNSmsg m;	int len;	OUdphdr *uh = (OUdphdr*)buf;	/* stuff port number into output buffer */	memset(uh, 0, sizeof(*uh));	hnputs(uh->rport, 53);	/* make request and convert it to output format */	memset(&m, 0, sizeof(m));	m.flags = flags;	m.id = reqno;	m.qd = rralloc(type);	m.qd->owner = dp;	m.qd->type = type;	len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);	if(len < 0)		abort(); /* "can't convert" */;	rrfree(m.qd);	return len;}/* for alarms in readreply */static voidding(void *x, char *msg){	USED(x);	if(strcmp(msg, "alarm") == 0)		noted(NCONT);	else		noted(NDFLT);}static voidfreeanswers(DNSmsg *mp){	rrfreelist(mp->qd);	rrfreelist(mp->an);	rrfreelist(mp->ns);	rrfreelist(mp->ar);}/* *  read replies to a request.  ignore any of the wrong type.  wait at most 5 seconds. */static intreadreply(int fd, DN *dp, int type, ushort req,	  uchar *ibuf, DNSmsg *mp, ulong endtime, Request *reqp){	char *err;	int len;	ulong now;	RR *rp;	notify(ding);	for(; ; freeanswers(mp)){		now = time(0);		if(now >= endtime)			return -1;	/* timed out */		/* timed read */		alarm((endtime - now) * 1000);		len = read(fd, ibuf, OUdphdrsize+Maxudpin);		alarm(0);		len -= OUdphdrsize;		if(len < 0)			return -1;	/* timed out */				/* convert into internal format  */		memset(mp, 0, sizeof(*mp));		err = convM2DNS(&ibuf[OUdphdrsize], len, mp);		if(err){			syslog(0, LOG, "input err %s: %I", err, ibuf);			continue;		}		if(debug)			logreply(reqp->id, ibuf, mp);		/* answering the right question? */		if(mp->id != req){			syslog(0, LOG, "%d: id %d instead of %d: %I", reqp->id,					mp->id, req, ibuf);			continue;		}		if(mp->qd == 0){			syslog(0, LOG, "%d: no question RR: %I", reqp->id, ibuf);			continue;		}		if(mp->qd->owner != dp){			syslog(0, LOG, "%d: owner %s instead of %s: %I", reqp->id,				mp->qd->owner->name, dp->name, ibuf);			continue;		}		if(mp->qd->type != type){			syslog(0, LOG, "%d: type %d instead of %d: %I", reqp->id,				mp->qd->type, type, ibuf);			continue;		}		/* remember what request this is in answer to */		for(rp = mp->an; rp; rp = rp->next)			rp->query = type;		return 0;	}}/* *	return non-0 if first list includes second list */intcontains(RR *rp1, RR *rp2){	RR *trp1, *trp2;	for(trp2 = rp2; trp2; trp2 = trp2->next){		for(trp1 = rp1; trp1; trp1 = trp1->next){			if(trp1->type == trp2->type)			if(trp1->host == trp2->host)			if(trp1->owner == trp2->owner)				break;		}		if(trp1 == 0)			return 0;	}	return 1;}typedef struct Dest	Dest;struct Dest{	uchar	a[IPaddrlen];	/* ip address */	DN	*s;		/* name server */	int	nx;		/* number of transmissions */	int	code;};/* *  return multicast version if any */intipisbm(uchar *ip){	if(isv4(ip)){		if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)			return 4;		if(ipcmp(ip, IPv4bcast) == 0)			return 4;	} else {		if(ip[0] == 0xff)			return 6;	}	return 0;}/* *  Get next server address */static intserveraddrs(RR *nsrp, Dest *dest, int nd, int depth, Request *reqp){	RR *rp, *arp, *trp;	Dest *cur;	if(nd >= Maxdest)		return 0;	/*	 *  look for a server whose address we already know.	 *  if we find one, mark it so we ignore this on	 *  subsequent passes.	 */	arp = 0;	for(rp = nsrp; rp; rp = rp->next){		assert(rp->magic == RRmagic);		if(rp->marker)			continue;		arp = rrlookup(rp->host, Ta, NOneg);		if(arp){			rp->marker = 1;			break;		}		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);		if(arp){			rp->marker = 1;			break;		}	}	/*	 *  if the cache and database lookup didn't find any new	 *  server addresses, try resolving one via the network.	 *  Mark any we try to resolve so we don't try a second time.	 */	if(arp == 0){		for(rp = nsrp; rp; rp = rp->next){			if(rp->marker)				continue;			rp->marker = 1;			/*			 *  avoid loops looking up a server under itself			 */			if(subsume(rp->owner->name, rp->host->name))				continue;			arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0, depth+1, Recurse, 1, 0);			rrfreelist(rrremneg(&arp));			if(arp)				break;		}	}	/* use any addresses that we found */	for(trp = arp; trp; trp = trp->next){		if(nd >= Maxdest)			break;		cur = &dest[nd];		parseip(cur->a, trp->ip->name);		if(ipisbm(cur->a))			continue;		cur->nx = 0;		cur->s = trp->owner;		cur->code = Rtimeout;		nd++;	}	rrfreelist(arp);	return nd;}/* *  cache negative responses */static voidcacheneg(DN *dp, int type, int rcode, RR *soarr){	RR *rp;	DN *soaowner;	ulong ttl;	/* no cache time specified, don' make anything up */	if(soarr != nil){		if(soarr->next != nil){			rrfreelist(soarr->next);			soarr->next = nil;		}		soaowner = soarr->owner;	} else 		soaowner = nil;	/* the attach can cause soarr to be freed so mine it now */	if(soarr != nil && soarr->soa != nil)		ttl = soarr->soa->minttl+now;	else		ttl = 5*Min;	/* add soa and negative RR to the database */	rrattach(soarr, 1);	rp = rralloc(type);	rp->owner = dp;	rp->negative = 1;	rp->negsoaowner = soaowner;	rp->negrcode = rcode;	rp->ttl = ttl;	rrattach(rp, 1);}/* *  query name servers.  If the name server returns a pointer to another *  name server, recurse. */static intnetquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth, uchar *ibuf, uchar *obuf){	int ndest, j, len, replywaits, rv;	ushort req;	RR *tp, *soarr;	Dest *p, *l, *np;	DN *ndp;	Dest dest[Maxdest];	DNSmsg m;	ulong endtime;	/* pack request into a message */	req = rand();	len = mkreq(dp, type, obuf, Frecurse|Oquery, req);	/* no server addresses yet */	l = dest;	/*	 *  transmit requests and wait for answers.	 *  at most Maxtrans attempts to each address.	 *  each cycle send one more message than the previous.	 */	for(ndest = 1; ndest < Maxdest; ndest++){		p = dest;		endtime = time(0);		if(endtime >= reqp->aborttime)			break;		/* get a server address if we need one */		if(ndest > l - p){			j = serveraddrs(nsrp, dest, l - p, depth, reqp);			l = &dest[j];		}		/* no servers, punt */		if(l == dest)			break;		/* send to first 'ndest' destinations */		j = 0;		for(; p < &dest[ndest] && p < l; p++){			/* skip destinations we've finished with */			if(p->nx >= Maxtrans)				continue;			j++;			/* exponential backoff of requests */			if((1<<p->nx) > ndest)				continue;			memmove(obuf, p->a, sizeof(p->a));			if(debug)				logsend(reqp->id, depth, obuf, p->s->name,					dp->name, type);			if(write(fd, obuf, len + OUdphdrsize) < 0)				warning("sending udp msg %r");			p->nx++;		}		if(j == 0)			break;		/* no destinations left */		/* wait up to 5 seconds for replies */		endtime = time(0) + 5;		if(endtime > reqp->aborttime)			endtime = reqp->aborttime;		for(replywaits = 0; replywaits < ndest; replywaits++){			if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0)				break;		/* timed out */			/* find responder */			for(p = dest; p < l; p++)				if(memcmp(p->a, ibuf, sizeof(p->a)) == 0)					break;			/* remove all addrs of responding server from list */			for(np = dest; np < l; np++)				if(np->s == p->s)					p->nx = Maxtrans;			/* ignore any error replies */			if((m.flags & Rmask) == Rserver){				rrfreelist(m.qd);				rrfreelist(m.an);				rrfreelist(m.ar);				rrfreelist(m.ns);				if(p != l)					p->code = Rserver;				continue;			}			/* ignore any bad delegations */			if(m.ns && baddelegation(m.ns, nsrp, ibuf)){				rrfreelist(m.ns);				m.ns = nil;				if(m.an == nil){					rrfreelist(m.qd);					rrfreelist(m.ar);					if(p != l)						p->code = Rserver;					continue;				}			}			/* remove any soa's from the authority section */			soarr = rrremtype(&m.ns, Tsoa);			/* incorporate answers */			if(m.an)				rrattach(m.an, (m.flags & Fauth) ? 1 : 0);			if(m.ar)				rrattach(m.ar, 0);			if(m.ns){				ndp = m.ns->owner;				rrattach(m.ns, 0);			} else				ndp = 0;			/* free the question */			if(m.qd)				rrfreelist(m.qd);			/*			 *  Any reply from an authoritative server,			 *  or a positive reply terminates the search			 */			if(m.an != nil || (m.flags & Fauth)){				if(m.an == nil && (m.flags & Rmask) == Rname)					dp->nonexistent = Rname;				else					dp->nonexistent = 0;				/*				 *  cache any negative responses, free soarr				 */				if((m.flags & Fauth) && m.an == nil)					cacheneg(dp, type, (m.flags & Rmask), soarr);				else					rrfreelist(soarr);				return 1;			}			rrfreelist(soarr);			/*			 *  if we've been given better name servers			 *  recurse			 */			if(m.ns){				tp = rrlookup(ndp, Tns, NOneg);				if(!contains(nsrp, tp)){					rv = netquery(dp, type, tp, reqp, depth+1);					rrfreelist(tp);					return rv;				} else					rrfreelist(tp);			}		}	}	/* if all servers returned failure, propogate it */	dp->nonexistent = Rserver;	for(p = dest; p < l; p++)		if(p->code != Rserver)			dp->nonexistent = 0;	return 0;}static intnetquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth){	uchar *obuf;	uchar *ibuf;	RR *rp;	int fd, rv;	if(depth > 12)		return 0;	/* use alloced buffers rather than ones from the stack */	ibuf = emalloc(Maxudpin+OUdphdrsize);	obuf = emalloc(Maxudp+OUdphdrsize);	slave(reqp);	/* prepare server RR's for incremental lookup */	for(rp = nsrp; rp; rp = rp->next)		rp->marker = 0;	fd = udpport();	if(fd < 0)		return 0;	rv = netquery1(fd, dp, type, nsrp, reqp, depth, ibuf, obuf);	close(fd);	free(ibuf);	free(obuf);	return rv;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -