* Copyright (c) 1986, 1988 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
static char sccsid
[] = "@(#)ns_resp.c 4.55 (Berkeley) 1/14/89";
#include <arpa/nameser.h>
extern time_t retrytime();
extern struct fwdinfo
*fwdtab
;
extern struct sockaddr_in from_addr
; /* Source addr of last packet */
extern int needs_prime_cache
;
struct qinfo
*sysquery();
register struct qinfo
*qp
;
register struct qserv
*qs
;
register struct databuf
*ns
, *ns2
;
struct databuf
*nsp
[NSMAX
], **nspp
;
int i
, c
, n
, ancount
, aucount
, nscount
, arcount
;
int type
, class, dbflags
;
int cname
= 0; /* flag for processing cname response */
int count
, founddata
, foundname
;
char name
[MAXDNAME
], *dname
;
extern struct netinfo
*local();
stats
[S_RESPONSES
].cnt
++;
if ((qp
= qfindid(hp
->id
)) == NULL
) {
fprintf(ddt
,"DUP? dropped (id %d)\n", ntohs(hp
->id
));
fprintf(ddt
,"%s response nsid=%d id=%d\n",
qp
->q_system
? "SYSTEM" : "USER",
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
)
|| (hp
->rcode
== NXDOMAIN
&& !hp
->aa
)
|| hp
->opcode
!= QUERY
) {
fprintf(ddt
,"resp: error (ret %d, op %d), dropped\n",
stats
[S_BADRESPONSES
].cnt
++;
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);
fprintf(ddt
,"resp: leaving, UPDATE*\n");
/* return code filled in by doupdate */
* If we were using nameservers, find the qinfo pointer and update
* the rtt and fact that we have called on this server before.
if (qp
->q_fwd
== (struct fwdinfo
*)0) {
for (n
= 0, qs
= qp
->q_addr
; n
< qp
->q_naddr
; n
++, qs
++)
if (bcmp((char *)&qs
->ns_addr
.sin_addr
,
&from_addr
.sin_addr
, sizeof(struct in_addr
)) == 0)
fprintf(ddt
, "Response from unexpected source %s\n",
inet_ntoa(from_addr
.sin_addr
));
/* assume response to have come from last query */
qs
= &qp
->q_addr
[qp
->q_curaddr
];
/* Handle response from different (untried) interface */
while (qs
> qp
->q_addr
&&
(qs
->stime
.tv_sec
== 0 || qs
->ns
!= ns
))
"Response from unused address %s, assuming %s\n",
inet_ntoa(from_addr
.sin_addr
),
inet_ntoa(qs
->ns_addr
.sin_addr
));
/* compute query round trip time */
rtrip
= ((tt
.tv_sec
- stp
->tv_sec
) * 1000 +
(tt
.tv_usec
- stp
->tv_usec
) / 1000);
fprintf(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 */
* Don't update nstime if this doesn't look
* like an address databuf now. XXX
if (ns
->d_type
== T_A
&& ns
->d_class
== qs
->ns
->d_class
) {
ns
->d_nstime
= (u_long
)rtrip
;
ns
->d_nstime
= ns
->d_nstime
* ALPHA
+
(1-ALPHA
) * (u_long
)rtrip
;
/* prevent floating point overflow, limit to 1000 sec */
if (ns
->d_nstime
> 1000000)
* Record the source so that we do not use this NS again.
if(qp
->q_nusedns
< NSMAX
) {
qp
->q_usedns
[qp
->q_nusedns
++] = qs
->ns
;
fprintf(ddt
, "NS #%d addr %s used, rtt %d\n",
n
, inet_ntoa(qs
->ns_addr
.sin_addr
),
* 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
++) {
if (ns2
->d_type
!= T_A
||
ns2
->d_class
!= qs
->ns
->d_class
) /* XXX */
ns2
->d_nstime
= rtrip
* BETA
;
ns2
->d_nstime
* BETA
+ (1-ALPHA
) * rtrip
;
if (ns2
->d_nstime
> 1000000)
ns2
->d_nstime
= ns2
->d_nstime
* GAMMA
;
fprintf(ddt
, "NS #%d %s rtt now %d\n", n
,
inet_ntoa(qs
->ns_addr
.sin_addr
),
cp
= msg
+ sizeof(HEADER
);
if ((*cp
& INDIR_MASK
) == 0)
n
= dn_skipname(cp
, msg
+ msglen
);
* Save answers, authority, and additional records for future use.
ancount
= ntohs(hp
->ancount
);
aucount
= ntohs(hp
->nscount
);
arcount
= ntohs(hp
->arcount
);
fprintf(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
== 1 || (ancount
== 0 && aucount
> 0)) {
n
= dn_skipname(tp
, msg
+ msglen
);
GETSHORT(i
, tp
); /* type */
tp
+= sizeof(u_short
); /* class */
tp
+= sizeof(u_long
); /* ttl */
GETSHORT(count
, tp
); /* dlen */
if (tp
- msg
> msglen
- count
)
if (ancount
&& i
== T_CNAME
) {
fprintf(ddt
,"CNAME - needs more processing\n");
qp
->q_cmsglen
= qp
->q_msglen
;
* See if authority record is a nameserver.
if (ancount
== 0 && i
== T_NS
)
* Add the info received in the response to the Data Base
c
= ancount
+ aucount
+ arcount
;
* 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
&&
fprintf(ddt
,"resp: leaving, no CNAME\n");
/* Cause us to put it in the cache later */
/* Nothing to store, just give user the answer */
dbflags
= DB_NOTAUTH
| DB_NODATA
;
dbflags
= DB_NOTAUTH
| DB_NODATA
| DB_NOHINTS
;
for (i
= 0; i
< c
; i
++) {
if ((n
= doupdate(msg
, msglen
, cp
, 0, &ns3
, dbflags
)) < 0) {
fprintf(ddt
,"resp: leaving, doupdate failed\n");
/* return code filled in by doupdate */
* Remember nameservers from the authority section
* (This is usually overwritten by findns below(?). XXX
if (ns3
&& i
>= ancount
&& i
< ancount
+ aucount
&&
if (qp
->q_system
&& ancount
) {
if (qp
->q_system
== PRIMING_CACHE
)
fprintf(ddt
,"resp: leaving, SYSQUERY ancount %d\n", ancount
);
* If there are addresses and this is a local query,
* sort them appropriately for the local context.
if (ancount
> 1 && (lp
= local(&qp
->q_from
)) != NULL
)
sort_response(tp
, ancount
, lp
, msg
+ msglen
);
* An answer to a C_ANY query or a successful answer to a
* regular query with no indirection, then just return answer.
if ((type
== C_ANY
&& ancount
) ||
(!cname
&& !qp
->q_cmsglen
&& ancount
)) {
fprintf(ddt
,"resp: got as much answer as there is\n");
* Eventually we will want to cache this negative answer.
if (ancount
== 0 && nscount
== 0 &&
(hp
->aa
|| qp
->q_fwd
|| class == C_ANY
)) {
/* We have an authoritative NO */
fprintf(ddt
,"resp: leaving auth NO\n");
msg
= (u_char
*)qp
->q_cmsg
;
* All messages in here need further processing. i.e. they
* are either CNAMEs or we got referred again.
if (!cname
&& qp
->q_cmsglen
&& ancount
) {
fprintf(ddt
,"Cname second pass\n");
newmsglen
= qp
->q_cmsglen
;
bcopy(qp
->q_cmsg
, newmsg
, newmsglen
);
bcopy(msg
, newmsg
, newmsglen
);
cp
= newmsg
+ sizeof(HEADER
);
cp
+= dn_skipname(cp
, newmsg
+ newmsglen
) + QFIXEDSZ
;
if ((n
= dn_expand(newmsg
, newmsg
+ newmsglen
,
cp
, dname
, sizeof(name
))) < 0) {
fprintf(ddt
,"dn_expand failed\n" );
cp
= newmsg
+ sizeof(HEADER
) +
(cname
? dn_skipname(cp
, newmsg
+ newmsglen
) : n
) + QFIXEDSZ
;
buflen
= sizeof(newmsg
) - (cp
- newmsg
);
fprintf(ddt
,"resp: nlookup(%s) type=%d\n",dname
, type
);
htp
= hashtab
; /* lookup relative to root */
np
= nlookup(dname
, &htp
, &fname
, 0);
fprintf(ddt
,"resp: %s '%s' as '%s' (cname=%d)\n",
np
== NULL
? "missed" : "found", dname
, fname
, cname
);
if (np
== NULL
|| fname
!= dname
)
n
= finddata(np
, class, type
, hp
, &dname
, &buflen
, &count
);
goto fetch_ns
; /* NO data available */
if (fname
!= dname
&& type
!= T_CNAME
&& type
!= T_ANY
) {
fprintf(ddt
,"resp: foundname = %d count = %d ", foundname
, count
);
fprintf(ddt
,"founddata = %d cname = %d\n", founddata
, cname
);
hp
->ancount
= htons(hp
->ancount
);
* Look for name servers to refer to and fill in the authority
* section or record the address for forwarding the query
switch (findns(&np
, class, nsp
, &count
)) {
case NXDOMAIN
: /* shouldn't happen */
fprintf(ddt
,"req: leaving (%s, rcode %d)\n",
* should return SOA if founddata == 0,
* but old named's are confused by an SOA
* in the auth. section if there's no error.
if (foundname
== 0 && np
) {
n
= doaddauth(hp
, cp
, buflen
, np
, nsp
[0]);
n
= add_data(np
, nsp
, cp
, buflen
);
hp
->nscount
= htons((u_short
)count
);
* 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
) {
fprintf(ddt
,"resp: MAXQUERIES exceeded (%s, class %d, type %d)\n",
"MAXQUERIES exceeded, possible data loop in resolving (%s)",
/* Reset the query control structure */
qp
->q_addr
[0].stime
= tt
;
if (nslookup(nsp
, qp
) == 0) {
fprintf(ddt
,"resp: no addrs found for NS's\n");
if (qp
->q_cname
++ == MAXCNAMES
) {
fprintf(ddt
,"resp: leaving, MAXCNAMES exceeded\n");
fprintf(ddt
,"q_cname = %d\n",qp
->q_cname
);
fprintf(ddt
,"resp: building recursive query; nslookup\n");
if ((qp
->q_msg
= malloc(BUFSIZ
)) == NULL
) {
fprintf(ddt
,"resp: malloc error\n");
qp
->q_msglen
= res_mkquery(QUERY
, dname
, class,
type
, (char *)NULL
, 0, NULL
, qp
->q_msg
, BUFSIZ
);
hp
= (HEADER
*) qp
->q_msg
;
hp
= (HEADER
*)qp
->q_msg
;
hp
->id
= qp
->q_nsid
= htons((u_short
)++nsid
);
schedretry(qp
, retrytime(qp
));
fprintf(ddt
,"resp: forw -> %s %d (%d) nsid=%d id=%d %dms\n",
inet_ntoa(Q_NEXTADDR(qp
,0)->sin_addr
),
ds
, ntohs(Q_NEXTADDR(qp
,0)->sin_port
),
ntohs(qp
->q_nsid
), ntohs(qp
->q_id
),
qp
->q_addr
[0].nsdata
->d_nstime
);
if (sendto(ds
, qp
->q_msg
, qp
->q_msglen
, 0,
(struct sockaddr
*)Q_NEXTADDR(qp
,0),
sizeof(struct sockaddr_in
)) < 0) {
fprintf(ddt
, "sendto error = %d\n", errno
);
fprintf(ddt
,"resp: Query sent.\n");
fprintf(ddt
,"FORMERR resp() from %s size err %d, msglen %d\n",
inet_ntoa(from_addr
.sin_addr
),
syslog(LOG_INFO
, "Malformed response from %s\n",
inet_ntoa(from_addr
.sin_addr
));
stats
[S_RESPFORMERR
].cnt
++;
/* The "standard" return code */
(void) send_msg(msg
, msglen
, qp
);
n
= doaddinfo(hp
, cp
, buflen
);
(void) send_msg(newmsg
, cp
- newmsg
, qp
);
hp
= (HEADER
*)(cname
? qp
->q_cmsg
: qp
->q_msg
);
(void) send_msg((char *)hp
, (cname
? qp
->q_cmsglen
: qp
->q_msglen
), qp
);
* Decode the resource record 'rrp' and update the database.
* If savens is true, record pointer for forwarding queries a second time.
doupdate(msg
, msglen
, rrp
, zone
, savens
, flags
)
int class, type
, dlen
, n1
;
register HEADER
*hp
= (HEADER
*) msg
;
fprintf(ddt
,"doupdate(zone %d, savens %x, flags %x)\n",
if ((n
= dn_expand(msg
, msg
+ msglen
, cp
, dname
, sizeof(dname
))) < 0) {
fprintf(ddt
,"doupdate: dname %s type %d class %d ttl %d\n",
dname
, type
, class, ttl
);
* Convert the resource record data into the internal
if ((n
= dn_expand(msg
, msg
+ msglen
, cp
, data
,
if ((n
= dn_expand(msg
, msg
+ msglen
, cp
, data
,
cp1
= data
+ (n
= strlen(data
) + 1);
n1
-= 5 * sizeof(u_long
);
if ((n
= dn_expand(msg
, msg
+ msglen
, cp
, cp1
, n1
)) < 0) {
bcopy(cp
, cp1
, n
= 5 * sizeof(u_long
));
bcopy(cp
,data
,sizeof(u_short
));
cp1
= data
+ sizeof(u_short
);
if ((n
= dn_expand(msg
, msg
+ msglen
, cp
, cp1
,
sizeof(data
)-sizeof(u_short
))) < 0)
/* compute end of data */
/* compute size of data */
fprintf(ddt
,"unknown type %d\n", type
);
return ((cp
- rrp
) + dlen
);
"update type %d: %d bytes is too much data\n",
hp
->rcode
= NOCHANGE
; /* XXX - FORMERR ??? */
* If this is a dynamic update request, process it specially; else,
* execute normal update code.
/* For UPDATEM and UPDATEMA, do UPDATED/UPDATEDA followed by UPDATEA */
* The named code for UPDATED and UPDATEDA is the same except that for
* UPDATEDA we we ignore any data that was passed: we just delete all
* RRs whose name, type, and class matches
if (type
== T_SOA
) { /* Not allowed */
fprintf(ddt
, "UDPATE: REFUSED - SOA delete\n");
* Don't check message length if doing UPDATEM/UPDATEMA,
* since the whole message wont have been demarshalled until
* we reach the code for UPDATEA
if ( (opcode
== UPDATED
) || (opcode
== UPDATEDA
) ) {
if (cp
!= msg
+ msglen
) {
fprintf(ddt
,"FORMERR UPDATE message length off\n");
if ((zonenum
= findzone(dname
, class)) == 0) {
if ( (opcode
== UPDATED
) || (opcode
== UPDATEM
) ) {
/* Make a dp for use in db_update, as old dp */
dp
= savedata(class, type
, 0, cp1
, n
);
n
= db_update(dname
, dp
, NULL
, DB_MEXIST
| DB_DELETE
,
fprintf(ddt
,"UPDATE: db_update failed\n");
free( (struct databuf
*) dp
);
} else { /* UPDATEDA or UPDATEMA */
/* Make a dp for use in db_update, as old dp */
dp
= savedata(class, type
, 0, NULL
, 0);
do { /* Loop and delete all matching RR(s) */
n
= db_update(dname
, dp
, NULL
, DB_DELETE
,
free( (struct databuf
*) dp
);
/* Ok for UPDATEMA not to have deleted any RRs */
if (!DeletedOne
&& opcode
== UPDATEDA
) {
fprintf(ddt
,"UPDATE: db_update failed\n");
if ( (opcode
== UPDATED
) || (opcode
== UPDATEDA
) )
* Else unmarshal the RR to be added and continue on to
* UPDATEA code for UPDATEM/UPDATEMA
dn_expand(msg
, msg
+msglen
, cp
, dname
, sizeof(dname
))) < 0) {
fprintf(ddt
,"FORMERR UPDATE expand name failed\n");
/**** XXX - need bounds checking here ****/
fprintf(ddt
,"UPDATE: too much data\n");
if (cp
!= msg
+ msglen
) {
fprintf(ddt
,"FORMERR UPDATE message length off\n");
if ((zonenum
= findzone(dname
, class)) == 0) {
dp
= savedata(class, type
, ttl
, cp1
, n
);
if ((n
= db_update(dname
, NULL
, dp
, DB_NODATA
,
fprintf(ddt
,"UPDATE: db_update failed\n");
dp
= savedata(class, type
, ttl
, cp1
, n
);
if ((n
= db_update(dname
, dp
, dp
, flags
, hashtab
)) < 0) {
if (debug
&& (n
!= DATAEXISTS
))
fprintf(ddt
,"update failed (%d)\n", n
);
fprintf(ddt
,"update failed (DATAEXISTS)\n");
} else if (type
== T_NS
&& savens
!= NULL
)
send_msg(msg
, msglen
, qp
)
extern struct qinfo
*qhead
;
fprintf(ddt
,"send_msg -> %s (%s %d %d) id=%d\n",
inet_ntoa(qp
->q_from
.sin_addr
),
qp
->q_stream
== QSTREAM_NULL
? "UDP" : "TCP", qp
->q_dfd
,
ntohs(qp
->q_from
.sin_port
),
for (tqp
= qhead
; tqp
!=QINFO_NULL
; tqp
= tqp
->q_link
) {
fprintf(ddt
, "qp %x q_id: %d q_nsid: %d q_msglen: %d ",
tqp
, tqp
->q_id
,tqp
->q_nsid
,tqp
->q_msglen
);
fprintf(ddt
,"q_naddr: %d q_curaddr: %d\n", tqp
->q_naddr
,
fprintf(ddt
,"q_next: %x q_link: %x\n", qp
->q_next
,
if (qp
->q_stream
== QSTREAM_NULL
) {
if (sendto(qp
->q_dfd
, msg
, msglen
, 0,
(struct sockaddr
*)&qp
->q_from
, sizeof(qp
->q_from
)) < 0) {
fprintf(ddt
, "sendto error errno= %d\n",errno
);
(void) writemsg(qp
->q_stream
->s_rfd
, msg
, msglen
);
qp
->q_stream
->s_time
= tt
.tv_sec
;
qp
->q_stream
->s_refcnt
--;
register struct qinfo
*oqp
;
if (dn_expand(oqp
->q_msg
, oqp
->q_msg
+ oqp
->q_msglen
,
oqp
->q_msg
+ sizeof(HEADER
), dname
, sizeof(dname
)) < 0)
fprintf(ddt
,"prime: %s\n", dname
);
(void) sysquery(dname
, class, type
);
register struct qinfo
*qp
;
fprintf(ddt
,"prime_cache: priming = %d\n", priming
);
stats
[S_PRIMECACHE
].cnt
++;
if (!priming
&& fcachetab
->h_tab
[0] != NULL
&& !forward_only
) {
if ((qp
= sysquery("", C_IN
, T_NS
)) == NULL
)
qp
->q_system
= PRIMING_CACHE
;
sysquery(dname
, class, type
)
extern struct qinfo
*qhead
;
register struct qinfo
*qp
, *oqp
;
struct databuf
*nsp
[NSMAX
];
fprintf(ddt
,"sysquery(%s, %d, %d)\n", dname
, class, type
);
stats
[S_SYSQUERIES
].cnt
++;
if (priming
&& dname
[0] == '\0')
else if ((np
= nlookup(dname
, &htp
, &fname
, 1)) == NULL
) {
fprintf(ddt
,"sysquery: nlookup error on %s?\n", dname
);
switch (findns(&np
, class, nsp
, &count
)) {
fprintf(ddt
,"sysquery: findns error on %s?\n", dname
);
/* build new qinfo struct */
qp
->q_cmsg
= qp
->q_msg
= NULL
;
qp
->q_addr
[0].stime
= tt
;
if ((qp
->q_msg
= malloc(BUFSIZ
)) == NULL
) {
qp
->q_msglen
= res_mkquery(QUERY
, dname
, class,
type
, (char *)NULL
, 0, NULL
, qp
->q_msg
, BUFSIZ
);
hp
= (HEADER
*) qp
->q_msg
;
hp
->id
= qp
->q_nsid
= htons((u_short
)++nsid
);
hp
->rd
= (qp
->q_fwd
? 1 : 0);
/* First check for an already pending query for this data */
for (oqp
= qhead
; oqp
!=QINFO_NULL
; oqp
= oqp
->q_link
) {
if (oqp
!= qp
&& oqp
->q_msglen
== qp
->q_msglen
&&
bcmp((char *)oqp
->q_msg
+2, qp
->q_msg
+2, qp
->q_msglen
-2) == 0) {
fprintf(ddt
, "sysquery: duplicate\n");
if (nslookup(nsp
, qp
) == 0) {
fprintf(ddt
,"resp: no addrs found for NS's\n");
schedretry(qp
, retrytime(qp
));
qp
->q_addr
[0].stime
= tt
;
fprintf(ddt
,"sysquery: send -> %s %d (%d), nsid=%d id=%d %dms\n",
inet_ntoa(Q_NEXTADDR(qp
,0)->sin_addr
),
qp
->q_dfd
, ntohs(Q_NEXTADDR(qp
,0)->sin_port
),
ntohs(qp
->q_nsid
), ntohs(qp
->q_id
),
qp
->q_addr
[0].nsdata
->d_nstime
);
fp_query(qp
->q_msg
, ddt
);
if (sendto(qp
->q_dfd
, qp
->q_msg
, qp
->q_msglen
, 0,
(struct sockaddr
*)Q_NEXTADDR(qp
,0),
sizeof(struct sockaddr_in
)) < 0){
fprintf(ddt
, "sendto error errno= %d\n",errno
);
* Check the list of root servers after receiving a response
* to a query for the root servers.
register struct databuf
*dp
, *pdp
;
register struct namebuf
*np
;
for (np
= hashtab
->h_tab
[0]; np
!= NULL
; np
= np
->n_next
)
if (np
->n_dname
[0] == '\0')
syslog(LOG_ERR
, "check_root: Can't find root!\n");
for (dp
= np
->n_data
; dp
!= NULL
; dp
= dp
->d_next
)
fprintf(ddt
,"%d root servers\n", count
);
"check_root: %d root servers after query to root server < min",
if (dp
->d_type
== T_NS
&& dp
->d_zone
== 0 &&
fprintf(ddt
,"deleting old root server '%s'\n",
dp
= rm_datum(dp
, np
, pdp
);
/* SHOULD DELETE FROM HINTS ALSO */
* Check the root to make sure that for each NS record we have a A RR
register struct databuf
*dp
, *tdp
;
register struct namebuf
*np
, *tnp
;
fprintf(ddt
,"check_ns()\n");
curtime
= (u_long
) tt
.tv_sec
;
for (np
= hashtab
->h_tab
[0]; np
!= NULL
; np
= np
->n_next
) {
for (dp
= np
->n_data
; dp
!= NULL
; dp
= dp
->d_next
) {
tnp
= nlookup(dname
, &htp
, &fname
, 0);
if (tnp
== NULL
|| fname
!= dname
) {
fprintf(ddt
,"check_ns: %s: not found %s %x\n",
(void) sysquery(dname
, dp
->d_class
, T_A
);
/* look for name server addresses */
for (tdp
=tnp
->n_data
; tdp
!=NULL
; tdp
=tdp
->d_next
) {
if (tdp
->d_type
!= T_A
||
tdp
->d_class
!= dp
->d_class
)
if ((tdp
->d_zone
== 0) &&
(tdp
->d_ttl
< curtime
)) {
fprintf(ddt
,"check_ns: stale entry '%s'\n",
/* Cache invalidate the address RR's */
delete_all(tnp
, dp
->d_class
, T_A
);
(void) sysquery(dname
, dp
->d_class
, T_A
);
#define MAXCLASS 255 /* belongs elsewhere */
int norootlogged
[MAXCLASS
];
* Find NS's or an SOA for the given dname (np) and fill in the
* nsp array. Returns OK on success, and SERVFAIL on error.
* We return NXDOMAIN to indicate we are authoritative.
findns(npp
, class, nsp
, countp
)
register struct namebuf
**npp
;
register struct namebuf
*np
= *npp
;
register struct databuf
*dp
;
register struct databuf
**nspp
;
struct hashbuf
*htp
= hashtab
;
if (priming
&& (np
== NULL
|| np
->n_dname
[0] == '\0'))
while (np
== NULL
&& htp
!= NULL
) {
fprintf(ddt
, "findns: using %s\n", htp
== hashtab
?
for (np
= htp
->h_tab
[0]; np
!= NULL
; np
= np
->n_next
)
if (np
->n_dname
[0] == '\0')
htp
= (htp
== hashtab
? fcachetab
: NULL
); /* Fallback */
fprintf(ddt
, "findns: np 0x%x\n", np
);
/* Look first for SOA records. */
for (dp
= np
->n_data
; dp
!= NULL
; dp
= dp
->d_next
) {
if (dp
->d_zone
!= 0 && match(dp
, class, T_SOA
)) {
fprintf(ddt
,"findns: SOA found\n");
if (zones
[dp
->d_zone
].z_auth
) {
/* If no SOA records, look for NS records. */
for (dp
= np
->n_data
; dp
!= NULL
; dp
= dp
->d_next
) {
if (dp
->d_type
!= T_NS
||
(dp
->d_class
!= class && class != C_ANY
))
* Don't use records that may become invalid to
* reference later when we do the rtt computation.
* Never delete our safety-belt information!
(dp
->d_ttl
< (tt
.tv_sec
+900)) &&
!(dp
->d_flags
& DB_F_HINT
)) {
fprintf(ddt
,"findns: stale entry '%s'\n",
/* Cache invalidate the NS RR's */
if (dp
->d_ttl
< tt
.tv_sec
)
delete_all(np
, class, T_NS
);
if (nspp
< &nsp
[NSMAX
-1])
fprintf(ddt
,"findns: %d NS's added for '%s'\n",
return(OK
); /* Success, got some NS's */
fprintf(ddt
, "findns: No root nameservers for class %d?\n",
if ((unsigned)class < MAXCLASS
&& norootlogged
[class] == 0) {
syslog(LOG_ERR
, "No root nameservers for class %d\n", class);
* 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.
finddata(np
, class, type
, hp
, dnamep
, lenp
, countp
)
register struct databuf
*dp
;
int buflen
, n
, count
= 0, foundstale
= 0;
cp
= ((char *)hp
) + *countp
;
for (dp
= np
->n_data
; dp
!= NULL
; dp
= dp
->d_next
) {
if (!wanted(dp
, class, type
)) {
if (type
== T_CNAME
&& class == dp
->d_class
) {
/* any data means no CNAME exists */
* Would like to call delete_all here
* and continue, but the data chain would get
* munged; can't restart, as make_rr has side
* effects (leaving pointers in dnptr).
* Just skip this entry for now
* and call delete_all at the end.
fprintf(ddt
,"finddata: stale entry '%s'\n",np
->n_dname
);
if ((n
= make_rr(*dnamep
, dp
, cp
, buflen
, 1)) < 0) {
/* this isn't right for glue records, aa is set in ns_req */
if (dp
->d_zone
&& zones
[dp
->d_zone
].z_auth
&& class != C_ANY
)
if (dp
->d_type
== T_CNAME
) {
if (type
!= T_ANY
) { /* or T_NS? */
if (dp
->d_zone
&& zones
[dp
->d_zone
].z_auth
&&
class != C_ANY
) /* XXX */
* Cache invalidate the other RR's of same type
delete_all(np
, class, type
);
fprintf(ddt
,"finddata: added %d class %d type %d RRs\n",
* Do we want this data record based on the class and type?
fprintf(ddt
,"wanted(%x, %d, %d) %d, %d\n", dp
, class, type
,
dp
->d_class
, dp
->d_type
);
if (dp
->d_class
!= class && class != C_ANY
)
* Add RR entries from dpp array to a query/response.
* Return the number of bytes added or negative the amount
* added if truncation was required. Typically you are
* adding NS records to a response.
add_data(np
, dpp
, cp
, buflen
)
register struct databuf
*dp
;
register int n
, count
= 0;
getname(np
, dname
, sizeof(dname
));
for(dp
= *dpp
++; dp
!= NULL
; dp
= *dpp
++) {
continue; /* ignore old cache entry */
if ((n
= make_rr(dname
, dp
, cp
, buflen
, 1)) < 0)
return(-count
); /* Truncation */
* This is best thought of as a "cache invalidate" function.
* It is called whenever a piece of data is determined to have
* timed out. It is better to have no information, than to
* have partial information you pass off as complete.
delete_all(np
, class, type
)
register struct namebuf
*np
;
register struct databuf
*dp
, *pdp
;
fprintf(ddt
,"delete_all: '%s' 0x%x class %d type %d\n",
np
->n_dname
, np
, class, type
);
if ((dp
->d_zone
== 0) && !(dp
->d_flags
& DB_F_HINT
)
&& match(dp
, class, type
)) {
dp
= rm_datum(dp
, np
, pdp
);