This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.sbin / named / ns_forw.c
CommitLineData
15637ed4
RG
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
35static 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
49struct qinfo *qhead = QINFO_NULL; /* head of allocated queries */
50struct qinfo *retryqp = QINFO_NULL; /* list of queries to retry */
51struct fwdinfo *fwdtab; /* list of forwarding hosts */
52
53int nsid; /* next forwarded query id */
54extern int forward_only; /* you are only a slave */
55extern int errno;
56extern u_short ns_port;
57
58time_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 */
68ns_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 */
172nslookup(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)
206fprintf(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);
272skipserver: ;
273 }
274out:
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
285qcomp(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 */
295schedretry(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 */
330unsched(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 */
357retry(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
428found:
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 */
470time_t
471retrytime(qp)
472register 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
492qflush()
493{
494 while (qhead)
495 qremove(qhead);
496 qhead = QINFO_NULL;
497}
498
499qremove(qp)
500register 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
510struct qinfo *
511qfindid(id)
512register 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
531struct qinfo *
532qnew()
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
553qfree(qp)
554struct 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}