diff options
Diffstat (limited to 'contrib/bind/bin/named/ns_xfr.c')
-rw-r--r-- | contrib/bind/bin/named/ns_xfr.c | 302 |
1 files changed, 246 insertions, 56 deletions
diff --git a/contrib/bind/bin/named/ns_xfr.c b/contrib/bind/bin/named/ns_xfr.c index 52d3464..e25a536 100644 --- a/contrib/bind/bin/named/ns_xfr.c +++ b/contrib/bind/bin/named/ns_xfr.c @@ -1,9 +1,9 @@ #if !defined(lint) && !defined(SABER) -static char rcsid[] = "$Id: ns_xfr.c,v 8.25 1998/03/25 18:47:34 halley Exp $"; +static const char rcsid[] = "$Id: ns_xfr.c,v 8.55 1999/10/13 16:39:13 vixie Exp $"; #endif /* not lint */ /* - * Copyright (c) 1996, 1997 by Internet Software Consortium. + * Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -24,6 +24,7 @@ static char rcsid[] = "$Id: ns_xfr.c,v 8.25 1998/03/25 18:47:34 halley Exp $"; #include <sys/param.h> #include <sys/file.h> #include <sys/socket.h> +#include <sys/un.h> #include <netinet/in.h> #include <arpa/nameser.h> @@ -32,26 +33,26 @@ static char rcsid[] = "$Id: ns_xfr.c,v 8.25 1998/03/25 18:47:34 halley Exp $"; #include <errno.h> #include <fcntl.h> #include <resolv.h> +#include <res_update.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <syslog.h> #include <time.h> +#include <unistd.h> #include <isc/eventlib.h> #include <isc/logging.h> #include <isc/memcluster.h> +#include <isc/dst.h> + #include "port_after.h" #include "named.h" static struct qs_x_lev *sx_freelev(struct qs_x_lev *lev); -static void sx_newmsg(struct qstream *qsp), - sx_sendlev(struct qstream *qsp), - sx_sendsoa(struct qstream *qsp); - static int sx_flush(struct qstream *qsp), sx_addrr(struct qstream *qsp, const char *dname, @@ -59,20 +60,18 @@ static int sx_flush(struct qstream *qsp), sx_nsrrs(struct qstream *qsp), sx_allrrs(struct qstream *qsp), sx_pushlev(struct qstream *qsp, struct namebuf *np); +static struct databuf *db_next(struct databuf *dp); /* * void - * ns_xfr(qsp, znp, zone, class, type, opcode, id) + * ns_xfr(qsp, znp, zone, class, type, opcode, id, serial_ixfr, in_tsig) * Initiate a concurrent (event driven) outgoing zone transfer. */ void ns_xfr(struct qstream *qsp, struct namebuf *znp, int zone, int class, int type, - int opcode, int id) + int opcode, int id, u_int32_t serial_ixfr, struct tsig_record *in_tsig) { - FILE *rfp; - int fdstat; - pid_t pid; server_info si; #ifdef SO_SNDBUF static const int sndbuf = XFER_BUFSIZE * 2; @@ -80,9 +79,26 @@ ns_xfr(struct qstream *qsp, struct namebuf *znp, #ifdef SO_SNDLOWAT static const int sndlowat = XFER_BUFSIZE; #endif + ns_updrec *changes; - ns_info(ns_log_xfer_out, "zone transfer of \"%s\" (%s) to %s", - zones[zone].z_origin, p_class(class), sin_ntoa(qsp->s_from)); + switch (type) { + case ns_t_axfr: /*FALLTHROUGH*/ + case ns_t_ixfr: +#ifdef BIND_ZXFR + case ns_t_zxfr: +#endif + ns_info(ns_log_xfer_out, + "zone transfer (%s) of \"%s\" (%s) to %s", + p_type(type), zones[zone].z_origin, p_class(class), + sin_ntoa(qsp->s_from)); + break; + default: + ns_warning(ns_log_xfer_out, + "unsupported XFR (type %s) of \"%s\" (%s) to %s", + p_type(type), zones[zone].z_origin, p_class(class), + sin_ntoa(qsp->s_from)); + goto abort; + } #ifdef SO_SNDBUF /* @@ -104,9 +120,10 @@ ns_xfr(struct qstream *qsp, struct namebuf *znp, if (sq_openw(qsp, 64*1024) == -1) goto abort; memset(&qsp->xfr, 0, sizeof qsp->xfr); - qsp->xfr.top = znp; + qsp->xfr.top.axfr = znp; qsp->xfr.zone = zone; qsp->xfr.class = class; + qsp->xfr.type = type; qsp->xfr.id = id; qsp->xfr.opcode = opcode; qsp->xfr.msg = memget(XFER_BUFSIZE); @@ -118,19 +135,76 @@ ns_xfr(struct qstream *qsp, struct namebuf *znp, zones[zone].z_numxfrs++; qsp->flags |= STREAM_AXFR; +#ifdef BIND_ZXFR + if (type == ns_t_zxfr) { + enum { rd = 0, wr = 1 }; + int z[2]; + pid_t p; + + if (pipe(z) < 0) { + ns_error(ns_log_xfer_out, "pipe: %s", strerror(errno)); + goto abort; + } + p = vfork(); + if (p < 0) { + ns_error(ns_log_xfer_out, "vfork: %s", strerror(errno)); + goto abort; + } + if (p == 0) { + /* Child. */ + dup2(z[rd], STDIN_FILENO); + dup2(qsp->s_rfd, STDOUT_FILENO); + execlp("gzip", "gzip", NULL); + ns_error(ns_log_xfer_out, "execlp: %s", strerror(errno)); + _exit(1); + } + ns_info(ns_log_xfer_out, "zxfr gzip pid %lu", p); + /* Parent. */ + dup2(z[wr], qsp->s_rfd); + close(z[wr]); + close(z[rd]); + + /* When a ZXFR completes, there can be no more requests. */ + qsp->flags |= STREAM_DONE_CLOSE; + } +#endif + si = find_server(qsp->s_from.sin_addr); if (si != NULL && si->transfer_format != axfr_use_default) qsp->xfr.transfer_format = si->transfer_format; else qsp->xfr.transfer_format = server_options->transfer_format; + if (in_tsig == NULL) + qsp->xfr.tsig_state = NULL; + else { + qsp->xfr.tsig_state = memget(sizeof(ns_tcp_tsig_state)); + ns_sign_tcp_init(in_tsig->key, in_tsig->sig, in_tsig->siglen, + qsp->xfr.tsig_state); + qsp->xfr.tsig_skip = 0; + } - if (sx_pushlev(qsp, znp) < 0) { - abort: - (void) shutdown(qsp->s_rfd, 2); - sq_remove(qsp); - return; + if (type == ns_t_ixfr) { + changes = ixfr_get_change_list(&zones[zone], serial_ixfr, + zones[zone].z_serial); + if (changes != NULL) + { + qsp->xfr.serial = serial_ixfr; + qsp->xfr.top.ixfr = changes; + } + else + type = ns_t_axfr; } - (void) sq_writeh(qsp, sx_sendsoa); + if (sx_pushlev(qsp, znp) < 0) { + abort: + (void) shutdown(qsp->s_rfd, 2); + sq_remove(qsp); + return; + } + if (type != ns_t_ixfr) + (void) sq_writeh(qsp, sx_sendsoa); + else + (void) sq_writeh(qsp, sx_send_ixfr); + } /* @@ -176,7 +250,7 @@ ns_freexfr(struct qstream *qsp) { * init the header of a message, reset the compression pointers, and * reset the write pointer to the first byte following the header. */ -static void +void sx_newmsg(struct qstream *qsp) { HEADER *hp = (HEADER *)qsp->xfr.msg; @@ -190,6 +264,11 @@ sx_newmsg(struct qstream *qsp) { qsp->xfr.ptrs[1] = NULL; qsp->xfr.cp = qsp->xfr.msg + HFIXEDSZ; + + qsp->xfr.eom = qsp->xfr.msg + XFER_BUFSIZE; + + if (qsp->xfr.tsig_state != NULL) + qsp->xfr.eom -= TSIG_BUF_SIZE; } /* @@ -205,12 +284,30 @@ sx_flush(struct qstream *qsp) { #ifdef DEBUG if (debug >= 10) - fp_nquery(qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg, - log_get_stream(packet_channel)); + res_pquery(&res, qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg, + log_get_stream(packet_channel)); #endif + if (qsp->xfr.tsig_state != NULL && qsp->xfr.tsig_skip == 0) { + int msglen = qsp->xfr.cp - qsp->xfr.msg; + + ns_sign_tcp(qsp->xfr.msg, &msglen, qsp->xfr.eom - qsp->xfr.msg, + NOERROR, qsp->xfr.tsig_state, + qsp->xfr.state == s_x_done); + + if (qsp->xfr.state == s_x_done) { + memput(qsp->xfr.tsig_state, sizeof(ns_tcp_tsig_state)); + qsp->xfr.tsig_state = NULL; + } + qsp->xfr.cp = qsp->xfr.msg + msglen; + + } ret = sq_write(qsp, qsp->xfr.msg, qsp->xfr.cp - qsp->xfr.msg); - if (ret >= 0) + if (ret >= 0) { qsp->xfr.cp = NULL; + qsp->xfr.tsig_skip = 0; + } + else + qsp->xfr.tsig_skip = 1; return (ret); } @@ -229,7 +326,7 @@ static int sx_addrr(struct qstream *qsp, const char *dname, struct databuf *dp) { HEADER *hp = (HEADER *)qsp->xfr.msg; u_char **edp = qsp->xfr.ptrs + sizeof qsp->xfr.ptrs / sizeof(u_char*); - int n; + int n, type; if (qsp->xfr.cp != NULL) { if (qsp->xfr.transfer_format == axfr_one_answer && @@ -238,15 +335,34 @@ sx_addrr(struct qstream *qsp, const char *dname, struct databuf *dp) { } if (qsp->xfr.cp == NULL) sx_newmsg(qsp); + + /* + * Add question to first answer. + */ + if (qsp->xfr.state == s_x_firstsoa && dp->d_type == T_SOA ) { + if ((qsp->xfr.type == ns_t_ixfr) || (qsp->flags & STREAM_AXFRIXFR)) { + n = dn_comp(dname, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, + qsp->xfr.ptrs, edp); + if (n > 0 && (qsp->xfr.cp + n + INT16SZ * 2) <= qsp->xfr.eom) { + qsp->xfr.cp += n; + type = (qsp->xfr.type == ns_t_zxfr) ? + ns_t_axfr : qsp->xfr.type; + PUTSHORT((u_int16_t) type, qsp->xfr.cp); + PUTSHORT((u_int16_t) qsp->xfr.class, qsp->xfr.cp); + hp->qdcount = htons(ntohs(hp->qdcount) + 1); + } + } + } + n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, - 0, qsp->xfr.ptrs, edp); + 0, qsp->xfr.ptrs, edp, 0); if (n < 0) { if (sx_flush(qsp) < 0) return (-1); if (qsp->xfr.cp == NULL) sx_newmsg(qsp); n = make_rr(dname, dp, qsp->xfr.cp, qsp->xfr.eom - qsp->xfr.cp, - 0, qsp->xfr.ptrs, edp); + 0, qsp->xfr.ptrs, edp, 0); INSIST(n >= 0); } hp->ancount = htons(ntohs(hp->ancount) + 1); @@ -264,18 +380,37 @@ sx_addrr(struct qstream *qsp, const char *dname, struct databuf *dp) { * side effects: * if progress was made, header and pointers will be advanced. */ -static int +int sx_soarr(struct qstream *qsp) { struct databuf *dp; + int added_soa = 0; - foreach_rr(dp, qsp->xfr.top, T_SOA, qsp->xfr.class, qsp->xfr.zone) { + foreach_rr(dp, qsp->xfr.top.axfr, T_SOA, qsp->xfr.class, + qsp->xfr.zone) { if (sx_addrr(qsp, zones[qsp->xfr.zone].z_origin, dp) < 0) { /* RR wouldn't fit. Bail out. */ return (-1); } - return (0); + added_soa = 1; + break; } - ns_panic(ns_log_xfer_out, 1, "no SOA at zone top"); + if (added_soa == 0) + ns_panic(ns_log_xfer_out, 1, "no SOA at zone top"); + if (qsp->xfr.state == s_x_firstsoa) { + foreach_rr(dp, qsp->xfr.top.axfr, T_SIG, qsp->xfr.class, + qsp->xfr.zone) + { + if (SIG_COVERS(dp) != T_SOA) + continue; + if (sx_addrr(qsp, zones[qsp->xfr.zone].z_origin, dp) < + 0) + { + /* RR wouldn't fit. Bail out. */ + return (-1); + } + } + } + return (0); } /* @@ -283,6 +418,9 @@ sx_soarr(struct qstream *qsp) { * sx_nsrrs(qsp) * add the NS RR's at the current level's current np, * to the assembly message + * This function also adds the SIG(NS), KEY, SIG(KEY), NXT, SIG(NXT) + * the reason for this these records are part of the delegation. + * * return: * >1 = number of NS RRs added, note that there may be more * 0 = success, there are no more NS RRs at this level @@ -303,11 +441,11 @@ sx_nsrrs(struct qstream *qsp) { int rrcount, class; class = qsp->xfr.class; - top = qsp->xfr.top; + top = qsp->xfr.top.axfr; rrcount = 0; for ((void)NULL; (dp = qsp->xfr.lev->dp) != NULL; - qsp->xfr.lev->dp = dp->d_next) { + qsp->xfr.lev->dp = db_next(dp)) { /* XYZZY foreach_rr? */ if (dp->d_class != class && class != C_ANY) continue; @@ -323,14 +461,21 @@ sx_nsrrs(struct qstream *qsp) { */ if (dp->d_zone == DB_Z_CACHE) continue; - if (dp->d_type != T_NS) + + if (dp->d_type != T_NS && dp->d_type != T_KEY && + dp->d_type != T_NXT && dp->d_type != T_SIG) + continue; + if (dp->d_type == T_SIG && ((SIG_COVERS(dp) != T_NS) && + (SIG_COVERS(dp) != T_KEY) && (SIG_COVERS(dp) != T_NXT))) continue; if (!(qsp->xfr.lev->flags & SXL_GLUING)) { if (sx_addrr(qsp, qsp->xfr.lev->dname, dp) < 0) { /* RR wouldn't fit. Bail out. */ return (-1); } - rrcount++; + if (dp->d_type != T_NS) /* no glue processing */ + continue; + rrcount++; /* only count NS records */ } /* @@ -373,6 +518,16 @@ sx_nsrrs(struct qstream *qsp) { */ return (-1); } + /* for IPv6 glue AAAA record transfer */ + /* patched by yasuhiro@nic.ad.jp, 1999/5/23 */ + foreach_rr(gdp, gnp, T_AAAA, class, DB_Z_CACHE) + if (sx_addrr(qsp, fname, gdp) < 0) { + /* + * Rats. We already sent the NS RR, too. + * Note that SXL_GLUING is being left on. + */ + return (-1); + } qsp->xfr.lev->flags &= ~SXL_GLUING; } return (rrcount); @@ -383,6 +538,8 @@ sx_nsrrs(struct qstream *qsp) { * sx_allrrs(qsp) * add the non-(SOA,NS) RR's at the current level's current np, * to the assembly message + * do not add the DNSSEC types KEY and NXT as the delegation check + * wrote these types out. * return: * >0 = number of RR's added, note that there may be more * 0 = success, there are no more RRs at this level @@ -396,20 +553,18 @@ sx_nsrrs(struct qstream *qsp) { */ static int sx_allrrs(struct qstream *qsp) { - struct databuf *dp, *tdp, *gdp; - struct namebuf *gnp, *tnp, *top; - struct hashbuf *htp; - const char *fname; + struct databuf *dp; + struct namebuf *top; int rrcount, class; u_int zone; class = qsp->xfr.class; - top = qsp->xfr.top; + top = qsp->xfr.top.axfr; zone = qsp->xfr.zone; rrcount = 0; for ((void)NULL; (dp = qsp->xfr.lev->dp) != NULL; - qsp->xfr.lev->dp = dp->d_next) { + qsp->xfr.lev->dp = db_next(dp)) { /* XYZZY foreach_rr? */ if (dp->d_class != class && class != C_ANY) continue; @@ -417,18 +572,13 @@ sx_allrrs(struct qstream *qsp) { continue; if (dp->d_zone != zone || stale(dp)) continue; - if (dp->d_type == T_SOA || dp->d_type == T_NS) + if (dp->d_type == T_SOA || dp->d_type == T_NS || + dp->d_type == T_NXT || dp->d_type == T_KEY) + continue; + if (dp->d_type == T_SIG && + (SIG_COVERS(dp) == T_SOA || SIG_COVERS(dp) == T_NS || + SIG_COVERS(dp) == T_KEY || SIG_COVERS(dp) == T_NXT)) continue; - /* XXXRTH I presume this is still relevant and that - this is the right place... */ -#if 0 /* Not yet implemented. Only a SHOULD in the I-D. -gnu@toad.com */ - /* skip the SIG AXFR record because we did it first too. */ - if (dp->d_type == T_SIG) { - int sig_rrtype = GETSHORT (dp->d_data); - if (sig_rrtype == T_AXFR) - continue; - } -#endif /* 0 */ INSIST(!(qsp->xfr.lev->flags & SXL_GLUING)); if (sx_addrr(qsp, qsp->xfr.lev->dname, dp) < 0) { @@ -452,7 +602,7 @@ sx_allrrs(struct qstream *qsp) { * qsp->xfr.state at the end of the topmost level. changes the * qsp->xfr.lev->state several times per domain name. */ -static void +void sx_sendlev(struct qstream *qsp) { struct qs_x_lev *lev; int rrcount; @@ -462,6 +612,12 @@ sx_sendlev(struct qstream *qsp) { switch (lev->state) { case sxl_ns: { while (lev->dp) { + /* Was the child zone reloaded under us? */ + if ((lev->dp->d_flags & DB_F_ACTIVE) == 0) { + (void) shutdown(qsp->s_rfd, 2); + sq_remove(qsp); + return; + } rrcount = sx_nsrrs(qsp); /* If we can't pack this one in, come back later. */ if (rrcount < 0) @@ -470,7 +626,7 @@ sx_sendlev(struct qstream *qsp) { * NS RRs other than those at the * zone top are zone cuts. */ - if (rrcount > 0 && qsp->xfr.top != lev->np) + if (rrcount > 0 && qsp->xfr.top.axfr != lev->np) lev->flags |= SXL_ZONECUT; } /* No more DP's for the NS RR pass on this NP. */ @@ -481,10 +637,18 @@ sx_sendlev(struct qstream *qsp) { /* No NS RR's, so it's safe to send other types. */ lev->state = sxl_all; lev->dp = lev->np->n_data; + if (lev->dp) + DRCNTINC(lev->dp); goto again; } case sxl_all: { while (lev->dp) { + /* Was a record updated under us? */ + if ((lev->dp->d_flags & DB_F_ACTIVE) == 0) { + (void) shutdown(qsp->s_rfd, 2); + sq_remove(qsp); + return; + } /* If we can't pack this one in, come back later. */ if (sx_allrrs(qsp) < 0) return; @@ -535,11 +699,15 @@ sx_sendlev(struct qstream *qsp) { * side effects: * changes qsp->xfr.state. adds RR to output buffer. */ -static void +void sx_sendsoa(struct qstream *qsp) { + HEADER * hp = (HEADER *) qsp->xfr.msg; + if (sx_soarr(qsp) == -1) return; /* No state change, come back here later. */ + hp->aa = 1; + switch (qsp->xfr.state) { case s_x_firstsoa: { /* Next thing to do is send the zone. */ @@ -549,8 +717,8 @@ sx_sendsoa(struct qstream *qsp) { } case s_x_lastsoa: { /* Next thing to do is go back and wait for another query. */ - (void)sx_flush(qsp); qsp->xfr.state = s_x_done; + (void)sx_flush(qsp); sq_writeh(qsp, sq_flushw); break; } @@ -581,6 +749,8 @@ sx_pushlev(struct qstream *qsp, struct namebuf *np) { new->state = sxl_ns; new->np = np; new->dp = np->n_data; + if (new->dp) + DRCNTINC(new->dp); getname(np, new->dname, sizeof new->dname); /* * We find the subdomains by looking in the hash table for this @@ -637,6 +807,26 @@ static struct qs_x_lev * sx_freelev(struct qs_x_lev *lev) { struct qs_x_lev *next = lev->next; + if (lev->dp) { + DRCNTDEC(lev->dp); + if (lev->dp->d_rcnt == 0) + db_freedata(lev->dp); + } memput(lev, sizeof *lev); return (next); } + +static struct databuf * +db_next(struct databuf *dp) { + struct databuf *next = dp->d_next; + + DRCNTDEC(dp); + if (dp->d_rcnt == 0) + db_freedata(dp); + + if (next) + DRCNTINC(next); + + return (next); +} + |