diff options
Diffstat (limited to 'contrib/bind/bin/named/ns_resp.c')
-rw-r--r-- | contrib/bind/bin/named/ns_resp.c | 4141 |
1 files changed, 0 insertions, 4141 deletions
diff --git a/contrib/bind/bin/named/ns_resp.c b/contrib/bind/bin/named/ns_resp.c deleted file mode 100644 index 7bc166a..0000000 --- a/contrib/bind/bin/named/ns_resp.c +++ /dev/null @@ -1,4141 +0,0 @@ -#if !defined(lint) && !defined(SABER) -static const char sccsid[] = "@(#)ns_resp.c 4.65 (Berkeley) 3/3/91"; -static const char rcsid[] = "$Id: ns_resp.c,v 8.186.6.5 2003/09/04 03:03:18 marka Exp $"; -#endif /* not lint */ - -/* - * Copyright (c) 1986, 1988, 1990 - * 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. - */ - -/* - * Portions Copyright (c) 1995 by International Business Machines, Inc. - * - * International Business Machines, Inc. (hereinafter called IBM) grants - * permission under its copyrights to use, copy, modify, and distribute this - * Software with or without fee, provided that the above copyright notice and - * all paragraphs of this notice appear in all copies, and that the name of IBM - * not be used in connection with the marketing of any product incorporating - * the Software or modifications thereof, without specific, written prior - * permission. - * - * To the extent it has a right to do so, IBM grants an immunity from suit - * under its patents, if any, for the use, sale or manufacture of products to - * the extent that such products are used for performing Domain Name System - * dynamic updates in TCP/IP networks by means of the Software. No immunity is - * granted for any product per se or for any other function of any product. - * - * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, - * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN - * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. - */ - -/* - * Portions Copyright (c) 1996-2000 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 - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM 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. - */ - -#include "port_before.h" - -#include <sys/types.h> -#include <sys/param.h> -#include <sys/socket.h> -#include <sys/file.h> -#include <sys/un.h> - -#include <netinet/in.h> -#include <arpa/nameser.h> -#include <arpa/inet.h> - -#include <errno.h> -#include <limits.h> -#include <resolv.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <time.h> - -#include <isc/eventlib.h> -#include <isc/logging.h> -#include <isc/memcluster.h> - -#include <isc/dst.h> - -#include "port_after.h" - -#include "named.h" - -static 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", - notSingleQuery[] = "not exactly one query", - expandFailedQuery[] = "dn_expand failed in query", - expandFailedAnswer[] = "dn_expand 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", - badNameFound[] = "found an invalid domain name", - wrongQuestion[] = "answer to wrong question", - danglingCname[] = "dangling CNAME pointer", - nonRecursiveForwarder[]= "non-recursive forwarder"; - -struct db_list { - struct db_list *db_next; - struct databuf *db_dp; -}; - -struct flush_set { - char * fs_name; - int fs_type; - int fs_class; - u_int fs_cred; - struct db_list *fs_list; - struct db_list *fs_last; -}; - -static void rrsetadd(struct flush_set *, const char *, - struct databuf *), - rrsetupdate(struct flush_set *, int flags, - struct sockaddr_in, int), - flushrrset(struct flush_set *, struct sockaddr_in), - free_flushset(struct flush_set *, int), - check_hints(struct flush_set *); -static int rrsetcmp(char *, struct db_list *, struct hashbuf *), - check_root(void), - check_ns(void), - wanted(const struct databuf *, int, int), - wantedsig(const struct databuf *, int, int), - rrextract(u_char *, int, u_char *, - struct databuf **, char *, int, - struct sockaddr_in, char **); -static void mark_bad(struct qinfo *qp, struct sockaddr_in from); -static void mark_lame(struct qinfo *qp, struct sockaddr_in from); -static int mark_noedns(struct qinfo *qp, struct sockaddr_in from, - int cache); -static void fast_retry(struct qinfo *qp, struct sockaddr_in from, - int samehost); -static void add_related_additional(char *); -static void free_related_additional(void); -static int related_additional(char *); -static void freestr_maybe(char **); -static enum ordering match_order(const struct namebuf *, int, int); -static int match_name(const struct namebuf *, const char *, size_t); - -#define MAX_RELATED 100 - -static int num_related = 0; -static char *related[MAX_RELATED]; - -static char * -learntFrom(struct qinfo *qp, struct sockaddr_in *server) { - static char *buf = NULL; - const char *a, *ns, *na; - struct databuf *db; - int i; - char nsbuf[20]; - char abuf[20]; - static const char fmt[] = " '%s': learnt (A=%s,NS=%s)"; - - a = ns = na = "<Not Available>"; - - for (i = 0; (u_int)i < qp->q_naddr; i++) { - if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, - server->sin_addr)) { - db = qp->q_addr[i].ns; - if (db != NULL) { - if (db->d_addr.s_addr != htonl(0)) { - strcpy(nsbuf, inet_ntoa(db->d_addr)); - ns = nsbuf; - } else { - ns = zones[db->d_zone].z_origin; - } - if (db->d_rcode == 0) - na = (char*)qp->q_addr[i].ns->d_data; - } - db = qp->q_addr[i].nsdata; - if (db != NULL) { - if (db->d_addr.s_addr != htonl(0)) { - strcpy(abuf, inet_ntoa(db->d_addr)); - a = abuf; - } else { - a = zones[db->d_zone].z_origin; - } - } - break; - } - } - - if (a == ns && ns == na) /* all "UNKNOWN" */ - return (NULL); - - if (*a == '\0') - a = "\".\""; - if (*ns == '\0') - ns = "\".\""; - if (*na == '\0') - na = "\".\""; - - - buf = newstr(sizeof fmt + strlen(na) + strlen(a) + strlen(ns), 0); - if (buf == NULL) - return (NULL); - sprintf(buf, fmt, na, a, ns); - return (buf); -} - -void -ns_resp(u_char *msg, int msglen, struct sockaddr_in from, struct qstream *qsp) -{ - struct qinfo *qp; - HEADER *hp; - struct qserv *qs = NULL; - struct databuf *ns, *ns2; - u_char *cp, *answers, *eom = msg + msglen; - struct flush_set *flushset = NULL; - int flushset_size = 0; - struct sockaddr_in *nsa; - struct databuf *nsp[NSMAX]; - int i, c, n, qdcount, ancount, aucount, nscount, arcount, arfirst; - int soacount; - u_int qtype, qclass; - int validanswer, dbflags; - int cname, lastwascname, externalcname, cachenegative; - int count, founddata, foundname; - int buflen; - int newmsglen; - char name[MAXDNAME], qname[MAXDNAME], aname[MAXDNAME]; - char msgbuf[MAXDNAME+100]; - char *dname, tmpdomain[MAXDNAME]; - const char *fname; - const char *formerrmsg = "brain damage"; - u_char newmsg[NS_MAXMSG]; - u_char **dpp, *tp; - time_t rtrip; - struct hashbuf *htp; - struct namebuf *np; - struct fwdinfo *fwd; - struct databuf *dp; - char *tname = NULL; - int sendto_errno = 0; - int has_tsig, oldqlen = 0; - u_char *oldqbuf = NULL; - u_char *smsg = NULL; - int smsglen, smsgsize = 0, siglen; - u_char sig[TSIG_SIG_SIZE]; - time_t tsig_time; - DST_KEY *key; - int expect_cname; - int pass = 0; - - nameserIncr(from.sin_addr, nssRcvdR); - nsp[0] = NULL; - hp = (HEADER *) msg; - if ((qp = qfindid(hp->id)) == NULL ) { - ns_debug(ns_log_default, 1, "DUP? dropped (id %d)", - ntohs(hp->id)); - nameserIncr(from.sin_addr, nssRcvdDupR); - return; - } - - if (ns_wouldlog(ns_log_default, 2)) { - ns_debug(ns_log_default, 2, "Response (%s %s %s) nsid=%d id=%d", - (qp->q_flags & Q_SYSTEM) ?"SYSTEM" :"USER", - (qp->q_flags & Q_PRIMING) ?"PRIMING" :"NORMAL", - (qp->q_flags & Q_ZSERIAL) ?"ZSERIAL" :"-", - ntohs(qp->q_nsid), ntohs(qp->q_id)); - } - - if (qp->q_nstsig == NULL) - has_tsig = 0; - else { - int ret; - - ret = ns_verify(msg, &msglen, qp->q_nstsig->key, - qp->q_nstsig->sig, qp->q_nstsig->siglen, - NULL, NULL, &tsig_time, 0); - if (ret == 0) - has_tsig = 1; - else { - if (hp->rcode == NOERROR) - hp->rcode = NOTAUTH; - ns_debug(ns_log_default, 1, - "resp: error bad tsig, record dropped"); - return; - } - } - - /* - * Here we handle high level formatting problems by parsing the header. - */ - qdcount = ntohs(hp->qdcount); - ancount = ntohs(hp->ancount); - aucount = ntohs(hp->nscount); - arcount = ntohs(hp->arcount); - free_addinfo(); /* sets addcount to zero */ - cp = msg + HFIXEDSZ; - dpp = dnptrs; - *dpp++ = msg; - if ((*cp & INDIR_MASK) == 0) - *dpp++ = cp; - *dpp = NULL; - if (qdcount == 1) { - n = dn_expand(msg, eom, cp, qname, sizeof(qname)); - if (n <= 0) { - formerrmsg = expandFailedQuery; - goto formerr; - } - cp += n; - if (cp + 2 * INT16SZ > eom) { - formerrmsg = outofDataQuery; - goto formerr; - } - GETSHORT(qtype, cp); - GETSHORT(qclass, cp); - if (!ns_nameok(qp, qname, qclass, NULL, response_trans, - ns_ownercontext(qtype, response_trans), - qname, from.sin_addr)) { - formerrmsg = badNameFound; - goto refused; - } - if (cp > eom) { - formerrmsg = outofDataQuery; - goto formerr; - } - if (qp->q_msg && qp->q_msglen && - !res_nameinquery(qname, qtype, qclass, - qp->q_msg, qp->q_msg + qp->q_msglen)) { - sprintf(msgbuf, - "query section mismatch (%s %s %s)", - qname, p_class(qclass), p_type(qtype)); - formerrmsg = msgbuf; - goto formerr; - } - if (ns_samename(qp->q_name, qname) != 1 || - qp->q_class != qclass || - qp->q_type != qtype) { - formerrmsg = wrongQuestion; - goto formerr; - } - } else { - strcpy(qname, qp->q_name); - qclass = qp->q_class; - qtype = qp->q_type; - } - - /* cp now points after the query section. */ - - /* - * Here we handle bad responses from servers. - * Several possibilities come to mind: - * The server is sick and returns SERVFAIL - * The server returns some garbage opcode (it's sick) - * The server can't understand our query and return FORMERR - * In all these cases, we drop the packet, disable retries on - * this server and immediately force a retry. - */ - if ((hp->rcode != NOERROR && hp->rcode != NXDOMAIN) - || (hp->opcode != QUERY -#ifdef BIND_NOTIFY - && hp->opcode != NS_NOTIFY_OP -#endif - )) { - int noedns = 1; - ns_debug(ns_log_default, 2, - "resp: error (ret %d, op %d), dropped", - hp->rcode, hp->opcode); - switch (hp->rcode) { - case SERVFAIL: - nameserIncr(from.sin_addr, nssRcvdFail); - noedns = mark_noedns(qp, from, 0); - break; - case FORMERR: - nameserIncr(from.sin_addr, nssRcvdFErr); - noedns = mark_noedns(qp, from, 1); - break; - case NOTIMP: - nameserIncr(from.sin_addr, nssRcvdErr); - noedns = mark_noedns(qp, from, 1); - break; - default: - nameserIncr(from.sin_addr, nssRcvdErr); - break; - } - if (ns_samename(qp->q_name, qp->q_domain) == 1 && - hp->rcode == SERVFAIL && hp->opcode == QUERY && - noedns) - mark_lame(qp, from); - if (noedns) - mark_bad(qp, from); - fast_retry(qp, from, noedns ? 0 : 1); - return; - } - - if (qdcount != 1) { - /* We don't generate or forward these (yet). */ - formerrmsg = notSingleQuery; - goto formerr; - } - - /* - * Determine if the response came from a forwarder. Packets from - * anyplace not listed as a forwarder or as a server to whom we - * might have forwarded the query will be dropped. - * XXX - should put this in STATS somewhere. - */ - for (fwd = NS_ZFWDTAB(qp->q_fzone); fwd; fwd = fwd->next) - if (ina_equal(fwd->fwddata->fwdaddr.sin_addr, from.sin_addr)) - break; - /* - * find the qinfo pointer and update - * the rtt and fact that we have called on this server before. - */ - { - struct timeval *stp; - - for (n = 0, qs = qp->q_addr; (u_int)n < qp->q_naddr; n++, qs++) - if (ina_equal(qs->ns_addr.sin_addr, from.sin_addr)) - break; - if ((u_int)n >= qp->q_naddr) { - if (!haveComplained(ina_ulong(from.sin_addr), - (u_long)"unexpected source")) { - ns_info(ns_log_default, - "Response from unexpected source (%s) for query \"%s %s %s\"", - sin_ntoa(from), - *(qp->q_name) ? qp->q_name : ".", - p_class(qp->q_class), p_type(qp->q_type)); - } - /* - * We don't know who this response came from so it - * gets dropped on the floor. - */ - return; - } - stp = &qs->stime; - - /* 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)) - qs--; - *stp = qs->stime; - /* XXX - sometimes stp still ends up pointing to - * a zero timeval, in spite of the above attempt. - * Why? What should we do about it? - */ - /* XXX - catch aliases here */ - } - - /* compute query round trip time */ - /* XXX - avoid integer overflow, which is quite likely if stp - * points to a zero timeval (see above). - * rtrip is of type time_t, which we assume is at least - * as big as an int. - */ - if ((tt.tv_sec - stp->tv_sec) > (INT_MAX-999)/1000) { - rtrip = INT_MAX; - } else { - rtrip = ((tt.tv_sec - stp->tv_sec) * 1000 + - (tt.tv_usec - stp->tv_usec) / 1000); - } - - if (ns_wouldlog(ns_log_default, 3)) { - ns_debug(ns_log_default, 3, - "stime %lu/%lu now %lu/%lu rtt %ld", - (u_long)stp->tv_sec, (u_long)stp->tv_usec, - (u_long)tt.tv_sec, (u_long)tt.tv_usec, - (long)rtrip); - } - - /* prevent floating point overflow, limit to 1000 sec */ - if (rtrip > 1000000) { - rtrip = 1000000; - } - ns = qs->nsdata; - /* - * Don't update nstime if this doesn't look - * like an address databuf now. XXX - */ - if (ns && - ns->d_type == T_A && - ns->d_class == qs->ns->d_class) { - u_long t; - - if (ns->d_nstime == 0) - t = rtrip; - else - t = ns->d_nstime * ALPHA - + - (1 - ALPHA) * rtrip; - if (t > 65535) - t = 65535; - else if (t == 0) - t = 1; - ns->d_nstime = (u_int16_t)t; - } - - /* - * Record the source so that we do not use this NS again. - */ - if (ns && qs->ns && (qp->q_nusedns < NSMAX)) { - qp->q_usedns[qp->q_nusedns++] = qs->ns; - if (ns_wouldlog(ns_log_default, 2)) { - ns_debug(ns_log_default, 2, - "NS #%d addr %s used, rtt %d", - n, sin_ntoa(qs->ns_addr), - ns->d_nstime); - } - } - - /* - * Penalize those who had earlier chances but failed - * by multiplying round-trip times by BETA (>1). - * Improve nstime for unused addresses by applying GAMMA. - * The GAMMA factor makes unused entries slowly - * improve, so they eventually get tried again. - * GAMMA should be slightly less than 1. - * Watch out for records that may have timed out - * and are no longer the correct type. XXX - */ - - for (n = 0, qs = qp->q_addr; - (u_int)n < qp->q_naddr; - n++, qs++) { - u_long t; - - ns2 = qs->nsdata; - if (!ns2 || ns2 == ns) - continue; - if (ns2->d_type != T_A || - ns2->d_class != qs->ns->d_class) /* XXX */ - continue; - if (qs->stime.tv_sec) { - if (ns2->d_nstime == 0) - t = (rtrip * BETA) + 1; - else - t = ns2->d_nstime * BETA - + - (1 - ALPHA) * rtrip + 1; - } else - t = ns2->d_nstime * GAMMA; - if (t > 65535) - t = 65535; - else if (t == 0) - t = 1; - ns2->d_nstime = (u_int16_t)t; - if (ns_wouldlog(ns_log_default, 2)) { - ns_debug(ns_log_default, 2, - "NS #%d %s rtt now %d", n, - sin_ntoa(qs->ns_addr), - ns2->d_nstime); - } - } - } - -#ifdef BIND_NOTIFY - /* - * For now, NOTIFY isn't defined for ANCOUNT!=0, AUCOUNT!=0, - * or ADCOUNT!=0. Therefore the only real work to be done for - * a NOTIFY-QR is to remove it from the query queue. - */ - if (hp->opcode == NS_NOTIFY_OP) { - ns_info(ns_log_notify, - "Received NOTIFY answer (%sAA) from %s for \"%s %s %s\"", - hp->aa ? "" : "!", - inet_ntoa(from.sin_addr), - *(qp->q_name) ? qp->q_name : ".", - p_class(qp->q_class), p_type(qp->q_type)); - qremove(qp); - return; - } -#endif - - if ((qp->q_flags & Q_ZSERIAL) != 0) { - if (hp->aa && ancount > 0 && hp->rcode == NOERROR && - qtype == T_SOA && (qclass == C_IN || qclass == C_HS)) - { - int n; - u_int type, class, dlen; - u_int32_t serial; - u_char *tp = cp; - u_char *rdatap; - - n = dn_expand(msg, eom, tp, name, sizeof name); - if (n < 0) { - formerrmsg = expandFailedAnswer; - goto formerr; - } - tp += n; /* name */ - if (tp + 3 * INT16SZ + INT32SZ > eom) { - formerrmsg = outofDataAnswer; - goto formerr; - } - GETSHORT(type, tp); /* type */ - GETSHORT(class, tp); /* class */ - tp += INT32SZ; /* ttl */ - GETSHORT(dlen, tp); /* dlen */ - rdatap = tp; /* start of rdata */ - if (!ns_nameok(qp, name, class, NULL, response_trans, - ns_ownercontext(type, response_trans), - name, from.sin_addr)) { - formerrmsg = badNameFound; - goto refused; - } - if (ns_samename(qname, name) != 1 || - qtype != type || qclass != class) { - sprintf(msgbuf, - "qserial answer mismatch (%s %s %s)", - name, p_class(class), p_type(type)); - formerrmsg = msgbuf; - goto formerr; - } - if (0 >= (n = dn_skipname(tp, eom))) { - formerrmsg = skipnameFailedAnswer; - goto formerr; - } - tp += n; /* mname */ - if (0 >= (n = dn_skipname(tp, eom))) { - formerrmsg = skipnameFailedAnswer; - goto formerr; - } - tp += n; /* rname */ - if (tp + 5 * INT32SZ > eom) { - formerrmsg = dlenUnderrunAnswer; - goto formerr; - } - GETLONG(serial, tp); - tp += 4 * INT32SZ; /* Skip rest of SOA. */ - if ((u_int)(tp - rdatap) != dlen) { - formerrmsg = dlenOverrunAnswer; - goto formerr; - } - for (n = 0, qs = qp->q_addr; (u_int)n < qp->q_naddr; - n++, qs++) - if (ina_equal(qs->ns_addr.sin_addr, - from.sin_addr)) - break; - if (n == qp->q_naddr) { - qserial_answer(qp); - qremove(qp); - return; - } - qs->serial = serial; - } - retry(qp, 0); - return; - } - - /* - * Non-authoritative, no answer, no error, with referral. - */ - if (hp->rcode == NOERROR && !hp->tc && !hp->aa && - ancount == 0 && aucount > 0 -#ifdef BIND_NOTIFY - && hp->opcode != NS_NOTIFY_OP -#endif - ) { - u_char *tp; - int type, class = 0, dlen; - int foundns, foundsoa; -#ifdef DEBUG - if (debug > 0) - res_pquery(&res, msg, msglen, - log_get_stream(packet_channel)); -#endif - /* - * Since there is no answer section (ancount == 0), - * we must be pointing at the authority section (aucount > 0). - */ - tp = cp; - foundns = foundsoa = 0; - for (i = 0 ; i < aucount ; i++) { - n = dn_expand(msg, eom, tp, name, sizeof name); - if (n < 0) { - formerrmsg = expandFailedAuth; - goto formerr; - } - tp += n; - if (tp + 3 * INT16SZ + INT32SZ > eom) { - formerrmsg = outofDataAuth; - goto formerr; - } - GETSHORT(type, tp); - GETSHORT(class, tp); - tp += INT32SZ; /* ttl */ - GETSHORT(dlen, tp); - if (!ns_nameok(qp, name, class, NULL, response_trans, - ns_ownercontext(type, response_trans), - name, from.sin_addr)) { - formerrmsg = badNameFound; - goto refused; - } - /* skip rest of record */ - if (tp + dlen > eom) { - formerrmsg = outofDataAuth; - goto formerr; - } - tp += dlen; - if (type == T_NS) { - strcpy(aname, name); - foundns = 1; - } - if (type == T_SOA) - foundsoa = 1; - } - - /* - * 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 (foundns && !foundsoa && - ns_samedomain(qp->q_domain, aname)) { - if (fwd == NULL) { - nameserIncr(from.sin_addr, nssRcvdLDel); - mark_lame(qp, from); - } - mark_bad(qp, from); - if (class == C_IN && fwd == NULL && - !haveComplained(ina_ulong(from.sin_addr), - nhash(qp->q_domain))) { - char *learnt_from = learntFrom(qp, &from); - - ns_info(ns_log_lame_servers, - "Lame server on '%s' (in '%s'?): %s%s", - qname, qp->q_domain, - sin_ntoa(from), - (learnt_from == NULL) ? "" : - learnt_from); - if (learnt_from != NULL) - learnt_from = freestr(learnt_from); - } else if (fwd != NULL) { - if (!haveComplained(ina_ulong(from.sin_addr), - (u_long)nonRecursiveForwarder)) - ns_warning(ns_log_default, "%s: %s", - nonRecursiveForwarder, - sin_ntoa(from)); - } - - fast_retry(qp, from, 0); - return; - } - } - - /* - * Add the info received in the response to the data base. - */ - arfirst = ancount + aucount; - c = arfirst + arcount; - - /* Don't return if it's a TSIG signed truncated message */ - if (has_tsig > 0 && hp->tc) - goto tcp_retry; - - /* -ve $ing non-existence of record, must handle non-authoritative - * NOERRORs with c == 0. - */ - if (!hp->aa && !hp->tc && hp->rcode == NOERROR && c == 0) - goto return_msg; - - if (qp->q_flags & Q_SYSTEM) - dbflags = DB_NOTAUTH | DB_NODATA; - else - dbflags = DB_NOTAUTH | DB_NODATA | DB_NOHINTS; - count = c; - if (qp->q_flags & Q_PRIMING) - dbflags |= DB_PRIMING; - if (hp->tc) { - count -= arcount; /* truncation had to affect this */ - if (!arcount) { - count -= aucount; /* guess it got this too */ - } - if (!(arcount || aucount)) { - count -= ancount; /* things are pretty grim */ - } - -tcp_retry: - /* retry using tcp provided this was not a tcp query */ - if (!(qp->q_flags & Q_USEVC)) { - qp->q_flags |= Q_USEVC; - unsched(qp); - schedretry(qp, 60); - - nsa = Q_NEXTADDR(qp, 0); - - key = qp->q_keys[0]; - if (key != NULL) - key = qp->q_keys[0] = - tsig_key_from_addr(nsa->sin_addr); - if (key != NULL) { - smsgsize = qp->q_msglen + TSIG_BUF_SIZE; - smsg = memget(smsgsize); - smsglen = qp->q_msglen; - siglen = sizeof(sig); - memcpy(smsg, qp->q_msg, qp->q_msglen); - n = ns_sign(smsg, &smsglen, smsgsize, - NOERROR, key, NULL, 0, - sig, &siglen, 0); - if (n == 0) { - oldqbuf = qp->q_msg; - oldqlen = qp->q_msglen; - qp->q_msglen = smsglen; - qp->q_msg = smsg; - has_tsig = 1; - free_tsig(qp->q_nstsig); - qp->q_nstsig = new_tsig(key, sig, - siglen); - } else { - has_tsig = 0; - free_tsig(qp->q_nstsig); - qp->q_nstsig = NULL; - INSIST(0); - } - } else { - has_tsig = 0; - free_tsig(qp->q_nstsig); - qp->q_nstsig = NULL; - } - - if (tcp_send(qp) != NOERROR) - /* - * We're probably in trouble if tcp_send - * failed, but we'll try to press on because - * there isn't anything else to do. - */ - retry(qp, 0); - - if (has_tsig == 1) { - memput(qp->q_msg, smsgsize); - qp->q_msg = oldqbuf; - qp->q_msglen = oldqlen; - } - return; - } else if (!qsp) { - /* outstanding udp response */ - return; - } - - /* XXX truncated tcp response */ - ns_error(ns_log_default, - "ns_resp: TCP truncated: \"%s\" %s %s from %s", - qname, p_class(qclass), p_type(qtype), - sin_ntoa(from)); - /* mark this server as bad */ - mark_bad(qp, from); - /* try another server, it may have a bigger write buffer */ - retry(qp, 0); - return; - } - - tp = cp; - - validanswer = -1; - nscount = 0; - soacount = 0; - cname = 0; - lastwascname = 0; - externalcname = 0; - cachenegative = 1; - strcpy(aname, qname); - - if (count) { - /* allocate 1 extra record for end of set detection */ - flushset_size = (count + 1) * sizeof *flushset; - flushset = memget(flushset_size); - if (flushset == NULL) - panic("flushset: out of memory", NULL); - memset(flushset, 0, flushset_size); - } else - flushset = NULL; - - expect_cname = 1; - for (i = 0; i < count; i++) { - struct databuf *dp; - int type; - - freestr_maybe(&tname); - if (cp >= eom) { - free_related_additional(); - if (flushset != NULL) - free_flushset(flushset, flushset_size); - formerrmsg = outofDataFinal; - goto formerr; - } - n = rrextract(msg, msglen, cp, &dp, name, sizeof name, from, - &tname); - if (n < 0) { - free_related_additional(); - freestr_maybe(&tname); - if (flushset != NULL) - free_flushset(flushset, flushset_size); - formerrmsg = outofDataFinal; - if (hp->rcode == REFUSED) - goto refused; - else - goto formerr; - } - cp += n; - if (!dp) - continue; - type = dp->d_type; - if (i < ancount) { - /* Answer section. */ - /* - * Check for attempts to overflow the buffer in - * getnameanswer. - */ - if (type == ns_t_cname && !expect_cname) { - ns_warning(ns_log_security, - "late CNAME in answer section for %s %s from %s", - *qname ? qname : ".", p_type(qtype), - sin_ntoa(from)); - - } else if (type != ns_t_cname && type != ns_t_dname && - type != ns_t_sig) - expect_cname = 0; - if (externalcname || ns_samename(name, aname) != 1) { - if (!externalcname) - ns_info(ns_log_resp_checks, - "wrong ans. name (%s != %s)", - name[0] ? name : ".", - aname[0] ? aname : "."); - else - ns_debug(ns_log_resp_checks, 3, - "ignoring answer '%s' after external cname", - name); - db_detach(&dp); - validanswer = 0; - cachenegative = 0; - continue; - } - if (type == T_CNAME && - qtype != T_CNAME && qtype != T_ANY) { - strcpy(aname, (char *)dp->d_data); - if (!ns_samedomain(aname, qp->q_domain)) - externalcname = 1; - cname++; - lastwascname = 1; - } else { - if (validanswer) - validanswer = 1; - lastwascname = 0; - } - - if (tname != NULL) { - add_related_additional(tname); - tname = NULL; - } - - /* Cache for current tick. */ - if (type == T_SOA) - dp->d_ttl = tt.tv_sec; - - dp->d_cred = (hp->aa && ns_samename(name, qname) == 1) - ? DB_C_AUTH - : DB_C_ANSWER; - } else { - /* After answer section. */ - if (lastwascname) { - ns_debug(ns_log_resp_checks, 3, - "last was cname, ignoring auth. and add."); - db_detach(&dp); - validanswer = 0; - cachenegative = 0; - break; - } - if (i < arfirst) { - /* Authority section. */ - switch (type) { - case T_NS: - case T_SOA: - if (!ns_samedomain(aname, name)) { - ns_info(ns_log_resp_checks, - "bad referral (%s !< %s) from %s", - aname[0] ? aname : ".", - name[0] ? name : ".", - sin_ntoa(from)); - db_detach(&dp); - validanswer = 0; - cachenegative = 0; - continue; - } else if (!ns_samedomain(name, - qp->q_domain)) { - if (fwd == NULL && - !externalcname) - ns_info(ns_log_resp_checks, - "bad referral (%s !< %s) from %s", - name[0] ? name : ".", - qp->q_domain[0] ? - qp->q_domain : ".", - sin_ntoa(from)); - db_detach(&dp); - validanswer = 0; - cachenegative = 0; - continue; - } - if (type == T_NS) { - nscount++; - add_related_additional(tname); - tname = NULL; - } - if (type == T_SOA) { - soacount++; - /* -ve caching only. */ - db_detach(&dp); - continue; - } - break; - case T_NXT: - /* XXX check */ - break; - case T_SIG: - /* XXX check that it relates to an - NS or SOA or NXT */ - break; - default: - ns_info(ns_log_resp_checks, - "invalid RR type '%s' in authority section (name = '%s') from %s", - p_type(type), name, - sin_ntoa(from)); - db_detach(&dp); - validanswer = 0; - continue; - } - dp->d_cred = (hp->aa && (cname == 0)) ? - DB_C_AUTH : (qp->q_flags & Q_PRIMING) - ? DB_C_ANSWER - : DB_C_ADDITIONAL; - } else { - /* Additional section. */ - switch (type) { - case T_A: - case ns_t_a6: - case T_AAAA: - case T_SRV: - if (externalcname || - !ns_samedomain(name, qp->q_domain)) { - ns_debug(ns_log_resp_checks, 3, - "ignoring additional info '%s' type %s", - name, p_type(type)); - db_detach(&dp); - validanswer = 0; - continue; - } - if (!related_additional(name)) { - ns_info(ns_log_resp_checks, - "unrelated additional info '%s' type %s from %s", - name, p_type(type), - sin_ntoa(from)); - db_detach(&dp); - validanswer = 0; - continue; - } - if (type == T_SRV && tname != NULL) { - add_related_additional(tname); - tname = NULL; - } - break; - case T_KEY: - /* XXX check? */ - break; - case T_SIG: - /* - * XXX a SIG RR should relate - * to some other RR in this section, - * although if it's the last RR - * it might be a transaction signature. - */ - break; - case ns_t_opt: - /* - * OPT does not get cached. - */ - db_detach(&dp); - validanswer = 0; - continue; - default: - ns_info(ns_log_resp_checks, - "invalid RR type '%s' in additional section (name = '%s') from %s", - p_type(type), name, - sin_ntoa(from)); - db_detach(&dp); - validanswer = 0; - continue; - } - dp->d_cred = (qp->q_flags & Q_PRIMING) - ? DB_C_ANSWER - : DB_C_ADDITIONAL; - } - } -#ifdef HITCOUNTS - ++dp->d_hitcnt; - ++db_total_hits; -#endif /* HITCOUNTS */ - rrsetadd(flushset, name, dp); - db_detach(&dp); - } - free_related_additional(); - freestr_maybe(&tname); - if (flushset != NULL) { - if ((qp->q_flags & Q_SYSTEM) && (qp->q_flags & Q_PRIMING)) { - check_hints(flushset); /* before rrsetupdate */ - rrsetupdate(flushset, dbflags, from, 1); - } else - rrsetupdate(flushset, dbflags, from, 0); - free_flushset(flushset, flushset_size); - } - if (lastwascname && !externalcname) - ns_debug(ns_log_cname, 3, "%s (%s) q(%s %s %s) %s qd(%s)", - danglingCname, aname, - (qname && *qname) ? qname : ".", - p_class(qclass), p_type(qtype), - sin_ntoa(from), qp->q_domain); - - if (cp > eom) { - formerrmsg = outofDataAFinal; - goto formerr; - } - - if ((qp->q_flags & Q_SYSTEM) && ancount) { - if ((qp->q_flags & Q_PRIMING) && !check_root()) { - /* mark server as bad */ - mark_bad(qp, from); - fast_retry(qp, from, 0); - return; - } - ns_debug(ns_log_default, 3, - "resp: leaving, SYSQUERY ancount %d", ancount); -#ifdef BIND_NOTIFY - if (qp->q_notifyzone != DB_Z_CACHE) { - struct zoneinfo *zp = &zones[qp->q_notifyzone]; - - qp->q_notifyzone = DB_Z_CACHE; - ns_notify(zp->z_origin, zp->z_class, ns_t_soa); - } -#endif - qremove(qp); - return; - } - - /* - * We might want to cache this negative answer. - * - * if ancount != 0 and rcode == NOERROR we cannot determine if the - * CNAME chain has been processed to completion or not, so just - * restart the query. DNS needs a NODATA return code! - * - * As some servers incorrectly return a NODATA indication when - * there is a CNAME chain instead of NXDOMAIN, we requery to get - * a definitive answer. - */ - if ((hp->rcode == NXDOMAIN && cname == ancount) || - (hp->rcode == NOERROR && ancount == 0 && - (nscount == 0 || soacount != 0) - ) - ) - { - if (cachenegative) - cache_n_resp(msg, msglen, from, qp->q_name, - qp->q_class, qp->q_type); - - if (!qp->q_cmsglen && validanswer) { - ns_debug(ns_log_default, 3, - "resp: leaving NO: auth = %d", hp->aa); - goto return_msg; - } - } - - /* - * All messages in here need further processing. i.e. they - * are either CNAMEs or we got referred again. - */ - count = 0; - founddata = 0; - dname = name; - /* - * 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 (qp->q_cmsglen != 0) { - ns_debug(ns_log_default, 1, "Cname second pass"); - newmsglen = MIN(EDNS_MESSAGE_SZ, qp->q_cmsglen); - memcpy(newmsg, qp->q_cmsg, newmsglen); - } else { - newmsglen = MIN(EDNS_MESSAGE_SZ, msglen); - memcpy(newmsg, msg, newmsglen); - } - hp = (HEADER *) newmsg; - hp->ancount = htons(0); - hp->nscount = htons(0); - hp->arcount = htons(0); - hp->rcode = NOERROR; - dnptrs[0] = newmsg; - dnptrs[1] = NULL; - cp = newmsg + HFIXEDSZ; - /* - * 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) { - ns_debug(ns_log_default, 1, "dn_expand failed"); - goto servfail; - } - if (!res_dnok(dname)) { - ns_debug(ns_log_default, 1, "bad name (%s)", dname); - goto servfail; - } - cp += n + QFIXEDSZ; - buflen = (qp->q_stream != NULL) ? NS_MAXMSG : - MIN(EDNS_MESSAGE_SZ, qp->q_udpsize); - buflen -= (cp - newmsg); - /* - * Reserve space for TSIG / EDNS - */ - if (qp->q_tsig != NULL) - buflen -= qp->q_tsig->tsig_size; - if ((qp->q_flags & Q_EDNS) != 0) - buflen -= 11; - cname = 0; - - try_again: - pass++; - ns_debug(ns_log_default, 1, "resp: nlookup(%s) qtype=%d", dname, - qtype); - foundname = 0; - fname = ""; - htp = hashtab; /* lookup relative to root */ - np = nlookup(dname, &htp, &fname, 0); - ns_debug(ns_log_default, 1, "resp: %s '%s' as '%s' (cname=%d)", - np == NULL ? "missed" : "found", dname, fname, cname); - if (np == NULL || fname != dname) - goto fetch_ns; - - foundname++; - answers = cp; - count = cp - newmsg; - /* - * Look for NXDOMAIN record. - */ - for (dp = np->n_data; dp; dp = dp->d_next) { - if (!stale(dp) && (dp->d_rcode == NXDOMAIN) && - (dp->d_class == (int)qclass)) { -#ifdef RETURNSOA - n = finddata(np, qclass, T_SOA, hp, &dname, - &buflen, &count, pass, 1); - if ( n != 0) { - if (count) { - cp += n; - buflen -= n; - newmsglen += n; - hp->nscount = htons((u_int16_t)count); - } - if (hp->rcode == NOERROR_NODATA) { - hp->rcode = NOERROR; - goto return_newmsg; - } - } -#else - count = 0; -#endif - hp->rcode = NXDOMAIN; - /* - * XXX forcing AA all the time isn't right, but - * we have to work that way by default - * for compatibility with older servers. - */ - if (!NS_OPTION_P(OPTION_NONAUTH_NXDOMAIN)) - hp->aa = 1; - ns_debug(ns_log_default, 3, "resp: NXDOMAIN aa = %d", - hp->aa); - if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) - goto return_newmsg; - founddata = 1; - goto fetch_ns; - } - } - n = finddata(np, qclass, qtype, hp, &dname, &buflen, &count, pass, 1); - if (n == 0) - goto fetch_ns; /* NO data available */ - if (hp->rcode) { - if (hp->rcode == NOERROR_NODATA) - hp->rcode = NOERROR; -#ifdef RETURNSOA - if (count) { - cp += n; - buflen -= n; - hp->nscount = htons((u_int16_t)count); - } -#endif - if ((count == 0) || NS_OPTION_P(OPTION_NORFC2308_TYPE1)) - goto return_newmsg; - founddata = 1; - goto fetch_ns; - } - cp += n; - buflen -= n; - hp->ancount = htons(ntohs(hp->ancount) + (u_int16_t)count); - if (fname != dname && qtype != T_CNAME && qtype != T_ANY) { - cname++; - goto try_again; - } - founddata = 1; - - ns_debug(ns_log_default, 3, - "resp: foundname=%d, count=%d, founddata=%d, cname=%d", - foundname, count, founddata, cname); - - if (count > 1 && qtype == T_A) - sort_response(answers, cp, count, &qp->q_from); - - fetch_ns: - 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 - * (recursion desired). - */ - free_nsp(nsp); - switch (findns(&np, qclass, nsp, &count, 0)) { - case NXDOMAIN: /* shouldn't happen */ - ns_debug(ns_log_default, 3, "req: leaving (%s, rcode %d)", - dname, hp->rcode); - if (!foundname) - hp->rcode = NXDOMAIN; - if (qclass != C_ANY) { - if (!cname) - hp->aa = 1; - if (np && (!foundname || !founddata)) { - n = doaddauth(hp, cp, buflen, np, nsp[0]); - cp += n; - buflen -= n; - } - } - goto return_newmsg; - - case SERVFAIL: - goto servfail; - } - - if (founddata) { - hp = (HEADER *)newmsg; - n = add_data(np, nsp, cp, buflen, &count); - if (n < 0) { - hp->tc = 1; - n = (-n); - } - cp += n; - buflen -= n; - hp->nscount = htons((u_int16_t)count + ntohs(hp->nscount)); - goto return_newmsg; - } - - /* - * If we get here, we don't have the answer yet and are about - * to iterate to try and get it. First, infinite loop avoidance. - */ - if (qp->q_nqueries++ > MAXQUERIES) { - ns_debug(ns_log_default, 1, - "resp: MAXQUERIES exceeded (%s %s %s)", - dname, p_class(qclass), p_type(qtype)); - ns_info(ns_log_default, - "MAXQUERIES exceeded, possible data loop in resolving (%s)", - dname); - goto servfail; - } - - /* Reset the query control structure */ - - ns_freeqns(qp); - qp->q_naddr = 0; - qp->q_curaddr = 0; - nsfwdadd(qp, NS_ZFWDTAB(qp->q_fzone)); - - if (qp->q_domain != NULL) - (void)freestr(qp->q_domain); - getname(np, tmpdomain, sizeof tmpdomain); - qp->q_domain = savestr(tmpdomain, 1); - - if (NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) - n = 0; - else if ((n = nslookup(nsp, qp, dname, "ns_resp")) <= 0) { - if (n < 0) { - if (n == -1) - ns_debug(ns_log_default, 3, - "resp: nslookup reports danger"); - if (cname) /* a remote CNAME that does not have data */ - goto return_newmsg; - goto servfail; - } else { - ns_debug(ns_log_default, 3, - "resp: no addrs found for NS's"); - /* - * Timeout while sysquery looks up the NS addresses. - * - * Hopefully we'll have them when the client asks - * again. - * - * too bad we can't just wait for the sysquery - * response to restart this query (it's too hard). - * - * We could try to crawl back up the tree looking - * for reachable servers, but we may have just - * gotten delegated down here by a response with - * no A RRs for the servers. If we blindly tried - * this strategy, we bang on the same server forever. - */ - goto timeout; - } - } - for (n = 0; (u_int)n < qp->q_naddr; n++) - qp->q_addr[n].stime.tv_sec = 0; - qp->q_addr[0].stime = tt; - if (cname) { - if (qp->q_cname++ == MAXCNAMES) { - ns_debug(ns_log_default, 3, - "resp: leaving, MAXCNAMES exceeded"); - goto servfail; - } - ns_debug(ns_log_default, 1, "q_cname = %d", qp->q_cname); - ns_debug(ns_log_default, 3, - "resp: building recursive query; nslookup"); - if (qp->q_cmsg == NULL) { - qp->q_cmsg = qp->q_msg; - qp->q_cmsglen = qp->q_msglen; - qp->q_cmsgsize = qp->q_msgsize; - } else if (qp->q_msg != NULL) - memput(qp->q_msg, qp->q_msgsize); - qp->q_msg = (u_char *)memget(PACKETSZ); - if (qp->q_msg == NULL) { - ns_notice(ns_log_default, "resp: memget error"); - goto servfail; - } - qp->q_msgsize = PACKETSZ; - n = res_nmkquery(&res, QUERY, dname, qclass, qtype, - NULL, 0, NULL, qp->q_msg, PACKETSZ); - if (n < 0) { - ns_info(ns_log_default, "resp: res_mkquery(%s) failed", - dname); - goto servfail; - } - if (qp->q_name != NULL) - (void)freestr(qp->q_name); - qp->q_name = savestr(dname, 1); - qp->q_msglen = n; - hp = (HEADER *) qp->q_msg; - hp->rd = 0; - } else - hp = (HEADER *) qp->q_msg; - hp->id = qp->q_nsid = htons(nsid_next()); - hp->rd = (qp->q_addr[0].forwarder ? 1 : 0); - unsched(qp); - schedretry(qp, retrytime(qp)); - nsa = Q_NEXTADDR(qp, 0); - if (ns_wouldlog(ns_log_default, 1)) { - ns_debug(ns_log_default, 1, - "resp: forw -> %s ds=%d nsid=%d id=%d %dms", - 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 - : -1); - } -#ifdef DEBUG - if (debug >= 10) - res_pquery(&res, qp->q_msg, qp->q_msglen, - log_get_stream(packet_channel)); -#endif - key = qp->q_keys[0]; - if (key == NULL) - key = qp->q_keys[0] = tsig_key_from_addr(nsa->sin_addr); - if (key != NULL || !qp->q_addr[0].noedns) { - smsgsize = qp->q_msglen + TSIG_BUF_SIZE + 11; - smsg = memget(smsgsize); - smsglen = qp->q_msglen; - siglen = sizeof(sig); - memcpy(smsg, qp->q_msg, qp->q_msglen); - } - - if (!qp->q_addr[0].noedns) - smsglen += ns_add_opt(smsg, smsg + smsglen, smsgsize, 0, 0, - server_options->edns_udp_size, - 0, NULL, 0); - if (key != NULL) { - n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, - sig, &siglen, 0); - if (n == 0) { - has_tsig = 1; - free_tsig(qp->q_nstsig); - qp->q_nstsig = new_tsig(key, sig, siglen); - } else { - has_tsig = 0; - free_tsig(qp->q_nstsig); - qp->q_nstsig = NULL; - INSIST(0); - } - } else { - has_tsig = 0; - free_tsig(qp->q_nstsig); - qp->q_nstsig = NULL; - } - - if (smsg != NULL) { - oldqbuf = qp->q_msg; - oldqlen = qp->q_msglen; - qp->q_msglen = smsglen; - qp->q_msg = smsg; - } - - if (qp->q_flags & Q_USEVC) { - if (tcp_send(qp) != NOERROR) { - if (!haveComplained(ina_ulong(nsa->sin_addr), - (u_long)tcpsendStr)) - ns_info(ns_log_default, - "ns_resp: tcp_send(%s) failed: %s", - sin_ntoa(*nsa), strerror(errno)); - } - } else if (sendto(ds, (char*)qp->q_msg, qp->q_msglen, 0, - (struct sockaddr *)nsa, - sizeof(struct sockaddr_in)) < 0) - { - sendto_errno = errno; - if (!haveComplained(ina_ulong(nsa->sin_addr), - (u_long)sendtoStr)) - ns_info(ns_log_default, "ns_resp: sendto(%s): %s", - sin_ntoa(*nsa), strerror(errno)); - nameserIncr(nsa->sin_addr, nssSendtoErr); - } - - if (smsgsize != 0) { - memput(smsg, smsgsize); - qp->q_msg = oldqbuf; - qp->q_msglen = oldqlen; - } - hp->rd = 0; /* leave set to 0 for dup detection */ - nameserIncr(nsa->sin_addr, nssSentFwdR); - nameserIncr(qp->q_from.sin_addr, nssRcvdFwdR); - ns_debug(ns_log_default, 3, "resp: Query sent."); - free_nsp(nsp); - switch (sendto_errno) { - case ENETDOWN: - case ENETUNREACH: - case EHOSTDOWN: - case EHOSTUNREACH: - unsched(qp); - schedretry(qp, (time_t) 0); - } - return; - - formerr: - if (!haveComplained(ina_ulong(from.sin_addr), (u_long)formerrmsg)) - ns_info(ns_log_resp_checks, "Malformed response from %s (%s)", - sin_ntoa(from), formerrmsg); - fast_retry(qp, from, 0); - free_nsp(nsp); - return; - - return_msg: - nameserIncr(from.sin_addr, nssRcvdFwdR); - nameserIncr(qp->q_from.sin_addr, nssSentFwdR); - nameserIncr(qp->q_from.sin_addr, nssSentAns); - if (!hp->aa) - nameserIncr(qp->q_from.sin_addr, nssSentNaAns); - if (hp->rcode == NXDOMAIN) - nameserIncr(qp->q_from.sin_addr, nssSentNXD); - /* The "standard" return code */ - hp->qr = 1; - hp->id = qp->q_id; - hp->rd = 1; - hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); - (void) send_msg(msg, msglen, qp); - qremove(qp); - free_nsp(nsp); - return; - - return_newmsg: - nameserIncr(qp->q_from.sin_addr, nssSentAns); - - if (!hp->aa) - nameserIncr(qp->q_from.sin_addr, nssSentNaAns); - if (hp->rcode == NXDOMAIN) - nameserIncr(qp->q_from.sin_addr, nssSentNXD); - n = doaddinfo(hp, cp, buflen); - cp += n; - buflen -= n; - hp->qr = 1; - hp->id = qp->q_id; - hp->rd = 1; - hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); - (void) send_msg(newmsg, cp - newmsg, qp); - qremove(qp); - free_nsp(nsp); - return; - - refused: - hp = (HEADER *)(qp->q_cmsglen ? qp->q_cmsg : qp->q_msg); - hp->rcode = REFUSED; - hp->qr = 1; - hp->id = qp->q_id; - hp->rd = 1; - hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); - (void) send_msg((u_char *)hp, - (qp->q_cmsglen ? qp->q_cmsglen : qp->q_msglen), - qp); - qremove(qp); - free_nsp(nsp); - return; - - servfail: - nameserIncr(qp->q_from.sin_addr, nssSentFail); - hp = (HEADER *)(qp->q_cmsglen ? qp->q_cmsg : qp->q_msg); - hp->rcode = SERVFAIL; - hp->qr = 1; - hp->id = qp->q_id; - hp->rd = 1; - hp->ra = (NS_OPTION_P(OPTION_NORECURSE) == 0); - (void) send_msg((u_char *)hp, - (qp->q_cmsglen ? qp->q_cmsglen : qp->q_msglen), - qp); - qremove(qp); - free_nsp(nsp); - return; - - timeout: - if (qp->q_stream) - sq_remove(qp->q_stream); - qremove(qp); - free_nsp(nsp); - return; -} - -#define BOUNDS_CHECK(ptr, count) \ - do { \ - if ((ptr) + (count) > eom) { \ - hp->rcode = FORMERR; \ - return (-1); \ - } \ - } while (0) - -static int -rrextract(u_char *msg, int msglen, u_char *rrp, struct databuf **dpp, - char *dname, int namelen, struct sockaddr_in from, char **tnamep) -{ - u_char *cp, *eom, *rdatap; - u_int class, type, dlen; - int n, n1, n2; - u_int32_t ttl; - u_char *cp1, data[MAXDATA*2]; - HEADER *hp = (HEADER *)msg; - enum context context; - - if (tnamep != NULL) - *tnamep = NULL; - - *dpp = NULL; - cp = rrp; - eom = msg + msglen; - if ((n = dn_expand(msg, eom, cp, dname, namelen)) < 0) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ); - GETSHORT(type, cp); - GETSHORT(class, cp); - if (type != ns_t_opt && class > CLASS_MAX) { - ns_debug(ns_log_default, 3, "bad class in rrextract"); - hp->rcode = FORMERR; - return (-1); - } - GETLONG(ttl, cp); - if (ttl > MAXIMUM_TTL) { - ns_debug(ns_log_default, 5, "%s: converted TTL > %u to 0", - dname, MAXIMUM_TTL); - ttl = 0; - } - GETSHORT(dlen, cp); - BOUNDS_CHECK(cp, dlen); - rdatap = cp; - if (!ns_nameok(NULL, dname, class, NULL, response_trans, - ns_ownercontext(type, response_trans), - dname, from.sin_addr)) { - hp->rcode = REFUSED; - return (-1); - } - ns_debug(ns_log_default, 3, - "rrextract: dname %s type %d class %d ttl %d", - dname, type, class, ttl); - /* - * Convert the resource record data into the internal - * database format. - * - * On entry to the switch: - * CP points to the RDATA section of the wire-format RR. - * DLEN is its length. - * The memory area at DATA is available for processing. - * - * On exit from the switch: - * CP has been incremented past the RR. - * CP1 points to the RDATA section of the database-format RR. - * N contains the length of the RDATA section of the dbase-format RR. - * - * The new data at CP1 for length N will be copied into the database, - * so it need not be in any particular storage location. - */ - switch (type) { - case T_A: - if (dlen != INT32SZ) { - hp->rcode = FORMERR; - return (-1); - } - /*FALLTHROUGH*/ - case T_WKS: - case T_HINFO: - case T_TXT: - case T_X25: - case T_ISDN: - case T_NSAP: - case T_AAAA: - case T_LOC: - case T_KEY: - case ns_t_cert: - case ns_t_opt: - cp1 = cp; - n = dlen; - cp += n; - break; - - case T_CNAME: - case T_MB: - case T_MG: - case T_MR: - case T_NS: - case T_PTR: - n = dn_expand(msg, eom, cp, (char *)data, sizeof data); - if (n < 0) { - hp->rcode = FORMERR; - return (-1); - } - if (!ns_nameok(NULL, (char *)data, class, NULL, response_trans, - type == T_PTR ?ns_ptrcontext(dname) :domain_ctx, - dname, from.sin_addr)) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - cp1 = data; - n = strlen((char *)data) + 1; - if (tnamep != NULL && (type == T_NS || type == T_MB)) - *tnamep = savestr((char *)cp1, 1); - break; - - case T_SOA: - context = hostname_ctx; - goto soa_rp_minfo; - case T_RP: - case T_MINFO: - context = mailname_ctx; - /* FALLTHROUGH */ - soa_rp_minfo: - n = dn_expand(msg, eom, cp, (char *)data, sizeof data); - if (n < 0) { - hp->rcode = FORMERR; - return (-1); - } - if (!ns_nameok(NULL, (char *)data, class, NULL, response_trans, - context, dname, from.sin_addr)) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - /* - * The next use of 'cp' is dn_expand(), so we don't have - * to BOUNDS_CHECK() here. - */ - cp1 = data + (n = strlen((char *)data) + 1); - n1 = sizeof(data) - n; - if (type == T_SOA) - n1 -= 5 * INT32SZ; - n = dn_expand(msg, eom, cp, (char *)cp1, n1); - if (n < 0) { - hp->rcode = FORMERR; - return (-1); - } - if (type == T_RP) - context = domain_ctx; - else - context = mailname_ctx; - if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, - context, dname, from.sin_addr)) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - cp1 += strlen((char *)cp1) + 1; - if (type == T_SOA) { - n = 5 * INT32SZ; - BOUNDS_CHECK(cp, n); - memcpy(cp1, cp, n); - cp += n; - cp1 += n; - } - n = cp1 - data; - cp1 = data; - if (tnamep != NULL && type == T_SOA) - *tnamep = savestr((char *)cp1, 1); - break; - - case T_NAPTR: - /* Grab weight and port. */ - BOUNDS_CHECK(cp, INT16SZ*2); - memcpy(data, cp, INT16SZ*2); - cp1 = data + INT16SZ*2; - cp += INT16SZ*2; - - /* Flags */ - BOUNDS_CHECK(cp, 1); - n = *cp++; - BOUNDS_CHECK(cp, n); - *cp1++ = n; - memcpy(cp1, cp, n); - cp += n; cp1 += n; - - /* Service */ - BOUNDS_CHECK(cp, 1); - n = *cp++; - BOUNDS_CHECK(cp, n); - *cp1++ = n; - memcpy(cp1, cp, n); - cp += n; cp1 += n; - - /* Regexp */ - BOUNDS_CHECK(cp, 1); - n = *cp++; - BOUNDS_CHECK(cp, n); - *cp1++ = n; - memcpy(cp1, cp, n); - cp += n; cp1 += n; - - /* Replacement */ - n = dn_expand(msg, eom, cp, (char *)cp1, - sizeof data - (cp1 - data)); - if (n < 0) { - hp->rcode = FORMERR; - return (-1); - } - if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, - hostname_ctx, dname, from.sin_addr)) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - - if (tnamep != NULL && *cp1 != 0) - *tnamep = savestr((char *)cp1, 1); - - /* compute end of data */ - cp1 += strlen((char *)cp1) + 1; - /* compute size of data */ - n = cp1 - data; - cp1 = data; - break; - - case T_MX: - case T_AFSDB: - case T_RT: - case T_SRV: - /* grab preference */ - BOUNDS_CHECK(cp, INT16SZ); - memcpy(data, cp, INT16SZ); - cp1 = data + INT16SZ; - cp += INT16SZ; - - if (type == T_SRV) { - /* Grab weight and port. */ - BOUNDS_CHECK(cp, INT16SZ*2); - memcpy(cp1, cp, INT16SZ*2); - cp1 += INT16SZ*2; - cp += INT16SZ*2; - } - - /* get name */ - n = dn_expand(msg, eom, cp, (char *)cp1, - sizeof data - (cp1 - data)); - if (n < 0) { - hp->rcode = FORMERR; - return (-1); - } - if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, - hostname_ctx, dname, from.sin_addr)) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - - if (tnamep != NULL) - *tnamep = savestr((char *)cp1, 1); - - /* compute end of data */ - cp1 += strlen((char *)cp1) + 1; - /* compute size of data */ - n = cp1 - data; - cp1 = data; - break; - - case T_PX: - /* grab preference */ - BOUNDS_CHECK(cp, INT16SZ); - memcpy(data, cp, INT16SZ); - cp1 = data + INT16SZ; - cp += INT16SZ; - - /* get MAP822 name */ - n = dn_expand(msg, eom, cp, (char *)cp1, - sizeof data - INT16SZ); - if (n < 0) { - hp->rcode = FORMERR; - return (-1); - } - if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, - domain_ctx, dname, from.sin_addr)) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - /* - * The next use of 'cp' is dn_expand(), so we don't have - * to BOUNDS_CHECK() here. - */ - cp1 += (n = strlen((char *)cp1) + 1); - n1 = sizeof(data) - n - INT16SZ; - n = dn_expand(msg, eom, cp, (char *)cp1, n1); - if (n < 0) { - hp->rcode = FORMERR; - return (-1); - } - if (!ns_nameok(NULL, (char *)cp1, class, NULL, response_trans, - domain_ctx, dname, from.sin_addr)) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - cp1 += strlen((char *)cp1) + 1; - n = cp1 - data; - cp1 = data; - break; - - case T_SIG: { - u_int32_t origTTL, exptime, signtime, timetilexp, now; - u_int8_t alg; - - /* Check signature time, expiration, and adjust TTL. */ - /* This code is similar to that in db_load.c. */ - - /* Skip coveredType, save alg, skip labels */ - BOUNDS_CHECK(cp, INT16SZ + 1 + 1 + 3*INT32SZ); - cp1 = cp + INT16SZ; - alg = *cp1++; - cp1++; - GETLONG(origTTL, cp1); - GETLONG(exptime, cp1); - GETLONG(signtime, cp1); - now = time(NULL); /* Get current time in GMT/UTC */ - - /* Don't let bogus name servers increase the signed TTL */ - if (ttl > origTTL) { - ns_debug(ns_log_default, 3, - "shrinking SIG TTL from %lu to origTTL %lu", - (unsigned long)ttl, (unsigned long)origTTL); - ttl = origTTL; - } - - /* - * Check that expire and signature times are internally - * consistant. - */ - if (!SEQ_GT(exptime, signtime) && exptime != signtime) { - ns_debug(ns_log_default, 3, - "ignoring SIG: signature expires before it was signed"); - return ((cp - rrp) + dlen); - } - - /* Don't let bogus signers "sign" in the future. */ - if (SEQ_GT(signtime, now)) { - ns_debug(ns_log_default, 3, - "ignoring SIG: signature date %s is in the future", - p_secstodate (signtime)); - return ((cp - rrp) + dlen); - } - - /* Ignore received SIG RR's that are already expired. */ - if (SEQ_GT(now, exptime)) { - ns_debug(ns_log_default, 3, - "ignoring SIG: expiration %s is in the past", - p_secstodate (exptime)); - return ((cp - rrp) + dlen); - } - - /* Lop off the TTL at the expiration time. */ - timetilexp = exptime - now; - if (timetilexp < ttl) { - ns_debug(ns_log_default, 3, - "shrinking expiring %s SIG TTL from %d to %d", - p_secstodate (exptime), ttl, timetilexp); - ttl = timetilexp; - } - - /* The following code is copied from named-xfer.c. */ - cp1 = (u_char *)data; - - /* first just copy over the type_covered, algorithm, */ - /* labels, orig ttl, two timestamps, and the footprint */ - BOUNDS_CHECK(cp, 18); - memcpy(cp1, cp, 18); - cp += 18; - cp1 += 18; - - /* then the signer's name */ - n = dn_expand(msg, eom, cp, (char *)cp1, (sizeof data) - 18); - if (n < 0 || n + NS_SIG_SIGNER > (int)dlen) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - cp1 += strlen((char*)cp1)+1; - - /* finally, we copy over the variable-length signature. - Its size is the total data length, minus what we copied. */ - n = dlen - (NS_SIG_SIGNER + n); - - if (n > (int)(sizeof data) - (cp1 - (u_char *)data)) { - hp->rcode = FORMERR; - return (-1); /* out of room! */ - } - - switch (alg) { - case NS_ALG_MD5RSA: - if (n < NS_MD5RSA_MIN_SIZE || n > NS_MD5RSA_MAX_SIZE) - hp->rcode = FORMERR; - break; - - case NS_ALG_DSA: - if (n != NS_DSA_SIG_SIZE) - hp->rcode = FORMERR; - break; - - default: - break; - } - - if (hp->rcode == FORMERR) - return (-1); - - memcpy(cp1, cp, n); - cp += n; - cp1 += n; - - /* compute size of data */ - n = cp1 - (u_char *)data; - cp1 = (u_char *)data; - break; - } - - case T_NXT: - n = dn_expand(msg, eom, cp, (char *)data, sizeof data); - /* - * By testing if n >= dlen, we are requiring that the type - * bitmap be at least one octet. This is reasonable - * because we always have to look at the 0 bit to see if - * this is a "different format" NXT or not. - */ - if (n < 0 || n >= (int)dlen) { - hp->rcode = FORMERR; - return (-1); - } - if (!ns_nameok(NULL, (char *)data, class, NULL, response_trans, - domain_ctx, dname, from.sin_addr)) { - hp->rcode = FORMERR; - return (-1); - } - cp += n; - n1 = strlen((char *)data) + 1; - cp1 = data + n1; - /* - * We don't need to BOUNDS_CHECK() cp here because we've - * previously checked that 'dlen' bytes are in bounds, and - * we know that n < dlen. - */ - n2 = dlen - n; - /* - * The first bit of the first octet determines the format - * of the NXT record. A format for types >= 128 has not - * yet been defined, so if bit zero is set, we just copy - * what's there because we don't understand it. - */ - if ((*cp & 0x80) == 0) { - /* - * Bit zero is not set; this is an ordinary NXT - * record. The bitmap must be at least 4 octets - * because the NXT bit should be set. It should be - * less than or equal to 16 octets because this NXT - * format is only defined for types < 128. - */ - if (n2 < 4 || n2 > 16) { - hp->rcode = FORMERR; - return (-1); - } - } - if (n2 > (int)(sizeof data - n1)) { - hp->rcode = FORMERR; - return (-1); - } - memcpy(cp1, cp, n2); - cp += n2; - cp1 += n2; - - /* compute size of data */ - n = cp1 - (u_char *)data; - cp1 = (u_char *)data; - break; - - default: - /* treat as opaque data */ - ns_debug(ns_log_default, 3, "unknown type %d", type); - cp1 = cp; - n = dlen; - cp += n; - } - - if (cp > eom) { - hp->rcode = FORMERR; - return (-1); - } - if ((u_int)(cp - rdatap) != dlen) { - ns_debug(ns_log_default, 3, - "encoded rdata length is %u, but actual length was %u", - dlen, (u_int)(cp - rdatap)); - hp->rcode = FORMERR; - return (-1); - } - if (n > MAXDATA) { - ns_debug(ns_log_default, 1, - "update type %d: %d bytes is too much data", - type, n); - hp->rcode = FORMERR; - return (-1); - } - - ttl += tt.tv_sec; - if (type == ns_t_opt) - class = 0; /* Lie. */ - *dpp = savedata(class, type, ttl, cp1, n); - return (cp - rrp); -} - -int -send_msg(u_char *msg, int msglen, struct qinfo *qp) { - HEADER *hp = (HEADER *) msg; - u_char *oldmsg; - int oldlen = 0; - int msgsize; - int ret; - int trunc; - int adjust = 0; - - if (qp->q_flags & Q_SYSTEM) - return (1); - - trunc = (qp->q_stream != NULL) ? NS_MAXMSG : qp->q_udpsize; - if (qp->q_tsig != NULL) - adjust += qp->q_tsig->tsig_size; - if ((qp->q_flags & Q_EDNS) != 0) - adjust += 11; - if (msglen > trunc - adjust) - msglen = trunc_adjust(msg, msglen, trunc - adjust); - - if (ns_wouldlog(ns_log_default, 1)) { - ns_debug(ns_log_default, 1, "send_msg -> %s (%s %d) id=%d", - sin_ntoa(qp->q_from), - qp->q_stream == NULL ? "UDP" : "TCP", - qp->q_stream == NULL ? qp->q_dfd : qp->q_stream->s_rfd, - ntohs(qp->q_id)); - } -#ifdef DEBUG - if (ns_wouldlog(ns_log_default, 4)) { - struct qinfo *tqp; - - for (tqp = nsqhead; tqp != NULL; tqp = tqp->q_link) { - ns_debug(ns_log_default, 4, - "qp %#lx q_id: %d q_nsid: %d q_msglen: %d", - (u_long)tqp, tqp->q_id, - tqp->q_nsid, tqp->q_msglen); - ns_debug(ns_log_default, 4, - "\tq_naddr: %d q_curaddr: %d", - tqp->q_naddr, tqp->q_curaddr); - ns_debug(ns_log_default, 4, - "\tq_next: %#lx q_link: %#lx", - (u_long)qp->q_next, (u_long)qp->q_link); - } - } -#endif /* DEBUG */ - - if (adjust != 0) { - oldmsg = msg; - oldlen = msglen; - msgsize = msglen + adjust; - msg = memget(msgsize); - memcpy(msg, oldmsg, oldlen); - } else - msgsize = msglen; /* silence compiler */ - - if ((qp->q_flags & Q_EDNS) != 0) - msglen += ns_add_opt(msg, msg + msglen, msgsize, 0, hp->rcode, - server_options->edns_udp_size, - 0, NULL, 0); - - if (qp->q_tsig != NULL) { - u_char sig[TSIG_SIG_SIZE]; - int siglen = sizeof(sig); - - ret = ns_sign(msg, &msglen, msgsize, NOERROR, qp->q_tsig->key, - qp->q_tsig->sig, qp->q_tsig->siglen, - sig, &siglen, 0); - - if (ret != 0) { - INSIST(0); - } - } - -#ifdef DEBUG - if (debug >= 6) - res_pquery(&res, msg, msglen, log_get_stream(packet_channel)); -#endif /* DEBUG */ - - if (qp->q_stream == NULL) { - /* - * Don't send FORMERR to certian well known ports. - */ - if (hp->rcode == FORMERR && - drop_port(ntohs(qp->q_from.sin_port))) - return (-1); - if (sendto(qp->q_dfd, (char*)msg, msglen, 0, - (struct sockaddr *)&qp->q_from, - sizeof(qp->q_from)) < 0) { - if (!haveComplained(ina_ulong(qp->q_from.sin_addr), - (u_long)sendtoStr)) -#if defined(SPURIOUS_ECONNREFUSED) - if (errno != ECONNREFUSED) -#endif - ns_info(ns_log_default, - "send_msg: sendto(%s): %s", - sin_ntoa(qp->q_from), - strerror(errno)); - nameserIncr(qp->q_from.sin_addr, nssSendtoErr); - return (1); - } - } else - writestream(qp->q_stream, (u_char*)msg, msglen); - - if (adjust != 0) - memput(msg, oldlen + adjust); - - return (0); -} - -static int -root_server_p(ns_class class) { - struct zoneinfo *zp = find_zone("", class); - - return (zp != NULL && - (zp->z_type == z_master || zp->z_type == z_slave)); -} - -void -prime_cache(void) { - int root = root_server_p(ns_c_in); - - ns_debug(ns_log_default, 1, "prime_cache: priming = %d, root = %d", - priming, root); - if (!priming && !root) { - struct qinfo *qp = sysquery("", ns_c_in, ns_t_ns, - NULL, NULL, 0, ns_port, - ns_o_query, 0); - - if (qp != NULL) { - qp->q_flags |= (Q_SYSTEM | Q_PRIMING); - priming++; - } - } - needs_prime_cache = 0; -} - -struct qinfo * -sysquery(const char *dname, int class, int type, - struct in_addr *nss, struct dst_key **keys, int nsc, - u_int16_t port, int opcode, int distance) -{ - struct qinfo *qp, *oqp; - HEADER *hp; - char tmpdomain[MAXDNAME]; - struct namebuf *np = NULL; - struct databuf *nsp[NSMAX]; - struct hashbuf *htp1; - struct hashbuf *htp2; - struct hashbuf *htp3; - struct sockaddr_in *nsa; - const char *fname; - int n, count; - int sendto_errno = 0; - u_char *oldqbuf = NULL; - int oldqlen = 0, has_tsig; - u_char *smsg = NULL; - int smsglen, smsgsize = 0, siglen; - u_char sig[TSIG_SIG_SIZE]; - DST_KEY *key; - - nsp[0] = NULL; - ns_debug(ns_log_default, 3, "sysquery(%s, %d, %d, %p, %p, %d, %d)", - dname, class, type, nss, keys, nsc, ntohs(port)); - qp = qnew(dname, class, type, (nss != NULL && nsc != 0) ? 0 : 1); - - qp->q_distance = distance; - - if (nss != NULL && nsc != 0) - np = NULL; - else if (!NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) { - htp1 = hashtab; - htp2 = hashtab; - htp3 = fcachetab; - if (priming && dname[0] == '\0') { - np = NULL; - } else if (((np = nlookup(dname, &htp1, &fname, 0)) == NULL) && - ((np = nlookup("", &htp2, &fname, 0)) == NULL) && - ((np = nlookup("", &htp3, &fname, 0)) == NULL)) { - ns_info(ns_log_default, - "sysquery: nlookup error on %s?", - dname); - err1: - ns_freeqry(qp); - return (NULL); - } - - n = findns(&np, class, nsp, &count, 0); - switch (n) { - case NXDOMAIN: - case SERVFAIL: - ns_info(ns_log_default, - "sysquery: findns error (%s) on %s?", - n == NXDOMAIN ? "NXDOMAIN" : "SERVFAIL", - dname); - err2: - free_nsp(nsp); - goto err1; - } - } - - /* Build new qinfo struct. */ - qp->q_cmsg = qp->q_msg = NULL; - qp->q_dfd = ds; - if (nss == NULL || nsc == 0) - nsfwdadd(qp, NS_ZFWDTAB(qp->q_fzone)); - qp->q_expire = tt.tv_sec + RETRY_TIMEOUT*2; - qp->q_flags |= Q_SYSTEM; - - getname(np, tmpdomain, sizeof tmpdomain); - qp->q_domain = savestr(tmpdomain, 1); - - if ((qp->q_msg = (u_char *)memget(PACKETSZ)) == NULL) { - ns_notice(ns_log_default, "sysquery: memget failed"); - goto err2; - } - qp->q_msgsize = PACKETSZ; - n = res_nmkquery(&res, opcode, dname, class, - type, NULL, 0, NULL, - qp->q_msg, PACKETSZ); - if (n < 0) { - ns_info(ns_log_default, - "sysquery: res_mkquery(%s) failed", dname); - goto err2; - } - qp->q_msglen = n; - hp = (HEADER *) qp->q_msg; - hp->id = qp->q_nsid = htons(nsid_next()); - hp->rd = (qp->q_addr[qp->q_curaddr].forwarder ? 1 : 0); - hp->aa = (opcode == NS_NOTIFY_OP); - - /* First check for an already pending query for this data. */ - for (oqp = nsqhead; oqp != NULL; oqp = oqp->q_link) { - if ((oqp != qp) - && (oqp->q_msglen == qp->q_msglen) - && memcmp(oqp->q_msg+2, qp->q_msg + 2, - qp->q_msglen - 2) == 0 - ) { -#ifdef BIND_NOTIFY - /* XXX - need fancier test to suppress duplicate - * NOTIFYs to the same server (compare nss?) - */ - if (opcode != NS_NOTIFY_OP) -#endif /*BIND_NOTIFY*/ - { - ns_debug(ns_log_default, 3, - "sysquery: duplicate"); - goto err2; - } - } - } - - if (nss != NULL && nsc != 0) { - int i; - struct qserv *qs; - - for (i = 0, qs = qp->q_addr; i < nsc; i++, qs++) { - qs->ns_addr.sin_family = AF_INET; - qs->ns_addr.sin_addr = nss[i]; - qs->ns_addr.sin_port = port; - if (keys != NULL) - qp->q_keys[i] = keys[i]; - qs->ns = NULL; - qs->nsdata = NULL; - qs->stime = tt; - qs->forwarder = 0; - qs->noedns = 1; /* XXXMPA */ - qs->lame = 0; - qs->nretry = 0; - } - qp->q_naddr = nsc; - } else if (!NS_ZOPTION_P(qp->q_fzone, OPTION_FORWARD_ONLY)) { - fetch_a: - count = nslookup(nsp, qp, dname, "sysquery"); - if (count <= 0) { - if (count < 0) { - if (n == -1) - ns_info(ns_log_default, - "sysquery: nslookup reports danger (%s)", - dname); - goto err2; - } else if (np && NAME(*np)[0] == '\0') { - /* - * It's not too serious if we don't have - * the root server addresses if we have to - * go through a forwarder anyway. Don't - * bother to log it, since prime_cache() - * won't do anything about it as currently - * implemented. - * - * XXX - should we skip setting - * needs_prime_cache as well? - * - * XXX - what happens when we implement - * selective forwarding? - */ - if (!NS_OPTION_P(OPTION_FORWARD_ONLY)) - ns_warning(ns_log_default, - "sysquery: no addrs found for root NS (%s)", - dname); - if (class == C_IN && !priming) - needs_prime_cache = 1; - goto err2; - } - if (np) { - free_nsp(nsp); - nsp[0] = NULL; - np = np_parent(np); - n = findns(&np, class, nsp, &count, 0); - switch (n) { - case NXDOMAIN: /*FALLTHROUGH*/ - case SERVFAIL: - ns_info(ns_log_default, - "sysquery: findns error (%d) on %s?", - n, dname); - goto err2; - } - getname(np, tmpdomain, sizeof tmpdomain); - if (qp->q_domain != NULL) - (void)freestr(qp->q_domain); - qp->q_domain = savestr(tmpdomain, 1); - goto fetch_a; - } - goto err2; - } - } - - schedretry(qp, retrytime(qp)); - qp->q_addr[0].stime = tt; /* XXX - why not every? */ - nsa = Q_NEXTADDR(qp, 0); - - if (ns_wouldlog(ns_log_default, 1)) { - ns_debug(ns_log_default, 1, - "sysquery: send -> %s dfd=%d nsid=%d id=%d retry=%ld", - sin_ntoa(*nsa), qp->q_dfd, - ntohs(qp->q_nsid), ntohs(qp->q_id), - (long)qp->q_time); - } -#ifdef DEBUG - if (debug >= 10) - res_pquery(&res, qp->q_msg, qp->q_msglen, - log_get_stream(packet_channel)); -#endif - - key = qp->q_keys[0]; - if (key == NULL) - key = qp->q_keys[0] = tsig_key_from_addr(nsa->sin_addr); - if (key != NULL || !qp->q_addr[0].noedns) { - smsgsize = qp->q_msglen + TSIG_BUF_SIZE + 11; - smsg = memget(smsgsize); - smsglen = qp->q_msglen; - siglen = sizeof(sig); - memcpy(smsg, qp->q_msg, qp->q_msglen); - } - - if (!qp->q_addr[0].noedns) - smsglen += ns_add_opt(smsg, smsg + smsglen, smsgsize, 0, 0, - server_options->edns_udp_size, - 0, NULL, 0); - - if (key != NULL) { - n = ns_sign(smsg, &smsglen, smsgsize, NOERROR, key, NULL, 0, - sig, &siglen, 0); - if (n == 0) { - has_tsig = 1; - free_tsig(qp->q_nstsig); - qp->q_nstsig = new_tsig(key, sig, siglen); - } else { - INSIST(0); - has_tsig = 0; - free_tsig(qp->q_nstsig); - qp->q_nstsig = NULL; - } - } else { - has_tsig = 0; - free_tsig(qp->q_nstsig); - qp->q_nstsig = NULL; - } - - if (smsgsize != 0) { - oldqbuf = qp->q_msg; - oldqlen = qp->q_msglen; - qp->q_msglen = smsglen; - qp->q_msg = smsg; - } - - if (sendto(qp->q_dfd, (char*)qp->q_msg, qp->q_msglen, 0, - (struct sockaddr *)nsa, - sizeof(struct sockaddr_in)) < 0) { - sendto_errno = errno; - if (!haveComplained(ina_ulong(nsa->sin_addr), - (u_long)sendtoStr)) - ns_info(ns_log_default, "sysquery: sendto(%s): %s", - sin_ntoa(*nsa), strerror(errno)); - nameserIncr(nsa->sin_addr, nssSendtoErr); - } - - if (smsgsize != 0) { - memput(smsg, smsgsize); - qp->q_msg = oldqbuf; - qp->q_msglen = oldqlen; - } - - nameserIncr(nsa->sin_addr, nssSentSysQ); - free_nsp(nsp); - switch (sendto_errno) { - case ENETDOWN: - case ENETUNREACH: - case EHOSTDOWN: - case EHOSTUNREACH: - unsched(qp); - schedretry(qp, (time_t) 0); - } - return (qp); -} - -/* - * Check the list of root servers after receiving a response - * to a query for the root servers. - */ -static int -check_root() { - struct databuf *dp, *pdp; - struct namebuf *np; - int count = 0; - - priming = 0; - for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) - if (NAME(*np)[0] == '\0') - break; - if (np == NULL) { - ns_notice(ns_log_default, "check_root: Can't find root!"); - return (0); - } - for (dp = np->n_data; dp != NULL; dp = dp->d_next) - if (dp->d_type == T_NS) - count++; - ns_debug(ns_log_default, 1, "%d root servers", count); - if (count < server_options->minroots) { - ns_notice(ns_log_default, - "check_root: %d root servers after query to root server < min", - count); - return (0); - } - pdp = NULL; - dp = np->n_data; - while (dp != NULL) { - if (dp->d_type == T_NS && dp->d_zone == DB_Z_CACHE && - dp->d_ttl < (u_int32_t)tt.tv_sec) { - ns_debug(ns_log_default, 1, - "deleting old root server '%s'", - dp->d_data); - dp = rm_datum(dp, np, pdp, NULL); - /* SHOULD DELETE FROM HINTS ALSO */ - continue; - } - pdp = dp; - dp = dp->d_next; - } - if (check_ns()) - return (1); - else { - priming = 1; - return (0); - } -} - -/* - * Check the root to make sure that for each NS record we have a A RR - */ -static int -check_ns() { - struct databuf *dp, *tdp; - struct namebuf *np, *tnp; - struct hashbuf *htp; - char *dname; - int found_arr; - const char *fname; - time_t curtime; - int servers = 0, rrsets = 0; - - ns_debug(ns_log_default, 2, "check_ns()"); - - curtime = (u_int32_t) tt.tv_sec; - for (np = hashtab->h_tab[0]; np != NULL; np = np->n_next) { - if (NAME(*np)[0] != '\0') - continue; - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - int cnames = 0; - - if (dp->d_rcode) - continue; - - if (dp->d_type != T_NS) - continue; - - servers++; - - /* look for A records */ - dname = (caddr_t) dp->d_data; - htp = hashtab; - tnp = nlookup(dname, &htp, &fname, 0); - if (tnp == NULL || fname != dname) { - ns_debug(ns_log_default, 3, - "check_ns: %s: not found %s %#lx", - dname, fname, (u_long)tnp); - sysquery(dname, dp->d_class, T_A, NULL, NULL, - 0, ns_port, QUERY, 0); - continue; - } - /* look for name server addresses */ - found_arr = 0; - (void)delete_stale(tnp); - for (tdp = tnp->n_data; - tdp != NULL; - tdp = tdp->d_next) { - if (tdp->d_rcode) - continue; - if (tdp->d_type == T_CNAME) - cnames++; - if (tdp->d_type != T_A || - tdp->d_class != dp->d_class) - continue; - if ((tdp->d_zone == DB_Z_CACHE) && - (tdp->d_ttl < (u_int32_t)curtime)) { - ns_debug(ns_log_default, 3, - "check_ns: stale entry '%s'", - NAME(*tnp)); - found_arr = 0; - break; - } - found_arr++; - } - if (found_arr) - rrsets++; - else if (cnames > 0) - ns_info(ns_log_default, - "Root NS %s -> CNAME %s", - NAME(*np), NAME(*tnp)); - else - sysquery(dname, dp->d_class, T_A, NULL, NULL, - 0, ns_port, QUERY, 0); - } - } - - ns_debug(ns_log_default, 2, "check_ns: %d %d", servers, rrsets); - return ((servers <= 2) - ? (rrsets == servers) - : ((rrsets * 2) >= servers) - ); -} - -/* int findns(npp, class, nsp, countp, flag) - * Find NS's or an SOA - * npp, class: - * dname whose most enclosing NS is wanted - * nsp, countp: - * result array and count; array will also be NULL terminated - * flag: - * boolean: we're being called from ADDAUTH, bypass authority checks - * return value: - * NXDOMAIN: we are authoritative for this {dname,class} - * *countp is bogus, but nsp[] has a single SOA returned in it. - * SERVFAIL: we are auth but zone isn't loaded; or, no root servers found - * *countp and nsp[] are bogus. - * OK: we are not authoritative, and here are the NS records we found. - * *countp and nsp[] return NS records of interest. - */ -int -findns(struct namebuf **npp, int class, - struct databuf **nsp, int *countp, int flag) -{ - struct namebuf *np = *npp; - struct databuf *dp; - struct databuf **nspp; - struct hashbuf *htp; - - nsp[0] = NULL; - - if (priming && (np == NULL || NAME(*np)[0] == '\0')) - htp = fcachetab; - else - htp = hashtab; - - try_again: - if (htp == fcachetab && class == C_IN && !priming) - /* - * XXX - do we want to set needs_prime_cache if - * OPTION_FORWARD_ONLY? - */ - needs_prime_cache = 1; - if (np == NULL) { - /* find the root */ - for (np = htp->h_tab[0]; np != NULL; np = np->n_next) - if (NAME(*np)[0] == '\0') - break; - } - while (np != NULL) { - ns_debug(ns_log_default, 5, "findns: np %p '%s'", np, - NAME(*np)); - /* Look first for SOA records. */ -#ifdef ADDAUTH - if (!flag) -#endif - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (dp->d_zone != DB_Z_CACHE && - ((zones[dp->d_zone].z_type == Z_PRIMARY) || - (zones[dp->d_zone].z_type == Z_SECONDARY)) && - match(dp, class, T_SOA) && dp->d_type == T_SOA) { - ns_debug(ns_log_default, 3, - "findns: SOA found"); - if (zones[dp->d_zone].z_flags & Z_AUTH) { - *npp = np; - nsp[0] = dp; - nsp[1] = NULL; - DRCNTINC(dp); - return (NXDOMAIN); - } else { - /* XXX: zone isn't loaded but we're - * primary or slave for it. - * should we fwd this? - */ - return (SERVFAIL); - } - } - } - - /* If no SOA records, look for NS records. */ - nspp = &nsp[0]; - *nspp = NULL; - (void)delete_stale(np); - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (!match(dp, class, T_NS)) - continue; - if (dp->d_rcode) - continue; - /* - * Don't use records that may become invalid to - * reference later when we do the rtt computation. - * Never delete our safety-belt information! - * - * XXX: this is horribly bogus. - */ - if ((dp->d_zone == DB_Z_CACHE) && - (dp->d_ttl < (u_int32_t)tt.tv_sec) && - !(dp->d_flags & DB_F_HINT)) { - ns_debug(ns_log_default, 1, - "findns: stale entry '%s'", - NAME(*np)); - /* - * We may have already added NS databufs - * and are going to throw them away. Fix - * reference counts. We don't need to free - * them here as we just got them from the - * cache. - */ - while (nspp > &nsp[0]) - db_detach(--nspp); - nsp[0] = NULL; - goto try_parent; - } - if (nspp < &nsp[NSMAX-1]) { - *nspp++ = dp; - DRCNTINC(dp); - } - } - - *countp = nspp - nsp; - if (*countp > 0) { - ns_debug(ns_log_default, 3, - "findns: %d NS's added for '%s'", - *countp, NAME(*np)); - *nspp = NULL; - *npp = np; - return (OK); /* Success, got some NS's */ - } - try_parent: - np = np_parent(np); - } - if (htp == hashtab) { - htp = fcachetab; - goto try_again; - } - ns_debug(ns_log_default, 1, - "findns: No root nameservers for class %s?", p_class(class)); - if (!NS_OPTION_P(OPTION_FORWARD_ONLY) && - (unsigned)class < MAXCLASS && norootlogged[class] == 0) { - norootlogged[class] = 1; - ns_info(ns_log_default, "No root nameservers for class %s", - p_class(class)); - } - return (SERVFAIL); -} - - -/* - * 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(struct namebuf *np, int class, int type, - HEADER *hp, char **dnamep, int *lenp, int *countp, int pass, - int glueok) -{ - struct databuf *dp; - char *cp; - int buflen, n, count = 0; - char *new_dnamep = NULL; - int defer = 0, found_count = 0, choice, i; - struct databuf **found = NULL; - struct databuf **tmpfound = NULL; - int foundcname; - int stalecount; - int ret = 0; - - stalecount = delete_stale(np); - - /* We don't want to return cached SIG records when asked for SIGs, - * since we may have an incomplete set. - */ - if (type == T_SIG && findMyZone(np, class) == DB_Z_CACHE) - return(0); - - if (type != T_ANY && type != T_PTR && type != T_NXT) { - found = memget((stalecount + 1) * sizeof *found); - tmpfound = memget((stalecount + 1) * sizeof *tmpfound); - if (found == NULL || tmpfound == NULL) - ns_panic(ns_log_default, 1, "finddata: out of memory"); - defer = 1; - } - - buflen = *lenp; - -#ifdef DEBUG - if (buflen > PACKETSZ) - ns_debug(ns_log_default, 1, "finddata(): buflen=%d", buflen); -#endif - cp = ((char *)hp) + *countp; - foundcname = 0; - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (!wanted(dp, class, type)) - continue; - if (dp->d_cred == DB_C_ADDITIONAL) { -#ifdef NOADDITIONAL - continue; -#else - /* we want to expire additional data very - * quickly. current strategy is to cut 5% - * off each time it is accessed. this makes - * stale(dp) true earlier when this datum is - * used often. - */ - dp->d_ttl = tt.tv_sec - + - 0.95 * (int) (dp->d_ttl - tt.tv_sec); -#endif - } - /* -ve $ing stuff, anant@isi.edu - * if we have a -ve $ed record, change the rcode on the - * header to reflect that - */ - if (dp->d_rcode == NOERROR_NODATA) { - if (count != 0) { - /* - * This should not happen, yet it does... - */ - ns_info(ns_log_default, - "NODATA & data for \"%s\" type %d class %d", - *dnamep, type, class); - continue; - } - if (type == T_ANY && dp->d_type != T_ANY) - continue; - hp->rcode = NOERROR_NODATA; - if (dp->d_size == 0) { /* !RETURNSOA */ - ret = 1; - goto done; - } - } - if (dp->d_rcode == NXDOMAIN) { - if (count != 0) { - /* - * This should not happen, yet it might... - */ - ns_info(ns_log_default, - "NXDOMAIN & data for \"%s\" type %d class %d", - *dnamep, type, class); - continue; - } - hp->rcode = NXDOMAIN; - if (dp->d_size == 0) { /* !RETURNSOA */ - ret = 1; - goto done; - } - } -#ifdef HITCOUNTS - ++dp->d_hitcnt; - ++db_total_hits; -#endif /* HITCOUNTS */ - - /* Don't put anything but key or sig RR's in response to - requests for key or sig */ - if (((type == T_SIG) || (type == T_KEY)) && - (!((dp->d_type == T_SIG) || (dp->d_type == T_KEY))) ) - continue; - - /* Don't return glue (NS/A/AAAA) */ - if (!glueok && findMyZone(np, class) == DB_Z_CACHE) - continue; - - if (!defer) { - if (foundcname != 0 && dp->d_type == T_CNAME) - continue; - - if ((n = make_rr(*dnamep, dp, (u_char *)cp, buflen, 1, - dnptrs, dnptrs_end, 0)) < 0) { - hp->tc = 1; - ret = *lenp - buflen; - goto done; - } - if (dp->d_secure != DB_S_SECURE) - hp->ad = 0; - cp += n; - buflen -= n; - count++; - - if (dp->d_type == T_CNAME) { - foundcname = 1; - -#define SETAA(pass, class, dp) \ - (pass == 1 && class != C_ANY && dp->d_zone != DB_Z_CACHE && \ - (zones[dp->d_zone].z_type == z_master || \ - zones[dp->d_zone].z_type == z_slave) && \ - (zones[dp->d_zone].z_flags & Z_AUTH) != 0) - - if (SETAA(pass, class, dp)) - hp->aa = 1; - -#define FOLLOWCNAME(type) \ - (type != T_KEY) && (type != T_SIG) && (type != T_NXT) && (type != T_ANY) - /* don't alias if querying for key, sig, nxt, or any */ - - if (FOLLOWCNAME(type)) - new_dnamep = (char *)dp->d_data; - } - } else { - if (dp->d_type == T_CNAME) { - foundcname = 1; - - if (SETAA(pass, class, dp)) - hp->aa = 1; - - } - found[found_count++] = dp; - } - } - - if (found_count == 0 && count == 0) { - ret = 0; - goto done; - } - - /* - * If the query type was SIG or ANY we will have returned the SIG - * records already. - */ - if (type != T_SIG && type != T_ANY) { - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (!wantedsig(dp, class, type)) - continue; - if (dp->d_cred == DB_C_ADDITIONAL) { -#ifdef NOADDITIONAL - continue; -#else - /* we want to expire additional data very - * quickly. current strategy is to cut 5% - * off each time it is accessed. this makes - * stale(dp) true earlier when this datum is - * used often. - */ - dp->d_ttl = tt.tv_sec - + - 0.95 * (int) (dp->d_ttl - tt.tv_sec); -#endif - } - if (!defer) { - if ((n = make_rr(*dnamep, dp, (u_char *)cp, - buflen, 1, dnptrs, dnptrs_end, - 0)) < 0) { - hp->tc = 1; - ret = *lenp - buflen; - goto done; - } - if (dp->d_secure != DB_S_SECURE) - hp->ad = 0; - cp += n; - buflen -= n; - count++; - } else - found[found_count++] = dp; - } - } - - if (defer && found_count > 0) { - int first_sig; - int non_sig_count; - int sig_count; /* number of SIG records in found */ - int idx, jdx; - enum ordering order; - - order = match_order(np, class, foundcname ? T_CNAME : type); - - /* - * shuffle the SIG records down to the bottom of the array - * as we need to make sure they get packed last, no matter - * what the ordering is. We're sure to maintain the - * original ordering within the two sets of records (so - * that fixed_order can work). - * First we pack the non-SIG records into the temp array. - */ - for (idx = jdx = 0 ; idx < found_count ; idx++) { - if (found[idx]->d_type != T_SIG) { - tmpfound[jdx++] = found[idx]; - } - } - non_sig_count = jdx; - sig_count = found_count - jdx; - first_sig = jdx ; - - /* - * now shift the SIG records down to the end of the array - * and copy in the non-SIG records - */ - for (i = idx = found_count - 1 ; i >= 0 ; idx--) { - if (i < non_sig_count) { - found[i] = tmpfound[i]; - i--; - } else if (found[idx]->d_type == T_SIG) { - found[i--] = found[idx] ; - } - } - - foundcname = 0; - switch (order) { - case fixed_order: - for (i = 0; i < found_count; i++) { - dp = found[i]; - if (foundcname != 0 && dp->d_type == T_CNAME) - continue; - if (dp->d_type == T_CNAME) { - foundcname = 1; - if (FOLLOWCNAME(type)) { - new_dnamep = (char *)dp->d_data; - } - } - if ((n = make_rr(*dnamep, dp, (u_char *)cp, - buflen, 1, - dnptrs, dnptrs_end, 0)) < 0) { - hp->tc = 1; - ret = *lenp - buflen; - goto done; - } - if (dp->d_secure != DB_S_SECURE) - hp->ad = 0; - cp += n; - buflen -= n; - count++; - } - break; - - case random_order: { - /* first we shuffle the non-SIG records */ - int iters = non_sig_count; - for (i = 0; i < iters; i++) { - choice = ((u_int)rand()>>3) % non_sig_count; - non_sig_count--; - dp = found[choice]; - found[choice] = found[non_sig_count]; - if (foundcname != 0 && dp->d_type == T_CNAME) - continue; - if (dp->d_type == T_CNAME) { - foundcname = 1; - if (FOLLOWCNAME(type)) { - new_dnamep = (char *)dp->d_data; - } - } - if ((n = make_rr(*dnamep, dp, (u_char *)cp, - buflen, 1, - dnptrs, dnptrs_end, 0)) < 0) { - hp->tc = 1; - ret = *lenp - buflen; - goto done; - } - if (dp->d_secure != DB_S_SECURE) - hp->ad = 0; - cp += n; - buflen -= n; - count++; - } - - /* now shuffle the SIG records */ - iters = sig_count; - for (i = 0; i < iters; i++) { - choice = ((u_int)rand()>>3) % sig_count; - choice += first_sig; - sig_count--; - dp = found[choice]; - found[choice] = found[sig_count + first_sig]; - if ((n = make_rr(*dnamep, dp, (u_char *)cp, - buflen, 1, - dnptrs, dnptrs_end, 0)) < 0) { - hp->tc = 1; - ret = *lenp - buflen; - goto done; - } - if (dp->d_secure != DB_S_SECURE) - hp->ad = 0; - cp += n; - buflen -= n; - count++; - } - break; - } - - case cyclic_order: - /* first we do the non-SIG records */ - if (non_sig_count > 0) - choice = ((u_int)rand()>>3) % non_sig_count; - else - choice = 0; - for (i = 0; i < non_sig_count ; i++) { - dp = found[(i + choice) % non_sig_count]; - if (foundcname != 0 && dp->d_type == T_CNAME) - continue; - if (dp->d_type == T_CNAME) { - foundcname = 1; - if (FOLLOWCNAME(type)) { - new_dnamep = (char *)dp->d_data; - } - } - if ((n = make_rr(*dnamep, dp, (u_char *)cp, - buflen, 1, - dnptrs, dnptrs_end, 0)) < 0) { - hp->tc = 1; - ret = *lenp - buflen; - goto done; - } - if (dp->d_secure != DB_S_SECURE) - hp->ad = 0; - cp += n; - buflen -= n; - count++; - } - - /* now do the SIG record rotation. */ - if (sig_count > 0) { - choice = ((u_int)rand()>>3) % sig_count; - choice += first_sig; - i = choice; - do { - dp = found[i]; - if ((n = make_rr(*dnamep, dp, - (u_char *)cp, - buflen, 1, - dnptrs, - dnptrs_end, 0)) < 0) { - hp->tc = 1; - ret = *lenp - buflen; - goto done; - } - if (dp->d_secure != DB_S_SECURE) - hp->ad = 0; - cp += n; - buflen -= n; - count++; - i++; - if (i >= found_count) - i = first_sig; - } while (i != choice); - } - - break; - - default: - ns_warning(ns_log_default, "finddata: unknown ordering: %d", - order); - break; - } - } - - if (new_dnamep != NULL) - *dnamep = new_dnamep; - - ns_debug(ns_log_default, 3, "finddata: added %d class %d type %d RRs", - count, class, type); - ret = *lenp - buflen; - done: - if (found != NULL) - memput(found, (stalecount + 1) * sizeof *found); - if (tmpfound != NULL) - memput(tmpfound, (stalecount + 1) * sizeof *tmpfound); - *countp = count; - return (ret); -} - -/* - * Do we want this data record based on the class and type? - */ -static int -wanted(const struct databuf *dp, int class, int type) { - const u_char *cp; - int coveredType; - time_t expiration; -#ifdef DEBUG - char pclass[15], ptype[15]; -#endif - -#ifdef DEBUG - strcpy(pclass, p_class(class)); - strcpy(ptype, p_type(type)); - ns_debug(ns_log_default, 3, "wanted(%p, %s %s) [%s %s]", - dp, pclass, ptype, - p_class(dp->d_class), p_type(dp->d_type)); -#endif - - if (dp->d_class != class && class != C_ANY) - return (0); - /* - * Must check SIG for expiration below, other matches - * return OK here. - */ - if (type == dp->d_type && (type != T_SIG)) - return (1); - /* For a T_ANY query, we do not want to return -ve $ed RRs. */ - if (type == T_ANY && dp->d_rcode == NOERROR_NODATA) - return (0); - - /* First, look at the type of RR. */ - switch (dp->d_type) { - - /* Cases to deal with: - T_ANY search, return all unexpired SIGs. - T_SIG search, return all unexpired SIGs. - T_<foo> search, return all unexp SIG <FOO>s. - */ - case T_SIG: - cp = dp->d_data; - GETSHORT(coveredType, cp); - cp += INT16SZ + INT32SZ; /* skip alg, labels, & orig TTL */ - GETLONG(expiration,cp); - - if (type == T_ANY || type == T_SIG) { - if (expiration > time(0)) - return (1); /* Unexpired matching SIG */ - } - return (0); /* We don't return this SIG. */ - - case T_ANY: - return (1); - case T_CNAME: - if (dp->d_rcode != NOERROR_NODATA) - return (1); - else - break; - } - /* OK, now look at the type of query. */ - if (type == ns_t_any) - return (1); - else if (type == ns_t_mailb) - switch (dp->d_type) { - case T_MR: - case T_MB: - case T_MG: - case T_MINFO: - return (1); - } - else if (ns_t_xfr_p(type)) { - /* - * This is used to validate transfer requests, not - * generate transfer responses. Is there an SOA? - */ - if (dp->d_type == ns_t_soa && dp->d_zone != DB_Z_CACHE - && (zones[dp->d_zone].z_flags & Z_AUTH)) - return (1); - } - return (0); -} - -static int -wantedsig(const struct databuf *dp, int class, int type) { - const u_char *cp; - int coveredType; - time_t expiration; -#ifdef DEBUG - char pclass[15], ptype[15]; -#endif - -#ifdef DEBUG - strcpy(pclass, p_class(class)); - strcpy(ptype, p_type(type)); - ns_debug(ns_log_default, 3, "wantedtsig(%p, %s %s) [%s %s]", - dp, pclass, ptype, - p_class(dp->d_class), p_type(dp->d_type)); -#endif - - if (dp->d_class != class && class != C_ANY) - return (0); - if (dp->d_type != T_SIG || dp->d_rcode != 0) - return (0); - - cp = dp->d_data; - GETSHORT(coveredType, cp); - cp += INT16SZ + INT32SZ; /* skip alg, labels, & orig TTL */ - GETLONG(expiration,cp); - if (expiration < time(0)) - return (0); - - if (type == T_ANY || type == T_SIG || type == coveredType) - return (1); - if (type == ns_t_mailb) { - switch (coveredType) { - case T_MR: - case T_MB: - case T_MG: - case T_MINFO: - return (1); - } - } - return (0); -} - -/* - * Add RR entries from dpp array to a query/response. - * Return the number of bytes added or negative the amount - * added if truncation occured. Typically you are - * adding NS records to a response. - */ -int -add_data(struct namebuf *np, struct databuf **dpp, - u_char *cp, int buflen, int *countp) -{ - struct databuf *dp; - char dname[MAXDNAME]; - int n, bytes; - - bytes = *countp = 0; - getname(np, dname, sizeof(dname)); - for (dp = *dpp++; dp != NULL; dp = *dpp++) { - if (stale(dp)) - continue; /* ignore old cache entry */ - if (dp->d_rcode) - continue; - if ((n = make_rr(dname, dp, cp, buflen, 1, - dnptrs, dnptrs_end, 0)) < 0) - return (-bytes); /* Truncation */ - cp += n; - buflen -= n; - bytes += n; - (*countp)++; - } - return (bytes); -} - -static void -rrsetadd(struct flush_set *flushset, const char *name, struct databuf *dp) { - struct flush_set *fs = flushset; - struct db_list *dbl; - - while (fs->fs_name && ( - ns_samename(fs->fs_name,name) != 1 || - (fs->fs_class != dp->d_class) || - (fs->fs_type != dp->d_type) || - (fs->fs_cred != dp->d_cred))) { - fs++; - } - if (!fs->fs_name) { - fs->fs_name = savestr(name, 1); - fs->fs_class = dp->d_class; - fs->fs_type = dp->d_type; - fs->fs_cred = dp->d_cred; - fs->fs_list = NULL; - fs->fs_last = NULL; - } - dbl = (struct db_list *)memget(sizeof(struct db_list)); - if (!dbl) - panic("rrsetadd: out of memory", NULL); - dbl->db_next = NULL; - dbl->db_dp = dp; - DRCNTINC(dbl->db_dp); - if (fs->fs_last == NULL) - fs->fs_list = dbl; - else - fs->fs_last->db_next = dbl; - fs->fs_last = dbl; -} - -static int -ttlcheck(const char *name, struct db_list *dbl, int update) { - int type = dbl->db_dp->d_type; - int class = dbl->db_dp->d_class; - struct hashbuf *htp = hashtab; - const char *fname; - struct namebuf *np; - struct db_list *dbp = dbl; - struct databuf *dp; - u_int32_t ttl = 0; /* Make gcc happy. */ - int first; - - - np = nlookup(name, &htp, &fname, 0); - if (np == NULL || fname != name || ns_wildcard(NAME(*np))) - return (1); - - /* check that all the ttl's we have are the same, if not return 1 */ - first = 1; - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (!match(dp, class, type)) - continue; - if (first) { - /* we can't update zone data so return early */ - if (dp->d_zone != DB_Z_CACHE) - return (0); - ttl = dp->d_ttl; - first = 0; - } else if (ttl != dp->d_ttl) - return (1); - } - - /* there are no records of this type in the cache */ - if (first) - return(1); - - /* - * the ttls of all records we have in the cache are the same - * if the ttls differ in the new set we don't want it. - */ - - /* check that all the ttl's we have are the same, if not return 0 */ - first = 1; - while (dbp) { - if (first) { - ttl = dbp->db_dp->d_ttl; - first = 0; - } else if (ttl != dbp->db_dp->d_ttl) { - return(0); - } - dbp = dbp->db_next; - } - - /* update ttl if required */ - if (update) { - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (!match(dp, class, type)) - continue; - if (dp->d_ttl > ttl) - break; - dp->d_ttl = ttl; - fixttl(dp); - } - } - - return(1); -} - -/* - * lookup rrset in table and compare to dbl - * tri state result - * -1: lookup failed - * 0: rrsets same - * 1: rrsets differ - */ - -static int -rrsetcmp(char * name, struct db_list * dbl, struct hashbuf * table) { - int type = dbl->db_dp->d_type; - int class = dbl->db_dp->d_class; - struct hashbuf *htp = table; - const char *fname; - struct namebuf *np; - struct db_list *dbp = dbl; - struct databuf *dp; - int exists = 0; - - - np = nlookup(name, &htp, &fname, 0); - if (np == NULL || fname != name || ns_wildcard(NAME(*np))) { - ns_debug(ns_log_default, 3, "rrsetcmp: name not in database"); - return (-1); - } - - /* check that all entries in dbl are in the cache */ - while (dbp) { - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (!match(dp, class, type)) - continue; - exists = 1; - if (!db_cmp(dp, dbp->db_dp) -#ifdef NOADDITIONAL - && ((dp->d_cred == dbp->db_dp->d_cred) || - (dp->d_cred != DB_C_ADDITIONAL)) -#endif - ) - break; - } - if (!dp) { - ns_debug(ns_log_default, 3, - "rrsetcmp: %srecord%s in database", - exists ? "" : "no ", exists ? " not" : "s"); - return (exists ? 1 : -1); - } - dbp = dbp->db_next; - } - - /* Check that all cache entries are in the list. */ - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (!match(dp, class, type)) - continue; -#ifdef NCACHE - if (dp->d_rcode) - return (1); -#endif - dbp = dbl; - while (dbp) { - if (!db_cmp(dp, dbp->db_dp)) - break; - dbp = dbp->db_next; - } - if (!dbp) { - ns_debug(ns_log_default, 3, - "rrsetcmp: record not in rrset"); - return (1); - } - } - ns_debug(ns_log_default, 3, "rrsetcmp: rrsets matched"); - return (0); -} - -/* - * verify incoming answer against what we already have in the hints - * issue warnings / errors if differences detected. - */ - -static void -check_hints(struct flush_set * flushset) { - struct zoneinfo *zp; - struct flush_set *fs; - struct db_list *dbp; - - /* We don't use hints when in forward only mode */ - if (NS_OPTION_P(OPTION_FORWARD_ONLY)) - return; - - /* find "." NS rrset and hence class */ - for (fs = flushset; fs->fs_name != NULL; fs++) { - if ((fs->fs_name[0] != '\0') || (fs->fs_type != ns_t_ns)) - continue; - - /* see if we are a root server */ - zp = find_zone(fs->fs_name, fs->fs_class); - if (zp != NULL && - (zp->z_type == z_master || zp->z_type == z_slave)) - return; - switch (rrsetcmp(fs->fs_name, fs->fs_list, fcachetab)) { - case -1: - ns_error(ns_log_default, - "check_hints: no NS records for class %d in hints", - fs->fs_class); - break; - case 1: - ns_warning(ns_log_default, - "check_hints: root NS list in hints for class %d does not match root NS list", - fs->fs_class); - break; - case 0: - break; - default: - ns_error(ns_log_default, - "check_hints: unexpected response from rrsetcmp"); - break; - } - break; - } - - if (fs->fs_name == NULL) /* no root NS records */ - return; - - dbp = fs->fs_list; - while (dbp) { - /* for each NS find A rrset in answer and check */ - for (fs = flushset; fs->fs_name != NULL; fs++) { - if (ns_samename(fs->fs_name, (char *)dbp->db_dp->d_data) != 1 - || fs->fs_type != ns_t_a) - continue; - switch (rrsetcmp(fs->fs_name, fs->fs_list, fcachetab)) { - case -1: - ns_error(ns_log_default, - "check_hints: no A records for %s class %d in hints", - fs->fs_name[0] ? fs->fs_name : ".", - fs->fs_class); - break; - case 1: - ns_warning(ns_log_default, - "check_hints: A records for %s class %d do not match hint records", - fs->fs_name[0] ? fs->fs_name : ".", - fs->fs_class); - break; - case 0: - break; - default: - ns_error(ns_log_default, - "check_hints: unexpected response from rrsetcmp"); - break; - } - break; - } - - if (fs->fs_name == NULL) - ns_debug(ns_log_default, 2, - "check_hints: no A records for %s", - dbp->db_dp->d_data); - - dbp = dbp->db_next; - } -} - -static void -rrsetupdate(struct flush_set * flushset, int flags, struct sockaddr_in from, - int updatettl) { - struct flush_set *fs = flushset; - struct db_list *dbp, *odbp; - int n; - void *state = NULL; - - while (fs->fs_name) { - ns_debug(ns_log_default, 2, "rrsetupdate: %s", - fs->fs_name[0] ? fs->fs_name : "."); - if ((n = rrsetcmp(fs->fs_name, fs->fs_list, hashtab)) && - ttlcheck(fs->fs_name, fs->fs_list, 0)) { - if (n > 0) - flushrrset(fs, from); - - dbp = fs->fs_list; - while (dbp) { - n = db_set_update(fs->fs_name, dbp->db_dp, - &state, flags, - &hashtab, from, NULL, - 0, NULL); - ns_debug(ns_log_default, 3, - "rrsetupdate: %s %d", - fs->fs_name[0] ? fs->fs_name : ".", - n); - odbp = dbp; - dbp = dbp->db_next; - db_detach(&odbp->db_dp); - memput(odbp, sizeof *odbp); - } - ns_debug(ns_log_default, 3, - "rrsetupdate: %s %d", - fs->fs_name[0] ? fs->fs_name : ".", n); - } else { - if ((n == 0) && updatettl) - (void)ttlcheck(fs->fs_name,fs->fs_list, 1); - dbp = fs->fs_list; - while (dbp) { - db_detach(&dbp->db_dp); - odbp = dbp; - dbp = dbp->db_next; - memput(odbp, sizeof *odbp); - } - } - fs->fs_list = NULL; - fs++; - } - n = db_set_update(NULL, NULL, &state, flags, &hashtab, from, - NULL, 0, NULL); -} - -static void -flushrrset(struct flush_set * fs, struct sockaddr_in from) { - struct databuf *dp; - int n; - - ns_debug(ns_log_default, 2, "flushrrset(%s, %s, %s, %d)", - fs->fs_name[0]?fs->fs_name:".", p_type(fs->fs_type), - p_class(fs->fs_class), fs->fs_cred); - dp = savedata(fs->fs_class, fs->fs_type, 0, NULL, 0); - dp->d_zone = DB_Z_CACHE; - dp->d_cred = fs->fs_cred; - dp->d_clev = 0; - do { - n = db_update(fs->fs_name, dp, NULL, NULL, DB_DELETE, hashtab, - from); - ns_debug(ns_log_default, 3, "flushrrset: %d", n); - } while (n == OK); - db_detach(&dp); -} - -static void -free_flushset(struct flush_set *flushset, int flushset_size) { - struct flush_set *fs; - struct db_list *dbl; - - for (fs = flushset; fs->fs_name != NULL; fs++) { - fs->fs_name = freestr(fs->fs_name); - while ((dbl = fs->fs_list) != NULL) { - fs->fs_list = dbl->db_next; - dbl->db_next = NULL; - db_detach(&dbl->db_dp); - memput(dbl, sizeof(*dbl)); - } - } - memput(flushset, flushset_size); -} - -/* - * This is best thought of as a "cache invalidate" function. - * It is called whenever a piece of data is determined to have - * 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 -delete_all(struct namebuf *np, int class, int type) { - struct databuf *dp, *pdp; - - ns_debug(ns_log_default, 3, "delete_all(%p:\"%s\" %s %s)", - np, NAME(*np), p_class(class), p_type(type)); - pdp = NULL; - dp = np->n_data; - while (dp != NULL) { - if (dp->d_zone == DB_Z_CACHE && (dp->d_flags & DB_F_HINT) == 0 - && match(dp, class, type)) { - dp = rm_datum(dp, np, pdp, NULL); - continue; - } - pdp = dp; - dp = dp->d_next; - } -} - -/* delete_stale(np) - * for all RRs associated with this name, check for staleness (& delete) - * arguments: - * np = pointer to namebuf to be cleaned. - * returns: - * number of RRs associated with this name. - * side effects: - * delete_all() can be called, freeing memory and relinking chains. - */ -int -delete_stale(np) - struct namebuf *np; -{ - struct databuf *dp; - int count; - again: - count = 0; - for (dp = np->n_data; dp != NULL; dp = dp->d_next) { - if (dp->d_zone == DB_Z_CACHE && stale(dp)) { - delete_all(np, dp->d_class, dp->d_type); - goto again; - } - count++; - } - return (count); -} - - -/* - * Adjust answer message so that it fits in outlen. Set tc if required. - * - * If outlen = msglen, can be used to verify qdcount, ancount, nscount - * and arcount. - * - * return new length - */ - -int -trunc_adjust(u_char *msg, int msglen, int outlen) { - register HEADER *hp; - u_int qdcount, ancount, nscount, arcount, dlen; - u_char *cp = msg, *cp1, *eom_in, *eom_out; - int n; - - eom_in = msg + msglen; - eom_out = msg + outlen; - - hp = (HEADER *)msg; - qdcount = ntohs(hp->qdcount); - ancount = ntohs(hp->ancount); - nscount = ntohs(hp->nscount); - arcount = ntohs(hp->arcount); - cp += HFIXEDSZ; - - while ((qdcount || ancount || nscount || arcount) && - cp < eom_in && cp < eom_out) { - - cp1 = cp; /* use temporary in case we break */ - - n = dn_skipname(cp1, eom_in); - if (n < 0) - break; - cp1 += n + 2 * INT16SZ; /* type, class */ - - if (!qdcount) { - cp1 += INT32SZ; /* ttl */ - if (cp1 + INT16SZ > eom_in) - break; - GETSHORT(dlen, cp1); - cp1 += dlen; - } - - if (cp1 > eom_in || cp1 > eom_out) - break; - - cp = cp1; - - if (qdcount) - qdcount--; - else if (ancount) - ancount--; - else if (nscount) - nscount--; - else - arcount--; - } - - if (qdcount || ancount || nscount || arcount) { - ns_debug(ns_log_default, 1, - "trunc_adjust:%s %d %d %d %d %d, %d %d %d %d %d", - hp->tc?" tc":"", msglen, - ntohs(hp->qdcount), ntohs(hp->ancount), - ntohs(hp->nscount), ntohs(hp->arcount), - cp-msg, qdcount, ancount, nscount, arcount); - hp->tc = 1; - hp->qdcount = htons(ntohs(hp->qdcount) - qdcount); - hp->ancount = htons(ntohs(hp->ancount) - ancount); - hp->nscount = htons(ntohs(hp->nscount) - nscount); - hp->arcount = htons(ntohs(hp->arcount) - arcount); - } - ENSURE(cp <= eom_out); - return (cp - msg); -} - -/* - * mark the server "from" bad in the qp structure so it won't be retried. - */ -static int -mark_noedns(struct qinfo *qp, struct sockaddr_in from, int cache) { - int i; - - for (i = 0; i < (int)qp->q_naddr; i++) - if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, from.sin_addr)) { - if (qp->q_addr[i].noedns) - return (1); - if (qp->q_addr[i].nsdata && cache) - qp->q_addr[i].nsdata->d_noedns = 1; - qp->q_addr[i].noedns = 1; - break; - } - return (0); -} - -static void -mark_bad(struct qinfo *qp, struct sockaddr_in from) { - int i; - - for (i = 0; i < (int)qp->q_naddr; i++) - if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, from.sin_addr)) - qp->q_addr[i].nretry = MAXRETRY; -} - -static void -mark_lame(struct qinfo *qp, struct sockaddr_in from) { - int i; - - for (i = 0; i < (int)qp->q_naddr; i++) - if (ina_equal(qp->q_addr[i].ns_addr.sin_addr, from.sin_addr) && - qp->q_addr[i].ns != NULL) { - qp->q_addr[i].ns->d_flags |= DB_F_LAME; - db_lame_add(qp->q_domain, - (char*)qp->q_addr[i].ns->d_data, - tt.tv_sec + server_options->lame_ttl); - } -} - -/* - * Retry the message if and only if from matches where the query was - * last sent to. The code does not handle responses sent from the - * wrong interface an a multihomed server. - */ -static void -fast_retry(struct qinfo *qp, struct sockaddr_in from, int samehost) { - if (ina_equal(qp->q_addr[qp->q_curaddr].ns_addr.sin_addr, - from.sin_addr)) - retry(qp, samehost); -} - -static void -add_related_additional(char *name) { - int i; - - if (num_related >= MAX_RELATED - 1) - return; - for (i = 0; i < num_related; i++) - if (ns_samename(name, related[i]) == 1) { - (void)freestr(name); - return; - } - related[num_related++] = name; -} - -static void -free_related_additional() { - int i; - - for (i = 0; i < num_related; i++) - related[i] = freestr(related[i]); - num_related = 0; -} - -static int -related_additional(char *name) { - int i; - - for (i = 0; i < num_related; i++) - if (ns_samename(name, related[i]) == 1) - return (1); - return (0); -} - -static void -freestr_maybe(char **tname) { - if (tname == NULL || *tname == NULL) - return; - *tname = freestr(*tname); -} - -/* - * Match a request namebuf against the configured rrset-order info. First - * match wins. There is an implicit '*.' at the front to the ordering names. - */ -static enum ordering -match_order(const struct namebuf *np, int class, int type) { - rrset_order_list orders = server_options->ordering; - rrset_order_element roe; - - if (orders == NULL) - return (DEFAULT_ORDERING); - - for (roe = orders->first ; roe != NULL ; roe = roe->next) { - if (roe->class != C_ANY && roe->class != class) - continue; - if (roe->type != T_ANY && roe->type != type) - continue; - - if (match_name(np, roe->name, strlen(roe->name)) == 0) { - return (roe->order); - } - } - - /* none matched so use default */ - return (DEFAULT_ORDERING); -} - -/* Do a simple compare of the NP data against the given NAME, recursively - * looking at the NP parent if necessary. NAMELEN is the length of the NAME - * that needs to be matched. Matching happen from right to left. Returns -1 - * on failure, on success the index of the first character of the matched - * portion of the string is returned. In the first level call a return - * value of 0 is of interest. - */ -static int -match_name(const struct namebuf *np, const char *name, size_t namelen) -{ - int matched ; - - if (name[0] == '*' && name[1] == '\0') - return 0; - - if (np->n_parent != NULL) { /* recurse to end of np list */ - matched = match_name(np->n_parent,name,namelen); - } else { - matched = namelen; - } - - if (matched > 0) { - int labellen = NAMELEN(*np); - char pch; - const char *start; - - if (labellen > matched) { - return -1; - } else if (labellen < matched) { - /* string is longer than this namebuf's data, so - make sure there's a period before the end of the - match so we don't just match a suffix. */ - start = name + (matched - labellen); - pch = start[-1]; - if (pch != '.') { - return -1; - } - } else { - start = name ; - } - - if (strncasecmp(start, NAME(*np), labellen) == 0) { - /* looking good. tell our caller what portion of - the tail of string has been matched */ - if (start == name) - return (0) ; - else - return (start - name - 1); /* matched '.' too */ - } else { - return (-1); - } - } - - return (matched); -} - |