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

Defined functions

ns_forw defined in line 46; used 2 times
nslookup defined in line 152; used 3 times
qcomp defined in line 265; used 2 times
qflush defined in line 469; used 1 times
qfree defined in line 530; used 6 times
qnew defined in line 508; used 3 times
qremove defined in line 476; used 7 times
retry defined in line 334; used 1 times
schedretry defined in line 277; used 5 times
unsched defined in line 307; used 4 times

Defined variables

fwdtab defined in line 29; used 4 times
nsid defined in line 31; used 1 times
qhead defined in line 27; used 10 times
retryqp defined in line 28; used 13 times
sccsid defined in line 14; never used
Last modified: 1988-09-12
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 806
Valid CSS Valid XHTML 1.0 Strict