xref: /386bsd/usr/src/usr.sbin/named/ns_forw.c (revision a2142627)
1 /*-
2  * Copyright (c) 1986 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static char sccsid[] = "@(#)ns_forw.c	4.32 (Berkeley) 3/3/91";
36 #endif /* not lint */
37 
38 #include <sys/param.h>
39 #include <sys/time.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <syslog.h>
43 #include <arpa/nameser.h>
44 #include <resolv.h>
45 #include <stdio.h>
46 #include "ns.h"
47 #include "db.h"
48 
49 struct	qinfo *qhead = QINFO_NULL;	/* head of allocated queries */
50 struct	qinfo *retryqp = QINFO_NULL;	/* list of queries to retry */
51 struct	fwdinfo *fwdtab;		/* list of forwarding hosts */
52 
53 int	nsid;				/* next forwarded query id */
54 extern int forward_only;		/* you are only a slave */
55 extern int errno;
56 extern u_short ns_port;
57 
58 time_t	retrytime();
59 
60 /*
61  * Forward the query to get the answer since its not in the database.
62  * Returns FW_OK if a request struct is allocated and the query sent.
63  * Returns FW_DUP if this is a duplicate of a pending request.
64  * Returns FW_NOSERVER if there were no addresses for the nameservers.
65  * Returns FW_SERVFAIL on malloc error.
66  * (no action is taken on errors and qpp is not filled in.)
67  */
68 ns_forw(nsp, msg, msglen, fp, qsp, dfd, qpp)
69 	struct databuf *nsp[];
70 	char *msg;
71 	int msglen;
72 	struct sockaddr_in *fp;
73 	struct qstream *qsp;
74 	int dfd;
75 	struct qinfo **qpp;
76 {
77 	register struct qinfo *qp;
78 	HEADER *hp;
79 	u_short id;
80 	extern char *calloc();
81 
82 #ifdef DEBUG
83 	if (debug >= 3)
84 		fprintf(ddt,"ns_forw()\n");
85 #endif
86 
87 	/* Don't forward if we're already working on it. */
88 	hp = (HEADER *) msg;
89 	id = hp->id;
90 	/* Look at them all */
91 	for (qp = qhead; qp!=QINFO_NULL; qp = qp->q_link) {
92 		if (qp->q_id == id &&
93 		    bcmp((char *)&qp->q_from, fp, sizeof(qp->q_from)) == 0 &&
94 		    ((qp->q_cmsglen == 0 && qp->q_msglen == msglen &&
95 		     bcmp((char *)qp->q_msg+2, msg+2, msglen-2) == 0) ||
96 		    (qp->q_cmsglen == msglen &&
97 		     bcmp((char *)qp->q_cmsg+2, msg+2, msglen-2) == 0))) {
98 #ifdef DEBUG
99 			if (debug >= 3)
100 				fprintf(ddt,"forw: dropped DUP id=%d\n", ntohs(id));
101 #endif
102 #ifdef STATS
103 			stats[S_DUPQUERIES].cnt++;
104 #endif
105 			return (FW_DUP);
106 		}
107 	}
108 
109 	qp = qnew();
110 	if (nslookup(nsp, qp) == 0) {
111 #ifdef DEBUG
112 		if (debug >= 2)
113 			fprintf(ddt,"forw: no nameservers found\n");
114 #endif
115 		qfree(qp);
116 		return (FW_NOSERVER);
117 	}
118 	qp->q_stream = qsp;
119 	qp->q_curaddr = 0;
120 	qp->q_fwd = fwdtab;
121 	qp->q_dfd = dfd;
122 	qp->q_id = id;
123 	hp->id = qp->q_nsid = htons((u_short)++nsid);
124 	hp->ancount = 0;
125 	hp->nscount = 0;
126 	hp->arcount = 0;
127 	qp->q_from = *fp;
128 	if ((qp->q_msg = malloc((unsigned)msglen)) == NULL) {
129 		syslog(LOG_ERR, "forw: %m");
130 		qfree(qp);
131 		return (FW_SERVFAIL);
132 	}
133 	bcopy(msg, qp->q_msg, qp->q_msglen = msglen);
134 	if (!qp->q_fwd) {
135 		hp->rd = 0;
136 		qp->q_addr[0].stime = tt;
137 	}
138 
139 	schedretry(qp, retrytime(qp));
140 #ifdef DEBUG
141 	if (debug)
142 		fprintf(ddt,
143 		   "forw: forw -> %s %d (%d) nsid=%d id=%d %dms retry %d sec\n",
144 			inet_ntoa(Q_NEXTADDR(qp,0)->sin_addr),
145 			ds, ntohs(Q_NEXTADDR(qp,0)->sin_port),
146 			ntohs(qp->q_nsid), ntohs(qp->q_id),
147 			qp->q_addr[0].nsdata->d_nstime,
148 			qp->q_time - tt.tv_sec);
149 	if ( debug >= 10)
150 		fp_query(msg, ddt);
151 #endif
152 	if (sendto(ds, msg, msglen, 0, (struct sockaddr *)Q_NEXTADDR(qp,0),
153 		   sizeof(struct sockaddr_in)) < 0){
154 #ifdef DEBUG
155 		if (debug >= 5)
156 			fprintf(ddt,"error returning msg errno=%d\n",errno);
157 #endif
158 	}
159 #ifdef STATS
160 	stats[S_OUTPKTS].cnt++;
161 #endif
162 	if (qpp)
163 		*qpp = qp;
164 	hp->rd = 1;
165 	return (0);
166 }
167 
168 /*
169  * Lookup the address for each nameserver in `nsp' and add it to
170  * the list saved in the qinfo structure.
171  */
172 nslookup(nsp, qp)
173 	struct databuf *nsp[];
174 	register struct qinfo *qp;
175 {
176 	register struct namebuf *np;
177 	register struct databuf *dp, *nsdp;
178 	register struct qserv *qs;
179 	register int n, i;
180 	struct hashbuf *tmphtp;
181 	char *dname, *fname;
182 	int oldn, naddr, class, found_arr;
183 	time_t curtime;
184 	int qcomp();
185 
186 #ifdef DEBUG
187 	if (debug >= 3)
188 		fprintf(ddt,"nslookup(nsp=x%x,qp=x%x)\n",nsp,qp);
189 #endif
190 
191 	naddr = n = qp->q_naddr;
192 	curtime = (u_long) tt.tv_sec;
193 	while ((nsdp = *nsp++) != NULL) {
194 		class = nsdp->d_class;
195 		dname = nsdp->d_data;
196 #ifdef DEBUG
197 		if (debug >= 3)
198 			fprintf(ddt,"nslookup: NS %s c%d t%d (x%x)\n",
199 				dname, class, nsdp->d_type, nsdp->d_flags);
200 #endif
201 		/* don't put in people we have tried */
202 		for (i = 0; i < qp->q_nusedns; i++)
203 			if (qp->q_usedns[i] == nsdp) {
204 #ifdef DEBUG
205 				if (debug >= 2)
206 fprintf(ddt, "skipping used NS w/name %s\n", nsdp->d_data);
207 #endif DEBUG
208 				goto skipserver;
209 			}
210 
211 		tmphtp = ((nsdp->d_flags & DB_F_HINT) ? fcachetab : hashtab);
212 		np = nlookup(dname, &tmphtp, &fname, 1);
213 		if (np == NULL || fname != dname) {
214 #ifdef DEBUG
215 			if (debug >= 3)
216 			    fprintf(ddt,"%s: not found %s %x\n",dname,fname,np);
217 #endif
218 			continue;
219 		}
220 		found_arr = 0;
221 		oldn = n;
222 		/* look for name server addresses */
223 		for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
224 			if (dp->d_type != T_A || dp->d_class != class)
225 				continue;
226 			/*
227 			 * Don't use records that may become invalid to
228 			 * reference later when we do the rtt computation.
229 			 * Never delete our safety-belt information!
230 			 */
231 			if ((dp->d_zone == 0) &&
232 			    (dp->d_ttl < (curtime+900)) &&
233 			    !(dp->d_flags & DB_F_HINT) )
234 		        {
235 #ifdef DEBUG
236 				if (debug >= 3)
237 					fprintf(ddt,"nslookup: stale entry '%s'\n",
238 					    np->n_dname);
239 #endif
240 				/* Cache invalidate the NS RR's */
241 				if (dp->d_ttl < curtime)
242 					delete_all(np, class, T_A);
243 				n = oldn;
244 				break;
245 			}
246 
247 			found_arr++;
248 			/* don't put in duplicates */
249 			qs = qp->q_addr;
250 			for (i = 0; i < n; i++, qs++)
251 				if (bcmp((char *)&qs->ns_addr.sin_addr,
252 				    dp->d_data, sizeof(struct in_addr)) == 0)
253 					goto skipaddr;
254 			qs->ns_addr.sin_family = AF_INET;
255 			qs->ns_addr.sin_port = ns_port;
256 			qs->ns_addr.sin_addr =
257 				    *(struct in_addr *)dp->d_data;
258 			qs->ns = nsdp;
259 			qs->nsdata = dp;
260 			qp->q_addr[n].nretry = 0;
261 			n++;
262 			if (n >= NSMAX)
263 				goto out;
264 	skipaddr:	;
265 		}
266 #ifdef DEBUG
267 		if (debug >= 3)
268 			fprintf(ddt,"nslookup: %d ns addrs\n", n);
269 #endif
270 		if (found_arr == 0 && qp->q_system == 0)
271 			(void) sysquery(dname, class, T_A);
272 skipserver:	;
273 	}
274 out:
275 #ifdef DEBUG
276 	if (debug >= 3)
277 		fprintf(ddt,"nslookup: %d ns addrs total\n", n);
278 #endif
279 	qp->q_naddr = n;
280 	if (n > 1)
281 		qsort((char *)qp->q_addr, n, sizeof(struct qserv), qcomp);
282 	return (n - naddr);
283 }
284 
285 qcomp(qs1, qs2)
286 	struct qserv *qs1, *qs2;
287 {
288 
289 	return (qs1->nsdata->d_nstime - qs2->nsdata->d_nstime);
290 }
291 
292 /*
293  * Arrange that forwarded query (qp) is retried after t seconds.
294  */
295 schedretry(qp, t)
296 	struct qinfo *qp;
297 	time_t t;
298 {
299 	register struct qinfo *qp1, *qp2;
300 
301 #ifdef DEBUG
302 	if (debug > 3) {
303 		fprintf(ddt,"schedretry(%#x, %dsec)\n", qp, t);
304 		if (qp->q_time)
305 		   fprintf(ddt,"WARNING: schedretry(%x,%d) q_time already %d\n", qp->q_time);
306 	}
307 #endif
308 	t += (u_long) tt.tv_sec;
309 	qp->q_time = t;
310 
311 	if ((qp1 = retryqp) == NULL) {
312 		retryqp = qp;
313 		qp->q_next = NULL;
314 		return;
315 	}
316 	if (t < qp1->q_time) {
317 		qp->q_next = qp1;
318 		retryqp = qp;
319 		return;
320 	}
321 	while ((qp2 = qp1->q_next) != NULL && qp2->q_time < t)
322 		qp1 = qp2;
323 	qp1->q_next = qp;
324 	qp->q_next = qp2;
325 }
326 
327 /*
328  * Unsched is called to remove a forwarded query entry.
329  */
330 unsched(qp)
331 	struct qinfo *qp;
332 {
333 	register struct qinfo *np;
334 
335 #ifdef DEBUG
336 	if (debug > 3) {
337 		fprintf(ddt,"unsched(%#x, %d )\n", qp, ntohs(qp->q_id));
338 	}
339 #endif
340 	if( retryqp == qp )  {
341 		retryqp = qp->q_next;
342 	} else {
343 		for( np=retryqp; np->q_next != QINFO_NULL; np = np->q_next ) {
344 			if( np->q_next != qp)
345 				continue;
346 			np->q_next = qp->q_next;	/* dequeue */
347 			break;
348 		}
349 	}
350 	qp->q_next = QINFO_NULL;		/* sanity check */
351 	qp->q_time = 0;
352 }
353 
354 /*
355  * Retry is called to retransmit query 'qp'.
356  */
retry(qp)357 retry(qp)
358 	register struct qinfo *qp;
359 {
360 	register int n;
361 	register HEADER *hp;
362 
363 #ifdef DEBUG
364 	if (debug > 3)
365 		fprintf(ddt,"retry(x%x) id=%d\n", qp, ntohs(qp->q_id));
366 #endif
367 	if((HEADER *)qp->q_msg == NULL) {		/*** XXX ***/
368 		qremove(qp);
369 		return;
370 	}						/*** XXX ***/
371 
372 	/* try next address */
373 	n = qp->q_curaddr;
374 	if (qp->q_fwd) {
375 		qp->q_fwd = qp->q_fwd->next;
376 		if (qp->q_fwd)
377 			goto found;
378 		/* out of forwarders, try direct queries */
379 	} else
380 		++qp->q_addr[n].nretry;
381 	if (!forward_only) {
382 		do {
383 			if (++n >= qp->q_naddr)
384 				n = 0;
385 			if (qp->q_addr[n].nretry < MAXRETRY)
386 				goto found;
387 		} while (n != qp->q_curaddr);
388 	}
389 	/*
390 	 * Give up. Can't reach destination.
391 	 */
392 	hp = (HEADER *)(qp->q_cmsg ? qp->q_cmsg : qp->q_msg);
393 	if (qp->q_system == PRIMING_CACHE) {
394 		/* Can't give up priming */
395 		unsched(qp);
396 		schedretry(qp, (time_t)60*60);	/* 1 hour */
397 		hp->rcode = NOERROR;	/* Lets be safe, reset the query */
398 		hp->qr = hp->aa = 0;
399 		qp->q_fwd = fwdtab;
400 		for (n = 0; n < qp->q_naddr; n++)
401 			qp->q_addr[n].nretry = 0;
402 		return;
403 	}
404 #ifdef DEBUG
405 	if (debug >= 5)
406 		fprintf(ddt,"give up\n");
407 #endif
408 	n = ((HEADER *)qp->q_cmsg ? qp->q_cmsglen : qp->q_msglen);
409 	hp->id = qp->q_id;
410 	hp->qr = 1;
411 	hp->ra = 1;
412 	hp->rd = 1;
413 	hp->rcode = SERVFAIL;
414 #ifdef DEBUG
415 	if (debug >= 10)
416 		fp_query(qp->q_msg, ddt);
417 #endif
418 	if (send_msg((char *)hp, n, qp)) {
419 #ifdef DEBUG
420 		if (debug)
421 			fprintf(ddt,"gave up retry(x%x) nsid=%d id=%d\n",
422 				qp, ntohs(qp->q_nsid), ntohs(qp->q_id));
423 #endif
424 	}
425 	qremove(qp);
426 	return;
427 
428 found:
429 	if (qp->q_fwd == 0 && qp->q_addr[n].nretry == 0)
430 		qp->q_addr[n].stime = tt;
431 	qp->q_curaddr = n;
432 	hp = (HEADER *)qp->q_msg;
433 	hp->rd = (qp->q_fwd ? 1 : 0);
434 #ifdef DEBUG
435 	if (debug)
436 		fprintf(ddt,"%s(addr=%d n=%d) -> %s %d (%d) nsid=%d id=%d %dms\n",
437 			(qp->q_fwd ? "reforw" : "resend"),
438 			n, qp->q_addr[n].nretry,
439 			inet_ntoa(Q_NEXTADDR(qp,n)->sin_addr),
440 			ds, ntohs(Q_NEXTADDR(qp,n)->sin_port),
441 			ntohs(qp->q_nsid), ntohs(qp->q_id),
442 			qp->q_addr[n].nsdata->d_nstime);
443 	if ( debug >= 10)
444 		fp_query(qp->q_msg, ddt);
445 #endif
446 	/* NOSTRICT */
447 	if (sendto(ds, qp->q_msg, qp->q_msglen, 0,
448 	    (struct sockaddr *)Q_NEXTADDR(qp,n),
449 	    sizeof(struct sockaddr_in)) < 0){
450 #ifdef DEBUG
451 		if (debug > 3)
452 			fprintf(ddt,"error resending msg errno=%d\n",errno);
453 #endif
454 	}
455 	hp->rd = 1;	/* leave set to 1 for dup detection */
456 #ifdef STATS
457 	stats[S_OUTPKTS].cnt++;
458 #endif
459 	unsched(qp);
460 	schedretry(qp, qp->q_fwd ? (2*RETRYBASE) : retrytime(qp));
461 }
462 
463 /*
464  * Compute retry time for the next server for a query.
465  * Use a minimum time of RETRYBASE (4 sec.) or twice the estimated
466  * service time; * back off exponentially on retries, but place a 45-sec.
467  * ceiling on retry times for now.  (This is because we don't hold a reference
468  * on servers or their addresses, and we have to finish before they time out.)
469  */
470 time_t
retrytime(qp)471 retrytime(qp)
472 register struct qinfo *qp;
473 {
474 	time_t t;
475 	struct qserv *ns = &qp->q_addr[qp->q_curaddr];
476 
477 #ifdef DEBUG
478 	if (debug > 3)
479 		fprintf(ddt,"retrytime: nstime %dms.\n",
480 		    ns->nsdata->d_nstime / 1000);
481 #endif
482 	t = (time_t) MAX(RETRYBASE, 2 * ns->nsdata->d_nstime / 1000);
483 	t <<= ns->nretry;
484 	t = MIN(t, 45);			/* max. retry timeout for now */
485 #ifdef notdef
486 	if (qp->q_system)
487 		return ((2 * t) + 5);	/* system queries can wait. */
488 #endif
489 	return (t);
490 }
491 
qflush()492 qflush()
493 {
494 	while (qhead)
495 		qremove(qhead);
496 	qhead = QINFO_NULL;
497 }
498 
qremove(qp)499 qremove(qp)
500 register struct qinfo *qp;
501 {
502 #ifdef DEBUG
503 	if(debug > 3)
504 		fprintf(ddt,"qremove(x%x)\n", qp);
505 #endif
506 	unsched(qp);			/* get off queue first */
507 	qfree(qp);
508 }
509 
510 struct qinfo *
qfindid(id)511 qfindid(id)
512 register u_short id;
513 {
514 	register struct qinfo *qp;
515 
516 #ifdef DEBUG
517 	if(debug > 3)
518 		fprintf(ddt,"qfindid(%d)\n", ntohs(id));
519 #endif
520 	for (qp = qhead; qp!=QINFO_NULL; qp = qp->q_link) {
521 		if (qp->q_nsid == id)
522 			return(qp);
523 	}
524 #ifdef DEBUG
525 	if (debug >= 5)
526 		fprintf(ddt,"qp not found\n");
527 #endif
528 	return(NULL);
529 }
530 
531 struct qinfo *
qnew()532 qnew()
533 {
534 	register struct qinfo *qp;
535 
536 	if ((qp = (struct qinfo *)calloc(1, sizeof(struct qinfo))) == NULL) {
537 #ifdef DEBUG
538 		if (debug >= 5)
539 			fprintf(ddt,"qnew: calloc error\n");
540 #endif
541 		syslog(LOG_ERR, "forw: %m");
542 		exit(12);
543 	}
544 #ifdef DEBUG
545 	if (debug >= 5)
546 		fprintf(ddt,"qnew(x%x)\n", qp);
547 #endif
548 	qp->q_link = qhead;
549 	qhead = qp;
550 	return( qp );
551 }
552 
553 qfree(qp)
554 struct qinfo *qp;
555 {
556 	register struct qinfo *np;
557 
558 #ifdef DEBUG
559 	if(debug > 3)
560 		fprintf(ddt,"qfree( x%x )\n", qp);
561 	if(debug && qp->q_next)
562 		fprintf(ddt,"WARNING:  qfree of linked ptr x%x\n", qp);
563 #endif
564 	if (qp->q_msg)
565 	 	free(qp->q_msg);
566  	if (qp->q_cmsg)
567  		free(qp->q_cmsg);
568 	if( qhead == qp )  {
569 		qhead = qp->q_link;
570 	} else {
571 		for( np=qhead; np->q_link != QINFO_NULL; np = np->q_link )  {
572 			if( np->q_link != qp )  continue;
573 			np->q_link = qp->q_link;	/* dequeue */
574 			break;
575 		}
576 	}
577 	(void)free((char *)qp);
578 }
579