summaryrefslogtreecommitdiffstats
path: root/contrib/bind/named/ns_forw.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bind/named/ns_forw.c')
-rw-r--r--contrib/bind/named/ns_forw.c1094
1 files changed, 1094 insertions, 0 deletions
diff --git a/contrib/bind/named/ns_forw.c b/contrib/bind/named/ns_forw.c
new file mode 100644
index 0000000..362bf8b
--- /dev/null
+++ b/contrib/bind/named/ns_forw.c
@@ -0,0 +1,1094 @@
+#if !defined(lint) && !defined(SABER)
+static char sccsid[] = "@(#)ns_forw.c 4.32 (Berkeley) 3/3/91";
+static char rcsid[] = "$Id: ns_forw.c,v 8.14 1996/08/05 08:31:30 vixie Exp $";
+#endif /* not lint */
+
+/*
+ * ++Copyright++ 1986
+ * -
+ * Copyright (c) 1986
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <syslog.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "named.h"
+
+/*
+ * Forward the query to get the answer since its not in the database.
+ * Returns FW_OK if a request struct is allocated and the query sent.
+ * Returns FW_DUP if this is a duplicate of a pending request.
+ * Returns FW_NOSERVER if there were no addresses for the nameservers.
+ * Returns FW_SERVFAIL on malloc error or if asked to do something
+ * dangerous, such as fwd to ourselves or fwd to the host that asked us.
+ *
+ * (no action is taken on errors and qpp is not filled in.)
+ */
+int
+ns_forw(nsp, msg, msglen, fp, qsp, dfd, qpp, dname, np)
+ struct databuf *nsp[];
+ u_char *msg;
+ int msglen;
+ struct sockaddr_in *fp;
+ struct qstream *qsp;
+ int dfd;
+ struct qinfo **qpp;
+ char *dname;
+ struct namebuf *np;
+{
+ register struct qinfo *qp;
+ struct sockaddr_in *nsa;
+ HEADER *hp;
+ u_int16_t id;
+ int n;
+
+ dprintf(3, (ddt, "ns_forw()\n"));
+
+ hp = (HEADER *) msg;
+ id = hp->id;
+ /* Look at them all */
+ for (qp = nsqhead; qp != QINFO_NULL; qp = qp->q_link) {
+ if (qp->q_id == id &&
+ bcmp((char *)&qp->q_from, fp, sizeof(qp->q_from)) == 0 &&
+ ((qp->q_cmsglen == 0 && qp->q_msglen == msglen &&
+ bcmp((char *)qp->q_msg+2, msg+2, msglen-2) == 0) ||
+ (qp->q_cmsglen == msglen &&
+ bcmp((char *)qp->q_cmsg+2, msg+2, msglen-2) == 0))) {
+ dprintf(3, (ddt,
+ "forw: dropped DUP id=%d\n", ntohs(id)));
+#ifdef XSTATS
+ nameserIncr(fp->sin_addr, nssRcvdDupQ);
+#endif
+ return (FW_DUP);
+ }
+ }
+
+ qp = qnew();
+#if defined(LAME_DELEGATION) || defined(VALIDATE)
+ getname(np, qp->q_domain, sizeof qp->q_domain);
+#endif
+ qp->q_from = *fp; /* nslookup wants to know this */
+ if ((n = nslookup(nsp, qp, dname, "ns_forw")) < 0) {
+ dprintf(2, (ddt, "forw: nslookup reports danger\n"));
+ qfree(qp);
+ return (FW_SERVFAIL);
+ } else if (n == 0 && !fwdtab) {
+ dprintf(2, (ddt, "forw: no nameservers found\n"));
+ qfree(qp);
+ return (FW_NOSERVER);
+ }
+ qp->q_stream = qsp;
+ qp->q_curaddr = 0;
+ qp->q_fwd = fwdtab;
+ qp->q_dfd = dfd;
+ qp->q_id = id;
+ qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2;
+ hp->id = qp->q_nsid = htons(nsid_next());
+ hp->ancount = htons(0);
+ hp->nscount = htons(0);
+ hp->arcount = htons(0);
+ if ((qp->q_msg = (u_char *)malloc((unsigned)msglen)) == NULL) {
+ syslog(LOG_NOTICE, "forw: malloc: %m");
+ qfree(qp);
+ return (FW_SERVFAIL);
+ }
+ bcopy(msg, qp->q_msg, qp->q_msglen = msglen);
+ if (!qp->q_fwd) {
+ hp->rd = 0;
+ qp->q_addr[0].stime = tt;
+ }
+
+#ifdef SLAVE_FORWARD
+ if (forward_only)
+ schedretry(qp, (time_t)slave_retry);
+ else
+#endif /* SLAVE_FORWARD */
+ schedretry(qp, qp->q_fwd ?(2*RETRYBASE) :retrytime(qp));
+
+ nsa = Q_NEXTADDR(qp, 0);
+ dprintf(1, (ddt,
+ "forw: forw -> [%s].%d ds=%d nsid=%d id=%d %dms retry %dsec\n",
+ inet_ntoa(nsa->sin_addr),
+ ntohs(nsa->sin_port), ds,
+ ntohs(qp->q_nsid), ntohs(qp->q_id),
+ (qp->q_addr[0].nsdata != NULL)
+ ? qp->q_addr[0].nsdata->d_nstime
+ : -1,
+ (int)(qp->q_time - tt.tv_sec)));
+#ifdef DEBUG
+ if (debug >= 10)
+ fp_nquery(msg, msglen, ddt);
+#endif
+ if (sendto(ds, (char *)msg, msglen, 0, (struct sockaddr *)nsa,
+ sizeof(struct sockaddr_in)) < 0) {
+ if (!haveComplained((char*)nsa->sin_addr.s_addr, sendtoStr))
+ syslog(LOG_INFO, "ns_forw: sendto([%s].%d): %m",
+ inet_ntoa(nsa->sin_addr), ntohs(nsa->sin_port));
+ nameserIncr(nsa->sin_addr, nssSendtoErr);
+ }
+#ifdef XSTATS
+ nameserIncr(fp->sin_addr, nssRcvdFwdQ);
+#endif
+ nameserIncr(nsa->sin_addr, nssSentFwdQ);
+ if (qpp)
+ *qpp = qp;
+ hp->rd = 1;
+ return (0);
+}
+
+/* struct qdatagram *
+ * aIsUs(addr)
+ * scan the datagramq (our list of interface addresses) for "addr"
+ * returns:
+ * pointer to qdatagram entry or NULL if no match is found
+ * notes:
+ * INADDR_ANY ([0.0.0.0]) is on the datagramq, so it's considered "us"
+ * author:
+ * Paul Vixie (DECWRL) April 1991
+ */
+struct qdatagram *
+aIsUs(addr)
+ struct in_addr addr;
+{
+ struct qdatagram *dqp;
+
+ for (dqp = datagramq; dqp != QDATAGRAM_NULL; dqp = dqp->dq_next) {
+ if (addr.s_addr == dqp->dq_addr.s_addr) {
+ return dqp;
+ }
+ }
+ return NULL;
+}
+
+/* haveComplained(tag1, tag2)
+ * check to see if we have complained about (tag1,tag2) recently
+ * (note that these are declared as pointers but are never deref'd)
+ * returns:
+ * boolean: have we complained recently?
+ * side-effects:
+ * outdated complaint records removed from our static list
+ * author:
+ * Paul Vixie (DECWRL) April 1991
+ */
+int
+haveComplained(tag1, tag2)
+ const char *tag1, *tag2;
+{
+ struct complaint {
+ const char *tag1, *tag2;
+ time_t expire;
+ struct complaint *next;
+ };
+ static struct complaint *List = NULL;
+ struct complaint *cur, *next, *prev;
+ int r = 0;
+
+ for (cur = List, prev = NULL; cur; prev = cur, cur = next) {
+ next = cur->next;
+ if (tt.tv_sec > cur->expire) {
+ if (prev)
+ prev->next = next;
+ else
+ List = next;
+ free((char*) cur);
+ cur = prev;
+ } else if ((tag1 == cur->tag1) && (tag2 == cur->tag2)) {
+ r++;
+ }
+ }
+ if (!r) {
+ cur = (struct complaint *)malloc(sizeof(struct complaint));
+ if (cur) {
+ cur->tag1 = tag1;
+ cur->tag2 = tag2;
+ cur->expire = tt.tv_sec + INIT_REFRESH; /* "10:00" */
+ cur->next = NULL;
+ if (prev)
+ prev->next = cur;
+ else
+ List = cur;
+ }
+ }
+ return (r);
+}
+
+/* void
+ * nslookupComplain(sysloginfo, queryname, complaint, dname, a_rr)
+ * Issue a complaint about a dangerous situation found by nslookup().
+ * params:
+ * sysloginfo is a string identifying the complainant.
+ * queryname is the domain name associated with the problem.
+ * complaint is a string describing what is wrong.
+ * dname and a_rr are the problematic other name server.
+ */
+static void
+nslookupComplain(sysloginfo, queryname, complaint, dname, a_rr, nsdp)
+ const char *sysloginfo, *queryname, *complaint, *dname;
+ const struct databuf *a_rr, *nsdp;
+{
+#ifdef STATS
+ char nsbuf[20];
+ char abuf[20];
+#endif
+ char *a, *ns;
+ const char *a_type;
+ int print_a;
+
+ dprintf(2, (ddt, "NS '%s' %s\n", dname, complaint));
+ if (sysloginfo && queryname && !haveComplained(queryname, complaint))
+ {
+ char buf[999];
+
+ a = ns = (char *)NULL;
+ print_a = (a_rr->d_type == T_A);
+ a_type = p_type(a_rr->d_type);
+#ifdef NCACHE
+ if (a_rr->d_rcode) {
+ print_a = 0;
+ switch(a_rr->d_rcode) {
+ case NXDOMAIN:
+ a_type = "NXDOMAIN";
+ break;
+ case NOERROR_NODATA:
+ a_type = "NODATA";
+ break;
+ }
+ }
+#endif
+#ifdef STATS
+ if (nsdp) {
+ if (nsdp->d_ns) {
+ strcpy(nsbuf, inet_ntoa(nsdp->d_ns->addr));
+ ns = nsbuf;
+ } else {
+ ns = zones[nsdp->d_zone].z_origin;
+ }
+ }
+ if (a_rr->d_ns) {
+ strcpy(abuf, inet_ntoa(a_rr->d_ns->addr));
+ a = abuf;
+ } else {
+ a = zones[a_rr->d_zone].z_origin;
+ }
+#endif
+ /* syslog only takes 5 params */
+ if ( a != NULL || ns != NULL)
+ sprintf(buf, "%s: query(%s) %s (%s:%s) learnt (%s=%s:NS=%s)",
+ sysloginfo, queryname,
+ complaint, dname,
+ print_a ?
+ inet_ntoa(data_inaddr(a_rr->d_data)) : "",
+ a_type,
+ a ? a : "<Not Available>",
+ ns ? ns : "<Not Available>" );
+ else
+ sprintf(buf, "%s: query(%s) %s (%s:%s)",
+ sysloginfo, queryname,
+ complaint, dname,
+ print_a ?
+ inet_ntoa(data_inaddr(a_rr->d_data)) : "");
+ syslog(LOG_INFO, buf);
+ }
+}
+
+/*
+ * nslookup(nsp, qp, syslogdname, sysloginfo)
+ * Lookup the address for each nameserver in `nsp' and add it to
+ * the list saved in the qinfo structure pointed to by `qp'.
+ * Omits information about nameservers that we shouldn't ask.
+ * Detects the following dangerous operations:
+ * One of the A records for one of the nameservers in nsp
+ * refers to the address of one of our own interfaces;
+ * One of the A records refers to the nameserver port on
+ * the host that asked us this question.
+ * returns: the number of addresses added, or -1 if a dangerous operation
+ * is detected.
+ * side effects:
+ * if a dangerous situation is detected and (syslogdname && sysloginfo),
+ * calls syslog.
+ */
+int
+nslookup(nsp, qp, syslogdname, sysloginfo)
+ struct databuf *nsp[];
+ register struct qinfo *qp;
+ const char *syslogdname;
+ const char *sysloginfo;
+{
+ register struct namebuf *np;
+ register struct databuf *dp, *nsdp;
+ register struct qserv *qs;
+ register int n;
+ register unsigned int i;
+ struct hashbuf *tmphtp;
+ char *dname;
+ const char *fname;
+ int oldn, naddr, class, found_arr, potential_ns;
+ time_t curtime;
+
+ dprintf(3, (ddt, "nslookup(nsp=0x%lx, qp=0x%lx, \"%s\")\n",
+ (u_long)nsp, (u_long)qp, syslogdname));
+
+ potential_ns = 0;
+ naddr = n = qp->q_naddr;
+ curtime = (u_long) tt.tv_sec;
+ while ((nsdp = *nsp++) != NULL) {
+ class = nsdp->d_class;
+ dname = (char *)nsdp->d_data;
+ dprintf(3, (ddt, "nslookup: NS \"%s\" c=%d t=%d (%#lx)\n",
+ dname, class, nsdp->d_type,
+ (u_long)nsdp->d_flags));
+
+ /* don't put in servers we have tried */
+ for (i = 0; i < qp->q_nusedns; i++) {
+ if (qp->q_usedns[i] == nsdp) {
+ dprintf(2, (ddt,
+ "skipping used NS w/name %s\n",
+ nsdp->d_data));
+ goto skipserver;
+ }
+ }
+
+ tmphtp = ((nsdp->d_flags & DB_F_HINT) ?fcachetab :hashtab);
+ np = nlookup(dname, &tmphtp, &fname, 1);
+ if (np == NULL) {
+ dprintf(3, (ddt, "%s: not found %s %lx\n",
+ dname, fname, (u_long)np));
+ found_arr = 0;
+ goto need_sysquery;
+ }
+ if (fname != dname) {
+ if (findMyZone(np, class) == DB_Z_CACHE) {
+ /*
+ * lifted from findMyZone()
+ * We really need to know if the NS
+ * is the bottom of one of our zones
+ * to see if we've got missing glue
+ */
+ for (; np; np = np_parent(np))
+ for (dp = np->n_data; dp; dp=dp->d_next)
+ if (match(dp, class, T_NS)) {
+#ifdef NCACHE
+ if (dp->d_rcode)
+ break;
+#endif
+ if (dp->d_zone) {
+ static char *complaint =
+ "Glue A RR missing";
+ nslookupComplain(sysloginfo,
+ syslogdname,
+ complaint,
+ dname, dp,
+ nsdp);
+ goto skipserver;
+ } else {
+ found_arr = 0;
+ goto need_sysquery;
+ }
+ }
+ /* shouldn't happen, but ... */
+ found_arr = 0;
+ goto need_sysquery;
+ } else {
+ static char *complaint =
+ "Authoritative A RR missing";
+ nslookupComplain(sysloginfo, syslogdname,
+ complaint, dname, dp, nsdp);
+ continue;
+ }
+ }
+ found_arr = 0;
+ oldn = n;
+
+ /* look for name server addresses */
+ for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
+ struct in_addr nsa;
+
+ if (dp->d_type == T_CNAME && dp->d_class == class) {
+ static char *complaint = "NS points to CNAME";
+#ifdef NCACHE
+ if (dp->d_rcode)
+ continue;
+#endif
+ nslookupComplain(sysloginfo, syslogdname,
+ complaint, dname, dp, nsdp);
+ goto skipserver;
+ }
+ if (dp->d_type != T_A || dp->d_class != class)
+ continue;
+#ifdef NCACHE
+ if (dp->d_rcode) {
+ static char *complaint =
+ "A RR negative cache entry";
+ nslookupComplain(sysloginfo, syslogdname,
+ complaint, dname, dp, nsdp);
+ goto skipserver;
+ }
+#endif
+ if (data_inaddr(dp->d_data).s_addr == INADDR_ANY) {
+ static char *complaint = "Bogus (0.0.0.0) A RR";
+ nslookupComplain(sysloginfo, syslogdname,
+ complaint, dname, dp, nsdp);
+ continue;
+ }
+#ifdef INADDR_LOOPBACK
+ if (ntohl(data_inaddr(dp->d_data).s_addr) ==
+ INADDR_LOOPBACK) {
+ static char *complaint = "Bogus LOOPBACK A RR";
+ nslookupComplain(sysloginfo, syslogdname,
+ complaint, dname, dp, nsdp);
+ continue;
+ }
+#endif
+#ifdef INADDR_BROADCAST
+ if (ntohl(data_inaddr(dp->d_data).s_addr) ==
+ INADDR_BROADCAST) {
+ static char *complaint = "Bogus BROADCAST A RR";
+ nslookupComplain(sysloginfo, syslogdname,
+ complaint, dname, dp, nsdp);
+ continue;
+ }
+#endif
+#ifdef IN_MULTICAST
+ if (IN_MULTICAST(ntohl(data_inaddr(dp->d_data).s_addr))) {
+ static char *complaint = "Bogus MULTICAST A RR";
+ nslookupComplain(sysloginfo, syslogdname,
+ complaint, dname, dp, nsdp);
+ continue;
+ }
+#endif
+ /*
+ * Don't use records that may become invalid to
+ * reference later when we do the rtt computation.
+ * Never delete our safety-belt information!
+ */
+ if ((dp->d_zone == 0) &&
+#ifdef DATUMREFCNT
+ (dp->d_ttl < curtime) &&
+#else
+ (dp->d_ttl < (curtime+900)) &&
+#endif
+ !(dp->d_flags & DB_F_HINT) )
+ {
+ dprintf(3, (ddt,
+ "nslookup: stale entry '%s'\n",
+ NAME(*np)));
+ /* Cache invalidate the NS RR's */
+#ifndef DATUMREFCNT
+ if (dp->d_ttl < curtime)
+#endif
+ {
+ delete_all(np, class, T_A);
+ n = oldn;
+ found_arr = 0;
+ goto need_sysquery;
+ }
+ }
+#ifdef VALIDATE
+ /* anant@isi.edu validation procedure, maintains a
+ * table of server names-addresses used recently
+ */
+ store_name_addr(dname, data_inaddr(dp->d_data),
+ syslogdname, sysloginfo);
+#endif /*VALIDATE*/
+
+ found_arr++;
+ nsa = data_inaddr(dp->d_data);
+ /* don't put in duplicates */
+ qs = qp->q_addr;
+ for (i = 0; i < n; i++, qs++)
+ if (qs->ns_addr.sin_addr.s_addr == nsa.s_addr)
+ goto skipaddr;
+ qs->ns_addr.sin_family = AF_INET;
+ qs->ns_addr.sin_port = ns_port;
+ qs->ns_addr.sin_addr = nsa;
+ qs->ns = nsdp;
+ qs->nsdata = dp;
+ qs->nretry = 0;
+ /*
+ * if we are being asked to fwd a query whose
+ * nameserver list includes our own name/address(es),
+ * then we have detected a lame delegation and rather
+ * than melt down the network and hose down the other
+ * servers (who will hose us in return), we'll return
+ * -1 here which will cause SERVFAIL to be sent to
+ * the client's resolver which will hopefully then
+ * shut up.
+ *
+ * (originally done in nsContainsUs by vix@dec mar92;
+ * moved into nslookup by apb@und jan1993)
+ *
+ * try to limp along instead of denying service
+ * gdonl mar96
+ */
+ if (aIsUs(nsa)) {
+ static char *complaint = "contains our address";
+ nslookupComplain(sysloginfo, syslogdname,
+ complaint, dname, dp, nsdp);
+ continue;
+ }
+ /*
+ * If we want to forward to a host that asked us
+ * this question then either we or they are sick
+ * (unless they asked from some port other than
+ * their nameserver port). (apb@und jan1993)
+ *
+ * try to limp along instead of denying service
+ * gdonl mar96
+ */
+ if (bcmp((char *)&qp->q_from, (char *)&qs->ns_addr,
+ sizeof(qp->q_from)) == 0)
+ {
+ static char *complaint = "forwarding loop";
+ nslookupComplain(sysloginfo, syslogdname,
+ complaint, dname, dp, nsdp);
+ continue;
+ }
+#ifdef BOGUSNS
+ /*
+ * Don't forward queries to bogus servers. Note
+ * that this is unlike the previous tests, which
+ * are fatal to the query. Here we just skip the
+ * server, which is only fatal if it's the last
+ * server. Note also that we antialias here -- all
+ * A RR's of a server are considered the same server,
+ * and if any of them is bogus we skip the whole
+ * server. Those of you using multiple A RR's to
+ * load-balance your servers will (rightfully) lose
+ * here. But (unfortunately) only if they are bogus.
+ */
+ if (addr_on_netlist(nsa, boglist))
+ goto skipserver;
+#endif
+
+ n++;
+ if (n >= NSMAX)
+ goto out;
+ skipaddr:
+ NULL;
+ }
+ dprintf(8, (ddt, "nslookup: %d ns addrs\n", n));
+ need_sysquery:
+ if (found_arr == 0) {
+ potential_ns++;
+ if (!(qp->q_flags & Q_SYSTEM))
+ (void) sysquery(dname, class, T_A, NULL, 0,
+ QUERY);
+ }
+ skipserver:
+ NULL;
+ }
+out:
+ dprintf(3, (ddt, "nslookup: %d ns addrs total\n", n));
+ qp->q_naddr = n;
+ if (n == 0 && potential_ns == 0 && !fwdtab) {
+ static char *complaint = "No possible A RRs";
+ if (sysloginfo && syslogdname &&
+ !haveComplained(syslogdname, complaint))
+ {
+ syslog(LOG_INFO, "%s: query(%s) %s",
+ sysloginfo, syslogdname, complaint);
+ }
+ return(-1);
+ }
+#ifdef DATUMREFCNT
+ /* must be run before the sort */
+ for (i = naddr ; i < n ; i++) {
+ qp->q_addr[i].nsdata->d_rcnt++;
+ qp->q_addr[i].ns->d_rcnt++;
+ }
+#endif
+ if (n > 1) {
+ qsort((char *)qp->q_addr, n, sizeof(struct qserv),
+ (int (*)__P((const void *, const void *)))qcomp);
+ }
+ return (n - naddr);
+}
+
+/*
+ * qcomp - compare two NS addresses, and return a negative, zero, or
+ * positive value depending on whether the first NS address is
+ * "better than", "equally good as", or "inferior to" the second
+ * NS address.
+ *
+ * How "goodness" is defined (for the purposes of this routine):
+ * - If the estimated round trip times differ by an amount deemed significant
+ * then the one with the smaller estimate is preferred; else
+ * - If we can determine which one is topologically closer then the
+ * closer one is preferred; else
+ * - The one with the smaller estimated round trip time is preferred
+ * (zero is returned if the two estimates are identical).
+ *
+ * How "topological closeness" is defined (for the purposes of this routine):
+ * Ideally, named could consult some magic map of the Internet and
+ * determine the length of the path to an arbitrary destination. Sadly,
+ * no such magic map exists. However, named does have a little bit of
+ * topological information in the form of the sortlist (which includes
+ * the directly connected subnet(s), the directly connected net(s), and
+ * any additional nets that the administrator has added using the "sortlist"
+ * directive in the bootfile. Thus, if only one of the addresses matches
+ * something in the sortlist then it is considered to be topologically
+ * closer. If both match, but match different entries in the sortlist,
+ * then the one that matches the entry closer to the beginning of the
+ * sorlist is considered to be topologically closer. In all other cases,
+ * topological closeness is ignored because it's either indeterminate or
+ * equal.
+ *
+ * How times are compared:
+ * Both times are rounded to the closest multiple of the NOISE constant
+ * defined below and then compared. If the rounded values are equal
+ * then the difference in the times is deemed insignificant. Rounding
+ * is used instead of merely taking the absolute value of the difference
+ * because doing the latter would make the ordering defined by this
+ * routine be incomplete in the mathematical sense (e.g. A > B and
+ * B > C would not imply A > C). The mathematics are important in
+ * practice to avoid core dumps in qsort().
+ *
+ * XXX: this doesn't solve the European root nameserver problem very well.
+ * XXX: we should detect and mark as inferior nameservers that give bogus
+ * answers
+ *
+ * (this was originally vixie's stuff but almquist fixed fatal bugs in it
+ * and wrote the above documentation)
+ */
+
+/*
+ * RTT delta deemed to be significant, in milliseconds. With the current
+ * definition of RTTROUND it must be a power of 2.
+ */
+#define NOISE 128 /* milliseconds; 0.128 seconds */
+
+#define sign(x) (((x) < 0) ? -1 : ((x) > 0) ? 1 : 0)
+#define RTTROUND(rtt) (((rtt) + (NOISE >> 1)) & ~(NOISE - 1))
+
+int
+qcomp(qs1, qs2)
+ struct qserv *qs1, *qs2;
+{
+ int pos1, pos2, pdiff;
+ u_long rtt1, rtt2;
+ long tdiff;
+
+ if ((!qs1->nsdata) || (!qs2->nsdata))
+ return 0;
+ rtt1 = qs1->nsdata->d_nstime;
+ rtt2 = qs2->nsdata->d_nstime;
+
+ dprintf(10, (ddt, "qcomp(%s, %s) %lu (%lu) - %lu (%lu) = %lu",
+ inet_ntoa(qs1->ns_addr.sin_addr),
+ inet_ntoa(qs2->ns_addr.sin_addr),
+ rtt1, RTTROUND(rtt1), rtt2, RTTROUND(rtt2),
+ rtt1 - rtt2));
+ if (RTTROUND(rtt1) == RTTROUND(rtt2)) {
+ pos1 = position_on_netlist(qs1->ns_addr.sin_addr, nettab);
+ pos2 = position_on_netlist(qs2->ns_addr.sin_addr, nettab);
+ pdiff = pos1 - pos2;
+ dprintf(10, (ddt, ", pos1=%d, pos2=%d\n", pos1, pos2));
+ if (pdiff)
+ return (pdiff);
+ } else {
+ dprintf(10, (ddt, "\n"));
+ }
+ tdiff = rtt1 - rtt2;
+ return (sign(tdiff));
+}
+#undef sign
+#undef RTTROUND
+
+/*
+ * Arrange that forwarded query (qp) is retried after t seconds.
+ * Query list will be sorted after z_time is updated.
+ */
+void
+schedretry(qp, t)
+ struct qinfo *qp;
+ time_t t;
+{
+ register struct qinfo *qp1, *qp2;
+
+#ifdef DEBUG
+ if (debug > 3) {
+ fprintf(ddt, "schedretry(0x%lx, %ld sec)\n",
+ (u_long)qp, (long)t);
+ if (qp->q_time)
+ fprintf(ddt,
+ "WARNING: schedretry(%#lx, %ld) q_time already %ld\n",
+ (u_long)qp, (long)t, (long)qp->q_time);
+ }
+#endif
+ t += (u_long) tt.tv_sec;
+ qp->q_time = t;
+
+ if ((qp1 = retryqp) == NULL) {
+ retryqp = qp;
+ qp->q_next = NULL;
+ return;
+ }
+ if (t < qp1->q_time) {
+ qp->q_next = qp1;
+ retryqp = qp;
+ return;
+ }
+ while ((qp2 = qp1->q_next) != NULL && qp2->q_time < t)
+ qp1 = qp2;
+ qp1->q_next = qp;
+ qp->q_next = qp2;
+}
+
+/*
+ * Unsched is called to remove a forwarded query entry.
+ */
+void
+unsched(qp)
+ struct qinfo *qp;
+{
+ register struct qinfo *np;
+
+ dprintf(3, (ddt, "unsched(%#lx, %d)\n", (u_long)qp, ntohs(qp->q_id)));
+ if (retryqp == qp) {
+ retryqp = qp->q_next;
+ } else {
+ for (np=retryqp; np->q_next != QINFO_NULL; np = np->q_next) {
+ if (np->q_next != qp)
+ continue;
+ np->q_next = qp->q_next; /* dequeue */
+ break;
+ }
+ }
+ qp->q_next = QINFO_NULL; /* sanity check */
+ qp->q_time = 0;
+}
+
+/*
+ * Retry is called to retransmit query 'qp'.
+ */
+void
+retry(qp)
+ register struct qinfo *qp;
+{
+ register int n;
+ register HEADER *hp;
+ struct sockaddr_in *nsa;
+
+ dprintf(3, (ddt, "retry(x%lx) id=%d\n", (u_long)qp, ntohs(qp->q_id)));
+
+ if (qp->q_msg == NULL) { /* XXX - why? */
+ qremove(qp);
+ return;
+ }
+
+ if (qp->q_expire && (qp->q_expire < tt.tv_sec)) {
+ dprintf(1, (ddt,
+ "retry(x%lx): expired @ %lu (%d secs before now (%lu))\n",
+ (u_long)qp, (u_long)qp->q_expire,
+ (int)(tt.tv_sec - qp->q_expire),
+ (u_long)tt.tv_sec));
+ if (qp->q_stream) /* return failure code on stream */
+ goto fail;
+ qremove(qp);
+ return;
+ }
+
+ /* try next address */
+ n = qp->q_curaddr;
+ if (qp->q_fwd) {
+ qp->q_fwd = qp->q_fwd->next;
+ if (qp->q_fwd)
+ goto found;
+ /* out of forwarders, try direct queries */
+ } else
+ ++qp->q_addr[n].nretry;
+ if (!forward_only) {
+ do {
+ if (++n >= (int)qp->q_naddr)
+ n = 0;
+ if (qp->q_addr[n].nretry < MAXRETRY)
+ goto found;
+ } while (n != qp->q_curaddr);
+ }
+fail:
+ /*
+ * Give up. Can't reach destination.
+ */
+ hp = (HEADER *)(qp->q_cmsg ? qp->q_cmsg : qp->q_msg);
+ if (qp->q_flags & Q_PRIMING) {
+ /* Can't give up priming */
+ unsched(qp);
+ schedretry(qp, (time_t)60*60); /* 1 hour */
+ hp->rcode = NOERROR; /* Lets be safe, reset the query */
+ hp->qr = hp->aa = 0;
+ qp->q_fwd = fwdtab;
+ for (n = 0; n < (int)qp->q_naddr; n++)
+ qp->q_addr[n].nretry = 0;
+ return;
+ }
+ dprintf(5, (ddt, "give up\n"));
+ n = ((HEADER *)qp->q_cmsg ? qp->q_cmsglen : qp->q_msglen);
+ hp->id = qp->q_id;
+ hp->qr = 1;
+ hp->ra = (NoRecurse == 0);
+ hp->rd = 1;
+ hp->rcode = SERVFAIL;
+#ifdef DEBUG
+ if (debug >= 10)
+ fp_nquery(qp->q_msg, n, ddt);
+#endif
+ if (send_msg((u_char *)hp, n, qp)) {
+ dprintf(1, (ddt, "gave up retry(x%lx) nsid=%d id=%d\n",
+ (u_long)qp, ntohs(qp->q_nsid), ntohs(qp->q_id)));
+ }
+#ifdef XSTATS
+ nameserIncr(qp->q_from.sin_addr, nssSentFail);
+#endif
+ qremove(qp);
+ return;
+
+found:
+ if (qp->q_fwd == 0 && qp->q_addr[n].nretry == 0)
+ qp->q_addr[n].stime = tt;
+ qp->q_curaddr = n;
+ hp = (HEADER *)qp->q_msg;
+ hp->rd = (qp->q_fwd ? 1 : 0);
+ nsa = Q_NEXTADDR(qp, n);
+ dprintf(1, (ddt,
+ "%s(addr=%d n=%d) -> [%s].%d ds=%d nsid=%d id=%d %dms\n",
+ (qp->q_fwd ? "reforw" : "resend"),
+ n, qp->q_addr[n].nretry,
+ inet_ntoa(nsa->sin_addr),
+ ntohs(nsa->sin_port), ds,
+ ntohs(qp->q_nsid), ntohs(qp->q_id),
+ (qp->q_addr[n].nsdata != 0)
+ ? qp->q_addr[n].nsdata->d_nstime
+ : (-1)));
+#ifdef DEBUG
+ if (debug >= 10)
+ fp_nquery(qp->q_msg, qp->q_msglen, ddt);
+#endif
+ /* NOSTRICT */
+ if (sendto(ds, (char*)qp->q_msg, qp->q_msglen, 0,
+ (struct sockaddr *)nsa,
+ sizeof(struct sockaddr_in)) < 0) {
+ dprintf(3, (ddt, "error resending msg errno=%d\n", errno));
+ }
+ hp->rd = 1; /* leave set to 1 for dup detection */
+ nameserIncr(nsa->sin_addr, nssSentDupQ);
+ unsched(qp);
+#ifdef SLAVE_FORWARD
+ if(forward_only)
+ schedretry(qp, (time_t)slave_retry);
+ else
+#endif /* SLAVE_FORWARD */
+ schedretry(qp, qp->q_fwd ? (2*RETRYBASE) : retrytime(qp));
+}
+
+/*
+ * Compute retry time for the next server for a query.
+ * Use a minimum time of RETRYBASE (4 sec.) or twice the estimated
+ * service time; * back off exponentially on retries, but place a 45-sec.
+ * ceiling on retry times for now. (This is because we don't hold a reference
+ * on servers or their addresses, and we have to finish before they time out.)
+ */
+time_t
+retrytime(qp)
+ struct qinfo *qp;
+{
+ time_t t, u, v;
+ struct qserv *ns = &qp->q_addr[qp->q_curaddr];
+
+ if (ns->nsdata != NULL)
+ t = (time_t) MAX(RETRYBASE, 2 * ns->nsdata->d_nstime / 1000);
+ else
+ t = (time_t) RETRYBASE;
+ u = t << ns->nretry;
+ v = MIN(u, RETRY_TIMEOUT); /* max. retry timeout for now */
+ dprintf(3, (ddt, "retrytime: nstime%ldms t%ld nretry%ld u%ld : v%ld\n",
+ ns->nsdata ?(long)(ns->nsdata->d_nstime / 1000) :(long)-1,
+ (long)t, (long)ns->nretry, (long)u, (long)v));
+ return (v);
+}
+
+void
+qflush()
+{
+ while (nsqhead)
+ qremove(nsqhead);
+ nsqhead = QINFO_NULL;
+}
+
+void
+qremove(qp)
+ register struct qinfo *qp;
+{
+ dprintf(3, (ddt, "qremove(x%lx)\n", (u_long)qp));
+
+ if (qp->q_flags & Q_ZSERIAL)
+ qserial_answer(qp, 0);
+ unsched(qp);
+ qfree(qp);
+}
+
+#if defined(__STDC__) || defined(__GNUC__)
+struct qinfo *
+qfindid(u_int16_t id)
+#else
+struct qinfo *
+qfindid(id)
+ register u_int16_t id;
+#endif
+{
+ register struct qinfo *qp;
+
+ dprintf(3, (ddt, "qfindid(%d)\n", ntohs(id)));
+ for (qp = nsqhead; qp!=QINFO_NULL; qp = qp->q_link) {
+ if (qp->q_nsid == id)
+ return(qp);
+ }
+ dprintf(5, (ddt, "qp not found\n"));
+ return (NULL);
+}
+
+struct qinfo *
+#ifdef DMALLOC
+qnew_tagged(file, line)
+ char *file;
+ int line;
+#else
+qnew()
+#endif
+{
+ register struct qinfo *qp;
+
+ qp = (struct qinfo *)
+#ifdef DMALLOC
+ dcalloc(file, line, 1, sizeof(struct qinfo));
+#else
+ calloc(1, sizeof(struct qinfo));
+#endif
+ if (qp == NULL) {
+ dprintf(5, (ddt, "qnew: calloc error\n"));
+ syslog(LOG_ERR, "forw: %m");
+ exit(12);
+ }
+ dprintf(5, (ddt, "qnew(x%lx)\n", (u_long)qp));
+#ifdef BIND_NOTIFY
+ qp->q_notifyzone = DB_Z_CACHE;
+#endif
+ qp->q_link = nsqhead;
+ nsqhead = qp;
+ return (qp);
+}
+
+void
+qfree(qp)
+ struct qinfo *qp;
+{
+ register struct qinfo *np;
+ register struct databuf *dp;
+#ifdef DATUMREFCNT
+ int i;
+#endif
+
+ dprintf(3, (ddt, "Qfree(x%lx)\n", (u_long)qp));
+ if (qp->q_next)
+ dprintf(1, (ddt, "WARNING: qfree of linked ptr x%lx\n",
+ (u_long)qp));
+ if (qp->q_msg)
+ free(qp->q_msg);
+ if (qp->q_cmsg)
+ free(qp->q_cmsg);
+#ifdef DATUMREFCNT
+ for (i = 0 ; i < (int)qp->q_naddr ; i++) {
+ dp = qp->q_addr[i].ns;
+ if (dp)
+ if (--(dp->d_rcnt)) {
+ dprintf(3, (ddt, "qfree: ns %s rcnt %d\n",
+ dp->d_data,
+ dp->d_rcnt));
+ } else {
+ dprintf(3, (ddt, "qfree: ns %s rcnt %d delayed\n",
+ dp->d_data,
+ dp->d_rcnt));
+ free((char*)dp);
+ }
+ dp = qp->q_addr[i].nsdata;
+ if (dp)
+ if ((--(dp->d_rcnt))) {
+ dprintf(3, (ddt, "qfree: nsdata %08.8X rcnt %d\n",
+ *(int32_t *)(dp->d_data),
+ dp->d_rcnt));
+ } else {
+ dprintf(3, (ddt, "qfree: nsdata %08.8X rcnt %d delayed\n",
+ *(int32_t *)(dp->d_data),
+ dp->d_rcnt));
+ free((char*)dp);
+ }
+ }
+#endif
+ if( nsqhead == qp ) {
+ nsqhead = qp->q_link;
+ } else {
+ for( np=nsqhead; np->q_link != QINFO_NULL; np = np->q_link ) {
+ if( np->q_link != qp ) continue;
+ np->q_link = qp->q_link; /* dequeue */
+ break;
+ }
+ }
+ free((char *)qp);
+}
OpenPOWER on IntegriCloud