|
|
1.1 ! root 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 the above copyright notice and this paragraph are ! 7: * duplicated in all such forms and that any documentation, ! 8: * advertising materials, and other materials related to such ! 9: * distribution and use acknowledge that the software was developed ! 10: * by the University of California, Berkeley. The name of the ! 11: * University may not be used to endorse or promote products derived ! 12: * from this software without specific prior written permission. ! 13: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ! 14: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ! 15: * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 16: */ ! 17: ! 18: #ifndef lint ! 19: static char sccsid[] = "@(#)ns_maint.c 4.24 (Berkeley) 6/18/88"; ! 20: #endif /* not lint */ ! 21: ! 22: #include <sys/param.h> ! 23: #include <sys/socket.h> ! 24: #include <sys/time.h> ! 25: #if defined(SYSV) ! 26: #include <unistd.h> ! 27: #endif SYSV ! 28: #include <netinet/in.h> ! 29: #include <stdio.h> ! 30: #include <syslog.h> ! 31: #include <signal.h> ! 32: #include <errno.h> ! 33: #include <arpa/nameser.h> ! 34: #include "ns.h" ! 35: #include "db.h" ! 36: ! 37: extern int errno; ! 38: extern int maint_interval; ! 39: ! 40: ! 41: /* ! 42: * Invoked at regular intervals by signal interrupt; refresh all secondary ! 43: * zones from primary name server and remove old cache entries. Also, ! 44: * ifdef'd ALLOW_UPDATES, dump database if it has changed since last ! 45: * dump/bootup. ! 46: */ ! 47: ns_maint() ! 48: { ! 49: register struct zoneinfo *zp; ! 50: struct itimerval ival; ! 51: time_t next_refresh = 0; ! 52: int zonenum; ! 53: ! 54: #ifdef DEBUG ! 55: if (debug) ! 56: fprintf(ddt,"ns_maint()\n"); ! 57: #endif ! 58: ! 59: for (zp = zones, zonenum = 0; zp < &zones[nzones]; zp++, zonenum++) { ! 60: switch(zp->z_type) { ! 61: #ifdef ALLOW_UPDATES ! 62: case Z_PRIMARY: ! 63: #endif ALLOW_UPDATES ! 64: case Z_SECONDARY: ! 65: case Z_CACHE: ! 66: break; ! 67: ! 68: default: ! 69: continue; ! 70: } ! 71: gettime(&tt); ! 72: #ifdef DEBUG ! 73: if (debug >= 2) ! 74: printzoneinfo(zonenum); ! 75: #endif ! 76: if (tt.tv_sec >= zp->z_time && zp->z_refresh > 0) { ! 77: if (zp->z_type == Z_CACHE) ! 78: doachkpt(); ! 79: if (zp->z_type == Z_SECONDARY) ! 80: zoneref(zp); ! 81: #ifdef ALLOW_UPDATES ! 82: /* ! 83: * Checkpoint the zone if it has changed ! 84: * since we last checkpointed ! 85: */ ! 86: if (zp->z_type == Z_PRIMARY && zp->hasChanged) ! 87: zonedump(zp); ! 88: #endif ALLOW_UPDATES ! 89: zp->z_time = tt.tv_sec + zp->z_refresh; ! 90: } ! 91: ! 92: /* ! 93: * Find when the next refresh needs to be and set ! 94: * interrupt time accordingly. ! 95: */ ! 96: if (next_refresh == 0 || ! 97: (zp->z_time != 0 && next_refresh > zp->z_time)) ! 98: next_refresh = zp->z_time; ! 99: } ! 100: ! 101: /* ! 102: * Schedule the next call to this function. ! 103: * Don't visit any sooner than maint_interval. ! 104: */ ! 105: bzero((char *)&ival, sizeof (ival)); ! 106: ival.it_value.tv_sec = next_refresh - tt.tv_sec; ! 107: if (ival.it_value.tv_sec < maint_interval) ! 108: ival.it_value.tv_sec = maint_interval; ! 109: (void) setitimer(ITIMER_REAL, &ival, (struct itimerval *)NULL); ! 110: #ifdef DEBUG ! 111: if (debug) ! 112: fprintf(ddt,"exit ns_maint() Next interrupt in %d sec\n", ! 113: ival.it_value.tv_sec); ! 114: #endif ! 115: } ! 116: ! 117: zoneref(zp) ! 118: struct zoneinfo *zp; ! 119: { ! 120: HEADER *hp; ! 121: u_short len; ! 122: u_long serial; ! 123: int s, n, l, tries; ! 124: int cnt, soacnt, error = 0; ! 125: int zone = zp - zones; ! 126: u_char *cp, *nmp, *eom; ! 127: u_char *tmp; ! 128: u_char buf[PACKETSZ]; ! 129: char name[MAXDNAME], name2[MAXDNAME]; ! 130: struct sockaddr_in sin; ! 131: struct zoneinfo zp_start, zp_finish; ! 132: struct itimerval ival; ! 133: struct itimerval zeroival; ! 134: extern struct sockaddr_in nsaddr; ! 135: extern int errno; ! 136: extern int read_interrupted; ! 137: extern int read_alarm(); ! 138: struct sigvec sv, osv; ! 139: ! 140: #ifdef DEBUG ! 141: if (debug) ! 142: fprintf(ddt,"zoneref() %s\n", zp->z_origin); ! 143: #endif ! 144: bzero((char *)&zeroival, sizeof(zeroival)); ! 145: ival = zeroival; ! 146: ival.it_value.tv_sec = 30; ! 147: sv.sv_handler = read_alarm; ! 148: sv.sv_onstack = 0; ! 149: sv.sv_mask = ~0; ! 150: (void) sigvec(SIGALRM, &sv, &osv); ! 151: ! 152: for( cnt = 0; cnt < zp->z_addrcnt; cnt++) { ! 153: error = 0; ! 154: bzero((char *)&sin, sizeof(sin)); ! 155: sin.sin_family = AF_INET; ! 156: sin.sin_port = nsaddr.sin_port; ! 157: sin.sin_addr = zp->z_addr[cnt]; ! 158: if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ! 159: syslog(LOG_ERR, "zoneref: socket: %m"); ! 160: error++; ! 161: break; ! 162: } ! 163: #ifdef DEBUG ! 164: if (debug >= 2) { ! 165: fprintf(ddt,"connecting to server #%d %s, %d\n", ! 166: cnt+1, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); ! 167: } ! 168: #endif ! 169: if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { ! 170: (void) close(s); ! 171: error++; ! 172: #ifdef DEBUG ! 173: if (debug >= 2) ! 174: fprintf(ddt,"connect failed, errno %d\n", errno); ! 175: #endif ! 176: continue; ! 177: } ! 178: if ((n = res_mkquery(QUERY, zp->z_origin, C_IN, ! 179: T_SOA, (char *)NULL, 0, NULL, buf, sizeof(buf))) < 0) { ! 180: syslog(LOG_ERR, "zoneref: res_mkquery failed"); ! 181: (void) close(s); ! 182: (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); ! 183: return; ! 184: } ! 185: /* ! 186: * Send length & message for zone transfer ! 187: */ ! 188: if (writemsg(s, buf, n) < 0) { ! 189: (void) close(s); ! 190: error++; ! 191: #ifdef DEBUG ! 192: if (debug >= 2) ! 193: fprintf(ddt,"writemsg failed\n"); ! 194: #endif ! 195: continue; ! 196: } ! 197: /* ! 198: * Get out your butterfly net and catch the SOA ! 199: */ ! 200: cp = buf; ! 201: l = sizeof(u_short); ! 202: read_interrupted = 0; ! 203: while (l > 0) { ! 204: (void) setitimer(ITIMER_REAL, &ival, ! 205: (struct itimerval *)NULL); ! 206: if ((n = recv(s, cp, l, 0)) > 0) { ! 207: cp += n; ! 208: l -= n; ! 209: } else { ! 210: if (errno == EINTR && !read_interrupted) ! 211: continue; ! 212: error++; ! 213: break; ! 214: } ! 215: } ! 216: (void) setitimer(ITIMER_REAL, &zeroival, ! 217: (struct itimerval *)NULL); ! 218: if (error) { ! 219: (void) close(s); ! 220: continue; ! 221: } ! 222: if ((len = htons(*(u_short *)buf)) == 0) { ! 223: (void) close(s); ! 224: if (zp->z_sysloged == 0) ! 225: syslog(LOG_ERR, ! 226: "no SOA from server %s, zone %s (len 0)\n", ! 227: inet_ntoa(sin.sin_addr), zp->z_origin); ! 228: continue; ! 229: } ! 230: l = len; ! 231: cp = buf; ! 232: while (l > 0) { ! 233: (void) setitimer(ITIMER_REAL, &ival, ! 234: (struct itimerval *)NULL); ! 235: if ((n = recv(s, cp, l, 0)) > 0) { ! 236: cp += n; ! 237: l -= n; ! 238: } else { ! 239: if (errno == EINTR && !read_interrupted) ! 240: continue; ! 241: error++; ! 242: break; ! 243: } ! 244: } ! 245: (void) setitimer(ITIMER_REAL, &zeroival, ! 246: (struct itimerval *)NULL); ! 247: if (error) { ! 248: (void) close(s); ! 249: continue; ! 250: } ! 251: #ifdef DEBUG ! 252: if (debug >= 3) { ! 253: fprintf(ddt,"len = %d\n", len); ! 254: fp_query(buf, ddt); ! 255: } ! 256: #endif DEBUG ! 257: zp_start = *zp; ! 258: tmp = buf + sizeof(HEADER); ! 259: eom = buf + len; ! 260: /* NEED TO CHECK MESSAGE LENGTH, ANCOUNT, AA */ ! 261: tmp += dn_skipname(tmp, eom) + QFIXEDSZ; ! 262: tmp += dn_skipname(tmp, eom); ! 263: soa_zinfo(&zp_start, tmp, eom); ! 264: if (zp->z_serial >= zp_start.z_serial && zp->z_auth) { ! 265: #ifdef DEBUG ! 266: if (debug) ! 267: fprintf(ddt,"zoneref: up to date (%d >= %d)\n", ! 268: zp->z_serial, zp_start.z_serial); ! 269: #endif DEBUG ! 270: zp->z_lastupdate = tt.tv_sec; ! 271: zp->z_refresh = zp_start.z_refresh; ! 272: (void) close(s); ! 273: (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); ! 274: if (zp->z_source) { ! 275: #if defined(SYSV) ! 276: struct utimbuf t; ! 277: ! 278: t.actime = t.modtime = tt.tv_sec; ! 279: (void) utime(zp->z_source, &t); ! 280: #else ! 281: struct timeval t[2]; ! 282: ! 283: t[0] = tt; ! 284: t[1] = tt; ! 285: (void) utimes(zp->z_source, t); ! 286: #endif /* SYSV */ ! 287: } ! 288: return; ! 289: } ! 290: #ifdef DEBUG ! 291: if (debug) ! 292: fprintf(ddt,"zoneref: need xfer (%d < %d)\n", ! 293: zp->z_serial, zp_start.z_serial); ! 294: #endif DEBUG ! 295: hp = (HEADER *) buf; ! 296: soacnt = 0; ! 297: /* mark all existing RR's for zone as "old" */ ! 298: mark_zone (hashtab, zone, 1); ! 299: for (tries = 0; ; tries++) { ! 300: if (soacnt == 0) { ! 301: /* delete unmarked (new) RR's for zone */ ! 302: if (tries) ! 303: clean_zone (hashtab, zone, 0); ! 304: if ((n = res_mkquery(QUERY, zp->z_origin, C_IN, ! 305: T_AXFR, (char *)NULL, 0, NULL, ! 306: buf, sizeof(buf))) < 0) { ! 307: syslog(LOG_ERR, "zoneref: res_mkquery failed"); ! 308: (void) close(s); ! 309: (void) sigvec(SIGALRM, &osv, ! 310: (struct sigvec *)0); ! 311: return; ! 312: } ! 313: /* ! 314: * Send length & message for zone transfer ! 315: */ ! 316: if (writemsg(s, buf, n) < 0) { ! 317: (void) close(s); ! 318: error++; ! 319: #ifdef DEBUG ! 320: if (debug >= 2) ! 321: fprintf(ddt,"writemsg failed\n"); ! 322: #endif ! 323: break; ! 324: } ! 325: } ! 326: /* ! 327: * Receive length & response ! 328: */ ! 329: cp = buf; ! 330: l = sizeof(u_short); ! 331: while (l > 0) { ! 332: (void) setitimer(ITIMER_REAL, &ival, ! 333: (struct itimerval *)NULL); ! 334: if ((n = recv(s, cp, l, 0)) > 0) { ! 335: cp += n; ! 336: l -= n; ! 337: } else { ! 338: if (errno == EINTR && !read_interrupted) ! 339: continue; ! 340: error++; ! 341: break; ! 342: } ! 343: } ! 344: (void) setitimer(ITIMER_REAL, &zeroival, ! 345: (struct itimerval *)NULL); ! 346: if (error) ! 347: break; ! 348: if ((len = htons(*(u_short *)buf)) == 0) ! 349: break; ! 350: l = len; ! 351: cp = buf; ! 352: eom = buf + len; ! 353: while (l > 0) { ! 354: (void) setitimer(ITIMER_REAL, &ival, ! 355: (struct itimerval *)NULL); ! 356: if ((n = recv(s, cp, l, 0)) > 0) { ! 357: cp += n; ! 358: l -= n; ! 359: } else { ! 360: if (errno == EINTR && !read_interrupted) ! 361: continue; ! 362: error++; ! 363: break; ! 364: } ! 365: } ! 366: (void) setitimer(ITIMER_REAL, &zeroival, ! 367: (struct itimerval *)NULL); ! 368: if (error) ! 369: break; ! 370: #ifdef DEBUG ! 371: if (debug >= 3) { ! 372: fprintf(ddt,"len = %d\n", len); ! 373: fp_query(buf, ddt); ! 374: } ! 375: #endif ! 376: cp = buf + sizeof(HEADER); ! 377: if (hp->qdcount) ! 378: cp += dn_skipname(cp, eom) + QFIXEDSZ; ! 379: nmp = cp; ! 380: tmp = cp + dn_skipname(cp, eom); ! 381: n = doupdate(buf, sizeof(buf), cp, zone, ! 382: (struct databuf **)0, DB_NODATA); ! 383: if (cp + n != eom) { ! 384: #ifdef DEBUG ! 385: if (debug) ! 386: fprintf(ddt,"zoneref: doupdate failed (%d, %d)\n", ! 387: cp - buf, n); ! 388: #endif ! 389: error++; ! 390: break; ! 391: } ! 392: GETSHORT(n, tmp); ! 393: if (n == T_SOA) { ! 394: if (soacnt == 0) { ! 395: soacnt++; ! 396: dn_expand(buf, buf + 512, nmp, name, ! 397: sizeof(name)); ! 398: tmp += 2 * sizeof(u_short) ! 399: + sizeof(u_long); ! 400: tmp += dn_skipname(tmp, eom); ! 401: tmp += dn_skipname(tmp, eom); ! 402: GETLONG(serial, tmp); ! 403: #ifdef DEBUG ! 404: if (debug) ! 405: fprintf(ddt, ! 406: "first SOA for %s, serial %d\n", ! 407: name, serial); ! 408: #endif DEBUG ! 409: continue; ! 410: } ! 411: dn_expand(buf, buf + 512, nmp, name2, ! 412: sizeof(name2)); ! 413: if (strcasecmp(name, name2) !=0) { ! 414: #ifdef DEBUG ! 415: if (debug) ! 416: fprintf(ddt, ! 417: "extraneous SOA for %s\n", ! 418: name2); ! 419: #endif DEBUG ! 420: continue; ! 421: } ! 422: tmp -= sizeof(u_short); ! 423: soa_zinfo(&zp_finish, tmp, eom); ! 424: #ifdef DEBUG ! 425: if (debug) ! 426: fprintf(ddt, ! 427: "SOA, serial %d\n", zp_finish.z_serial); ! 428: #endif DEBUG ! 429: if (serial != zp_finish.z_serial) { ! 430: soacnt = 0; ! 431: #ifdef DEBUG ! 432: if (debug) ! 433: fprintf(ddt, ! 434: "serial changed, restart\n"); ! 435: #endif DEBUG ! 436: } else ! 437: break; ! 438: } ! 439: } ! 440: (void) close(s); ! 441: if ( error == 0) { ! 442: zp->z_refresh = zp_finish.z_refresh; ! 443: zp->z_retry = zp_finish.z_retry; ! 444: zp->z_expire = zp_finish.z_expire; ! 445: zp->z_minimum = zp_finish.z_minimum; ! 446: zp->z_serial = zp_finish.z_serial; ! 447: zp->z_lastupdate = tt.tv_sec; ! 448: zp->z_sysloged = 0; ! 449: zp->z_auth = 1; ! 450: /* delete previously marked RR's here, then dump */ ! 451: clean_zone (hashtab, zone, 1); ! 452: zonedump(zp); ! 453: (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); ! 454: return; ! 455: } ! 456: /* error: delete unmarked RR's here; remove old marks */ ! 457: clean_zone (hashtab, zone, 0); ! 458: mark_zone (hashtab, zone, 0); ! 459: #ifdef DEBUG ! 460: if (debug >= 2) ! 461: fprintf(ddt,"error receiving zone transfer\n"); ! 462: #endif ! 463: } ! 464: (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); ! 465: /* ! 466: * Freedom at last!! ! 467: * ! 468: * The land where all repressed slaves dream of. ! 469: * ! 470: * Can't find a master to talk to. ! 471: * syslog it and hope we can find a master during next maintenance. ! 472: */ ! 473: if (error && (!zp->z_sysloged)) { ! 474: syslog(LOG_WARNING, ! 475: "zoneref: Masters for secondary zone %s unreachable", ! 476: zp->z_origin); ! 477: zp->z_sysloged++; ! 478: } ! 479: zp->z_refresh = zp->z_retry; ! 480: if (tt.tv_sec - zp->z_lastupdate > zp->z_expire) ! 481: zp->z_auth = 0; ! 482: } ! 483: ! 484: #ifdef unused ! 485: /* ! 486: * Recursively delete all domains (except for root SOA records), ! 487: * starting from head of list pointed to by np. ! 488: */ ! 489: static DelDom(fnp, isroot) ! 490: struct namebuf *fnp; ! 491: int isroot; ! 492: { ! 493: register struct databuf *dp, *pdp = NULL; ! 494: register struct namebuf *np = fnp; ! 495: struct namebuf **npp, **nppend; ! 496: ! 497: #ifdef DEBUG ! 498: if (debug >= 3) ! 499: fprintf(ddt, "DelDom('%s', %d)\n", fnp->n_dname, isroot); ! 500: #endif DEBUG ! 501: ! 502: /* first do data records */ ! 503: for (dp = np->n_data; dp != NULL; ) { ! 504: /* skip the root SOA record (marks end of data) */ ! 505: if (isroot && dp->d_type == T_SOA) { ! 506: pdp = dp; ! 507: dp = dp->d_next; ! 508: continue; ! 509: } ! 510: dp = rm_datum(dp, np, pdp); ! 511: } ! 512: ! 513: /* next do subdomains */ ! 514: if (np->n_hash == NULL) ! 515: return; ! 516: npp = np->n_hash->h_tab; ! 517: nppend = npp + np->n_hash->h_size; ! 518: while (npp < nppend) { ! 519: for (np = *npp++; np != NULL; np = np->n_next) { ! 520: DelDom(np, 0); ! 521: } ! 522: } ! 523: } ! 524: #endif unused ! 525: ! 526: #ifdef DEBUG ! 527: printzoneinfo(zonenum) ! 528: int zonenum; ! 529: { ! 530: struct timeval tt; ! 531: struct zoneinfo *zp = &zones[zonenum]; ! 532: char *ZoneType; ! 533: ! 534: if (!debug) ! 535: return; /* Else fprintf to ddt will bomb */ ! 536: fprintf(ddt, "printzoneinfo(%d):\n", zonenum); ! 537: ! 538: gettime(&tt); ! 539: switch (zp->z_type) { ! 540: case Z_PRIMARY: ZoneType = "Primary"; break; ! 541: case Z_SECONDARY: ZoneType = "Secondary"; break; ! 542: case Z_CACHE: ZoneType = "Cache"; break; ! 543: default: ZoneType = "Unknown"; ! 544: } ! 545: if (zp->z_origin[0] == '\0') ! 546: fprintf(ddt,"origin ='.'"); ! 547: else ! 548: fprintf(ddt,"origin ='%s'", zp->z_origin); ! 549: fprintf(ddt,", type = %s", ZoneType); ! 550: fprintf(ddt,", source = %s\n", zp->z_source); ! 551: fprintf(ddt,"z_refresh = %ld", zp->z_refresh); ! 552: fprintf(ddt,", retry = %ld", zp->z_retry); ! 553: fprintf(ddt,", expire = %ld", zp->z_expire); ! 554: fprintf(ddt,", minimum = %ld", zp->z_minimum); ! 555: fprintf(ddt,", serial = %ld\n", zp->z_serial); ! 556: fprintf(ddt,"z_time = %d", zp->z_time); ! 557: fprintf(ddt,", now time : %d sec", tt.tv_sec); ! 558: fprintf(ddt,", time left: %d sec\n", zp->z_time - tt.tv_sec); ! 559: } ! 560: #endif DEBUG ! 561: ! 562: /* ! 563: * New code added by Rich Wales (UCLA), October 1986: ! 564: * ! 565: * The following routines manipulate the d_mark field. When a zone ! 566: * is being refreshed, the old RR's are marked. This allows old RR's to ! 567: * be cleaned up after the new copy of the zone has been completely read ! 568: * -- or new RR's to be cleaned up if an error prevents transfer of a ! 569: * new zone copy. ! 570: * ! 571: */ ! 572: ! 573: /* ! 574: * Set the "d_mark" field to on each RR in the zone "zone". ! 575: * Initially called with "htp" equal to "hashtab", this routine ! 576: * calls itself recursively in order to traverse all subdomains. ! 577: */ ! 578: mark_zone (htp, zone, flag) ! 579: struct hashbuf *htp; ! 580: register int zone; ! 581: register int flag; ! 582: { ! 583: register struct databuf *dp; ! 584: register struct namebuf *np; ! 585: register struct namebuf **npp, **nppend; ! 586: ! 587: nppend = htp->h_tab + htp->h_size; ! 588: for (npp = htp->h_tab; npp < nppend; npp++) { ! 589: for (np = *npp; np != NULL; np = np->n_next) { ! 590: for (dp = np->n_data; dp != NULL; dp = dp->d_next) ! 591: if (dp->d_zone == zone) ! 592: dp->d_mark = flag; ! 593: if (np->n_hash != NULL) /* mark subdomains */ ! 594: mark_zone (np->n_hash, zone, flag); ! 595: } ! 596: } ! 597: } ! 598: ! 599: /* ! 600: * clean_zone (htp, zone, flag) -- ! 601: * Delete all RR's in the zone "zone" whose "d_mark" values are ! 602: * equal to "flag". Originally called with "htp" equal to ! 603: * "hashtab", this routine calls itself recursively in order to ! 604: * traverse all subdomains. ! 605: */ ! 606: clean_zone (htp, zone, flag) ! 607: register struct hashbuf *htp; ! 608: register int zone; ! 609: register int flag; ! 610: { ! 611: register struct databuf *dp, *pdp; ! 612: register struct namebuf *np; ! 613: struct namebuf **npp, **nppend; ! 614: ! 615: nppend = htp->h_tab + htp->h_size; ! 616: for (npp = htp->h_tab; npp < nppend; npp++) { ! 617: for (np = *npp; np != NULL; np = np->n_next) { ! 618: for (pdp = NULL, dp = np->n_data; dp != NULL; ) { ! 619: if (dp->d_zone == zone && dp->d_mark == flag) ! 620: dp = rm_datum(dp, np, pdp); ! 621: else { ! 622: pdp = dp; ! 623: dp = dp->d_next; ! 624: } ! 625: } ! 626: /* Call recursively to clean up subdomains. */ ! 627: if (np->n_hash != NULL) ! 628: clean_zone (np->n_hash, zone, flag); ! 629: } ! 630: } ! 631: } ! 632:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.