* Copyright (c) 1986 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_forw.c 4.27 (Berkeley) 6/18/88";
#include <arpa/nameser.h>
struct qinfo
*qhead
= QINFO_NULL
; /* head of allocated queries */
struct qinfo
*retryqp
= QINFO_NULL
; /* list of queries to retry */
struct fwdinfo
*fwdtab
; /* list of forwarding hosts */
int nsid
; /* next forwarded query id */
extern int forward_only
; /* you are only a slave */
* Forward the query to get the answer since its not in the database.
* Returns FW_OK if a request struct is allocated and the query sent.
* Returns FW_DUP if this is a duplicate of a pending request.
* Returns FW_NOSERVER if there were no addresses for the nameservers.
* Returns FW_SERVFAIL on malloc error.
* (no action is taken on errors and qpp is not filled in.)
ns_forw(nsp
, msg
, msglen
, fp
, qsp
, dfd
, qpp
)
register struct qinfo
*qp
;
fprintf(ddt
,"ns_forw()\n");
/* Don't forward if we're already working on it. */
for (qp
= qhead
; qp
!=QINFO_NULL
; qp
= qp
->q_link
) {
bcmp((char *)&qp
->q_from
, fp
, sizeof(qp
->q_from
)) == 0 &&
(qp
->q_cmsglen
== 0 && qp
->q_msglen
== msglen
&&
bcmp((char *)qp
->q_msg
+2, msg
+2, msglen
-2) == 0) ||
(qp
->q_cmsglen
== msglen
&&
bcmp((char *)qp
->q_cmsg
+2, msg
+2, msglen
-2) == 0)) {
fprintf(ddt
,"forw: dropped DUP id=%d\n", ntohs(id
));
stats
[S_DUPQUERIES
].cnt
++;
if (nslookup(nsp
, qp
) == 0) {
fprintf(ddt
,"forw: no nameservers found\n");
hp
->id
= qp
->q_nsid
= htons((u_short
)++nsid
);
qp
->q_addr
[0].stime
= tt
;
if ((qp
->q_msg
= malloc((unsigned)msglen
)) == NULL
) {
syslog(LOG_ERR
, "forw: %m");
bcopy(msg
, qp
->q_msg
, qp
->q_msglen
= msglen
);
schedretry(qp
, retrytime(qp
));
"forw: forw -> %s %d (%d) nsid=%d id=%d %dms retry %d sec\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
, msg
, msglen
, 0, (struct sockaddr
*)Q_NEXTADDR(qp
,0),
sizeof(struct sockaddr_in
)) < 0){
fprintf(ddt
,"error returning msg errno=%d\n",errno
);
* Lookup the address for each nameserver in `nsp' and add it to
* the list saved in the qinfo structure.
register struct qinfo
*qp
;
register struct namebuf
*np
;
register struct databuf
*dp
, *nsdp
;
register struct qserv
*qs
;
int oldn
, naddr
, class, found_arr
;
fprintf(ddt
,"nslookup(nsp=x%x,qp=x%x)\n",nsp
,qp
);
curtime
= (u_long
) tt
.tv_sec
;
while ((nsdp
= *nsp
++) != NULL
) {
fprintf(ddt
,"nslookup: NS %s c%d t%d (x%x)\n",
dname
, class, nsdp
->d_type
, nsdp
->d_flags
);
/* don't put in people we have tried */
for (i
= 0; i
< qp
->q_nusedns
; i
++)
if (qp
->q_usedns
[i
] == nsdp
) {
fprintf(ddt
, "skipping used NS w/name %s\n", nsdp
->d_data
);
tmphtp
= ((nsdp
->d_flags
& DB_F_HINT
) ? fcachetab
: hashtab
);
np
= nlookup(dname
, &tmphtp
, &fname
, 1);
if (np
== NULL
|| fname
!= dname
) {
fprintf(ddt
,"%s: not found %s %x\n",dname
,fname
,np
);
/* look for name server addresses */
for (dp
= np
->n_data
; dp
!= NULL
; dp
= dp
->d_next
) {
if (dp
->d_type
!= T_A
|| dp
->d_class
!= class)
* 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
< (curtime
+900)) &&
!(dp
->d_flags
& DB_F_HINT
) )
fprintf(ddt
,"nslookup: stale entry '%s'\n",
/* Cache invalidate the NS RR's */
delete_all(np
, class, T_A
);
/* don't put in duplicates */
for (i
= 0; i
< n
; i
++, qs
++)
if (bcmp((char *)&qs
->ns_addr
.sin_addr
,
dp
->d_data
, sizeof(struct in_addr
)) == 0)
qs
->ns_addr
.sin_family
= AF_INET
;
qs
->ns_addr
.sin_port
= (u_short
)ns_port
;
*(struct in_addr
*)dp
->d_data
;
qp
->q_addr
[n
].nretry
= 0;
fprintf(ddt
,"nslookup: %d ns addrs\n", n
);
if (found_arr
== 0 && qp
->q_system
== 0)
(void) sysquery(dname
, class, T_A
);
fprintf(ddt
,"nslookup: %d ns addrs total\n", n
);
qsort((char *)qp
->q_addr
, n
, sizeof(struct qserv
), qcomp
);
return (qs1
->nsdata
->d_nstime
- qs2
->nsdata
->d_nstime
);
* Arrange that forwarded query (qp) is retried after t seconds.
register struct qinfo
*qp1
, *qp2
;
fprintf(ddt
,"schedretry(%#x, %dsec)\n", qp
, t
);
fprintf(ddt
,"WARNING: schedretry(%x,%d) q_time already %d\n", qp
->q_time
);
if ((qp1
= retryqp
) == NULL
) {
while ((qp2
= qp1
->q_next
) != NULL
&& qp2
->q_time
< t
)
* Unsched is called to remove a forwarded query entry.
register struct qinfo
*np
;
fprintf(ddt
,"unsched(%#x, %d )\n", qp
, ntohs(qp
->q_id
));
for( np
=retryqp
; np
->q_next
!= QINFO_NULL
; np
= np
->q_next
) {
np
->q_next
= qp
->q_next
; /* dequeue */
qp
->q_next
= QINFO_NULL
; /* sanity check */
* Retry is called to retransmit query 'qp'.
register struct qinfo
*qp
;
fprintf(ddt
,"retry(x%x) id=%d\n", qp
, ntohs(qp
->q_id
));
if((HEADER
*)qp
->q_msg
== NULL
) { /*** XXX ***/
qp
->q_fwd
= qp
->q_fwd
->next
;
/* out of forwarders, try direct queries */
if (qp
->q_addr
[n
].nretry
< MAXRETRY
)
} while (n
!= qp
->q_curaddr
);
* Give up. Can't reach destination.
hp
= (HEADER
*)(qp
->q_cmsg
? qp
->q_cmsg
: qp
->q_msg
);
if (qp
->q_system
== PRIMING_CACHE
) {
/* Can't give up priming */
schedretry(qp
, (time_t)60*60); /* 1 hour */
hp
->rcode
= NOERROR
; /* Lets be safe, reset the query */
for (n
= 0; n
< qp
->q_naddr
; n
++)
qp
->q_addr
[n
].nretry
= 0;
fprintf(ddt
,"give up\n");
n
= ((HEADER
*)qp
->q_cmsg
? qp
->q_cmsglen
: qp
->q_msglen
);
fp_query(qp
->q_msg
, ddt
);
if (send_msg((char *)hp
, n
, qp
)) {
fprintf(ddt
,"gave up retry(x%x) nsid=%d id=%d\n",
qp
, ntohs(qp
->q_nsid
), ntohs(qp
->q_id
));
if (qp
->q_fwd
== 0 && qp
->q_addr
[n
].nretry
== 0)
qp
->q_addr
[n
].stime
= tt
;
hp
= (HEADER
*)qp
->q_msg
;
hp
->rd
= (qp
->q_fwd
? 1 : 0);
fprintf(ddt
,"%s(addr=%d n=%d) -> %s %d (%d) nsid=%d id=%d %dms\n",
(qp
->q_fwd
? "reforw" : "resend"),
inet_ntoa(Q_NEXTADDR(qp
,n
)->sin_addr
),
ds
, ntohs(Q_NEXTADDR(qp
,n
)->sin_port
),
ntohs(qp
->q_nsid
), ntohs(qp
->q_id
),
qp
->q_addr
[n
].nsdata
->d_nstime
);
fp_query(qp
->q_msg
, ddt
);
if (sendto(ds
, qp
->q_msg
, qp
->q_msglen
, 0,
(struct sockaddr
*)Q_NEXTADDR(qp
,n
),
sizeof(struct sockaddr_in
)) < 0){
fprintf(ddt
,"error resending msg errno=%d\n",errno
);
hp
->rd
= 0; /* leave set to 0 for dup detection */
schedretry(qp
, qp
->q_fwd
? (2*RETRYBASE
) : retrytime(qp
));
* Compute retry time for the next server for a query.
* Use a minimum time of RETRYBASE (4 sec.) or twice the estimated
* service time; * back off exponentially on retries, but place a 45-sec.
* ceiling on retry times for now. (This is because we don't hold a reference
* on servers or their addresses, and we have to finish before they time out.)
register struct qinfo
*qp
;
struct qserv
*ns
= &qp
->q_addr
[qp
->q_curaddr
];
fprintf(ddt
,"retrytime: nstime %dms.\n",
ns
->nsdata
->d_nstime
/ 1000);
t
= (time_t) MAX(RETRYBASE
, 2 * ns
->nsdata
->d_nstime
/ 1000);
t
= MIN(t
, 45); /* max. retry timeout for now */
return ((2 * t
) + 5); /* system queries can wait. */
register struct qinfo
*qp
;
fprintf(ddt
,"qremove(x%x)\n", qp
);
unsched(qp
); /* get off queue first */
register struct qinfo
*qp
;
fprintf(ddt
,"qfindid(%d)\n", ntohs(id
));
for (qp
= qhead
; qp
!=QINFO_NULL
; qp
= qp
->q_link
) {
fprintf(ddt
,"qp not found\n");
register struct qinfo
*qp
;
if ((qp
= (struct qinfo
*)calloc(1, sizeof(struct qinfo
))) == NULL
) {
fprintf(ddt
,"qnew: calloc error\n");
syslog(LOG_ERR
, "forw: %m");
fprintf(ddt
,"qnew(x%x)\n", qp
);
register struct qinfo
*np
;
fprintf(ddt
,"qfree( x%x )\n", qp
);
fprintf(ddt
,"WARNING: qfree of linked ptr x%x\n", qp
);
for( np
=qhead
; np
->q_link
!= QINFO_NULL
; np
= np
->q_link
) {
if( np
->q_link
!= qp
) continue;
np
->q_link
= qp
->q_link
; /* dequeue */