xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision 5aa528fabc16cf8efb2814bf4829050e8175d743)
1 #include <u.h>
2 #include <libc.h>
3 #include "dns.h"
4 #include "ip.h"
5 
6 enum
7 {
8 	Maxdest=	24,	/* maximum destinations for a request message */
9 	Maxtrans=	3,	/* maximum transmissions to a server */
10 };
11 
12 static int	netquery(DN*, int, RR*, Request*, int);
13 static RR*	dnresolve1(char*, int, int, Request*, int, int);
14 
15 char *LOG = "dns";
16 
17 /*
18  * reading /proc/pid/args yields either "name" or "name [display args]",
19  * so return only display args, if any.
20  */
21 static char *
22 procgetname(void)
23 {
24 	int fd, n;
25 	char *lp, *rp;
26 	char buf[256];
27 
28 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
29 	if((fd = open(buf, OREAD)) < 0)
30 		return strdup("");
31 	*buf = '\0';
32 	n = read(fd, buf, sizeof buf-1);
33 	close(fd);
34 	if (n >= 0)
35 		buf[n] = '\0';
36 	if ((lp = strchr(buf, '[')) == nil ||
37 	    (rp = strrchr(buf, ']')) == nil)
38 		return strdup("");
39 	*rp = '\0';
40 	return strdup(lp+1);
41 }
42 
43 /*
44  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
45  *  looking it up as a canonical name.
46  */
47 RR*
48 dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
49 	int recurse, int rooted, int *status)
50 {
51 	RR *rp, *nrp, *drp;
52 	DN *dp;
53 	int loops;
54 	char *procname;
55 	char nname[Domlen];
56 
57 	if(status)
58 		*status = 0;
59 
60 	procname = procgetname();
61 	/*
62 	 *  hack for systems that don't have resolve search
63 	 *  lists.  Just look up the simple name in the database.
64 	 */
65 	if(!rooted && strchr(name, '.') == 0){
66 		rp = nil;
67 		drp = domainlist(class);
68 		for(nrp = drp; nrp != nil; nrp = nrp->next){
69 			snprint(nname, sizeof nname, "%s.%s", name,
70 				nrp->ptr->name);
71 			rp = dnresolve(nname, class, type, req, cn, depth,
72 				recurse, rooted, status);
73 			rrfreelist(rrremneg(&rp));
74 			if(rp != nil)
75 				break;
76 		}
77 		if(drp != nil)
78 			rrfree(drp);
79 		procsetname(procname);
80 		free(procname);
81 		return rp;
82 	}
83 
84 	/*
85 	 *  try the name directly
86 	 */
87 	rp = dnresolve1(name, class, type, req, depth, recurse);
88 	if(rp) {
89 		procsetname(procname);
90 		free(procname);
91 		return randomize(rp);
92 	}
93 
94 	/* try it as a canonical name if we weren't told the name didn't exist */
95 	dp = dnlookup(name, class, 0);
96 	if(type != Tptr && dp->nonexistent != Rname)
97 		for(loops=0; rp == nil && loops < 32; loops++){
98 			rp = dnresolve1(name, class, Tcname, req, depth, recurse);
99 			if(rp == nil)
100 				break;
101 
102 			if(rp->negative){
103 				rrfreelist(rp);
104 				rp = nil;
105 				break;
106 			}
107 
108 			name = rp->host->name;
109 			if(cn)
110 				rrcat(cn, rp);
111 			else
112 				rrfreelist(rp);
113 
114 			rp = dnresolve1(name, class, type, req, depth, recurse);
115 		}
116 
117 	/* distinction between not found and not good */
118 	if(rp == nil && status != nil && dp->nonexistent != 0)
119 		*status = dp->nonexistent;
120 
121 	procsetname(procname);
122 	free(procname);
123 	return randomize(rp);
124 }
125 
126 static RR*
127 dnresolve1(char *name, int class, int type, Request *req, int depth,
128 	int recurse)
129 {
130 	DN *dp, *nsdp;
131 	RR *rp, *nsrp, *dbnsrp;
132 	char *cp;
133 
134 	if(debug)
135 		syslog(0, LOG, "[%d] dnresolve1 %s %d %d",
136 			getpid(), name, type, class);
137 
138 	/* only class Cin implemented so far */
139 	if(class != Cin)
140 		return nil;
141 
142 	dp = dnlookup(name, class, 1);
143 
144 	/*
145 	 *  Try the cache first
146 	 */
147 	rp = rrlookup(dp, type, OKneg);
148 	if(rp)
149 		if(rp->db){
150 			/* unauthenticated db entries are hints */
151 			if(rp->auth)
152 				return rp;
153 		} else
154 			/* cached entry must still be valid */
155 			if(rp->ttl > now)
156 				/* but Tall entries are special */
157 				if(type != Tall || rp->query == Tall)
158 					return rp;
159 
160 	rrfreelist(rp);
161 
162 	/*
163 	 * try the cache for a canonical name. if found punt
164 	 * since we'll find it during the canonical name search
165 	 * in dnresolve().
166 	 */
167 	if(type != Tcname){
168 		rp = rrlookup(dp, Tcname, NOneg);
169 		rrfreelist(rp);
170 		if(rp)
171 			return nil;
172 	}
173 
174 	/*
175 	 *  if we're running as just a resolver, go to our
176 	 *  designated name servers
177 	 */
178 	if(resolver){
179 		nsrp = randomize(getdnsservers(class));
180 		if(nsrp != nil) {
181 			if(netquery(dp, type, nsrp, req, depth+1)){
182 				rrfreelist(nsrp);
183 				return rrlookup(dp, type, OKneg);
184 			}
185 			rrfreelist(nsrp);
186 		}
187 	}
188 
189 	/*
190  	 *  walk up the domain name looking for
191 	 *  a name server for the domain.
192 	 */
193 	for(cp = name; cp; cp = walkup(cp)){
194 		/*
195 		 *  if this is a local (served by us) domain,
196 		 *  return answer
197 		 */
198 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
199 		if(dbnsrp && dbnsrp->local){
200 			rp = dblookup(name, class, type, 1, dbnsrp->ttl);
201 			rrfreelist(dbnsrp);
202 			return rp;
203 		}
204 
205 		/*
206 		 *  if recursion isn't set, just accept local
207 		 *  entries
208 		 */
209 		if(recurse == Dontrecurse){
210 			if(dbnsrp)
211 				rrfreelist(dbnsrp);
212 			continue;
213 		}
214 
215 		/* look for ns in cache */
216 		nsdp = dnlookup(cp, class, 0);
217 		nsrp = nil;
218 		if(nsdp)
219 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
220 
221 		/* if the entry timed out, ignore it */
222 		if(nsrp && nsrp->ttl < now){
223 			rrfreelist(nsrp);
224 			nsrp = nil;
225 		}
226 
227 		if(nsrp){
228 			rrfreelist(dbnsrp);
229 
230 			/* try the name servers found in cache */
231 			if(netquery(dp, type, nsrp, req, depth+1)){
232 				rrfreelist(nsrp);
233 				return rrlookup(dp, type, OKneg);
234 			}
235 			rrfreelist(nsrp);
236 			continue;
237 		}
238 
239 		/* use ns from db */
240 		if(dbnsrp){
241 			/* try the name servers found in db */
242 			if(netquery(dp, type, dbnsrp, req, depth+1)){
243 				/* we got an answer */
244 				rrfreelist(dbnsrp);
245 				return rrlookup(dp, type, NOneg);
246 			}
247 			rrfreelist(dbnsrp);
248 		}
249 	}
250 
251 	/* settle for a non-authoritative answer */
252 	rp = rrlookup(dp, type, OKneg);
253 	if(rp)
254 		return rp;
255 
256 	/* noone answered.  try the database, we might have a chance. */
257 	return dblookup(name, class, type, 0, 0);
258 }
259 
260 /*
261  *  walk a domain name one element to the right.
262  *  return a pointer to that element.
263  *  in other words, return a pointer to the parent domain name.
264  */
265 char*
266 walkup(char *name)
267 {
268 	char *cp;
269 
270 	cp = strchr(name, '.');
271 	if(cp)
272 		return cp+1;
273 	else if(*name)
274 		return "";
275 	else
276 		return 0;
277 }
278 
279 /*
280  *  Get a udpport for requests and replies.  Put the port
281  *  into "headers" mode.
282  */
283 static char *hmsg = "headers";
284 static char *ohmsg = "oldheaders";
285 
286 int
287 udpport(void)
288 {
289 	int fd, ctl;
290 	char ds[64], adir[64];
291 
292 	/* get a udp port */
293 	snprint(ds, sizeof(ds), "%s/udp!*!0", mntpt);
294 	ctl = announce(ds, adir);
295 	if(ctl < 0){
296 		/* warning("can't get udp port"); */
297 		return -1;
298 	}
299 
300 	/* turn on header style interface */
301 	if(write(ctl, hmsg, strlen(hmsg)) , 0){
302 		close(ctl);
303 		warning(hmsg);
304 		return -1;
305 	}
306 	write(ctl, ohmsg, strlen(ohmsg));
307 
308 	/* grab the data file */
309 	snprint(ds, sizeof(ds), "%s/data", adir);
310 	fd = open(ds, ORDWR);
311 	close(ctl);
312 	if(fd < 0)
313 		warning("can't open udp port: %r");
314 	return fd;
315 }
316 
317 int
318 mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
319 {
320 	DNSmsg m;
321 	int len;
322 	OUdphdr *uh = (OUdphdr*)buf;
323 
324 	/* stuff port number into output buffer */
325 	memset(uh, 0, sizeof(*uh));
326 	hnputs(uh->rport, 53);
327 
328 	/* make request and convert it to output format */
329 	memset(&m, 0, sizeof(m));
330 	m.flags = flags;
331 	m.id = reqno;
332 	m.qd = rralloc(type);
333 	m.qd->owner = dp;
334 	m.qd->type = type;
335 	len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);
336 	if(len < 0)
337 		abort();	/* "can't convert" */
338 	rrfree(m.qd);
339 	return len;
340 }
341 
342 /* for alarms in readreply */
343 static void
344 ding(void *x, char *msg)
345 {
346 	USED(x);
347 	if(strcmp(msg, "alarm") == 0)
348 		noted(NCONT);
349 	else
350 		noted(NDFLT);
351 }
352 
353 static void
354 freeanswers(DNSmsg *mp)
355 {
356 	rrfreelist(mp->qd);
357 	rrfreelist(mp->an);
358 	rrfreelist(mp->ns);
359 	rrfreelist(mp->ar);
360 	mp->qd = mp->an = mp->ns = mp->ar = nil;
361 }
362 
363 /*
364  *  read replies to a request.  ignore any of the wrong type.
365  *  wait at most 5 seconds.
366  */
367 static int
368 readreply(int fd, DN *dp, int type, ushort req,
369 	  uchar *ibuf, DNSmsg *mp, ulong endtime, Request *reqp)
370 {
371 	char *err;
372 	int len;
373 	ulong now;
374 	RR *rp;
375 
376 	notify(ding);
377 
378 	for(; ; freeanswers(mp)){
379 		now = time(0);
380 		if(now >= endtime)
381 			return -1;	/* timed out */
382 
383 		/* timed read */
384 		alarm((endtime - now) * 1000);
385 		len = read(fd, ibuf, OUdphdrsize+Maxudpin);
386 		alarm(0);
387 		len -= OUdphdrsize;
388 		if(len < 0)
389 			return -1;	/* timed out */
390 
391 		/* convert into internal format  */
392 		memset(mp, 0, sizeof(*mp));
393 		err = convM2DNS(&ibuf[OUdphdrsize], len, mp, nil);
394 		if(err){
395 			syslog(0, LOG, "input err: %s: %I", err, ibuf);
396 			continue;
397 		}
398 		if(debug)
399 			logreply(reqp->id, ibuf, mp);
400 
401 		/* answering the right question? */
402 		if(mp->id != req){
403 			syslog(0, LOG, "%d: id %d instead of %d: %I", reqp->id,
404 					mp->id, req, ibuf);
405 			continue;
406 		}
407 		if(mp->qd == 0){
408 			syslog(0, LOG, "%d: no question RR: %I", reqp->id, ibuf);
409 			continue;
410 		}
411 		if(mp->qd->owner != dp){
412 			syslog(0, LOG, "%d: owner %s instead of %s: %I",
413 				reqp->id, mp->qd->owner->name, dp->name, ibuf);
414 			continue;
415 		}
416 		if(mp->qd->type != type){
417 			syslog(0, LOG, "%d: type %d instead of %d: %I",
418 				reqp->id, mp->qd->type, type, ibuf);
419 			continue;
420 		}
421 
422 		/* remember what request this is in answer to */
423 		for(rp = mp->an; rp; rp = rp->next)
424 			rp->query = type;
425 
426 		return 0;
427 	}
428 }
429 
430 /*
431  *	return non-0 if first list includes second list
432  */
433 int
434 contains(RR *rp1, RR *rp2)
435 {
436 	RR *trp1, *trp2;
437 
438 	for(trp2 = rp2; trp2; trp2 = trp2->next){
439 		for(trp1 = rp1; trp1; trp1 = trp1->next){
440 			if(trp1->type == trp2->type)
441 			if(trp1->host == trp2->host)
442 			if(trp1->owner == trp2->owner)
443 				break;
444 		}
445 		if(trp1 == nil)
446 			return 0;
447 	}
448 	return 1;
449 }
450 
451 
452 typedef struct Dest	Dest;
453 struct Dest
454 {
455 	uchar	a[IPaddrlen];	/* ip address */
456 	DN	*s;		/* name server */
457 	int	nx;		/* number of transmissions */
458 	int	code;
459 };
460 
461 
462 /*
463  *  return multicast version if any
464  */
465 int
466 ipisbm(uchar *ip)
467 {
468 	if(isv4(ip)){
469 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
470 		    ipcmp(ip, IPv4bcast) == 0)
471 			return 4;
472 	} else
473 		if(ip[0] == 0xff)
474 			return 6;
475 	return 0;
476 }
477 
478 /*
479  *  Get next server address
480  */
481 static int
482 serveraddrs(RR *nsrp, Dest *dest, int nd, int depth, Request *reqp)
483 {
484 	RR *rp, *arp, *trp;
485 	Dest *cur;
486 
487 	if(nd >= Maxdest)
488 		return 0;
489 
490 	/*
491 	 *  look for a server whose address we already know.
492 	 *  if we find one, mark it so we ignore this on
493 	 *  subsequent passes.
494 	 */
495 	arp = 0;
496 	for(rp = nsrp; rp; rp = rp->next){
497 		assert(rp->magic == RRmagic);
498 		if(rp->marker)
499 			continue;
500 		arp = rrlookup(rp->host, Ta, NOneg);
501 		if(arp){
502 			rp->marker = 1;
503 			break;
504 		}
505 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
506 		if(arp){
507 			rp->marker = 1;
508 			break;
509 		}
510 	}
511 
512 	/*
513 	 *  if the cache and database lookup didn't find any new
514 	 *  server addresses, try resolving one via the network.
515 	 *  Mark any we try to resolve so we don't try a second time.
516 	 */
517 	if(arp == 0)
518 		for(rp = nsrp; rp; rp = rp->next){
519 			if(rp->marker)
520 				continue;
521 			rp->marker = 1;
522 
523 			/*
524 			 *  avoid loops looking up a server under itself
525 			 */
526 			if(subsume(rp->owner->name, rp->host->name))
527 				continue;
528 
529 			arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0,
530 				depth+1, Recurse, 1, 0);
531 			rrfreelist(rrremneg(&arp));
532 			if(arp)
533 				break;
534 		}
535 
536 	/* use any addresses that we found */
537 	for(trp = arp; trp; trp = trp->next){
538 		if(nd >= Maxdest)
539 			break;
540 		cur = &dest[nd];
541 		parseip(cur->a, trp->ip->name);
542 		if(ipisbm(cur->a))
543 			continue;
544 		cur->nx = 0;
545 		cur->s = trp->owner;
546 		cur->code = Rtimeout;
547 		nd++;
548 	}
549 	rrfreelist(arp);
550 	return nd;
551 }
552 
553 /*
554  *  cache negative responses
555  */
556 static void
557 cacheneg(DN *dp, int type, int rcode, RR *soarr)
558 {
559 	RR *rp;
560 	DN *soaowner;
561 	ulong ttl;
562 
563 	/* no cache time specified, don't make anything up */
564 	if(soarr != nil){
565 		if(soarr->next != nil){
566 			rrfreelist(soarr->next);
567 			soarr->next = nil;
568 		}
569 		soaowner = soarr->owner;
570 	} else
571 		soaowner = nil;
572 
573 	/* the attach can cause soarr to be freed so mine it now */
574 	if(soarr != nil && soarr->soa != nil)
575 		ttl = soarr->soa->minttl+now;
576 	else
577 		ttl = 5*Min;
578 
579 	/* add soa and negative RR to the database */
580 	rrattach(soarr, 1);
581 
582 	rp = rralloc(type);
583 	rp->owner = dp;
584 	rp->negative = 1;
585 	rp->negsoaowner = soaowner;
586 	rp->negrcode = rcode;
587 	rp->ttl = ttl;
588 	rrattach(rp, 1);
589 }
590 
591 /*
592  *  query name servers.  If the name server returns a pointer to another
593  *  name server, recurse.
594  */
595 static int
596 netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth,
597 	uchar *ibuf, uchar *obuf)
598 {
599 	int ndest, j, len, replywaits, rv;
600 	ulong endtime;
601 	ushort req;
602 	char buf[12];
603 	DN *ndp;
604 	DNSmsg m;
605 	Dest *p, *l, *np;
606 	Dest dest[Maxdest];
607 	RR *tp, *soarr;
608 
609 	/* pack request into a message */
610 	req = rand();
611 	len = mkreq(dp, type, obuf, Frecurse|Oquery, req);
612 
613 	/* no server addresses yet */
614 	l = dest;
615 
616 	/*
617 	 *  transmit requests and wait for answers.
618 	 *  at most Maxtrans attempts to each address.
619 	 *  each cycle send one more message than the previous.
620 	 */
621 	for(ndest = 1; ndest < Maxdest; ndest++){
622 		p = dest;
623 
624 		endtime = time(0);
625 		if(endtime >= reqp->aborttime)
626 			break;
627 
628 		/* get a server address if we need one */
629 		if(ndest > l - p){
630 			j = serveraddrs(nsrp, dest, l - p, depth, reqp);
631 			l = &dest[j];
632 		}
633 
634 		/* no servers, punt */
635 		if(l == dest)
636 			break;
637 
638 		/* send to first 'ndest' destinations */
639 		j = 0;
640 		for(; p < &dest[ndest] && p < l; p++){
641 			/* skip destinations we've finished with */
642 			if(p->nx >= Maxtrans)
643 				continue;
644 
645 			j++;
646 
647 			/* exponential backoff of requests */
648 			if((1<<p->nx) > ndest)
649 				continue;
650 
651 			procsetname("req slave: query to %I/%s %s %s",
652 				obuf, p->s->name, dp->name,
653 				rrname(type, buf, sizeof buf));
654 			memmove(obuf, p->a, sizeof p->a);
655 			if(debug)
656 				logsend(reqp->id, depth, obuf, p->s->name,
657 					dp->name, type);
658 			if(write(fd, obuf, len + OUdphdrsize) < 0)
659 				warning("sending udp msg %r");
660 			p->nx++;
661 		}
662 		if(j == 0)
663 			break;		/* no destinations left */
664 
665 		/* wait up to 5 seconds for replies */
666 		endtime = time(0) + 5;
667 		if(endtime > reqp->aborttime)
668 			endtime = reqp->aborttime;
669 
670 		for(replywaits = 0; replywaits < ndest; replywaits++){
671 			procsetname("req slave: reading reply from %I for %s %s",
672 				obuf, dp->name, rrname(type, buf, sizeof buf));
673 			memset(&m, 0, sizeof m);
674 			if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0)
675 				break;		/* timed out */
676 
677 			/* find responder */
678 			for(p = dest; p < l; p++)
679 				if(memcmp(p->a, ibuf, sizeof(p->a)) == 0)
680 					break;
681 
682 			/* remove all addrs of responding server from list */
683 			for(np = dest; np < l; np++)
684 				if(np->s == p->s)
685 					p->nx = Maxtrans;
686 
687 			/* ignore any error replies */
688 			if((m.flags & Rmask) == Rserver){
689 				rrfreelist(m.qd);
690 				rrfreelist(m.an);
691 				rrfreelist(m.ar);
692 				rrfreelist(m.ns);
693 				if(p != l)
694 					p->code = Rserver;
695 				continue;
696 			}
697 
698 			/* ignore any bad delegations */
699 			if(m.ns && baddelegation(m.ns, nsrp, ibuf)){
700 				rrfreelist(m.ns);
701 				m.ns = nil;
702 				if(m.an == nil){
703 					rrfreelist(m.qd);
704 					rrfreelist(m.ar);
705 					if(p != l)
706 						p->code = Rserver;
707 					continue;
708 				}
709 			}
710 
711 
712 			/* remove any soa's from the authority section */
713 			soarr = rrremtype(&m.ns, Tsoa);
714 
715 			/* incorporate answers */
716 			if(m.an)
717 				rrattach(m.an, (m.flags & Fauth) != 0);
718 			if(m.ar)
719 				rrattach(m.ar, 0);
720 			if(m.ns){
721 				ndp = m.ns->owner;
722 				rrattach(m.ns, 0);
723 			} else
724 				ndp = 0;
725 
726 			/* free the question */
727 			if(m.qd)
728 				rrfreelist(m.qd);
729 
730 			/*
731 			 *  Any reply from an authoritative server,
732 			 *  or a positive reply terminates the search
733 			 */
734 			if(m.an != nil || (m.flags & Fauth)){
735 				if(m.an == nil && (m.flags & Rmask) == Rname)
736 					dp->nonexistent = Rname;
737 				else
738 					dp->nonexistent = 0;
739 
740 				/*
741 				 *  cache any negative responses, free soarr
742 				 */
743 				if((m.flags & Fauth) && m.an == nil)
744 					cacheneg(dp, type, (m.flags & Rmask),
745 						soarr);
746 				else
747 					rrfreelist(soarr);
748 				return 1;
749 			}
750 			rrfreelist(soarr);
751 
752 			/*
753 			 *  if we've been given better name servers
754 			 *  recurse
755 			 */
756 			if(m.ns){
757 				tp = rrlookup(ndp, Tns, NOneg);
758 				if(!contains(nsrp, tp)){
759 					procsetname(
760 					 "req slave: recursive query for %s %s",
761 						dp->name,
762 						rrname(type, buf, sizeof buf));
763 					rv = netquery(dp, type, tp, reqp,
764 						depth + 1);
765 					rrfreelist(tp);
766 					return rv;
767 				} else
768 					rrfreelist(tp);
769 			}
770 		}
771 	}
772 
773 	/* if all servers returned failure, propagate it */
774 	dp->nonexistent = Rserver;
775 	for(p = dest; p < l; p++)
776 		if(p->code != Rserver)
777 			dp->nonexistent = 0;
778 
779 	return 0;
780 }
781 
782 static int
783 netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth)
784 {
785 	int fd, rv;
786 //	int pid;
787 	uchar *obuf, *ibuf;
788 	RR *rp;
789 
790 	if(depth > 12)			/* in a recursive loop? */
791 		return 0;
792 
793 	/* use alloced buffers rather than ones from the stack */
794 	ibuf = emalloc(Maxudpin+OUdphdrsize);
795 	obuf = emalloc(Maxudp+OUdphdrsize);
796 
797 //	pid = getpid();
798 	slave(reqp);
799 	/* parent process longjmped to req->mret; we're the child slave */
800 
801 	/* prepare server RR's for incremental lookup */
802 	for(rp = nsrp; rp; rp = rp->next)
803 		rp->marker = 0;
804 
805 //	if (pid != getpid())
806 //		syslog(0, LOG, "[%d] netquery: forked child for %s",
807 //			getpid(), dp->name);
808 	fd = udpport();
809 	if(fd < 0)
810 		rv = 0;
811 	else
812 		rv = netquery1(fd, dp, type, nsrp, reqp, depth, ibuf, obuf);
813 	close(fd);
814 	free(ibuf);
815 	free(obuf);
816 
817 	return rv;
818 }
819