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