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