diff options
Diffstat (limited to 'contrib/bind/bin/named/ns_req.c')
-rw-r--r-- | contrib/bind/bin/named/ns_req.c | 514 |
1 files changed, 406 insertions, 108 deletions
diff --git a/contrib/bind/bin/named/ns_req.c b/contrib/bind/bin/named/ns_req.c index 252ddbd..6695881 100644 --- a/contrib/bind/bin/named/ns_req.c +++ b/contrib/bind/bin/named/ns_req.c @@ -1,6 +1,6 @@ #if !defined(lint) && !defined(SABER) static const char sccsid[] = "@(#)ns_req.c 4.47 (Berkeley) 7/1/91"; -static const char rcsid[] = "$Id: ns_req.c,v 8.138.2.4 2001/08/10 03:00:14 marka Exp $"; +static const char rcsid[] = "$Id: ns_req.c,v 8.162 2002/02/01 00:05:36 marka Exp $"; #endif /* not lint */ /* @@ -153,11 +153,11 @@ static enum req_action req_query(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp, int *msglenp, u_char *msg, int dfd, int *ra, struct sockaddr_in from, - struct tsig_record *in_tsig); + struct tsig_record *in_tsig, + u_int16_t udpsize); static enum req_action req_iquery(HEADER *hp, u_char **cpp, u_char *eom, - int *buflenp, u_char *msg, - struct sockaddr_in from); + int *buflenp, struct sockaddr_in from); #ifdef BIND_NOTIFY static enum req_action req_notify(HEADER *hp, u_char **cpp, u_char *eom, @@ -165,6 +165,132 @@ static enum req_action req_notify(HEADER *hp, u_char **cpp, u_char *eom, #endif /* + * See if there is a OPT record at the end of the message. + * + * Results: + * -1 FORMERR + * 0 last RR is not a OPT record + * n>0 lenght of OPT record + */ +int +ns_get_opt(u_char *msg, u_char *eom, + u_int8_t *versionp, u_int16_t *rcodep, u_int16_t *flagp, + u_int16_t *bufsizep, u_char **optionsp, size_t *optsizep) +{ + HEADER *hp = (HEADER *) msg; + u_char *start, *options, *cp; + u_int8_t version; + u_int16_t rdlen, type, bufsize, flags, optsize, rcode; + int i, n, root; + + if (msg == NULL || eom == NULL || (msg + HFIXEDSZ) > eom) + return (-1); + + if (ntohs(hp->arcount) == 0) + return (0); + + cp = msg + HFIXEDSZ; + n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount)); + if (n < 0) + return (-1); + cp += n; + n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount)); + if (n < 0) + return (-1); + cp += n; + n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount)); + if (n < 0) + return (-1); + cp += n; + i = ntohs(hp->arcount); + while (i-- > 0) { + start = cp; + if (cp >= eom) + return (-1); + root = (*cp == 0); + n = dn_skipname(cp, eom); + if (n < 0) + return (-1); + cp += n; + if (cp + (2 + 2 + 4 + 2) > eom) + return (-1); + GETSHORT(type, cp); + if (type != ns_t_opt) { + cp += INT16SZ + INT32SZ; /* class, ttl */ + GETSHORT(rdlen, cp); + if (cp + rdlen > eom) + return (-1); + cp += rdlen; + continue; + } + /* We have the OPT record. Check it out in detail. */ + if (!root) + return (-1); + GETSHORT(bufsize, cp); + rcode = (*cp++ <<4) + hp->rcode ; + version = *cp++; + GETSHORT(flags, cp); + GETSHORT(rdlen, cp); + /* ensure options are well formed */ + options = cp; + optsize = rdlen; + while (rdlen != 0) { + u_int16_t code; + u_int16_t len; + + if (rdlen < 4) + return (-1); + GETSHORT(code, cp); + GETSHORT(len, cp); + rdlen -= 4; + if (len > rdlen) + return (-1); + cp += len; + rdlen -= len; + } + /* Everything checks out. */ + if (versionp != NULL) + *versionp = version; + if (rcodep != NULL) + *rcodep = rcode; + if (flagp != NULL) + *flagp = flags; + if (bufsizep != NULL) + *bufsizep = bufsize; + if (optionsp != NULL) + *optionsp = options; + if (optsizep != NULL) + *optsizep = optsize; + return (cp - start); + } + /* OPT not found */ + return (0); +} + +int +ns_add_opt(u_char *msg, u_char *cp, size_t buflen, u_int8_t version, + u_int16_t rcode, u_int16_t size, u_int16_t flags, + u_char *options, size_t optlen) +{ + HEADER *hp = (HEADER *) msg; + + if ((cp + 1 + 2 + 2 + 4 + 2 + optlen) > (msg + buflen)) + return (-1); + + *cp++ = 0; /* "." */ + PUTSHORT(ns_t_opt, cp); /* type */ + PUTSHORT(size, cp); /* class (udp size) */ + *cp++ = (rcode >> 4) & 0xff; /* ttl (rcode + version + flags) */ + hp->rcode = rcode & 0xf; + *cp++ = version; + PUTSHORT(flags, cp); + PUTSHORT(optlen, cp); /* rdlen */ + memcpy(cp, options, optlen); /* options */ + hp->arcount = htons(ntohs(hp->arcount) + 1); + return (1 + 2 + 2 + 4 + 2 + optlen); +} + +/* * Process request using database; assemble and send response. */ void @@ -173,8 +299,8 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, { HEADER *hp = (HEADER *) msg; u_char *cp, *eom; - enum req_action action; - int n, ra, has_tsig, tsig_size, sig2len; + enum req_action action = Return; + int n, ra, has_tsig, tsig_size = 0, opt_size = 0, sig2len; u_char *tsigstart; u_char sig[TSIG_SIG_SIZE], sig2[TSIG_SIG_SIZE]; struct tsig_record *in_tsig = NULL; @@ -182,8 +308,13 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, int msglen_orig = msglen; int buflen_orig = buflen; int siglen = sizeof sig; - DST_KEY *key; + DST_KEY *key = NULL; time_t tsig_time; + int opt = 0; + u_int8_t version = 0; + u_int16_t rcode = ns_r_noerror; + u_int16_t udpsize = 0; + int drop; #ifdef DEBUG if (debug > 3) { @@ -192,6 +323,10 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, } #endif + drop = drop_port(ntohs(from.sin_port)); + if (qsp == NULL && drop == 1) + return; + tsigstart = ns_find_tsig(msg, msg + msglen); if (tsigstart == NULL) has_tsig = 0; @@ -260,6 +395,7 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, in_tsig->siglen = siglen; memcpy(in_tsig->sig, sig, siglen); tsig_size = msglen_orig - msglen; + in_tsig->tsig_size = tsig_size; } else if (has_tsig) { action = Finish; in_tsig = memget(sizeof(struct tsig_record)); @@ -268,6 +404,7 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, in_tsig->key = NULL; in_tsig->siglen = 0; tsig_size = msg + msglen - tsigstart; + in_tsig->tsig_size = tsig_size; msglen = tsigstart - msg; } @@ -275,6 +412,30 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, nsid_hash((u_char *)&tt, sizeof(tt)); nsid_hash(msg, (msglen > 512) ? 512 : msglen); + if (error == NOERROR) { + + opt = ns_get_opt(msg, msg + msglen, &version, + NULL, NULL, &udpsize, NULL, NULL); + if (opt < 0) { + rcode = ns_r_formerr; + action = Finish; + } else if (opt == 0) { + if (qsp == NULL && buflen > PACKETSZ) + buflen_orig = buflen = PACKETSZ; + } else if (opt > 0) { + if (version != 0) { + rcode = ns_r_badvers; + action = Finish; + } + opt_size = 11; + if (udpsize < 512) + udpsize = 512; + if (qsp == NULL && buflen > udpsize) + buflen_orig = buflen = udpsize; + } + } else if (qsp == NULL && buflen > PACKETSZ) + buflen_orig = buflen = PACKETSZ; + /* * It's not a response so these bits have no business * being set. will later simplify work if we can @@ -290,6 +451,8 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, if (error == NOERROR) hp->rcode = ns_r_noerror; + if (rcode == ns_r_noerror) + rcode = hp->rcode; cp = msg + HFIXEDSZ; eom = msg + msglen; buflen -= HFIXEDSZ; @@ -297,16 +460,17 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, free_addinfo(); /* sets addcount to zero */ dnptrs[0] = NULL; - if (error == NOERROR) { + if (error == NOERROR && rcode == ns_r_noerror) { switch (hp->opcode) { case ns_o_query: action = req_query(hp, &cp, eom, qsp, &buflen, &msglen, - msg, dfd, &ra, from, in_tsig); + msg, dfd, &ra, from, + in_tsig, udpsize); break; case ns_o_iquery: - action = req_iquery(hp, &cp, eom, &buflen, msg, from); + action = req_iquery(hp, &cp, eom, &buflen, from); break; #ifdef BIND_NOTIFY @@ -317,8 +481,7 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, #ifdef BIND_UPDATE case ns_o_update: - action = req_update(hp, cp, eom, msg, qsp, dfd, from, - in_tsig); + action = req_update(hp, cp, eom, msg, from, in_tsig); break; #endif /* BIND_UPDATE */ @@ -334,6 +497,7 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, hp->rcode = ns_r_notimpl; action = Finish; } + rcode = hp->rcode; } if (in_tsig != NULL) { @@ -342,13 +506,20 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, } /* + * Loop advoidance. + */ + if (qsp == NULL && drop == 2 && + (hp->rcode == FORMERR || hp->rcode == NOTIMP)) + action = Return; + + /* * Vector via internal opcode. */ switch (action) { case Return: return; case Refuse: - hp->rcode = ns_r_refused; + rcode = hp->rcode = ns_r_refused; cp = eom; /*FALLTHROUGH*/ case Finish: @@ -365,13 +536,14 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, hp->qr = 1; /* set Response flag */ hp->ra = ra; /* init above, may be modified by req_query */ - if (!hp->tc && has_tsig > 0 && buflen < tsig_size) + if (!hp->tc && (has_tsig > 0 || opt > 0) && + buflen < (tsig_size + opt_size)) hp->tc = 1; /* * If there was a format error, then we don't know what the msg has. */ - if (hp->rcode == ns_r_formerr) { + if (hp->rcode == ns_r_formerr || rcode == ns_r_badvers) { hp->qdcount = htons(0); hp->ancount = htons(0); hp->nscount = htons(0); @@ -380,44 +552,67 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp, } /* - * If the query had a TSIG and the message is truncated or there was - * a TSIG error, build a new message with no data and a TSIG. + * If the query had a TSIG / OPT and the message is truncated or + * there was a TSIG error, build a new message with no data and a + * TSIG / OPT. */ - if ((hp->tc || error != NOERROR) && has_tsig > 0) { + if ((hp->tc || error != NOERROR) && (has_tsig > 0 || opt > 0)) { sign_again: hp->ancount = htons(0); hp->nscount = htons(0); hp->arcount = htons(0); cp = msg + HFIXEDSZ; cp += ns_skiprr(cp, msg + msglen, ns_s_qd, ntohs(hp->qdcount)); - sig2len = sizeof sig2; - msglen = cp - msg; - buflen = buflen_orig - msglen; - n = ns_sign(msg, &msglen, msglen + buflen, error, key, - sig, siglen, sig2, &sig2len, tsig_time); - if (n == NS_TSIG_ERROR_NO_SPACE && ntohs(hp->qdcount) != 0) { - hp->qdcount = htons(0); - goto sign_again; + if (opt > 0) { + n = ns_add_opt(msg, cp, buflen_orig, 0, + rcode, EDNS_MESSAGE_SZ, 0, NULL, 0); + if (n < 0) { + hp->qdcount = htons(0); + goto sign_again; + } + cp += n; + } + if (has_tsig > 0) { + sig2len = sizeof sig2; + msglen = cp - msg; + buflen = buflen_orig - msglen; + n = ns_sign(msg, &msglen, msglen + buflen, error, key, + sig, siglen, sig2, &sig2len, tsig_time); + if (n == NS_TSIG_ERROR_NO_SPACE && + ntohs(hp->qdcount) != 0) { + hp->qdcount = htons(0); + goto sign_again; + } + if (n != 0) + ns_info(ns_log_default, + "ns_req: unable to sign response"); + cp = msg + msglen; } - if (n != 0) - ns_info(ns_log_default, - "ns_req: unable to sign response"); - cp = msg + msglen; } - /* Either the message is not truncated or there was no TSIG */ + /* Either the message is not truncated or there was no TSIG & OPT */ else { /* * Reserve space for tsig if required. */ - if (has_tsig > 0) - buflen -= tsig_size; + if (has_tsig > 0 || opt_size != 0) + buflen -= tsig_size + opt_size; + INSIST(buflen >= 0); + msglen = cp - msg; n = doaddinfo(hp, cp, buflen); cp += n; buflen -= n; + msglen += n; + if (opt > 0) { + buflen += opt_size; + n = ns_add_opt(msg, cp, msglen + buflen, 0, + rcode, EDNS_MESSAGE_SZ, 0, NULL, 0); + INSIST(n > 0); + cp += n; + buflen -= n; + } if (has_tsig > 0) { buflen += tsig_size; sig2len = sizeof sig2; - msglen = cp - msg; n = ns_sign(msg, &msglen, msglen + buflen, error, key, sig, siglen, sig2, &sig2len, tsig_time); if (n != 0) { @@ -608,16 +803,42 @@ req_notify(HEADER *hp, u_char **cpp, u_char *eom, u_char *msg, } #endif /*BIND_NOTIFY*/ +static int +add_bind(HEADER *hp, u_char **cpp, u_char *msg, int *msglenp, + const char *label, const char *data) +{ + u_char *tp; + + hp->ancount = htons(1); + hp->nscount = htons(0); + hp->arcount = htons(0); + hp->rcode = ns_r_noerror; + hp->aa = 1; + hp->ra = 0; + copyCharString(cpp, label); /* Name */ + copyCharString(cpp, "BIND"); + *(*cpp)++ = 0x00; + PUTSHORT(T_TXT, *cpp); /* Type */ + PUTSHORT(C_CHAOS, *cpp); /* Class */ + PUTLONG(0, *cpp); /* TTL */ + tp = *cpp; /* Temp RdLength */ + PUTSHORT(0, *cpp); + copyCharString(cpp, data); + PUTSHORT((*cpp) - (tp + INT16SZ), tp); /* Real RdLength */ + *msglenp = *cpp - msg; /* Total message length */ + return (Finish); +} static enum req_action req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, int *buflenp, int *msglenp, u_char *msg, int dfd, int *ra, - struct sockaddr_in from, struct tsig_record *in_tsig) + struct sockaddr_in from, struct tsig_record *in_tsig, + u_int16_t udpsize) { int n, class, type, count, zone, foundname, founddata, omsglen, cname; int recursion_blocked_by_acl; u_int16_t id; - u_int32_t serial_ixfr; + u_int32_t serial_ixfr = 0; int ixfr_found; int ixfr_error = 0; char dnbuf2[MAXDNAME]; @@ -631,6 +852,8 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, struct zoneinfo *zp; struct databuf *dp; DST_KEY *in_key = (in_tsig != NULL) ? in_tsig->key : NULL; + int access_class; + int adjustlen = 0; nameserIncr(from.sin_addr, nssRcvdQ); @@ -648,14 +871,19 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, /* valid queries have one question and zero answers */ if ((ntohs(hp->qdcount) != 1) - || ntohs(hp->ancount) != 0 - || ntohs(hp->arcount) != 0) { + || ntohs(hp->ancount) != 0) { ns_debug(ns_log_default, 1, "FORMERR Query header counts wrong"); hp->rcode = ns_r_formerr; return (Finish); } + if (ntohs(hp->arcount) != 0) { + ns_debug(ns_log_default, 1, "Ignoring addition section"); + hp->arcount = htons(0); + adjustlen = 1; + } + /* * Get domain name, class, and type. */ @@ -680,8 +908,9 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, GETSHORT(type, *cpp); GETSHORT(class, *cpp); if (*cpp < eom && type != ns_t_ixfr) { - ns_debug(ns_log_default, 6, - "message length > received message"); + if (!adjustlen) + ns_debug(ns_log_default, 6, + "message length > received message"); *msglenp = *cpp - msg; } @@ -748,7 +977,7 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, } GETLONG(serial_ixfr, *cpp); /* ignore other soa counters */ - if ((*cpp + (4 * INT32SZ)) < eom) + if ((*cpp + (4 * INT32SZ)) < eom && !adjustlen) ns_debug(ns_log_default, 6, "ixfr: message length > received message"); /* Reset msglenp to cover just the question. */ @@ -838,9 +1067,15 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, /* * Begin Access Control Point */ - zone = DB_Z_CACHE; + + /* + * Map class ANY to to class IN for the purpose of access control. + */ + access_class = (class == C_ANY && !ns_t_xfr_p(type)) ? C_IN : class; + if (np) { +#ifndef FORWARD_ALLOWS struct namebuf *access_np; /* @@ -855,13 +1090,35 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, for (access_np = np; access_np != NULL; access_np = np_parent(access_np)) { dp = access_np->n_data; - while (dp && dp->d_class != class) + while (dp && dp->d_class != access_class) dp = dp->d_next; if (dp != NULL) { zone = dp->d_zone; break; } } +#else + /* + * Try looking for forward zone. It can be deeper than + * any entry in the cache. + */ + if (zone == DB_Z_CACHE) { + char *s = dname; + int escape = 0; + while ((zp = find_zone(s, access_class)) == NULL) { + if (*s == '\0') + break; + while (*s != '\0' && (escape || *s != '.')) { + escape = escape ? 0 : (*s == '\\'); + s++; + } + if (*s == '.') + s++; + } + if (zp != NULL) + zone = zp - zones; + } +#endif } zp = &zones[zone]; @@ -941,8 +1198,9 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, for (access_np = np; access_np != NULL; access_np = np_parent(access_np)) { dp = access_np->n_data; - while (dp && (dp->d_class != class || - dp->d_zone == DB_Z_CACHE)) + while (dp && + (dp->d_class != access_class || + dp->d_zone == DB_Z_CACHE)) dp = dp->d_next; if (dp != NULL) { zone = dp->d_zone; @@ -961,9 +1219,9 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, } } ns_notice(ns_log_security, - "denied query from %s for \"%s\" %s", + "denied query from %s for \"%s\" %s/%s", sin_ntoa(from), *dname ? dname : ".", - p_class(class)); + p_type(type), p_class(class)); nameserIncr(from.sin_addr, nssRcvdUQ); return (Refuse); } @@ -1054,28 +1312,18 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, * Yow! */ if (class == ns_c_chaos && type == ns_t_txt && - ns_samename(dnbuf, "VERSION.BIND") == 1) { - u_char *tp; + ns_samename(dnbuf, "VERSION.BIND") == 1 && + server_options->version != NULL && + server_options->version[0] != '\0') + return (add_bind(hp, cpp, msg, msglenp, + "VERSION", server_options->version)); - hp->ancount = htons(1); - hp->nscount = htons(0); - hp->arcount = htons(0); - hp->rcode = ns_r_noerror; - hp->aa = 1; - hp->ra = 0; - copyCharString(cpp, "VERSION"); /* Name */ - copyCharString(cpp, "BIND"); - *(*cpp)++ = 0x00; - PUTSHORT(T_TXT, *cpp); /* Type */ - PUTSHORT(C_CHAOS, *cpp); /* Class */ - PUTLONG(0, *cpp); /* TTL */ - tp = *cpp; /* Temp RdLength */ - PUTSHORT(0, *cpp); - copyCharString(cpp, server_options->version); - PUTSHORT((*cpp) - (tp + INT16SZ), tp); /* Real RdLength */ - *msglenp = *cpp - msg; /* Total message length */ - return (Finish); - } + if (class == ns_c_chaos && type == ns_t_txt && + ns_samename(dnbuf, "HOSTNAME.BIND") == 1 && + server_options->hostname != NULL && + server_options->hostname[0] != '\0') + return (add_bind(hp, cpp, msg, msglenp, + "HOSTNAME", server_options->hostname)); /* * If we don't know anything about the requested name, @@ -1365,8 +1613,9 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, if (n < 0) { ns_info(ns_log_default, "res_mkquery(%s) failed", dname); - hp->rcode = ns_r_servfail; + memcpy(msg, omsg, omsglen); memput(omsg, omsglen); + hp->rcode = ns_r_servfail; free_nsp(nsp); return (Finish); } @@ -1375,7 +1624,9 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, n = ns_forw(nsp, msg, *msglenp, from, qsp, dfd, &qp, dname, class, type, np, 0, in_tsig); if (n != FW_OK && cname) { + memcpy(msg, omsg, omsglen); memput(omsg, omsglen); + *msglenp = omsglen; omsg = NULL; } switch (n) { @@ -1387,6 +1638,11 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, qp->q_cmsgsize = omsglen; qp->q_id = id; } + if (udpsize != 0) { + qp->q_flags |= Q_EDNS; + qp->q_udpsize = udpsize; + } else + qp->q_udpsize = PACKETSZ; break; case FW_DUP: break; /* Duplicate request dropped */ @@ -1441,7 +1697,7 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp, static enum req_action req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp, - u_char *msg, struct sockaddr_in from) + struct sockaddr_in from) { u_int rdata_offset; size_t alen; @@ -1561,7 +1817,7 @@ req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp, *buflenp -= INT16SZ; hp->qdcount = htons(1); - if (alen > *buflenp) { + if ((int)alen > *buflenp) { hp->tc = 1; return (Finish); } @@ -1578,6 +1834,10 @@ int stale(struct databuf *dp) { struct zoneinfo *zp = &zones[dp->d_zone]; +#ifdef CHECK_MAGIC + INSIST(dp->d_magic == DATABUF_MAGIC); +#endif + switch (zp->z_type) { case z_master: @@ -1662,6 +1922,7 @@ make_rr(const char *name, struct databuf *dp, u_char *buf, int32_t n; int16_t type = dp->d_type; u_int32_t ttl; + u_char naptr_flag; ns_debug(ns_log_default, 5, "make_rr(%s, %lx, %lx, %d, %d) %d zone %d ttl %lu", @@ -1801,6 +2062,7 @@ make_rr(const char *name, struct databuf *dp, u_char *buf, buflen -= n + 1; if (buflen < 0) goto cleanup; + naptr_flag = (n == 1) ? *cp1 : 0; *cp++ = n; memcpy(cp, cp1, n); cp += n; @@ -1839,6 +2101,14 @@ make_rr(const char *name, struct databuf *dp, u_char *buf, if (n < 0) goto cleanup; cp += n; + if (doadd && *cp1 != 0) { + if (naptr_flag == 's' || naptr_flag == 'S') + addname((char*)cp1, name, type, T_SRV, + dp->d_class); + if (naptr_flag == 'a' || naptr_flag == 'A') + addname((char*)cp1, name, type, T_A, + dp->d_class); + } /* save data length */ n = (u_int16_t)((cp - sp) - INT16SZ); @@ -2022,8 +2292,9 @@ doaddinfo(HEADER *hp, u_char *msg, int msglen) { const char *fname; register int n, count; register int ns_logging; - int finishedA = 0; - int save_addcount = addcount; + int pass = 0; + int i, doadd; + if (!addcount) return (0); @@ -2043,15 +2314,18 @@ doaddinfo(HEADER *hp, u_char *msg, int msglen) { count = 0; cp = msg; loop: - for (ap = addinfo; --addcount >= 0; ap++) { + for (ap = addinfo, i = 0; i < addcount; ap++, i++) { int foundany = 0, foundcname = 0, save_count = count, save_msglen = msglen; u_char *save_cp = cp; - if ((finishedA == 1 && ap->a_type == T_A) || - (finishedA == 0 && ap->a_type == T_KEY)) + if ((pass != 0 && + (pass != 1 || server_options->preferred_glue == 0) && + ap->a_type == T_A) || + (pass != 0 && ap->a_type == T_SRV) || + (pass != 2 && ap->a_type == T_KEY)) continue; if (ns_logging) ns_debug(ns_log_default, 3, @@ -2066,35 +2340,52 @@ loop: /* look for the data */ (void)delete_stale(np); for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (dp->d_rcode) + if (dp->d_rcode == NXDOMAIN) { + if (dp->d_class == ap->a_class) + foundany++; continue; + } if ((match(dp, (int)ap->a_class, T_CNAME) && - dp->d_type == T_CNAME) || - (match(dp, C_IN, T_CNAME) && dp->d_type == T_CNAME)) { foundcname++; break; } + if (pass == 0 && ap->a_type == T_A && + server_options->preferred_glue != 0 && + !match(dp, (int)ap->a_class, + server_options->preferred_glue)) { + continue; + } + if (pass != 0 && ap->a_type == T_A && + server_options->preferred_glue != 0 && + match(dp, (int)ap->a_class, + server_options->preferred_glue)) { + continue; + } if (ap->a_type == T_A && !match(dp, (int)ap->a_class, T_A) && - !match(dp, C_IN, T_A) && !match(dp, (int)ap->a_class, T_AAAA) && - !match(dp, C_IN, T_AAAA) && - !match(dp, (int)ap->a_class, ns_t_a6) && - !match(dp, C_IN, ns_t_a6)) { + !match(dp, (int)ap->a_class, ns_t_a6)) { continue; } if (ap->a_type == T_KEY && - !match(dp, (int)ap->a_class, T_KEY) && - !match(dp, C_IN, T_KEY)) + !match(dp, (int)ap->a_class, T_KEY)) + continue; + if (ap->a_type == T_SRV && + !match(dp, (int)ap->a_class, T_SRV)) continue; foundany++; + if (dp->d_rcode) + continue; /* * Should be smart and eliminate duplicate * data here. XXX */ - if ((n = make_rr(ap->a_dname, dp, cp, msglen, 0, + doadd = 0; + if (ap->a_type == T_SRV) + doadd = 1; + if ((n = make_rr(ap->a_dname, dp, cp, msglen, doadd, dnptrs, dnptrs_end, 0)) < 0) { /* truncation in the additional-data section * is not all that serious. we do not set TC, @@ -2130,7 +2421,8 @@ loop: (ap->a_type == T_A || ap->a_type == T_AAAA)) { /* ask a real server for this info */ (void) sysquery(ap->a_dname, (int)ap->a_class, - ap->a_type, NULL, 0, ns_port, QUERY); + ap->a_type, NULL, NULL, 0, ns_port, + QUERY, 0); } if (foundcname) { if (!haveComplained(nhash(ap->a_dname), @@ -2141,15 +2433,15 @@ loop: p_type(ap->a_rtype), ap->a_dname); } } - freestr(ap->a_dname); - freestr(ap->a_rname); } - if (finishedA == 0) { - finishedA = 1; - addcount = save_addcount; + if (pass++ < 2) goto loop; /* now do the KEYs... */ - } hp->arcount = htons((u_int16_t)count); + for (ap = addinfo, i = 0; i < addcount; ap++, i++) { + ap->a_dname = freestr(ap->a_dname); + ap->a_rname = freestr(ap->a_rname); + } + addcount = 0; return (cp - msg); } @@ -2188,27 +2480,16 @@ free_addinfo() { struct addinfo *ap; for (ap = addinfo; --addcount >= 0; ap++) { - freestr(ap->a_dname); - freestr(ap->a_rname); + ap->a_dname = freestr(ap->a_dname); + ap->a_rname = freestr(ap->a_rname); } addcount = 0; } void free_nsp(struct databuf **nsp) { - while (*nsp) { - DRCNTDEC(*nsp); - if ((*nsp)->d_rcnt) - ns_debug(ns_log_default, 3, "free_nsp: %s rcnt %d", - (*nsp)->d_data, (*nsp)->d_rcnt); - else { - ns_debug(ns_log_default, 3, - "free_nsp: %s rcnt %d delayed", - (*nsp)->d_data, (*nsp)->d_rcnt); - db_freedata(*nsp); /* delayed free */ - } - *nsp++ = NULL; - } + while (*nsp) + db_detach(nsp++); } static void @@ -2218,3 +2499,20 @@ copyCharString(u_char **dst, const char *src) { memcpy(*dst, src, len); *dst += len; } + +/* + * Questionable source ports for queries / responses. + */ +int +drop_port(u_int16_t port) { + switch (port) { + case 7: /* echo */ + case 13: /* daytime */ + case 19: /* chargen */ + case 37: /* time */ + return (1); + case 464: /* kpasswd */ + return (2); + } + return (0); +} |