📄 ns_resp.c
字号:
#if !defined(lint) && !defined(SABER)static char sccsid[] = "@(#)ns_resp.c 4.65 (Berkeley) 3/3/91";static char rcsid[] = "$Id: ns_resp.c,v 4.9.1.10 1993/12/06 00:43:02 vixie Exp $";#endif /* not lint *//* * ++Copyright++ 1986, 1988, 1990 * - * 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. * - * --Copyright-- */#include <sys/param.h>#include <sys/socket.h>#include <sys/file.h>#include <netinet/in.h>#include <arpa/nameser.h>#include <arpa/inet.h>#include <syslog.h>#include <errno.h>#include <stdio.h>#include <resolv.h>#include "named.h"static void check_root __P((void)), check_ns __P((void));static int norootlogged[MAXCLASS];static char skipnameFailedAnswer[] = "skipname failed in answer", skipnameFailedQuery[] = "skipname failed in query", outofDataQuery[] = "ran out of data in query", outofDataAnswer[] = "ran out of data in answer", dlenOverrunAnswer[] = "dlen overrun in answer", dlenUnderrunAnswer[] = "dlen underrun in answer", outofDataFinal[] = "out of data in final pass", outofDataAFinal[] = "out of data after final pass";voidns_resp(msg, msglen) u_char *msg; int msglen;{ register struct qinfo *qp; register HEADER *hp; register struct qserv *qs; register struct databuf *ns, *ns2; register u_char *cp;#ifdef VALIDATE register u_char *tempcp; struct sockaddr_in *server = &from_addr; int *validatelist; int lesscount;#endif struct databuf *nsp[NSMAX], **nspp; int i, c, n, ancount, aucount, nscount, arcount; int old_ancount; int type, class, dbflags; int cname = 0; /* flag for processing cname response */ int count, founddata, foundname; int buflen; int newmsglen; char name[MAXDNAME], *dname; char *fname; char *formerrmsg = "brain damage"; u_char newmsg[BUFSIZ]; u_char **dpp, *tp; time_t rtrip; struct hashbuf *htp; struct namebuf *np; struct netinfo *lp; struct fwdinfo *fwd; stats[S_RESPONSES].cnt++;#ifdef DATUMREFCNT nsp[0] = NULL;#endif hp = (HEADER *) msg; if ((qp = qfindid(hp->id)) == NULL ) { dprintf(1, (ddt, "DUP? dropped (id %d)\n", ntohs(hp->id))); stats[S_DUPRESP].cnt++; return; } dprintf(2, (ddt, "Response (%s %s %s) nsid=%d id=%d\n", (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))); /* * 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 (its sick) * The server can't understand our query and return FORMERR * In all these cases, we simply drop the packet and force * a retry. This will make him look bad due to unresponsiveness. * Be sure not to include authoritative NXDOMAIN */ if ((hp->rcode != NOERROR && hp->rcode != NXDOMAIN)#ifndef NCACHE || (hp->rcode == NXDOMAIN && !hp->aa) /* must accept this one if * we allow negative caching */#endif /*NCACHE*/ || hp->opcode != QUERY) { dprintf(2, (ddt, "resp: error (ret %d, op %d), dropped\n", hp->rcode, hp->opcode)); stats[S_BADRESPONSES].cnt++; return; }#ifdef ALLOW_UPDATES if ( (hp->rcode == NOERROR) && (hp->opcode == UPDATEA || hp->opcode == UPDATED || hp->opcode == UPDATEDA || hp->opcode == UPDATEM || hp->opcode == UPDATEMA) ) { /* * Update the secondary's copy, now that the primary * successfully completed the update. Zone doesn't matter * for dyn. update -- doupdate calls findzone to find it */ doupdate(qp->q_msg, qp->q_msglen, qp->q_msg + sizeof(HEADER), 0, (struct databuf *)0, 0#ifdef CRED , DB_C_AUTH#endif ); dprintf(3, (ddt, "resp: leaving, UPDATE*\n")); /* return code filled in by doupdate */ goto return_msg; }#endif /* ALLOW_UPDATES */ /* * 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. */ for (fwd = fwdtab; fwd != (struct fwdinfo *)NULL; fwd = fwd->next) { if (fwd->fwdaddr.sin_addr.s_addr == from_addr.sin_addr.s_addr) { /* XXX - should put this in STATS somewhere */ break; } } /* * If we were using nameservers, find the qinfo pointer and update * the rtt and fact that we have called on this server before. */ if (fwd == (struct fwdinfo *)NULL) { struct timeval *stp; for (n = 0, qs = qp->q_addr; n < qp->q_naddr; n++, qs++) if (qs->ns_addr.sin_addr.s_addr == from_addr.sin_addr.s_addr) break; if (n >= qp->q_naddr) { dprintf(1, (ddt, "Response from unexpected source [%s].%d\n", inet_ntoa(from_addr.sin_addr), ntohs(from_addr.sin_port))); stats[S_MARTIANS].cnt++; /* * 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? */ dprintf(1, (ddt, "Response from unused address %s, assuming %s\n", inet_ntoa(from_addr.sin_addr), inet_ntoa(qs->ns_addr.sin_addr))); /* 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); } dprintf(3, (ddt, "stime %d/%d now %d/%d rtt %d\n", stp->tv_sec, stp->tv_usec, tt.tv_sec, tt.tv_usec, 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)){ if (ns->d_nstime == 0) ns->d_nstime = (u_int32_t)rtrip; else ns->d_nstime = (u_int32_t) (ns->d_nstime * ALPHA + (1-ALPHA) * (u_int32_t)rtrip); /* prevent floating point overflow, * limit to 1000 sec */ if (ns->d_nstime > 1000000) ns->d_nstime = 1000000; } /* * 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; dprintf(2, (ddt, "NS #%d addr [%s] used, rtt %d\n", n, inet_ntoa(qs->ns_addr.sin_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; n < qp->q_naddr; n++, qs++) { 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) ns2->d_nstime = (u_int32_t)(rtrip * BETA); else ns2->d_nstime = (u_int32_t)( ns2->d_nstime * BETA + (1-ALPHA) * rtrip ); if (ns2->d_nstime > 1000000) ns2->d_nstime = 1000000; } else ns2->d_nstime = (u_int32_t)(ns2->d_nstime * GAMMA); dprintf(2, (ddt, "NS #%d [%s] rtt now %d\n", n, inet_ntoa(qs->ns_addr.sin_addr), ns2->d_nstime)); } } /* * Skip query section */ free_addinfo(); /* sets addcount to zero */ cp = msg + sizeof(HEADER); dpp = dnptrs; *dpp++ = msg; if ((*cp & INDIR_MASK) == 0) *dpp++ = cp; *dpp = NULL; if (hp->qdcount) { n = dn_skipname(cp, msg + msglen); if (n <= 0) { formerrmsg = skipnameFailedQuery; goto formerr; } cp += n; GETSHORT(type, cp); GETSHORT(class, cp); if (cp - msg > msglen) { formerrmsg = outofDataQuery; goto formerr; } } /* * Save answers, authority, and additional records for future use. */ ancount = ntohs(hp->ancount); aucount = ntohs(hp->nscount); arcount = ntohs(hp->arcount); nscount = 0; tp = cp; dprintf(3, (ddt, "resp: ancount %d, aucount %d, arcount %d\n", ancount, aucount, arcount)); /* * If there's an answer, check if it's a CNAME response; * if no answer but aucount > 0, see if there is an NS * or just an SOA. (NOTE: ancount might be 1 with a CNAME, * and NS records may still be in the authority section; * we don't bother counting them, as we only use nscount * if ancount == 0.) */ if (ancount == 1 || (ancount == 0 && aucount > 0)) { c = aucount; do { if (tp - msg >= msglen) { formerrmsg = outofDataAnswer; goto formerr; } n = dn_skipname(tp, msg + msglen); if (n <= 0) { formerrmsg = skipnameFailedAnswer; goto formerr; } tp += n; /* name */ GETSHORT(i, tp); /* type */ tp += sizeof(u_int16_t); /* class */ tp += sizeof(u_int32_t); /* ttl */ GETSHORT(count, tp); /* dlen */ if (tp - msg > msglen - count) { formerrmsg = dlenOverrunAnswer; goto formerr; } tp += count; if (ancount && i == T_CNAME) { cname++; dprintf(1, (ddt, "CNAME - needs more processing\n" ) ); if (!qp->q_cmsglen) { qp->q_cmsg = qp->q_msg; qp->q_cmsglen = qp->q_msglen; qp->q_msg = NULL; qp->q_msglen = 0; } } /* * See if authority record is a nameserver. */ if (ancount == 0 && i == T_NS) nscount++; } while (--c > 0); tp = cp; } if (qp->q_flags & Q_ZSERIAL) { if ((hp->aa) && (ancount != 0) && (hp->rcode == NOERROR) && (type == T_SOA) && ((class == C_IN) || (class == C_HS)) ) { /* XXX - should check name, too */ int n; u_int16_t dlen; u_int32_t serial; u_char *tp = cp; if (0 >= (n = dn_skipname(tp, msg + msglen))) { formerrmsg = skipnameFailedAnswer; goto formerr; } tp += n /* name */ + sizeof(u_int16_t) /* type */ + sizeof(u_int16_t) /* class */ + sizeof(u_int32_t); /* ttl */ GETSHORT(dlen, tp); /* dlen */ if (dlen < (5 * sizeof(u_int32_t))) { formerrmsg = dlenUnderrunAnswer; goto formerr; } if (0 >= (n = dn_skipname(tp, msg + msglen))) { formerrmsg = skipnameFailedAnswer; goto formerr; } tp += n; /* mname */ if (0 >= (n = dn_skipname(tp, msg + msglen))) { formerrmsg = skipnameFailedAnswer; goto formerr; } tp += n; /* rname */ GETLONG(serial, tp); qserial_answer(qp, serial); } qremove(qp); return; } /* * Add the info received in the response to the Data Base */ c = ancount + aucount + arcount;#ifdef NCACHE /* -ve $ing non-existence of record, must handle non-authoritative * NOERRORs with c == 0. */ if (!hp->aa && hp->rcode == NOERROR && c == 0) { goto return_msg; } /*should ideally validate message before returning it*/#endif /*NCACHE*/#ifdef notdef /* * If the request was for a CNAME that doesn't exist, * but the name is valid, fetch any other data for the name. * DON'T do this now, as it will requery if data are already * in the cache (maybe later with negative caching). */ if (hp->qdcount && type == T_CNAME && c == 0 && hp->rcode == NOERROR && !(qp->q_flags & Q_SYSTEM)) { dprintf(4, (ddt, "resp: leaving, no CNAME\n")); /* Cause us to put it in the cache later */ prime(class, T_ANY, qp); /* Nothing to store, just give user the answer */ goto return_msg; }#endif /* notdef */ nspp = nsp; if (qp->q_flags & Q_SYSTEM) dbflags = DB_NOTAUTH | DB_NODATA; else dbflags = DB_NOTAUTH | DB_NODATA | DB_NOHINTS; count = c; 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 */ } /* XXX - should retry this query with TCP */ }#ifdef VALIDATE tempcp = cp; validatelist = (int *)malloc(count*sizeof(int)); lesscount = 0; /*initialize*/ old_ancount = ancount; for (i = 0; i < count; i++) { int VCode; if (tempcp >= msg + msglen) { formerrmsg = outofDataFinal;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -