summaryrefslogtreecommitdiffstats
path: root/contrib/bind/bin/named/ns_req.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bind/bin/named/ns_req.c')
-rw-r--r--contrib/bind/bin/named/ns_req.c843
1 files changed, 637 insertions, 206 deletions
diff --git a/contrib/bind/bin/named/ns_req.c b/contrib/bind/bin/named/ns_req.c
index ee60ce4..d7ee0b5 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 char sccsid[] = "@(#)ns_req.c 4.47 (Berkeley) 7/1/91";
-static char rcsid[] = "$Id: ns_req.c,v 8.46 1998/03/27 00:21:03 halley Exp $";
+static const char sccsid[] = "@(#)ns_req.c 4.47 (Berkeley) 7/1/91";
+static const char rcsid[] = "$Id: ns_req.c,v 8.104 1999/10/15 19:49:04 vixie Exp $";
#endif /* not lint */
/*
@@ -82,7 +82,7 @@ static char rcsid[] = "$Id: ns_req.c,v 8.46 1998/03/27 00:21:03 halley Exp $";
*/
/*
- * Portions Copyright (c) 1996, 1997 by Internet Software Consortium.
+ * Portions 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
@@ -105,6 +105,7 @@ static char rcsid[] = "$Id: ns_req.c,v 8.46 1998/03/27 00:21:03 halley Exp $";
#include <sys/uio.h>
#include <sys/file.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
@@ -123,6 +124,8 @@ static char rcsid[] = "$Id: ns_req.c,v 8.46 1998/03/27 00:21:03 halley Exp $";
#include <isc/logging.h>
#include <isc/memcluster.h>
+#include <isc/dst.h>
+
#include "port_after.h"
#include "named.h"
@@ -131,7 +134,8 @@ struct addinfo {
char *a_dname; /* domain name */
char *a_rname; /* referred by */
u_int16_t a_rtype; /* referred by */
- u_int16_t a_class; /* class for address */
+ u_int16_t a_type; /* type for data */
+ u_int16_t a_class; /* class for data */
};
#ifndef BIND_UPDATE
@@ -140,14 +144,15 @@ enum req_action { Finish, Refuse, Return };
static struct addinfo addinfo[NADDRECS];
static void addname(const char *, const char *,
- u_int16_t, u_int16_t);
+ u_int16_t, u_int16_t, u_int16_t);
static void copyCharString(u_char **, const char *);
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,
- struct sockaddr_in from);
+ u_char *msg, int dfd, int *ra,
+ struct sockaddr_in from,
+ struct tsig_record *in_tsig);
static enum req_action req_iquery(HEADER *hp, u_char **cpp, u_char *eom,
int *buflenp, u_char *msg,
@@ -168,14 +173,89 @@ 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;
+ int n, ra, has_tsig, msglen_orig, tsig_size, siglen, sig2len;
+ u_char *tsigstart;
+ u_char sig[TSIG_SIG_SIZE], sig2[TSIG_SIG_SIZE];
+ struct tsig_record *in_tsig = NULL;
+ int error = NOERROR;
+ DST_KEY *key;
+ time_t tsig_time;
#ifdef DEBUG
if (debug > 3) {
ns_debug(ns_log_packet, 3, "ns_req(from %s)", sin_ntoa(from));
- fp_nquery(msg, msglen, log_get_stream(packet_channel));
+ res_pquery(&res, msg, msglen, log_get_stream(packet_channel));
}
#endif
+ msglen_orig = msglen;
+ siglen = sizeof(sig);
+
+ tsigstart = ns_find_tsig(msg, msg + msglen);
+ if (tsigstart == NULL)
+ has_tsig = 0;
+ else {
+ char buf[MAXDNAME];
+
+ has_tsig = 1;
+ ns_name_ntop(tsigstart, buf, sizeof(buf));
+ key = find_key(buf, NULL);
+ if (key == NULL) {
+ error = ns_r_badkey;
+ ns_debug(ns_log_default, 1,
+ "ns_req: TSIG verify failed - unknown key %s",
+ buf);
+ }
+ }
+ if (has_tsig && key != NULL) {
+ n = ns_verify(msg, &msglen, key, NULL, 0, sig, &siglen,
+ &tsig_time, 0);
+ if (n != 0) {
+ hp->rcode = ns_r_notauth;
+ /* A query should never have an error code set */
+ if (n == ns_r_badsig || n == ns_r_badkey ||
+ n == ns_r_badtime) {
+ ns_debug(ns_log_default, 1,
+ "ns_req: TSIG verify failed - query had error %s (%d) set",
+ p_rcode(n), n);
+ error = n;
+ action = Return;
+ }
+ /* If there's a processing error just respond */
+ else if (n == -ns_r_badsig || n == -ns_r_badkey ||
+ n == -ns_r_badtime) {
+ n = -n;
+ ns_debug(ns_log_default, 1,
+ "ns_req: TSIG verify failed - %s (%d)",
+ p_rcode(n), n);
+ error = n;
+ } else {
+ ns_debug(ns_log_default, 1,
+ "ns_req: TSIG verify failed - FORMERR");
+ error = ns_r_formerr;
+ }
+ action = Finish;
+ }
+ in_tsig = memget(sizeof(struct tsig_record));
+ if (in_tsig == NULL)
+ ns_panic(ns_log_default, 1, "memget failed");
+ in_tsig->key = key;
+ in_tsig->siglen = siglen;
+ memcpy(in_tsig->sig, sig, siglen);
+ tsig_size = msglen_orig - msglen;
+ } else if (has_tsig) {
+ action = Finish;
+ in_tsig = memget(sizeof(struct tsig_record));
+ if (in_tsig == NULL)
+ ns_panic(ns_log_default, 1, "memget failed");
+ in_tsig->key = NULL;
+ in_tsig->siglen = 0;
+ tsig_size = msg + msglen - tsigstart;
+ msglen = tsigstart - msg;
+ }
+
+ /* Hash some stuff so it's nice and random */
+ nsid_hash((u_char *)&tt, sizeof(tt));
+ nsid_hash(msg, (msglen > 512) ? 512 : msglen);
/*
* It's not a response so these bits have no business
@@ -184,8 +264,10 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp,
* comes in.
*/
hp->aa = hp->ra = 0;
+ ra = (NS_OPTION_P(OPTION_NORECURSE) == 0);
- hp->rcode = NOERROR;
+ if (error == NOERROR)
+ hp->rcode = ns_r_noerror;
cp = msg + HFIXEDSZ;
eom = msg + msglen;
buflen -= HFIXEDSZ;
@@ -193,49 +275,58 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp,
free_addinfo(); /* sets addcount to zero */
dnptrs[0] = NULL;
- switch (hp->opcode) {
- case ns_o_query:
- action = req_query(hp, &cp, eom, qsp,
- &buflen, &msglen,
- msg, dfd, from);
- break;
+ if (error == NOERROR) {
+ switch (hp->opcode) {
+ case ns_o_query:
+ action = req_query(hp, &cp, eom, qsp,
+ &buflen, &msglen,
+ msg, dfd, &ra, from, in_tsig);
+ break;
- case ns_o_iquery:
- action = req_iquery(hp, &cp, eom, &buflen, msg, from);
- break;
+ case ns_o_iquery:
+ action = req_iquery(hp, &cp, eom, &buflen, msg, from);
+ break;
#ifdef BIND_NOTIFY
- case ns_o_notify:
- action = req_notify(hp, &cp, eom, msg, from);
- break;
+ case ns_o_notify:
+ action = req_notify(hp, &cp, eom, msg, from);
+ break;
#endif
#ifdef BIND_UPDATE
- case ns_o_update:
- action = req_update(hp, cp, eom, msg, qsp, dfd, from);
- break;
+ case ns_o_update:
+ action = req_update(hp, cp, eom, msg, qsp, dfd, from,
+ in_tsig);
+ break;
#endif /* BIND_UPDATE */
- default:
- ns_debug(ns_log_default, 1,
- "ns_req: Opcode %d not implemented", hp->opcode);
- /* XXX - should syslog, limited by haveComplained */
- hp->qdcount = htons(0);
- hp->ancount = htons(0);
- hp->nscount = htons(0);
- hp->arcount = htons(0);
- hp->rcode = NOTIMP;
- action = Finish;
+ default:
+ ns_debug(ns_log_default, 1,
+ "ns_req: Opcode %d not implemented",
+ hp->opcode);
+ /* XXX - should syslog, limited by haveComplained */
+ hp->qdcount = htons(0);
+ hp->ancount = htons(0);
+ hp->nscount = htons(0);
+ hp->arcount = htons(0);
+ hp->rcode = ns_r_notimpl;
+ action = Finish;
+ }
+ }
+
+ if (in_tsig != NULL) {
+ memput(in_tsig, sizeof(struct tsig_record));
+ in_tsig = NULL;
}
/*
- * vector via internal opcode. (yes, it was even uglier before.)
+ * Vector via internal opcode.
*/
switch (action) {
case Return:
return;
case Refuse:
- hp->rcode = REFUSED;
+ hp->rcode = ns_r_refused;
cp = eom;
/*FALLTHROUGH*/
case Finish:
@@ -247,22 +338,73 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp,
}
/*
- * apply final polish
+ * Apply final polish.
*/
hp->qr = 1; /* set Response flag */
- hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0);
+ hp->ra = ra; /* init above, may be modified by req_query */
- n = doaddinfo(hp, cp, buflen);
- cp += n;
- buflen -= n;
+ if (!hp->tc && has_tsig > 0 && buflen < tsig_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) {
+ hp->qdcount = htons(0);
+ hp->ancount = htons(0);
+ hp->nscount = htons(0);
+ hp->arcount = htons(0);
+ }
+
+ /*
+ * 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 ((hp->tc || error != NOERROR) && has_tsig > 0) {
+ 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);
+ buflen += (msglen - (cp - msg));
+ msglen = cp - msg;
+ n = ns_sign(msg, &msglen, msglen + buflen, error, key,
+ sig, siglen, sig2, &sig2len, tsig_time);
+ if (n != 0) {
+ INSIST(0);
+ }
+ cp = msg + msglen;
+
+ }
+ /* Either the message is not truncated or there was no TSIG */
+ else {
+ if (has_tsig > 0)
+ buflen -= tsig_size;
+ n = doaddinfo(hp, cp, buflen);
+ 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) {
+ INSIST(0);
+ }
+ cp = msg + msglen;
+ }
+ }
#ifdef DEBUG
ns_debug(ns_log_default, 1,
- "ns_req: answer -> %s fd=%d id=%d size=%d",
+ "ns_req: answer -> %s fd=%d id=%d size=%d rc=%d",
sin_ntoa(from), (qsp == NULL) ? dfd : qsp->s_rfd,
- ntohs(hp->id), cp - msg);
+ ntohs(hp->id), cp - msg, hp->rcode);
if (debug >= 10)
- fp_nquery(msg, cp - msg, log_get_stream(packet_channel));
+ res_pquery(&res, msg, cp - msg,
+ log_get_stream(packet_channel));
#endif /*DEBUG*/
if (qsp == NULL) {
if (sendto(dfd, (char*)msg, cp - msg, 0,
@@ -276,7 +418,7 @@ ns_req(u_char *msg, int msglen, int buflen, struct qstream *qsp,
nameserIncr(from.sin_addr, nssSendtoErr);
}
nameserIncr(from.sin_addr, nssSentAns);
- if (hp->rcode == NXDOMAIN)
+ if (hp->rcode == ns_r_nxdomain)
nameserIncr(from.sin_addr, nssSentNXD);
if (!hp->aa)
nameserIncr(from.sin_addr, nssSentNaAns);
@@ -307,22 +449,13 @@ req_notify(HEADER *hp, u_char **cpp, u_char *eom, u_char *msg,
{
int n, type, class, zn;
char dnbuf[MAXDNAME];
- struct namebuf *np;
- const char *fname;
- struct hashbuf *htp = hashtab; /* lookup relative to root */
+ struct zoneinfo *zp;
- /* valid notify's have one question and zero answers */
- if ((ntohs(hp->qdcount) != 1)
- || ntohs(hp->ancount) != 0
- || ntohs(hp->nscount) != 0
- || ntohs(hp->arcount) != 0) {
+ /* valid notify's have one question */
+ if (ntohs(hp->qdcount) != 1) {
ns_debug(ns_log_notify, 1,
"FORMERR Notify header counts wrong");
- hp->qdcount = htons(0);
- hp->ancount = htons(0);
- hp->nscount = htons(0);
- hp->arcount = htons(0);
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
@@ -330,14 +463,14 @@ req_notify(HEADER *hp, u_char **cpp, u_char *eom, u_char *msg,
if (n < 0) {
ns_debug(ns_log_notify, 1,
"FORMERR Query expand name failed");
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
*cpp += n;
if (*cpp + 2 * INT16SZ > eom) {
ns_debug(ns_log_notify, 1,
"FORMERR notify too short");
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
GETSHORT(type, *cpp);
@@ -347,76 +480,95 @@ req_notify(HEADER *hp, u_char **cpp, u_char *eom, u_char *msg,
/* XXX - when answers are allowed, we'll need to do compression
* correctly here, and we will need to check for packet underflow.
*/
- np = nlookup(dnbuf, &htp, &fname, 0);
- if (!np) {
+ /* Find the zone this NOTIFY refers to. */
+ zp = find_auth_zone(dnbuf, class);
+ if (zp == NULL) {
ns_info(ns_log_notify,
- "rcvd NOTIFY for \"%s\", name not in cache",
+ "rcvd NOTIFY for \"%s\", name not one of our zones",
dnbuf);
- hp->rcode = SERVFAIL;
+ hp->rcode = ns_r_servfail;
return (Finish);
}
- zn = findMyZone(np, class);
- if (zn == DB_Z_CACHE || zones[zn].z_type != z_slave) {
- /* this can come if a user did an AXFR of some zone somewhere
- * and that zone's server now wants to tell us that the SOA
- * has changed. AXFR's always come from nonpriv ports so it
- * isn't possible to know whether it was the server or just
- * "dig". this condition can be avoided by using secure zones
- * since that way only real secondaries can AXFR from you.
- */
- ns_info(ns_log_notify,
- "NOTIFY for non-secondary name (%s), from %s",
- dnbuf, sin_ntoa(from));
- goto refuse;
- }
- if (findZonePri(&zones[zn], from) == -1) {
- ns_info(ns_log_notify,
- "NOTIFY from non-master server (zone %s), from %s",
- zones[zn].z_origin, sin_ntoa(from));
- goto refuse;
- }
+ /* Access control. */
switch (type) {
case T_SOA:
- if (strcasecmp(dnbuf, zones[zn].z_origin) != 0) {
+ if (zp->z_type != z_slave) {
+ /*
+ * This can come if a user did an AXFR of some zone
+ * somewhere and that zone's server now wants to
+ * tell us that the SOA has changed. AXFR's always
+ * come from nonpriv ports so it isn't possible to
+ * know whether it was the server or just "dig".
+ * This condition can be avoided by using secure
+ * zones since that way only real secondaries can
+ * AXFR from you.
+ */
+ ns_info(ns_log_notify,
+ "NOTIFY(SOA) for non-secondary name (%s), from %s",
+ dnbuf, sin_ntoa(from));
+ goto refuse;
+ }
+ if (ns_samename(dnbuf, zp->z_origin) != 1) {
ns_info(ns_log_notify,
"NOTIFY(SOA) for non-origin (%s), from %s",
dnbuf, sin_ntoa(from));
goto refuse;
}
- if (zones[zn].z_flags &
+ if (findZonePri(zp, from) == -1) {
+ ns_info(ns_log_notify,
+ "NOTIFY(SOA) from non-master server (zone %s), from %s",
+ zp->z_origin, sin_ntoa(from));
+ goto refuse;
+ }
+ break;
+ default:
+ /* No access requirements defined for other types. */
+ break;
+ }
+ /* The work occurs here. */
+ switch (type) {
+ case T_SOA:
+ if (zp->z_flags &
(Z_NEED_RELOAD|Z_NEED_XFER|Z_QSERIAL|Z_XFER_RUNNING)) {
ns_info(ns_log_notify,
"NOTIFY(SOA) for zone already xferring (%s)",
dnbuf);
goto noerror;
}
- zones[zn].z_time = tt.tv_sec;
- qserial_query(&zones[zn]);
- sched_zone_maint(&zones[zn]);
+ zp->z_time = tt.tv_sec;
+ qserial_query(zp);
+ sched_zone_maint(zp);
break;
default:
- /* unimplemented, but it's not a protocol error, just
+ /*
+ * Unimplemented, but it's not a protocol error, just
* something to be ignored.
*/
- break;
+ hp->rcode = ns_r_notimpl;
+ return (Finish);
}
noerror:
- hp->rcode = NOERROR;
+ hp->rcode = ns_r_noerror;
return (Finish);
refuse:
- hp->rcode = REFUSED;
+ hp->rcode = ns_r_refused;
return (Finish);
}
#endif /*BIND_NOTIFY*/
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,
- struct sockaddr_in from)
+ int *buflenp, int *msglenp, u_char *msg, int dfd, int *ra,
+ struct sockaddr_in from, struct tsig_record *in_tsig)
{
int n, class, type, count, zone, foundname, founddata, omsglen, cname;
+ int recursion_blocked_by_acl;
u_int16_t id;
- u_char **dpp, *omsg, *answers;
+ u_int32_t serial_ixfr;
+ int ixfr_found;
+ int ixfr_error = 0;
+ char dnbuf2[MAXDNAME];
+ u_char **dpp, *omsg, *answers, *afterq;
char dnbuf[MAXDNAME], *dname;
const char *fname;
struct hashbuf *htp;
@@ -425,6 +577,7 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
struct qinfo *qp;
struct zoneinfo *zp;
struct databuf *dp;
+ DST_KEY *in_key = (in_tsig != NULL) ? in_tsig->key : NULL;
nameserIncr(from.sin_addr, nssRcvdQ);
@@ -438,19 +591,15 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
omsglen = 0;
omsg = NULL;
id = 0;
+ recursion_blocked_by_acl = 0;
/* valid queries have one question and zero answers */
if ((ntohs(hp->qdcount) != 1)
|| ntohs(hp->ancount) != 0
- || ntohs(hp->nscount) != 0
|| ntohs(hp->arcount) != 0) {
ns_debug(ns_log_default, 1,
"FORMERR Query header counts wrong");
- hp->qdcount = htons(0);
- hp->ancount = htons(0);
- hp->nscount = htons(0);
- hp->arcount = htons(0);
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
@@ -464,35 +613,101 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
if (n < 0) {
ns_debug(ns_log_default, 1,
"FORMERR Query expand name failed");
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
*cpp += n;
+ answers = *cpp;
if (*cpp + 2 * INT16SZ > eom) {
ns_debug(ns_log_default, 1,
"FORMERR Query message length short");
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
GETSHORT(type, *cpp);
GETSHORT(class, *cpp);
- if (*cpp < eom) {
+ if (*cpp < eom && type != ns_t_ixfr) {
ns_debug(ns_log_default, 6,
"message length > received message");
*msglenp = *cpp - msg;
}
+ if (((ntohs(hp->nscount) != 0) && (type != ns_t_ixfr)) ||
+ ((ntohs(hp->nscount) != 1) && (type == ns_t_ixfr)))
+ {
+ ns_debug(ns_log_default, 1, "FORMERR Query nscount wrong");
+ hp->rcode = ns_r_formerr;
+ return (Finish);
+ }
+
+ afterq = *cpp;
qtypeIncr(type);
/*
* Process query.
*/
- if (type == T_AXFR) {
+ if (type == ns_t_ixfr) {
+ hp->nscount = htons(0);
+ hp->rd = 0; /* Force IXFR queries to be non recursive. */
+ n = dn_expand(msg, eom, *cpp, dnbuf2, sizeof dnbuf2);
+ if (n < 0) {
+ ns_debug(ns_log_default, 1,
+ "FORMERR Query expand name failed");
+ hp->rcode = ns_r_formerr;
+ return (Finish);
+ }
+ *cpp += n;
+ if (*cpp + 3 * INT16SZ + INT32SZ > eom) {
+ ns_debug(ns_log_default, 1,
+ "ran out of data in IXFR query");
+ hp->rcode = ns_r_formerr;
+ return (Finish);
+ }
+ GETSHORT(n, *cpp);
+ if (n != ns_t_soa || ns_samename(dnbuf, dnbuf2) != 1) {
+ ns_debug(ns_log_default, 1,
+ "FORMERR SOA record expected");
+ hp->rcode = ns_r_formerr;
+ return (Finish);
+ }
+ *cpp += INT32SZ + INT16SZ * 2; /* skip class, ttl, dlen */
+ if (0 >= (n = dn_skipname(*cpp, eom))) {
+ ns_debug(ns_log_default, 1,
+ "FORMERR Query expand name failed");
+ hp->rcode = ns_r_formerr;
+ return (Finish);
+ }
+ *cpp += n; /* mname */
+ if (0 >= (n = dn_skipname(*cpp, eom))) {
+ ns_debug(ns_log_default, 1,
+ "FORMERR Query expand name failed");
+ hp->rcode = ns_r_formerr;
+ return (Finish);
+ }
+ *cpp += n; /* rname */
+ if (*cpp + 5 * INT32SZ > eom) {
+ ns_debug(ns_log_default, 1,
+ "ran out of data in IXFR query");
+ hp->rcode = ns_r_formerr;
+ return (Finish);
+ }
+ GETLONG(serial_ixfr, *cpp);
+ /* ignore other soa counters */
+ if ((*cpp + (4 * INT32SZ)) < eom)
+ ns_debug(ns_log_default, 6,
+ "ixfr: message length > received message");
+ /* Reset msglenp to cover just the question. */
+ *msglenp = afterq - msg;
+ }
+ *cpp = afterq;
+
+ if (!ns_t_udp_p(type)) {
/* Refuse request if not a TCP connection. */
if (qsp == NULL) {
ns_info(ns_log_default,
- "rejected UDP AXFR from %s for \"%s\"",
- sin_ntoa(from), *dnbuf ? dnbuf : ".");
+ "rejected UDP %s from %s for \"%s\"",
+ p_type(type), sin_ntoa(from),
+ *dnbuf ? dnbuf : ".");
return (Refuse);
}
/* The position of this is subtle. */
@@ -507,10 +722,11 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
#ifdef QRYLOG
if (qrylog) {
- ns_info(ns_log_queries, "XX /%s/%s/%s",
+ ns_info(ns_log_queries, "%s/%s/%s/%s/%s",
+ (hp->rd) ? "XX+" : "XX ",
inet_ntoa(from.sin_addr),
(dname[0] == '\0') ? "." : dname,
- p_type(type));
+ p_type(type), p_class(class));
}
#endif /*QRYLOG*/
@@ -542,7 +758,7 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
struct in_addr ina;
if (inet_aton(dname, &ina)) {
- hp->rcode = NXDOMAIN;
+ hp->rcode = ns_r_nxdomain;
hp->aa = 1;
ns_debug(ns_log_default, 3,
"ypkludge: hit as '%s'", dname);
@@ -582,10 +798,42 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
zp = &zones[zone];
+ ixfr_found = 0;
+ if (type == ns_t_ixfr && zone != DB_Z_CACHE) {
+ if (SEQ_GT(serial_ixfr, zp->z_serial))
+ ixfr_found = 0;
+ else {
+ ixfr_error = ixfr_have_log(zp, serial_ixfr, zp->z_serial);
+ if (ixfr_error < 0) {
+ ns_debug(ns_log_default,
+ 1, "ixfr_have_log(%d %d) failed %d",
+ serial_ixfr, zp->z_serial, ixfr_error);
+ ixfr_found = 0;
+ /* Refuse IXFR and send AXFR */
+ type = ns_t_axfr;
+ } else
+ ixfr_found = 1;
+ }
+ }
+ /*
+ * If recursion is turned on, we need to check recursion ACL
+ * if it exists - and return result to caller.
+ */
+ {
+ ip_match_list recursion_acl;
+
+ recursion_acl = server_options->recursion_acl;
+ if (!NS_OPTION_P(OPTION_NORECURSE) && recursion_acl != NULL
+ && !ip_address_allowed(recursion_acl, from.sin_addr)) {
+ recursion_blocked_by_acl = 1;
+ *ra = 0;
+ }
+ }
+
/*
* Are queries allowed from this host?
*/
- if (type != T_AXFR) {
+ if (!ns_t_xfr_p(type)) {
ip_match_list query_acl;
if (zp->z_query_acl != NULL)
@@ -594,7 +842,52 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
query_acl = server_options->query_acl;
if (query_acl != NULL
- && !ip_address_allowed(query_acl, from.sin_addr)) {
+ && !ip_addr_or_key_allowed(query_acl, from.sin_addr,
+ in_key))
+ {
+ /*
+ * If this is *not* a zone acl and we would not
+ * have recursed and we have some answer return
+ * what we have with a referral.
+ */
+ if ((zp->z_query_acl == NULL) &&
+ (!hp->rd || NS_OPTION_P(OPTION_NORECURSE) ||
+ recursion_blocked_by_acl) &&
+ (ntohs(hp->ancount) != 0)) {
+ goto fetchns;
+ }
+
+ /*
+ * See if we would have made a referral from
+ * an enclosing zone if we are actually in the
+ * cache.
+ */
+ if (zp->z_type == z_cache && np != NULL) {
+ struct namebuf *access_np;
+
+ zone = DB_Z_CACHE;
+ 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))
+ dp = dp->d_next;
+ if (dp != NULL) {
+ zone = dp->d_zone;
+ np = access_np;
+ break;
+ }
+ }
+ zp = &zones[zone];
+ if (zp->z_type != z_cache &&
+ zp->z_query_acl != NULL &&
+ ip_addr_or_key_allowed(zp->z_query_acl,
+ from.sin_addr, in_key) &&
+ (!hp->rd || recursion_blocked_by_acl ||
+ NS_OPTION_P(OPTION_NORECURSE))) {
+ goto fetchns;
+ }
+ }
ns_notice(ns_log_security,
"unapproved query from %s for \"%s\"",
sin_ntoa(from), *dname ? dname : ".");
@@ -611,10 +904,23 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
transfer_acl = server_options->transfer_acl;
if (transfer_acl != NULL
- && !ip_address_allowed(transfer_acl, from.sin_addr)) {
+ && !ip_addr_or_key_allowed(transfer_acl, from.sin_addr,
+ in_key))
+ {
ns_notice(ns_log_security,
- "unapproved AXFR from %s for \"%s\" (acl)",
- sin_ntoa(from), *dname ? dname : ".");
+ "unapproved %s from %s for \"%s\" (acl)",
+ p_type(type), sin_ntoa(from),
+ *dname ? dname : ".");
+ return (Refuse);
+ }
+
+ /* Are we master or slave? */
+
+ if (zp->z_type != z_master && zp->z_type != z_slave) {
+ ns_notice(ns_log_security,
+ "unapproved %s from %s for \"%s\" (not master/slave)",
+ p_type(type), sin_ntoa(from),
+ *dname ? dname : ".");
return (Refuse);
}
@@ -622,39 +928,40 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
if ((zp->z_flags & Z_AUTH) == 0) {
ns_notice(ns_log_security,
- "unapproved AXFR from %s for \"%s\" (not auth)",
- sin_ntoa(from), *dname ? dname : ".");
+ "unapproved %s from %s for \"%s\" (not authoritative)",
+ p_type(type), sin_ntoa(from),
+ *dname ? dname : ".");
return (Refuse);
}
/* Is the name at a zone cut? */
- if (strcasecmp(zp->z_origin, dname) != 0) {
+ if (ns_samename(zp->z_origin, dname) != 1) {
ns_notice(ns_log_security,
- "unapproved AXFR from %s for \"%s\" (not zone top)",
- sin_ntoa(from), *dname ? dname : ".");
+ "unapproved %s from %s for \"%s\" (not zone top)",
+ p_type(type), sin_ntoa(from),
+ *dname ? dname : ".");
return (Refuse);
}
- ns_info(ns_log_security, "approved AXFR from %s for \"%s\"",
- sin_ntoa(from), *dname ? dname : ".");
+ ns_info(ns_log_security, "approved %s from %s for \"%s\"",
+ p_type(type), sin_ntoa(from), *dname ? dname : ".");
}
/*
* End Access Control Point
*/
-
/*
* Yow!
*/
- if (!strcasecmp(dnbuf, "VERSION.BIND") &&
- class == C_CHAOS && type == T_TXT) {
+ if (class == ns_c_chaos && type == ns_t_txt &&
+ ns_samename(dnbuf, "VERSION.BIND") == 1) {
u_char *tp;
hp->ancount = htons(1);
hp->nscount = htons(0);
hp->arcount = htons(0);
- hp->rcode = NOERROR;
+ hp->rcode = ns_r_noerror;
hp->aa = 1;
hp->ra = 0;
copyCharString(cpp, "VERSION"); /* Name */
@@ -665,7 +972,7 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
PUTLONG(0, *cpp); /* TTL */
tp = *cpp; /* Temp RdLength */
PUTSHORT(0, *cpp);
- copyCharString(cpp, ShortVersion);
+ copyCharString(cpp, server_options->version);
PUTSHORT((*cpp) - (tp + INT16SZ), tp); /* Real RdLength */
*msglenp = *cpp - msg; /* Total message length */
return (Finish);
@@ -682,11 +989,14 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
answers = *cpp;
count = *cpp - msg;
+ /* The response is authoritative until we add insecure data */
+ hp->ad = 1;
+
/* Look for NXDOMAIN record with appropriate class
* if found return immediately
*/
for (dp = np->n_data; dp; dp = dp->d_next) {
- if (!stale(dp) && (dp->d_rcode == NXDOMAIN) &&
+ if (!stale(dp) && (dp->d_rcode == ns_r_nxdomain) &&
(dp->d_class == class)) {
#ifdef RETURNSOA
n = finddata(np, class, T_SOA, hp, &dname,
@@ -700,12 +1010,14 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
}
if (hp->rcode == NOERROR_NODATA) {
/* this should not occur */
- hp->rcode = NOERROR;
+ hp->rcode = ns_r_noerror;
return (Finish);
}
}
+#else
+ count = 0;
#endif
- hp->rcode = NXDOMAIN;
+ hp->rcode = ns_r_nxdomain;
/*
* XXX forcing AA all the time isn't right, but
* we have to work that way by default
@@ -715,7 +1027,10 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
hp->aa = 1;
ns_debug(ns_log_default, 3, "NXDOMAIN aa = %d",
hp->aa);
- return (Finish);
+ if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1))
+ return (Finish);
+ founddata = 1;
+ goto fetchns;
}
}
@@ -727,19 +1042,19 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
n = finddata(np, class, type, hp, &dname, buflenp, &count);
if (n == 0) {
/*
- * NO data available. Refuse AXFR requests, or
+ * NO data available. Refuse transfer requests, or
* look for better servers for other requests.
*/
- if (type == T_AXFR) {
+ if (ns_t_xfr_p(type)) {
ns_debug(ns_log_default, 1,
- "T_AXFR refused: no data");
+ "transfer refused: no data");
return (Refuse);
}
goto fetchns;
}
if (hp->rcode == NOERROR_NODATA) {
- hp->rcode = NOERROR;
+ hp->rcode = ns_r_noerror;
#ifdef RETURNSOA
if (count) {
*cpp += n;
@@ -749,7 +1064,10 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
}
#endif
founddata = 1;
- return (Finish);
+ ns_debug(ns_log_default, 1, "count = %d", count);
+ if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1))
+ return (Finish);
+ goto fetchns;
}
*cpp += n;
@@ -760,7 +1078,7 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
if (cname++ >= MAXCNAMES) {
ns_debug(ns_log_default, 3,
"resp: leaving, MAXCNAMES exceeded");
- hp->rcode = SERVFAIL;
+ hp->rcode = ns_r_servfail;
return (Finish);
}
goto try_again;
@@ -770,11 +1088,46 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
"req: foundname=%d, count=%d, founddata=%d, cname=%d",
foundname, count, founddata, cname);
- if (type == T_AXFR) {
- ns_xfr(qsp, np, zone, class, type, hp->opcode, ntohs(hp->id));
+ if (ns_t_xfr_p(type)) {
+#ifdef BIND_UPDATE
+ if ((zp->z_flags & Z_NEED_SOAUPDATE) != 0)
+ if (incr_serial(zp) < 0)
+ ns_error(ns_log_default,
+ "error updating serial number for %s from %d",
+ zp->z_origin, zp->z_serial);
+#endif
+ /*
+ * Just return SOA if "up to date".
+ */
+ if (type == ns_t_ixfr) {
+ hp->aa = 1;
+ if ((SEQ_GT(serial_ixfr, zp->z_serial) ||
+ serial_ixfr == zp->z_serial))
+ return (Finish);
+ }
+
+ /*
+ * We don't handle UDP based IXFR queries (yet).
+ * Tell client to retry with TCP by returning SOA.
+ */
+ if (qsp == NULL)
+ return (Finish);
+ else {
+ if (!ixfr_found) {
+ qsp->flags |= STREAM_AXFRIXFR;
+ hp->qdcount = htons(1);
+ }
+ ns_xfr(qsp, np, zone, class, type,
+ hp->opcode, ntohs(hp->id),
+ serial_ixfr, in_tsig);
+ }
return (Return);
}
+ if (count > 1 && type == T_A && !NS_OPTION_P(OPTION_NORECURSE) &&
+ hp->rd)
+ sort_response(answers, *cpp, count, &from);
+
fetchns:
/*
* If we're already out of room in the response, we're done.
@@ -782,6 +1135,9 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
if (hp->tc)
return (Finish);
+ if (hp->ancount == 0)
+ hp->ad = 0;
+
/*
* Look for name servers to refer to and fill in the authority
* section or record the address for forwarding the query
@@ -794,7 +1150,7 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
case NXDOMAIN:
/* We are authoritative for this np. */
if (!foundname)
- hp->rcode = NXDOMAIN;
+ hp->rcode = ns_r_nxdomain;
ns_debug(ns_log_default, 3, "req: leaving (%s, rcode %d)",
dname, hp->rcode);
if (class != C_ANY) {
@@ -838,21 +1194,29 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
case SERVFAIL:
/* We're authoritative but the zone isn't loaded. */
if (!founddata &&
- !(NS_OPTION_P(OPTION_FORWARD_ONLY) &&
- server_options->fwdtab)) {
- hp->rcode = SERVFAIL;
+ !(NS_ZOPTION_P(zp, OPTION_FORWARD_ONLY) &&
+ NS_ZFWDTAB(zp))) {
+ hp->rcode = ns_r_servfail;
free_nsp(nsp);
return (Finish);
}
}
+ if (!founddata && hp->rd && recursion_blocked_by_acl) {
+ ns_notice(ns_log_security,
+ "unapproved recursive query from %s for %s",
+ sin_ntoa(from), *dname ? dname : ".");
+ }
+
/*
* If we successfully found the answer in the cache,
* or this is not a recursive query, or we are purposely
- * never recursing, then add the nameserver references
- * ("authority section") here and we're done.
+ * never recursing, or recursion is prohibited by ACL, then
+ * add the nameserver references("authority section") here
+ * and we're done.
*/
- if (founddata || !hp->rd || NS_OPTION_P(OPTION_NORECURSE)) {
+ if (founddata || !hp->rd || NS_OPTION_P(OPTION_NORECURSE)
+ || recursion_blocked_by_acl) {
/*
* If the qtype was NS, and the np of the authority is
* the same as the np of the data, we don't need to add
@@ -867,7 +1231,8 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
}
*cpp += n;
*buflenp -= n;
- hp->nscount = htons((u_int16_t)count);
+ hp->nscount = htons(ntohs(hp->nscount) +
+ (u_int16_t)count);
}
free_nsp(nsp);
@@ -886,29 +1251,31 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
omsg = (u_char *)memget((unsigned) *msglenp);
if (omsg == NULL) {
ns_info(ns_log_default, "ns_req: Out Of Memory");
- hp->rcode = SERVFAIL;
+ hp->rcode = ns_r_servfail;
free_nsp(nsp);
return (Finish);
}
id = hp->id;
omsglen = *msglenp;
memcpy(omsg, msg, omsglen);
- n = res_mkquery(QUERY, dname, class, type,
- NULL, 0, NULL, msg,
- *msglenp + *buflenp);
+ n = res_nmkquery(&res, QUERY, dname, class, type,
+ NULL, 0, NULL, msg,
+ *msglenp + *buflenp);
if (n < 0) {
ns_info(ns_log_default, "res_mkquery(%s) failed",
dname);
- hp->rcode = SERVFAIL;
+ hp->rcode = ns_r_servfail;
free_nsp(nsp);
return (Finish);
}
*msglenp = n;
}
n = ns_forw(nsp, msg, *msglenp, from, qsp, dfd, &qp,
- dname, class, type, np, 0);
- if (n != FW_OK && cname)
+ dname, class, type, np, 0, in_tsig);
+ if (n != FW_OK && cname) {
memput(omsg, omsglen);
+ omsg = NULL;
+ }
switch (n) {
case FW_OK:
if (cname) {
@@ -931,7 +1298,7 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
if (NAME(*np)[0] == '\0') {
ns_notice(ns_log_default,
"ns_req: no address for root server");
- hp->rcode = SERVFAIL;
+ hp->rcode = ns_r_servfail;
free_nsp(nsp);
return (Finish);
}
@@ -961,7 +1328,7 @@ req_query(HEADER *hp, u_char **cpp, u_char *eom, struct qstream *qsp,
goto fetchns; /* Try again. */
case FW_SERVFAIL:
do_servfail:
- hp->rcode = SERVFAIL;
+ hp->rcode = ns_r_servfail;
free_nsp(nsp);
return (Finish);
}
@@ -984,11 +1351,7 @@ req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp,
|| ntohs(hp->arcount) != 0) {
ns_debug(ns_log_default, 1,
"FORMERR IQuery header counts wrong");
- hp->qdcount = htons(0);
- hp->ancount = htons(0);
- hp->nscount = htons(0);
- hp->arcount = htons(0);
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
@@ -998,14 +1361,14 @@ req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp,
if ((n = dn_skipname(*cpp, eom)) < 0) {
ns_debug(ns_log_default, 1,
"FORMERR IQuery packet name problem");
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
*cpp += n;
if (*cpp + 3 * INT16SZ + INT32SZ > eom) {
ns_debug(ns_log_default, 1,
"FORMERR IQuery message too short");
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
GETSHORT(type, *cpp);
@@ -1016,7 +1379,7 @@ req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp,
if (*cpp != eom) {
ns_debug(ns_log_default, 1,
"FORMERR IQuery message length off");
- hp->rcode = FORMERR;
+ hp->rcode = ns_r_formerr;
return (Finish);
}
@@ -1025,10 +1388,18 @@ req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp,
*/
switch (type) {
case T_A:
- if (!NS_OPTION_P(OPTION_FAKE_IQUERY) || dlen != INT32SZ)
+ if (!NS_OPTION_P(OPTION_FAKE_IQUERY) || dlen != INT32SZ) {
+ if (dlen != INT32SZ)
+ ns_warning(ns_log_security,
+ "bad iquery from %s",
+ inet_ntoa(from.sin_addr));
return (Refuse);
+ }
break;
default:
+ ns_warning(ns_log_security,
+ "unsupported iquery type from %s",
+ inet_ntoa(from.sin_addr));
return (Refuse);
}
ns_debug(ns_log_default, 1,
@@ -1036,8 +1407,12 @@ req_iquery(HEADER *hp, u_char **cpp, u_char *eom, int *buflenp,
fname = (char *)msg + HFIXEDSZ;
alen = (char *)*cpp - fname;
- if ((size_t)alen > sizeof anbuf)
+ if ((size_t)alen > sizeof anbuf) {
+ ns_warning(ns_log_security,
+ "bad iquery from %s",
+ inet_ntoa(from.sin_addr));
return (Refuse);
+ }
memcpy(anbuf, fname, alen);
data = anbuf + alen - dlen;
*cpp = (u_char *)fname;
@@ -1124,7 +1499,7 @@ stale(struct databuf *dp) {
zp->z_origin);
}
zp->z_flags &= ~Z_AUTH;
- if (!(zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING))) {
+ if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0) {
zp->z_time = tt.tv_sec;
sched_zone_maint(zp);
}
@@ -1137,7 +1512,7 @@ stale(struct databuf *dp) {
zp->z_origin);
}
zp->z_flags &= ~Z_AUTH;
- if (!(zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING))) {
+ if ((zp->z_flags & (Z_QSERIAL|Z_XFER_RUNNING)) == 0) {
zp->z_time = tt.tv_sec;
sched_zone_maint(zp);
}
@@ -1146,6 +1521,7 @@ stale(struct databuf *dp) {
return (0);
case z_hint:
+ case z_cache:
if (dp->d_flags & DB_F_HINT ||
dp->d_ttl >= (u_int32_t)tt.tv_sec)
return (0);
@@ -1169,7 +1545,8 @@ stale(struct databuf *dp) {
*/
int
make_rr(const char *name, struct databuf *dp, u_char *buf,
- int buflen, int doadd, u_char **comp_ptrs, u_char **edp)
+ int buflen, int doadd, u_char **comp_ptrs, u_char **edp,
+ int use_minimum)
{
u_char *cp;
u_char *cp1, *sp;
@@ -1177,20 +1554,13 @@ make_rr(const char *name, struct databuf *dp, u_char *buf,
int32_t n;
int16_t type = dp->d_type;
u_int32_t ttl;
-#ifdef BIND_UPDATE
- u_int32_t serial;
-#endif
ns_debug(ns_log_default, 5,
"make_rr(%s, %lx, %lx, %d, %d) %d zone %d ttl %lu",
name, (u_long)dp, (u_long)buf,
buflen, doadd, dp->d_size, dp->d_zone, (u_long)dp->d_ttl);
- if (dp->d_rcode
-#ifdef RETURNSOA
- && dp->d_size == 0
-#endif
- )
+ if (dp->d_rcode && dp->d_size == 0)
panic("make_rr: impossible d_rcode value", NULL);
zp = &zones[dp->d_zone];
@@ -1202,7 +1572,7 @@ make_rr(const char *name, struct databuf *dp, u_char *buf,
} else
ttl = dp->d_ttl - (u_int32_t) tt.tv_sec;
} else {
- if (dp->d_ttl != USE_MINIMUM)
+ if (dp->d_ttl != USE_MINIMUM && !use_minimum)
ttl = dp->d_ttl;
else
ttl = zp->z_minimum; /* really default */
@@ -1251,9 +1621,11 @@ make_rr(const char *name, struct databuf *dp, u_char *buf,
return (-1);
PUTSHORT((u_int16_t)n, sp);
cp += n;
- if (doadd)
+ if (doadd) {
addname((char*)dp->d_data, name,
- type, dp->d_class);
+ type, T_A, dp->d_class);
+ addname(name, name, type, T_KEY, dp->d_class);
+ }
break;
case T_SOA:
@@ -1284,6 +1656,8 @@ make_rr(const char *name, struct databuf *dp, u_char *buf,
n = 5 * INT32SZ;
memcpy(cp, cp1, n);
cp += n;
+ if (doadd)
+ addname(name, name, type, T_KEY, dp->d_class);
}
n = (u_int16_t)((cp - sp) - INT16SZ);
PUTSHORT((u_int16_t)n, sp);
@@ -1389,7 +1763,9 @@ make_rr(const char *name, struct databuf *dp, u_char *buf,
cp1 += INT16SZ*2;
}
- n = dn_comp((char *)cp1, cp, buflen, comp_ptrs, edp);
+ n = dn_comp((char *)cp1, cp, buflen,
+ (type == ns_t_mx) ? comp_ptrs : NULL,
+ (type == ns_t_mx) ? edp : NULL);
if (n < 0)
return (-1);
cp += n;
@@ -1398,7 +1774,7 @@ make_rr(const char *name, struct databuf *dp, u_char *buf,
n = (u_int16_t)((cp - sp) - INT16SZ);
PUTSHORT((u_int16_t)n, sp);
if (doadd)
- addname((char*)cp1, name, type, dp->d_class);
+ addname((char*)cp1, name, type, T_A, dp->d_class);
break;
case T_PX:
@@ -1461,7 +1837,32 @@ make_rr(const char *name, struct databuf *dp, u_char *buf,
PUTSHORT((u_int16_t)n, sp);
break;
+ case T_NXT:
+ cp1 = dp->d_data;
+ n = dn_comp((char *)cp1, cp, buflen, NULL, NULL);
+ if (n < 0)
+ return (-1);
+
+ cp += n;
+ buflen -=n;
+ cp1 += strlen((char *)cp1) + 1;
+
+ /* copy nxt bit map */
+ n = dp->d_size - (u_int16_t)((cp1 - dp->d_data));
+ if (n > buflen)
+ return (-1); /* out of room! */
+ memcpy(cp, cp1, n);
+ cp += n;
+ buflen -= n;
+
+ n = (u_int16_t)((cp - sp) - INT16SZ);
+ PUTSHORT((u_int16_t)n, sp);
+
+ break;
+
default:
+ if ((type == T_A || type == T_AAAA) && doadd)
+ addname(name, name, type, T_KEY, dp->d_class);
if (dp->d_size > buflen)
return (-1);
memcpy(cp, dp->d_data, dp->d_size);
@@ -1473,13 +1874,13 @@ make_rr(const char *name, struct databuf *dp, u_char *buf,
static void
addname(const char *dname, const char *rname,
- u_int16_t rtype, u_int16_t class)
+ u_int16_t rtype, u_int16_t type, u_int16_t class)
{
struct addinfo *ap;
int n;
for (ap = addinfo, n = addcount; --n >= 0; ap++)
- if (strcasecmp(ap->a_dname, dname) == 0)
+ if (ns_samename(ap->a_dname, dname) == 1 && ap->a_type == type)
return;
/* add domain name to additional section */
@@ -1488,28 +1889,36 @@ addname(const char *dname, const char *rname,
ap->a_dname = savestr(dname, 1);
ap->a_rname = savestr(rname, 1);
ap->a_rtype = rtype;
+ ap->a_type = type;
ap->a_class = class;
}
}
/*
- * Lookup addresses for names in addinfo and put into the message's
+ * Lookup addresses/keys for names in addinfo and put into the message's
* additional section.
*/
int
doaddinfo(HEADER *hp, u_char *msg, int msglen) {
- struct namebuf *np;
- struct databuf *dp;
- struct addinfo *ap;
- u_char *cp;
+ register struct namebuf *np;
+ register struct databuf *dp;
+ register struct addinfo *ap;
+ register u_char *cp;
struct hashbuf *htp;
const char *fname;
- int n, count;
+ register int n, count;
+ register int ns_logging;
+ int finishedA = 0;
+ int save_addcount = addcount;
if (!addcount)
return (0);
- ns_debug(ns_log_default, 3, "doaddinfo() addcount = %d", addcount);
+ ns_logging = ns_wouldlog(ns_log_default, 3);
+
+ if (ns_logging)
+ ns_debug(ns_log_default, 3,
+ "doaddinfo() addcount = %d", addcount);
if (hp->tc) {
ns_debug(ns_log_default, 4,
@@ -1519,6 +1928,7 @@ doaddinfo(HEADER *hp, u_char *msg, int msglen) {
count = 0;
cp = msg;
+loop:
for (ap = addinfo; --addcount >= 0; ap++) {
int foundany = 0,
foundcname = 0,
@@ -1526,37 +1936,50 @@ doaddinfo(HEADER *hp, u_char *msg, int msglen) {
save_msglen = msglen;
u_char *save_cp = cp;
- ns_debug(ns_log_default, 3,
- "do additional \"%s\" (from \"%s\")",
- ap->a_dname, ap->a_rname);
+ if ((finishedA == 1 && ap->a_type == T_A) ||
+ (finishedA == 0 && ap->a_type == T_KEY))
+ continue;
+ if (ns_logging)
+ ns_debug(ns_log_default, 3,
+ "do additional \"%s\" (from \"%s\")",
+ ap->a_dname, ap->a_rname);
htp = hashtab; /* because "nlookup" stomps on arg. */
np = nlookup(ap->a_dname, &htp, &fname, 0);
if (np == NULL || fname != ap->a_dname)
goto next_rr;
- ns_debug(ns_log_default, 3, "found it");
+ if (ns_logging)
+ ns_debug(ns_log_default, 3, "found it");
/* look for the data */
- delete_stale(np);
+ (void)delete_stale(np);
for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
if (dp->d_rcode)
continue;
- if (match(dp, (int)ap->a_class, T_CNAME) ||
- match(dp, C_IN, T_CNAME)) {
+ 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 (!match(dp, (int)ap->a_class, T_A) &&
+ 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)) {
continue;
}
+ if (ap->a_type == T_KEY &&
+ !match(dp, (int)ap->a_class, T_KEY) &&
+ !match(dp, C_IN, T_KEY))
+ continue;
+
foundany++;
/*
* Should be smart and eliminate duplicate
* data here. XXX
*/
if ((n = make_rr(ap->a_dname, dp, cp, msglen, 0,
- dnptrs, dnptrs_end)) < 0) {
+ dnptrs, dnptrs_end, 0)) < 0) {
/* truncation in the additional-data section
* is not all that serious. we do not set TC,
* since the answer and authority sections are
@@ -1583,10 +2006,11 @@ doaddinfo(HEADER *hp, u_char *msg, int msglen) {
}
next_rr:
if (!NS_OPTION_P(OPTION_NOFETCHGLUE) &&
- !foundcname && !foundany) {
+ !foundcname && !foundany &&
+ (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, T_A,
- NULL, 0, QUERY);
+ (void) sysquery(ap->a_dname, (int)ap->a_class,
+ ap->a_type, NULL, 0, ns_port, QUERY);
}
if (foundcname) {
if (!haveComplained(nhash(ap->a_dname),
@@ -1600,6 +2024,11 @@ doaddinfo(HEADER *hp, u_char *msg, int msglen) {
freestr(ap->a_dname);
freestr(ap->a_rname);
}
+ if (finishedA == 0) {
+ finishedA = 1;
+ addcount = save_addcount;
+ goto loop; /* now do the KEYs... */
+ }
hp->arcount = htons((u_int16_t)count);
return (cp - msg);
}
@@ -1618,7 +2047,7 @@ doaddauth(HEADER *hp, u_char *cp, int buflen,
dnbuf, buflen);
return (0);
}
- n = make_rr(dnbuf, dp, cp, buflen, 1, dnptrs, dnptrs_end);
+ n = make_rr(dnbuf, dp, cp, buflen, 1, dnptrs, dnptrs_end, 1);
if (n <= 0) {
ns_debug(ns_log_default, 1,
"doaddauth: can't add oversize '%s' (%d) (n=%d)",
@@ -1628,6 +2057,8 @@ doaddauth(HEADER *hp, u_char *cp, int buflen,
}
return (0);
}
+ if (dp->d_secure != DB_S_SECURE)
+ hp->ad = 0;
hp->nscount = htons(ntohs(hp->nscount) + 1);
return (n);
}
OpenPOWER on IntegriCloud