summaryrefslogtreecommitdiffstats
path: root/usr.sbin/named/ns_resp.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/named/ns_resp.c')
-rw-r--r--usr.sbin/named/ns_resp.c736
1 files changed, 395 insertions, 341 deletions
diff --git a/usr.sbin/named/ns_resp.c b/usr.sbin/named/ns_resp.c
index 208cc75..4a987ab8 100644
--- a/usr.sbin/named/ns_resp.c
+++ b/usr.sbin/named/ns_resp.c
@@ -1,6 +1,6 @@
#if !defined(lint) && !defined(SABER)
static char sccsid[] = "@(#)ns_resp.c 4.65 (Berkeley) 3/3/91";
-static char rcsid[] = "$Id: ns_resp.c,v 1.1.1.3 1995/10/23 09:26:24 peter Exp $";
+static char rcsid[] = "$Id: ns_resp.c,v 8.18 1995/12/29 21:08:13 vixie Exp $";
#endif /* not lint */
/*
@@ -77,6 +77,7 @@ static void check_root __P((void)),
static u_int8_t norootlogged[MAXCLASS]; /* XXX- should be a bitmap */
static const char skipnameFailedAnswer[] = "skipname failed in answer",
+ skipnameFailedAuth[] = "skipname failed in authority",
skipnameFailedQuery[] = "skipname failed in query",
outofDataQuery[] = "ran out of data in query",
outofDataAnswer[] = "ran out of data in answer",
@@ -86,6 +87,7 @@ static const char skipnameFailedAnswer[] = "skipname failed in answer",
expandFailedAuth[] = "dn_expand failed in authority",
outofDataAuth[] = "ran out of data in authority",
dlenOverrunAnswer[] = "dlen overrun in answer",
+ dlenOverrunAuth[] = "dlen overrun in authority",
dlenUnderrunAnswer[] = "dlen underrun in answer",
outofDataFinal[] = "out of data in final pass",
outofDataAFinal[] = "out of data after final pass",
@@ -155,14 +157,14 @@ learntFrom(qp, server)
#else
# define LEARNTFROM " '%s'"
#endif
- if (buf = malloc(strlen(a = (*a ? a : "\".\"")) +
- strlen(ns = (*ns ? ns : "\".\"")) +
- strlen(na = (*na ? na : "\".\"")) +
- sizeof(LEARNTFROM))) {
- sprintf(buf, LEARNTFROM, na, a, ns);
- return (buf);
- }
- return("");
+ buf = malloc(strlen(a = (*a ? a : "\".\"")) +
+ strlen(ns = (*ns ? ns : "\".\"")) +
+ strlen(na = (*na ? na : "\".\"")) +
+ sizeof(LEARNTFROM));
+ if (!buf)
+ return ("");
+ sprintf(buf, LEARNTFROM, na, a, ns);
+ return (buf);
}
void
@@ -176,17 +178,19 @@ ns_resp(msg, msglen)
register struct databuf *ns, *ns2;
register u_char *cp;
u_char *eom = msg + msglen;
-#ifdef VALIDATE
register u_char *tempcp;
+#ifdef VALIDATE
struct sockaddr_in *server = &from_addr;
- int *validatelist;
- int lesscount, old_ancount;
+ struct { char *name; int type, class; u_int cred; } defer_rm[99];
+ int defer_rm_count;
#endif
struct sockaddr_in *nsa;
- struct databuf *nsp[NSMAX], **nspp;
+ struct databuf *nsp[NSMAX];
int i, c, n, qdcount, ancount, aucount, nscount, arcount;
int qtype, qclass, dbflags;
- int cname = 0; /* flag for processing cname response */
+ int restart; /* flag for processing cname response */
+ int validanswer;
+ int cname;
int count, founddata, foundname;
int buflen;
int newmsglen;
@@ -194,7 +198,7 @@ ns_resp(msg, msglen)
char *dname;
const char *fname;
const char *formerrmsg = "brain damage";
- u_char newmsg[BUFSIZ];
+ u_char newmsg[PACKETSZ];
u_char **dpp, *tp;
time_t rtrip;
struct hashbuf *htp;
@@ -264,7 +268,7 @@ ns_resp(msg, msglen)
qclass = 0;
}
- /* cp now points after the query section (if there was one). */
+ /* cp now points after the query section. */
/*
* Here we handle bad responses from servers.
@@ -276,11 +280,6 @@ ns_resp(msg, msglen)
* this server and immediately force a retry.
*/
if ((hp->rcode != NOERROR && hp->rcode != NXDOMAIN)
-#ifndef NCACHE
- || (hp->rcode == NXDOMAIN && !hp->aa) /* must accept this one if
- * we allow negative caching
- */
-#endif
|| (hp->opcode != QUERY
#ifdef BIND_NOTIFY
&& hp->opcode != NS_NOTIFY_OP
@@ -301,12 +300,13 @@ ns_resp(msg, msglen)
}
/* mark server as bad */
if (!qp->q_fwd)
- for (i = 0; i < (int)qp->q_naddr; i++)
- if (qp->q_addr[i].ns_addr.sin_addr.s_addr
- == from_addr.sin_addr.s_addr)
- qp->q_addr[i].nretry = MAXRETRY;
- /* XXX - doesn't handle responses sent from the wrong
- * interface on a multihomed server
+ for (i = 0; i < (int)qp->q_naddr; i++)
+ if (qp->q_addr[i].ns_addr.sin_addr.s_addr
+ == from_addr.sin_addr.s_addr)
+ qp->q_addr[i].nretry = MAXRETRY;
+ /*
+ * XXX: doesn't handle responses sent from the wrong
+ * interface on a multihomed server.
*/
if (qp->q_fwd ||
qp->q_addr[qp->q_curaddr].ns_addr.sin_addr.s_addr
@@ -321,85 +321,6 @@ ns_resp(msg, msglen)
goto formerr;
}
-#ifdef LAME_DELEGATION
- /*
- * Non-authoritative, no answer, no error
- */
- if (qdcount == 1 && hp->rcode == NOERROR && !hp->aa && ancount == 0
- && aucount > 0
-#ifdef BIND_NOTIFY
- && hp->opcode != NS_NOTIFY_OP
-#endif
- ) {
- u_char *tp;
- int type, class;
-#ifdef DEBUG
- if (debug > 0)
- fp_nquery(msg, msglen, ddt);
-#endif
- /*
- * Since there is no answer section (ancount == 0),
- * we must be pointing at the authority section (aucount > 0).
- */
- tp = cp;
- n = dn_expand(msg, eom, tp, name, sizeof name);
- if (n < 0) {
- formerrmsg = expandFailedAuth;
- goto formerr;
- }
- tp += n;
- GETSHORT(type, tp);
- if (tp >= eom) {
- formerrmsg = outofDataAuth;
- goto formerr;
- }
- GETSHORT(class, tp);
- if (tp >= eom) {
- formerrmsg = outofDataAuth;
- goto formerr;
- }
-
- /*
- * If the answer delegates us either to the same level in
- * the hierarchy or closer to the root, we consider this
- * server lame. Note that for now we only log the message
- * if the T_NS was C_IN, which is technically wrong (NS is
- * visible in all classes) but necessary anyway (non-IN
- * classes tend to not have good strong delegation graphs).
- */
-
- if (type == T_NS && samedomain(qp->q_domain, name)) {
- nameserIncr(from_addr.sin_addr, nssRcvdLDel);
- /* mark server as bad */
- if (!qp->q_fwd)
- for (i = 0; i < (int)qp->q_naddr; i++)
- if (qp->q_addr[i].ns_addr.sin_addr.s_addr
- == from_addr.sin_addr.s_addr)
- qp->q_addr[i].nretry = MAXRETRY;
-#ifdef LAME_LOGGING
- if (class == C_IN &&
- !haveComplained((char*)nhash(inet_etoa(&from_addr)),
- (char*)nhash(qp->q_domain)))
- syslog(LAME_LOGGING,
- "Lame server on '%s' (in '%s'?): %s%s\n",
- qname, qp->q_domain,
- inet_etoa(&from_addr),
- learntFrom(qp, &from_addr)
- );
-
-#endif /* LAME_LOGGING */
- /* XXX - doesn't handle responses sent from the wrong
- * interface on a multihomed server
- */
- if (qp->q_fwd ||
- qp->q_addr[qp->q_curaddr].ns_addr.sin_addr.s_addr
- == from_addr.sin_addr.s_addr)
- retry(qp);
- return;
- }
- }
-#endif /* LAME_DELEGATION */
-
#ifdef ALLOW_UPDATES
if ( (hp->rcode == NOERROR) &&
(hp->opcode == UPDATEA || hp->opcode == UPDATED ||
@@ -427,11 +348,12 @@ ns_resp(msg, msglen)
for (fwd = fwdtab; fwd != (struct fwdinfo *)NULL; fwd = fwd->next) {
if (fwd->fwdaddr.sin_addr.s_addr ==
from_addr.sin_addr.s_addr) {
- /* XXX - should put this in STATS somewhere */
+ /* XXX - should put this in STATS somewhere. */
break;
}
}
- /* XXX: note bad ambiguity here. if one of our forwarders is also
+ /*
+ * XXX: note bad ambiguity here. if one of our forwarders is also
* a delegated server for some domain, then we will not update
* the RTT information on any replies we get from those servers.
* Workaround: disable recursion on authoritative servers so that
@@ -453,7 +375,7 @@ ns_resp(msg, msglen)
"unexpected source")) {
syslog(LOG_INFO,
"Response from unexpected source (%s)",
- inet_etoa(&from_addr));
+ sin_ntoa(&from_addr));
}
/*
* We don't know who this response came from so it
@@ -466,8 +388,8 @@ ns_resp(msg, msglen)
/* Handle response from different (untried) interface */
if ((qs->ns != NULL) && (stp->tv_sec == 0)) {
ns = qs->ns;
- while (qs > qp->q_addr &&
- (qs->stime.tv_sec == 0 || qs->ns != ns))
+ while (qs > qp->q_addr
+ && (qs->stime.tv_sec == 0 || qs->ns != ns))
qs--;
*stp = qs->stime;
/* XXX - sometimes stp still ends up pointing to
@@ -475,9 +397,9 @@ ns_resp(msg, msglen)
* Why? What should we do about it?
*/
dprintf(1, (ddt,
- "Response from unused address %s, assuming %s\n",
- inet_etoa(&from_addr),
- inet_etoa(&qs->ns_addr)));
+ "Response from unused address %s, assuming %s\n",
+ sin_ntoa(&from_addr),
+ sin_ntoa(&qs->ns_addr)));
/* XXX - catch aliases here */
}
@@ -488,10 +410,10 @@ ns_resp(msg, msglen)
* as big as an int.
*/
if ((tt.tv_sec - stp->tv_sec) > (INT_MAX-999)/1000) {
- rtrip = INT_MAX;
+ rtrip = INT_MAX;
} else {
- rtrip = ((tt.tv_sec - stp->tv_sec) * 1000 +
- (tt.tv_usec - stp->tv_usec) / 1000);
+ rtrip = ((tt.tv_sec - stp->tv_sec) * 1000 +
+ (tt.tv_usec - stp->tv_usec) / 1000);
}
dprintf(3, (ddt, "stime %lu/%lu now %lu/%lu rtt %ld\n",
@@ -529,7 +451,7 @@ ns_resp(msg, msglen)
if (ns && qs->ns && (qp->q_nusedns < NSMAX)) {
qp->q_usedns[qp->q_nusedns++] = qs->ns;
dprintf(2, (ddt, "NS #%d addr %s used, rtt %d\n",
- n, inet_etoa(&qs->ns_addr),
+ n, sin_ntoa(&qs->ns_addr),
ns->d_nstime));
}
@@ -544,7 +466,9 @@ ns_resp(msg, msglen)
* and are no longer the correct type. XXX
*/
- for (n = 0, qs = qp->q_addr; (u_int)n < qp->q_naddr; n++, qs++) {
+ for (n = 0, qs = qp->q_addr;
+ (u_int)n < qp->q_naddr;
+ n++, qs++) {
ns2 = qs->nsdata;
if ((!ns2) || (ns2 == ns))
continue;
@@ -563,7 +487,7 @@ ns_resp(msg, msglen)
} else
ns2->d_nstime = (u_int32_t)(ns2->d_nstime * GAMMA);
dprintf(2, (ddt, "NS #%d %s rtt now %d\n", n,
- inet_etoa(&qs->ns_addr),
+ sin_ntoa(&qs->ns_addr),
ns2->d_nstime));
}
}
@@ -579,68 +503,83 @@ ns_resp(msg, msglen)
}
#endif
- /*************************************************************/
-
+#ifdef LAME_DELEGATION
/*
- * Save answers, authority, and additional records for future use.
+ * Non-authoritative, no answer, no error
*/
- nscount = 0;
- tp = cp;
- dprintf(3, (ddt, "resp: ancount %d, aucount %d, arcount %d\n",
- ancount, aucount, arcount));
+ if (qdcount == 1 && hp->rcode == NOERROR && !hp->aa && ancount == 0
+ && aucount > 0
+#ifdef BIND_NOTIFY
+ && hp->opcode != NS_NOTIFY_OP
+#endif
+ ) {
+ u_char *tp;
+ int type, class;
+#ifdef DEBUG
+ if (debug > 0)
+ fp_nquery(msg, msglen, ddt);
+#endif
+ /*
+ * Since there is no answer section (ancount == 0),
+ * we must be pointing at the authority section (aucount > 0).
+ */
+ tp = cp;
+ n = dn_expand(msg, eom, tp, name, sizeof name);
+ if (n < 0) {
+ formerrmsg = expandFailedAuth;
+ goto formerr;
+ }
+ tp += n;
+ GETSHORT(type, tp);
+ if (tp >= eom) {
+ formerrmsg = outofDataAuth;
+ goto formerr;
+ }
+ GETSHORT(class, tp);
+ if (tp >= eom) {
+ formerrmsg = outofDataAuth;
+ goto formerr;
+ }
- /*
- * If there's an answer, check if it's a CNAME response;
- * if no answer but aucount > 0, see if there is an NS
- * or just an SOA. (NOTE: ancount might be 1 with a CNAME,
- * and NS records may still be in the authority section;
- * we don't bother counting them, as we only use nscount
- * if ancount == 0.)
- */
- if (ancount == 1 || (ancount == 0 && aucount > 0)) {
- c = aucount;
- do {
- if (tp >= eom) {
- formerrmsg = outofDataAnswer;
- goto formerr;
- }
- n = dn_skipname(tp, eom);
- if (n <= 0) {
- formerrmsg = skipnameFailedAnswer;
- goto formerr;
- }
- tp += n; /* name */
- GETSHORT(i, tp); /* type */
- tp += INT16SZ; /* class */
- tp += INT32SZ; /* ttl */
- GETSHORT(count, tp); /* dlen */
- if (tp + count > eom) {
- formerrmsg = dlenOverrunAnswer;
- goto formerr;
- }
- tp += count;
- if (ancount && i == T_CNAME) {
- cname++;
- dprintf(1,
- (ddt,
- "CNAME - needs more processing\n"
- )
- );
- if (!qp->q_cmsglen) {
- qp->q_cmsg = qp->q_msg;
- qp->q_cmsglen = qp->q_msglen;
- qp->q_msg = NULL;
- qp->q_msglen = 0;
- }
- }
- /*
- * See if authority record is a nameserver.
+ /*
+ * If the answer delegates us either to the same level in
+ * the hierarchy or closer to the root, we consider this
+ * server lame. Note that for now we only log the message
+ * if the T_NS was C_IN, which is technically wrong (NS is
+ * visible in all classes) but necessary anyway (non-IN
+ * classes tend to not have good strong delegation graphs).
+ */
+
+ if (type == T_NS && samedomain(qp->q_domain, name)) {
+ nameserIncr(from_addr.sin_addr, nssRcvdLDel);
+ /* mark server as bad */
+ if (!qp->q_fwd)
+ for (i = 0; i < (int)qp->q_naddr; i++)
+ if (qp->q_addr[i].ns_addr.sin_addr.s_addr
+ == from_addr.sin_addr.s_addr)
+ qp->q_addr[i].nretry = MAXRETRY;
+#ifdef LAME_LOGGING
+ if (class == C_IN &&
+ !haveComplained((char*)nhash(sin_ntoa(&from_addr)),
+ (char*)nhash(qp->q_domain)))
+ syslog(LAME_LOGGING,
+ "Lame server on '%s' (in '%s'?): %s%s\n",
+ qname, qp->q_domain,
+ sin_ntoa(&from_addr),
+ learntFrom(qp, &from_addr));
+
+#endif /* LAME_LOGGING */
+ /* XXX - doesn't handle responses sent from the wrong
+ * interface on a multihomed server
*/
- if (ancount == 0 && i == T_NS)
- nscount++;
- } while (--c > 0);
- tp = cp;
+ if (qp->q_fwd ||
+ qp->q_addr[qp->q_curaddr].ns_addr.sin_addr.s_addr
+ == from_addr.sin_addr.s_addr)
+ retry(qp);
+ return;
+ }
}
+#endif /* LAME_DELEGATION */
if (qp->q_flags & Q_ZSERIAL) {
if (hp->aa && ancount > 0 && hp->rcode == NOERROR &&
@@ -702,14 +641,13 @@ ns_resp(msg, msglen)
* Add the info received in the response to the data base.
*/
c = ancount + aucount + arcount;
-#ifdef NCACHE
+
/* -ve $ing non-existence of record, must handle non-authoritative
* NOERRORs with c == 0.
*/
- if (!hp->aa && hp->rcode == NOERROR && c == 0) {
+ if (!hp->aa && hp->rcode == NOERROR && c == 0)
goto return_msg;
- } /*should ideally validate message before returning it*/
-#endif /*NCACHE*/
+
#ifdef notdef
/*
* If the request was for a CNAME that doesn't exist,
@@ -729,7 +667,6 @@ ns_resp(msg, msglen)
}
#endif /* notdef */
- nspp = nsp;
if (qp->q_flags & Q_SYSTEM)
dbflags = DB_NOTAUTH | DB_NODATA;
else
@@ -747,88 +684,175 @@ ns_resp(msg, msglen)
}
/* XXX - should retry this query with TCP */
}
+
+ tp = cp;
+
+ restart = 0;
+ validanswer = 0;
+ nscount = 0;
+ cname = 0;
#ifdef VALIDATE
- tempcp = cp;
- validatelist = (int *)malloc(count * sizeof(int));
- lesscount = 0; /*initialize*/
- old_ancount = ancount;
+ defer_rm_count = 0;
+#endif
+
for (i = 0; i < count; i++) {
+ struct databuf *ns3 = NULL;
+ u_char cred;
int VCode;
- if (tempcp >= eom) {
- free((char *)validatelist);
+ u_int16_t type, class;
+
+ if (cp >= eom) {
formerrmsg = outofDataFinal;
goto formerr;
}
- if ((n = dovalidate(msg, msglen, tempcp, 0,
- dbflags, server, &VCode)) < 0) {
- dprintf(1, (ddt,
- "resp: leaving, dovalidate failed\n"));
- free((char *)validatelist);
- /* return code filled in by dovalidate */
- goto return_msg;
+ /* Get the DNAME. */
+ tempcp = cp;
+ n = dn_expand(msg, eom, tempcp, name, sizeof name);
+ if (n <= 0) {
+ formerrmsg = outofDataFinal;
+ goto formerr;
}
- validatelist[i] = VCode;
- if (VCode == INVALID) lesscount++;
tempcp += n;
- }
-
- /* need to delete INVALID records from the message
- * and change fields appropriately
- */
- n = update_msg(msg, &msglen, validatelist, count);
- free((char *)validatelist);
- if (n < 0) {
- formerrmsg = editFailed;
- goto formerr;
- }
- count -= lesscount;
+ GETSHORT(type, tempcp);
+ GETSHORT(class, tempcp);
- ancount = ntohs(hp->ancount);
- if (old_ancount && !ancount) {
- /* We lost all the answers */
- dprintf(1, (ddt, "validate count -> 0"));
- return;
- }
-#endif
-
- for (i = 0; i < count; i++) {
- struct databuf *ns3;
- u_char cred;
+ /*
+ * See if there are any NS RRs in the authority section
+ * for the negative caching logic below. We'll count
+ * these before validation.
+ */
+ if (type == T_NS && i >= ancount && i < ancount + aucount)
+ nscount++;
- if (cp >= eom) {
- formerrmsg = outofDataFinal;
- goto formerr;
- }
+ /* Decide what credibility this ought to have in the cache. */
if (i < ancount)
- cred = hp->aa ? DB_C_AUTH : DB_C_ANSWER;
+ cred = (hp->aa && !strcasecmp(name, qname))
+ ? DB_C_AUTH
+ : DB_C_ANSWER;
else
cred = (qp->q_flags & Q_PRIMING)
? DB_C_ANSWER
: DB_C_ADDITIONAL;
- ns3 = 0;
+#ifdef VALIDATE
+ if ((n = dovalidate(msg, msglen, cp, 0,
+ dbflags, qp->q_domain, server,
+ &VCode)) < 0) {
+ formerrmsg = outofDataFinal;
+ goto formerr;
+ }
+ if (VCode == INVALID && !(qp->q_flags & Q_SYSTEM)) {
+ /*
+ * If anything in the answer section fails
+ * validation this means that it definitely did
+ * not reside below the domain owning the NS RRs
+ * that we sent the query to. This means either
+ * that it was the target of a CNAME early in the
+ * response, in which case we will treat this the
+ * same as if the answer was incomplete and restart
+ * the query on the CNAME target, or that someone
+ * was trying to spoof us.
+ */
+ if (i < ancount)
+ restart = 1;
+ /*
+ * Restart or no, if we're here it means we are not
+ * going to cache this RR. That being the case, we
+ * must burn down whatever partial RRset we've got
+ * in the cache now, lest we inadvertently answer
+ * with a truncated RRset in some future section.
+ */
+ for (c = 0; c < defer_rm_count; c++)
+ if (!strcasecmp(defer_rm[c].name, name) &&
+ defer_rm[c].class == class &&
+ defer_rm[c].type == type)
+ break;
+ if (c < defer_rm_count) {
+ if (defer_rm[c].cred < cred)
+ defer_rm[c].cred = cred;
+ } else {
+ if (defer_rm_count+1 >=
+ (sizeof defer_rm / sizeof defer_rm[0])) {
+ formerrmsg = "too many RRs in ns_resp";
+ goto formerr;
+ }
+ defer_rm[defer_rm_count].name = savestr(name);
+ defer_rm[defer_rm_count].type = type;
+ defer_rm[defer_rm_count].class = class;
+ defer_rm[defer_rm_count].cred = cred;
+ defer_rm_count++;
+ }
+ } else {
+#endif
+ if (i < ancount) {
+ /*
+ * If there are any non-CNAME RRs (or
+ * CNAME RRs if they are an acceptable)
+ * then the query is complete unless an
+ * intermediate CNAME didn't pass validation,
+ * but that's OK.
+ */
+ if (type != T_CNAME || qtype == T_CNAME ||
+ qtype == T_ANY)
+ validanswer = 1;
+ else
+ cname = 1;
+ }
n = doupdate(msg, msglen, cp, 0, &ns3, dbflags, cred);
- if (n < 0) {
- dprintf(1, (ddt, "resp: leaving, doupdate failed\n"));
-
- /* return code filled in by doupdate */
- goto return_msg;
+#ifdef VALIDATE
}
- /*
- * Remember nameservers from the authority section
- * for referrals.
- * (This is usually overwritten by findns below(?). XXX
- */
- if (ns3 && i >= ancount && i < ancount + aucount &&
- nspp < &nsp[NSMAX-1]) {
- *nspp++ = ns3;
-#ifdef DATUMREFCNT
- ns3->d_rcnt++;
- *nspp = NULL;
#endif
+ if (n < 0) {
+ dprintf(1, (ddt, "resp: leaving, doupdate failed\n"));
+ formerrmsg = outofDataFinal;
+ goto formerr;
}
cp += n;
}
+#ifdef VALIDATE
+ if (defer_rm_count > 0) {
+ for (i = 0; i < defer_rm_count; i++) {
+ register struct databuf *db = NULL;
+
+ fname = "";
+ htp = hashtab; /* lookup relative to root */
+ np = nlookup(defer_rm[i].name, &htp, &fname, 0);
+ if (np && fname == defer_rm[i].name &&
+ defer_rm[i].class != C_ANY &&
+ defer_rm[i].type != T_ANY) {
+ /*
+ * If doupdate() wouldn't have cached this
+ * RR anyway, there's no need to delete it.
+ */
+ for (db = np->n_data;
+ db != NULL;
+ db = db->d_next) {
+ if (!db->d_zone &&
+ match(db, defer_rm[i].class,
+ defer_rm[i].type) &&
+ db->d_cred >= defer_rm[i].cred) {
+ break;
+ }
+ }
+ if (db == NULL)
+ delete_all(np, defer_rm[i].class,
+ defer_rm[i].type);
+ /* XXX: should delete name node if empty? */
+ }
+ syslog(LOG_DEBUG, "defer_rm [%s %s %s] (np%#x, db%#x)",
+ defer_rm[i].name,
+ p_class(defer_rm[i].class),
+ p_type(defer_rm[i].type),
+ np, db);
+ free(defer_rm[i].name);
+ }
+ }
+#endif
+
+ if (cp > eom) {
+ formerrmsg = outofDataAFinal;
+ goto formerr;
+ }
if ((qp->q_flags & Q_SYSTEM) && ancount) {
if (qp->q_flags & Q_PRIMING)
@@ -847,30 +871,31 @@ ns_resp(msg, msglen)
}
#endif
qremove(qp);
-#ifdef DATUMREFCNT
- free_nsp(nsp);
-#endif
return;
}
- if (cp > eom) {
- formerrmsg = outofDataAFinal;
- goto formerr;
- }
+ if (ancount && !validanswer)
+ /*
+ * Everything passed validation but we didn't get the
+ * final answer. The response must have contained
+ * a dangling CNAME. Force a restart of the query.
+ */
+ restart = 1;
/*
* If there are addresses and this is a local query,
* sort them appropriately for the local context.
*/
- if (ancount > 1 && (lp = local(&qp->q_from)) != NULL)
+#ifdef SORT_RESPONSE
+ if (!restart && ancount > 1 && (lp = local(&qp->q_from)) != NULL)
sort_response(tp, ancount, lp, eom);
+#endif
/*
* An answer to a T_ANY query or a successful answer to a
* regular query with no indirection, then just return answer.
*/
- if ((qtype == T_ANY && ancount) ||
- (!cname && !qp->q_cmsglen && ancount)) {
+ if (!restart && ancount && (qtype == T_ANY || !qp->q_cmsglen)) {
dprintf(3, (ddt, "resp: got as much answer as there is\n"));
goto return_msg;
}
@@ -884,6 +909,7 @@ ns_resp(msg, msglen)
/* we have an authoritative NO */
dprintf(3, (ddt, "resp: leaving auth NO\n"));
if (qp->q_cmsglen) {
+ /* XXX - what about additional CNAMEs in the chain? */
msg = qp->q_cmsg;
msglen = qp->q_cmsglen;
hp = (HEADER *)msg;
@@ -906,7 +932,17 @@ ns_resp(msg, msglen)
founddata = 0;
foundname = 0;
dname = name;
- if (!cname && qp->q_cmsglen && ancount) {
+ /*
+ * Even with VALIDATE, if restart==0 and ancount > 0, we should
+ * have some valid data because because the data in the answer
+ * section is owned by the query name and that passes the
+ * validation test by definition
+ *
+ * XXX - the restart stuff doesn't work if any of the answer RRs
+ * is not cacheable (TTL==0 or unknown RR type), since all of the
+ * answer must pass through the cache and be re-assembled.
+ */
+ if ((!restart || !cname) && qp->q_cmsglen && ancount) {
dprintf(1, (ddt, "Cname second pass\n"));
newmsglen = qp->q_cmsglen;
bcopy(qp->q_cmsg, newmsg, newmsglen);
@@ -915,23 +951,32 @@ ns_resp(msg, msglen)
bcopy(msg, newmsg, newmsglen);
}
hp = (HEADER *) newmsg;
- hp->ancount = 0;
- hp->nscount = 0;
- hp->arcount = 0;
+ hp->ancount = htons(0);
+ hp->nscount = htons(0);
+ hp->arcount = htons(0);
dnptrs[0] = newmsg;
dnptrs[1] = NULL;
cp = newmsg + HFIXEDSZ;
- if (cname)
- cp += dn_skipname(cp, newmsg + newmsglen) + QFIXEDSZ;
- n = dn_expand(newmsg, newmsg + newmsglen, cp, dname, sizeof name);
+ /*
+ * Keep in mind that none of this code works when QDCOUNT>1.
+ * cp ends up pointed just past the query section in both cases.
+ */
+ /*
+ * Arrange for dname to contain the query name. The query
+ * name can be either the original query name if restart==0
+ * or the target of the last CNAME if we are following a
+ * CNAME chain and were referred.
+ */
+ n = dn_expand(newmsg, newmsg + newmsglen, cp, dname,
+ sizeof name);
if (n < 0) {
dprintf(1, (ddt, "dn_expand failed\n"));
goto servfail;
}
- if (!cname)
- cp += n + QFIXEDSZ;
+ cp += n + QFIXEDSZ;
buflen = sizeof(newmsg) - (cp - newmsg);
+ cname = 0;
try_again:
dprintf(1, (ddt, "resp: nlookup(%s) qtype=%d\n", dname, qtype));
fname = "";
@@ -949,7 +994,7 @@ ns_resp(msg, msglen)
goto fetch_ns; /* NO data available */
cp += n;
buflen -= n;
- hp->ancount += count;
+ hp->ancount = htons(ntohs(hp->ancount) + (u_int16_t)count);
if (fname != dname && qtype != T_CNAME && qtype != T_ANY) {
cname++;
goto try_again;
@@ -961,7 +1006,10 @@ ns_resp(msg, msglen)
foundname, count, founddata, cname));
fetch_ns:
- hp->ancount = htons(hp->ancount);
+
+ if (hp->tc)
+ goto return_newmsg;
+
/*
* Look for name servers to refer to and fill in the authority
* section or record the address for forwarding the query
@@ -983,9 +1031,9 @@ ns_resp(msg, msglen)
* in the auth. section if there's no error.
*/
if (foundname == 0 && np) {
- n = doaddauth(hp, cp, buflen, np, nsp[0]);
- cp += n;
- buflen -= n;
+ n = doaddauth(hp, cp, buflen, np, nsp[0]);
+ cp += n;
+ buflen -= n;
}
}
goto return_newmsg;
@@ -996,7 +1044,7 @@ ns_resp(msg, msglen)
if (founddata) {
hp = (HEADER *)newmsg;
- n = add_data(np, nsp, cp, buflen);
+ n = add_data(np, nsp, cp, buflen, &count);
if (n < 0) {
hp->tc = 1;
n = (-n);
@@ -1028,36 +1076,36 @@ ns_resp(msg, msglen)
const char *result;
if (qp->q_addr[i].ns != NULL) {
+ if ((--(qp->q_addr[i].ns->d_rcnt)))
+ result = busy;
+ else
+ result = freed;
dprintf(1, (ddt, "ns_resp: ns %s rcnt %d (%s)\n",
qp->q_addr[i].ns->d_data,
qp->q_addr[i].ns->d_rcnt,
result));
- if ((--(qp->q_addr[i].ns->d_rcnt)))
- result = busy;
- else {
+ if (result == freed)
free((char*)qp->q_addr[i].ns);
- result = freed;
- }
}
if (qp->q_addr[i].nsdata != NULL) {
if ((--(qp->q_addr[i].nsdata->d_rcnt)))
result = busy;
- else {
- free((char*)qp->q_addr[i].nsdata);
+ else
result = freed;
- }
dprintf(1, (ddt,
"ns_resp: nsdata %08.8X rcnt %d (%s)\n",
*(int32_t *)(qp->q_addr[i].nsdata->d_data),
qp->q_addr[i].nsdata->d_rcnt,
result));
+ if (result == freed)
+ free((char*)qp->q_addr[i].nsdata);
}
}
#endif
qp->q_naddr = 0;
qp->q_curaddr = 0;
qp->q_fwd = fwdtab;
-#ifdef LAME_DELEGATION
+#if defined(LAME_DELEGATION) || defined(VALIDATE)
getname(np, qp->q_domain, sizeof(qp->q_domain));
#endif /* LAME_DELEGATION */
if ((n = nslookup(nsp, qp, dname, "ns_resp")) <= 0) {
@@ -1080,10 +1128,13 @@ ns_resp(msg, msglen)
"resp: leaving, MAXCNAMES exceeded\n"));
goto servfail;
}
- dprintf(1, (ddt, "q_cname = %d\n",qp->q_cname));
+ dprintf(1, (ddt, "q_cname = %d\n", qp->q_cname));
dprintf(3, (ddt,
"resp: building recursive query; nslookup\n"));
- if (qp->q_msg)
+ if (!qp->q_cmsg) {
+ qp->q_cmsg = qp->q_msg;
+ qp->q_cmsglen = qp->q_msglen;
+ } else if (qp->q_msg)
(void) free(qp->q_msg);
if ((qp->q_msg = (u_char *)malloc(BUFSIZ)) == NULL) {
syslog(LOG_NOTICE, "resp: malloc error\n");
@@ -1098,7 +1149,7 @@ ns_resp(msg, msglen)
}
qp->q_msglen = n;
hp = (HEADER *) qp->q_msg;
- hp->rd = 0;
+ hp->rd = 0;
} else
hp = (HEADER *) qp->q_msg;
hp->id = qp->q_nsid = htons(nsid_next());
@@ -1108,7 +1159,7 @@ ns_resp(msg, msglen)
schedretry(qp, retrytime(qp));
nsa = Q_NEXTADDR(qp, 0);
dprintf(1, (ddt, "resp: forw -> %s ds=%d nsid=%d id=%d %dms\n",
- inet_etoa(nsa), ds,
+ sin_ntoa(nsa), ds,
ntohs(qp->q_nsid), ntohs(qp->q_id),
(qp->q_addr[0].nsdata != NULL)
? qp->q_addr[0].nsdata->d_nstime
@@ -1122,7 +1173,7 @@ ns_resp(msg, msglen)
sizeof(struct sockaddr_in)) < 0) {
if (!haveComplained((char*)nsa->sin_addr.s_addr, sendtoStr))
syslog(LOG_INFO, "ns_resp: sendto(%s): %m",
- inet_etoa(nsa));
+ sin_ntoa(nsa));
nameserIncr(nsa->sin_addr, nssSendtoErr);
}
hp->rd = 0; /* leave set to 0 for dup detection */
@@ -1138,7 +1189,7 @@ ns_resp(msg, msglen)
if (!haveComplained((char*)from_addr.sin_addr.s_addr,
(char*)nhash(formerrmsg)))
syslog(LOG_INFO, "Malformed response from %s (%s)\n",
- inet_etoa(&from_addr), formerrmsg);
+ sin_ntoa(&from_addr), formerrmsg);
nameserIncr(from_addr.sin_addr, nssSentFErr);
#ifdef DATUMREFCNT
free_nsp(nsp);
@@ -1408,13 +1459,11 @@ doupdate(msg, msglen, rrp, zone, savens, flags, cred)
*/
if ( (hp->opcode == UPDATED) || (hp->opcode == UPDATEDA) ) {
if (cp != (u_char *)(msg + msglen)) {
- dprintf(1,
- (ddt,
- "FORMERR UPDATE message length off\n"
- )
- );
- hp->rcode = FORMERR;
- return (-1);
+ dprintf(1, (ddt,
+ "FORMERR UPDATE message length off\n"
+ ));
+ hp->rcode = FORMERR;
+ return (-1);
}
}
if ((zonenum = findzone(dname, class)) == 0) {
@@ -1533,7 +1582,7 @@ doupdate(msg, msglen, rrp, zone, savens, flags, cred)
ttl += tt.tv_sec;
#if defined(TRACEROOT) || defined(BOGUSNS)
if ((type == T_NS) && (savens != NULL)) {
- char qname[MAXDNAME], *temp;
+ char *temp, qname[MAXDNAME];
register int bogus = 0;
int bogusns = 0;
#ifdef BOGUSNS
@@ -1558,7 +1607,7 @@ doupdate(msg, msglen, rrp, zone, savens, flags, cred)
"bogus root NS"))
syslog(LOG_NOTICE,
"bogus root NS %s rcvd from %s on query for \"%s\"",
- data, inet_etoa(&from_addr), qname);
+ data, sin_ntoa(&from_addr), qname);
return (cp - rrp);
}
#ifdef BOGUSNS
@@ -1566,8 +1615,8 @@ doupdate(msg, msglen, rrp, zone, savens, flags, cred)
if (!haveComplained((char*)from_addr.sin_addr.s_addr,
"bogus nonroot NS"))
syslog(LOG_INFO,
- "bogus nonroot NS %s rcvd from %s on query for \"%s\"",
- data, inet_etoa(&from_addr), qname);
+ "bogus nonroot NS %s rcvd from %s on query for \"%s\"",
+ data, sin_ntoa(&from_addr), qname);
return (cp - rrp);
}
#endif
@@ -1602,13 +1651,13 @@ send_msg(msg, msglen, qp)
#ifdef DEBUG
if (debug) {
fprintf(ddt,"send_msg -> %s (%s %d) id=%d\n",
- inet_etoa(&qp->q_from),
+ sin_ntoa(&qp->q_from),
qp->q_stream == QSTREAM_NULL ? "UDP" : "TCP",
qp->q_stream == QSTREAM_NULL ? qp->q_dfd
: qp->q_stream->s_rfd,
ntohs(qp->q_id));
}
- if (debug>4) {
+ if (debug > 4) {
struct qinfo *tqp;
for (tqp = nsqhead; tqp!=QINFO_NULL; tqp = tqp->q_link) {
@@ -1623,7 +1672,7 @@ send_msg(msg, msglen, qp)
(u_long)qp->q_next, (u_long)qp->q_link);
}
}
- if (debug >= 10)
+ if (debug > 5)
fp_nquery(msg, msglen, ddt);
#endif /* DEBUG */
if (qp->q_stream == QSTREAM_NULL) {
@@ -1637,7 +1686,7 @@ send_msg(msg, msglen, qp)
#endif
syslog(LOG_INFO,
"send_msg: sendto(%s): %m",
- inet_etoa(&qp->q_from));
+ sin_ntoa(&qp->q_from));
nameserIncr(qp->q_from.sin_addr, nssSendtoErr);
return (1);
}
@@ -1889,7 +1938,7 @@ sysquery(dname, class, type, nss, nsc, opcode)
qp->q_fwd = fwdtab;
qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2;
qp->q_flags |= Q_SYSTEM;
-#ifdef LAME_DELEGATION
+#if defined(LAME_DELEGATION) || defined(VALIDATE)
getname(np, qp->q_domain, sizeof(qp->q_domain));
#endif /* LAME_DELEGATION */
@@ -1910,7 +1959,7 @@ sysquery(dname, class, type, nss, nsc, opcode)
hp->rd = (qp->q_fwd ? 1 : 0);
/* First check for an already pending query for this data */
- for (oqp = nsqhead; oqp != QINFO_NULL; oqp = oqp->q_link) {
+ for (oqp = nsqhead; oqp != QINFO_NULL; oqp = oqp->q_link) {
if ((oqp != qp)
&& (oqp->q_msglen == qp->q_msglen)
&& bcmp((char *)oqp->q_msg+2,
@@ -1968,8 +2017,8 @@ sysquery(dname, class, type, nss, nsc, opcode)
nsa = Q_NEXTADDR(qp, 0);
dprintf(1, (ddt,
- "sysquery: send -> %s dfd=%d nsid=%d id=%d retry=%ld\n",
- inet_etoa(nsa), qp->q_dfd,
+ "sysquery: send -> %s dfd=%d nsid=%d id=%d retry=%ld\n",
+ sin_ntoa(nsa), qp->q_dfd,
ntohs(qp->q_nsid), ntohs(qp->q_id),
qp->q_time));
#ifdef DEBUG
@@ -1981,7 +2030,7 @@ sysquery(dname, class, type, nss, nsc, opcode)
sizeof(struct sockaddr_in)) < 0) {
if (!haveComplained((char*)nsa->sin_addr.s_addr, sendtoStr))
syslog(LOG_INFO, "sysquery: sendto(%s): %m",
- inet_etoa(nsa));
+ sin_ntoa(nsa));
nameserIncr(nsa->sin_addr, nssSendtoErr);
}
nameserIncr(nsa->sin_addr, nssSentSysQ);
@@ -2057,9 +2106,9 @@ check_ns()
for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) {
if (np->n_dname[0] != 0)
continue;
- for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
- if (dp->d_type != T_NS)
- continue;
+ for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
+ if (dp->d_type != T_NS)
+ continue;
/* look for A records */
dname = (caddr_t) dp->d_data;
@@ -2069,29 +2118,31 @@ check_ns()
dprintf(3, (ddt,
"check_ns: %s: not found %s %#lx\n",
dname, fname, (u_long)tnp));
- sysquery(dname, dp->d_class, T_A, NULL, 0, QUERY);
- continue;
+ sysquery(dname, dp->d_class, T_A, NULL,
+ 0, QUERY);
+ continue;
}
/* look for name server addresses */
found_arr = 0;
- for (tdp=tnp->n_data; tdp!=NULL; tdp=tdp->d_next) {
- if (tdp->d_type != T_A ||
- tdp->d_class != dp->d_class)
- continue;
- if ((tdp->d_zone == 0) &&
- (tdp->d_ttl < curtime)) {
- dprintf(3, (ddt,
- "check_ns: stale entry '%s'\n",
- tnp->n_dname));
- /* Cache invalidate the address RR's */
- delete_all(tnp, dp->d_class, T_A);
- found_arr = 0;
- break;
- }
- found_arr++;
+ for (tdp=tnp->n_data; tdp != NULL; tdp=tdp->d_next) {
+ if (tdp->d_type != T_A ||
+ tdp->d_class != dp->d_class)
+ continue;
+ if ((tdp->d_zone == 0) &&
+ (tdp->d_ttl < curtime)) {
+ dprintf(3, (ddt,
+ "check_ns: stale entry '%s'\n",
+ tnp->n_dname));
+ /* Cache invalidate the address RR's */
+ delete_all(tnp, dp->d_class, T_A);
+ found_arr = 0;
+ break;
+ }
+ found_arr++;
}
if (!found_arr)
- sysquery(dname, dp->d_class, T_A, NULL, 0, QUERY);
+ sysquery(dname, dp->d_class, T_A, NULL,
+ 0, QUERY);
}
}
}
@@ -2240,9 +2291,9 @@ try_parent:
}
/*
- * Extract RR's from the given node that match class and type.
- * Return number of bytes added to response.
- * If no matching data is found, then 0 is returned.
+ * Extract RR's from the given node that match class and type.
+ * Return number of bytes added to response.
+ * If no matching data is found, then 0 is returned.
*/
int
finddata(np, class, type, hp, dnamep, lenp, countp)
@@ -2262,7 +2313,7 @@ finddata(np, class, type, hp, dnamep, lenp, countp)
register struct databuf **dpp;
- for (dpp = &np->n_data; dp = *dpp; dpp = &dp->d_next) {
+ for (dpp = &np->n_data; dp = *dpp; dpp = &dp->d_next) {
if (dp->d_next && wanted(dp, class, type)) {
register struct databuf *lp;
@@ -2279,7 +2330,7 @@ finddata(np, class, type, hp, dnamep, lenp, countp)
}
}
#endif /*ROUND_ROBIN*/
-
+
buflen = *lenp;
#ifdef DEBUG
if (buflen > PACKETSZ)
@@ -2336,7 +2387,7 @@ finddata(np, class, type, hp, dnamep, lenp, countp)
* This should not happen, yet it does...
*/
syslog(LOG_INFO,
- "NODATA & data for \"%s\" type %d class %d",
+ "NODATA & data for \"%s\" type %d class %d",
*dnamep, type, class);
continue;
}
@@ -2444,12 +2495,12 @@ wanted(dp, class, type)
return (1);
case T_CNAME:
#ifdef NCACHE
- if (dp->d_rcode != NOERROR_NODATA)
+ if (dp->d_rcode != NOERROR_NODATA)
#endif
- return (1);
+ return (1);
#ifdef NCACHE
- else
- break;
+ else
+ break;
#endif
}
switch (type) {
@@ -2479,20 +2530,21 @@ wanted(dp, class, type)
/*
* Add RR entries from dpp array to a query/response.
* Return the number of bytes added or negative the amount
- * added if truncation was required. Typically you are
+ * added if truncation occured. Typically you are
* adding NS records to a response.
*/
int
-add_data(np, dpp, cp, buflen)
+add_data(np, dpp, cp, buflen, countp)
struct namebuf *np;
struct databuf **dpp;
register u_char *cp;
- int buflen;
+ int buflen, *countp;
{
register struct databuf *dp;
char dname[MAXDNAME];
- register int n, count = 0;
+ register int n, bytes;
+ bytes = *countp = 0;
getname(np, dname, sizeof(dname));
for (dp = *dpp++; dp != NULL; dp = *dpp++) {
if (stale(dp))
@@ -2502,18 +2554,20 @@ add_data(np, dpp, cp, buflen)
continue;
#endif
if ((n = make_rr(dname, dp, cp, buflen, 1)) < 0)
- return (-count); /* Truncation */
+ return (-bytes); /* Truncation */
cp += n;
buflen -= n;
- count += n;
+ bytes += n;
+ (*countp)++;
}
- return (count);
+ return (bytes);
}
/*
* This is best thought of as a "cache invalidate" function.
* It is called whenever a piece of data is determined to have
- * timed out. It is better to have no information, than to
+ * become invalid either through a timeout or a validation
+ * failure. It is better to have no information, than to
* have partial information you pass off as complete.
*/
void
OpenPOWER on IntegriCloud