/**************************************************************************
* ns_validate.c (was security.c in original ISI contribution)
* contributed: March 17, 1993
* implements validation procedure for RR's received from a server as a
#include <arpa/nameser.h>
static int isvalid
__P((struct namebuf
*, int, int, char *, int)),
check_addr_ns
__P((struct databuf
**,
check_in_tables
__P((struct databuf
**,
static void stick_in_queue
__P((char *, int, int, char *));
static NAMEADDR nameaddrlist
[MAXNAMECACHE
];
static TO_Validate
*validateQ
, *currentVQ
;
/*****************************************************************
* validate() is called from dovalidate(). it takes as parameters,
* the domain name sought, the class, type etc. of record, the server
* that gave us the answer and the data it gave us
* it returns VALID if it is able to validate the record, INVALID if it cannot.
* furtehr VALID is split into VALID_CACHE if we need to cache this record
* since the domainname is not something we are authoritative for and
* VALID_NO_CACHE if the name is something we are authoritative for.
* pseudocode for function validate is as follows:
* validate(domain, server, type, class, data, dlen, rcode) {
* if(dname or a higher level name not found in cache)
* if (NS records for "domain" found in cache){
* if(we are authoritative) /findns() returned NXDOMAIN;/
* if(we did not have an exact match on names)
* =>the name does not exist in our database
* => data is bad: return INVALID
* if(data agrees with what we have)
* if(we are not authoritative) /findns() returned OK;/
* if (address records for NS's found in cache){
* if("server" = one of the addresses){
* stick in queue of "to_validate" data;
* This performs the validation procedure described above. Checks
* for the longest component of the dname that has a NS record
* associated with it. At any stage, if no data is found, it implies
* that the name is bad (has an unknown domain identifier) thus, we
* If address of one of these servers matches the address of the server
* that returned us this data, we are happy!
* since findns will set needs_prime_cache if np = NULL is passed, we always
* reset it. will let ns_req do it when we are searching for ns records to
* query someone. hence in all the three cases of switch(findns())
* we have needs_prime_cache = 0;
*****************************************************************************/
validate(dname
, server
, type
, class, data
, dlen
struct sockaddr_in
*server
;
struct namebuf
*np
, *dnamep
;
struct databuf
*nsp
[NSMAX
];
"validate(), d:%s, s:[%s], t:%d, c:%d\n",
dname
, inet_ntoa(server
->sin_addr
), type
, class));
/* everything from forwarders is the GOSPEL */
for (fwd
= fwdtab
; fwd
!= NULL
; fwd
= fwd
->next
) {
if (server
->sin_addr
.s_addr
== fwd
->fwdaddr
.sin_addr
.s_addr
)
if (priming
&& (dname
[0] == '\0'))
np
= nlookup(dname
, &htp
, &fname
, 0);
/* we were able to locate namebufs for this domain, or a parent domain,
"validate:namebuf found np:0x%x, d:\"%s\", f:\"%s\"\n",
/* save the namebuf if we were able to locate the exact dname */
if (!strcasecmp(dname
, fname
)) {
if (np
== NULL
&& fname
!= NULL
) {
switch (findns(&np
, class, nsp
, &count
, 0)) {
/** we are authoritative for this domain, lookup name
* in our zone data, if it matches, return valid.
* in either case, do not cache
dprintf(5, (ddt
, "validate: auth data found\n"));
/* If we had an exactmatch on the name, we found the
* name in our authority database, so this couldn't
* have been a bad name. INVALID data, say so
/* we did not have an exactmatch, the data is
* good, we do not NCACHE stuff we are
* authoritative for, though.
if (!strcasecmp(dname
, np
->n_dname
)) {
/* if the name we seek is the same as that we have ns
* records for, compare the data we have to see if it
* matches. if it does, return valid_no_cache, if it
if (isvalid(np
, type
, class, data
, dlen
))
/* we found ns records in a higher level, if we were unable to
* locate the exact name earlier, it means we are
* authoritative for this domain but do not have records for
* this name. this name is obviously invalid
/* we found the exact name earlier and we are obviously
* authoritative so check for data records and see if any
if (isvalid(dnamep
, type
, class, data
, dlen
))
case SERVFAIL
:/* could not find name server records*/
/* stick_in_queue(dname, type, class, data); */
"validate:found ns records:calling check_addr_ns\n"));
if (check_addr_ns(nsp
, server
, dname
)) {
/* server is not one of those we know of */
/* stick_in_queue(dname, type, class, data); */
/***********************************************************************
* validate rr returned by somebody against your own database, if you are
* authoritative for the information. if you have a record that matches,
* return 1, else return 0. validate() above will use this and determine
* if the record should be returned/discarded.
***********************************************************************/
isvalid(np
, type
, class, data
, dlen
)
register struct databuf
*dp
;
for (dp
= np
->n_data
; dp
!= NULL
; dp
= dp
->d_next
) {
if (!wanted(dp
, class, type
)) {
if ((type
== T_CNAME
) && (class == dp
->d_class
)) {
/* if a cname exists, any other will not */
/* we come here only for zone info,
* so -ve $ed info can't be
/* type and class match, if i get here
* let's now compare the data section, per RR type
/* unless, of course, the data was negative, in which case
* we should return FAILURE since we should not have found
if ((data
== NULL
) || (dlen
== 0)) {
x
= memcmp(dp
->d_data
, data
, dlen
);
dprintf(3, (ddt
, "type = %d, GOOD = %d\n",
x
= strncasecmp((char *)dp
->d_data
, data
, dlen
);
dprintf(3, (ddt
, "type = %d, GOOD = %d\n",
/* compare first string */
x
= strncasecmp((char *)dp
->d_data
, data
,
/* move to second string */
td
= data
+ (strlen(data
) + 1);
(strlen((char *)dp
->d_data
)+1);
/* compare second string */
x
= strncasecmp(td
, (char *)tdp
,
/* move beyond second string, to
* RP and MINFO stuff really
td
= td
+ strlen((char *)td
) + 1;
tdp
= tdp
+ strlen((char *)tdp
) + 1;
x
= memcmp(td
, (char *)tdp
,
/* everything was equal, wow!
x
= memcmp(dp
->d_data
, data
,
td
= data
+ sizeof(u_int16_t
);
tdp
= dp
->d_data
+ sizeof(u_int16_t
);
x
= strncasecmp(td
, (char *)tdp
,
dprintf(3, (ddt
, "unknown type %d\n", type
));
/* continue in loop if record did not match */
/* saw no record of interest in whole chain
* If the data we were trying to validate was negative, we succeeded!
if ((data
== NULL
) || (dlen
== 0)) {
/* negative data, report success */
/* positive data, no such RR, validation failed */
/******************************************************************
* get a list of databufs that have ns addresses for the closest domain
* you know about, get their addresses and confirm that server indeed
* is one of them. if yes return 1 else 0.
* first checks the cache that we build in nslookup() earlier
* when we ns_forw(). if unableto find it there, it checks the entire
* hash table to do address translations.
*******************************************************************/
check_addr_ns(nsp
, server
, dname
)
struct sockaddr_in
*server
;
struct in_addr
*saddr
= &(server
->sin_addr
);
"check_addr_ns: s:[%s], db:0x%x, d:\"%s\"\n",
inet_ntoa(*saddr
), nsp
, dname
));
for(i
= lastNA
; i
!= firstNA
; i
= (i
+1) % MAXNAMECACHE
) {
(char *)&(nameaddrlist
[i
].ns_addr
),
sizeof(struct in_addr
))) {
strcpy(sname
, nameaddrlist
[i
].nsname
);
"check_addr_ns: found address:[%s]\n",
for (nsdp
= nsp
; *nsdp
!= NULL
;nsdp
++) {
"check_addr_ns:names are:%s, %s\n",
if (!strcasecmp(sname
,(char *)((*nsdp
)->d_data
))) {
/* syslog(LOG_ERR,"check_addr_ns: %s != %s? %s",
inet_ntoa(*saddr), sname, *dname?dname:"'.'"); */
/* could not find name in my cache of servers, must go through the
dprintf(2, (ddt
, "check_addr_ns:calling check_in_tables()\n"));
return (check_in_tables(nsp
, server
, dname
));
/*************************************************************************
* checks in hash tables for the address of servers whose name is in the
* data section of nsp records. borrows code from nslookup()/ns_forw.c
*************************************************************************/
check_in_tables(nsp
, server
, syslogdname
)
struct sockaddr_in
*server
;
register struct namebuf
*np
;
register struct databuf
*dp
, *nsdp
;
dprintf(3, (ddt
, "check_in_tables(nsp=x%x,qp=x%x,'%s')\n",
nsp
, server
, syslogdname
));
while ((nsdp
= *nsp
++) != NULL
) {
dname
= (char *)nsdp
->d_data
;
dprintf(3, (ddt
, "check_in_tables: NS %s c%d t%d (x%x)\n",
dname
, class, nsdp
->d_type
, nsdp
->d_flags
));
tmphtp
= ((nsdp
->d_flags
& DB_F_HINT
) ? fcachetab
: hashtab
);
np
= nlookup(dname
, &tmphtp
, &fname
, 1);
if (np
== NULL
|| fname
!= dname
) {
dprintf(3, (ddt
, "%s: not found %s %x\n",
/* 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)
if (!bcmp((char *)dp
->d_data
,
(char *)&(server
->sin_addr
),
sizeof(struct in_addr
))) {
return (0); /* haven't been able to locate the right address */
/************************************************************************
* is called in nslookup() and stores the name vs address of a name server
* --& check_in_tables above--
* we contact, in a list of a maximum MAXNAMECACHE entries. we later refer
* -- NAMEADDR nameaddrlist[MAXNAMECACHE]; --
* to this list when we are trying to resolve the name in check_addr_ns().
*************************************************************************/
store_name_addr(servername
, serveraddr
, syslogdname
, sysloginfo
)
struct in_addr
*serveraddr
;
"store_name_addr:s:%s, a:[%s]\n",
servername
, inet_ntoa(*serveraddr
)));
/* if we already have the name address pair in cache, return */
for(i
= lastNA
; i
!= firstNA
; i
= (i
+1) % MAXNAMECACHE
) {
if (strcasecmp(servername
, nameaddrlist
[i
].nsname
) == 0) {
nameaddrlist
[i
].ns_addr
.s_addr
) {
"store_name_addr:found n and a [%s] [%s] in our $\n",
inet_ntoa(nameaddrlist
[i
].ns_addr
),
inet_ntoa(*serveraddr
)));
} else if (serveraddr
->s_addr
nameaddrlist
[i
].ns_addr
.s_addr
) {
* log this as it needs to be fixed.
* replace old name by new, next query likely to have
(char*)dhash((u_char
*)nameaddrlist
[i
].nsname
,
strlen(nameaddrlist
[i
].nsname
)
(char*)dhash((u_char
*)servername
,
"%s: server name mismatch for [%s]: (%s != %s) (server for %s)",
nameaddrlist
[i
].nsname
, servername
,
free(nameaddrlist
[i
].nsname
);
malloc((unsigned)strlen(servername
)+1);
strcpy(nameaddrlist
[i
].nsname
, servername
);
/* we have to add this one to our cache */
nameaddrlist
[firstNA
].nsname
= malloc((unsigned)strlen(servername
)+1);
strcpy(nameaddrlist
[firstNA
].nsname
, servername
);
bcopy((char *)serveraddr
,
(char *)&(nameaddrlist
[firstNA
].ns_addr
),
dprintf(2, (ddt
, "store_name_addr:added entry #:%d n:%s a:[%s]\n",
firstNA
, nameaddrlist
[firstNA
].nsname
,
inet_ntoa(nameaddrlist
[firstNA
].ns_addr
)));
firstNA
= (firstNA
+1) % MAXNAMECACHE
;
free(nameaddrlist
[firstNA
].nsname
);
nameaddrlist
[firstNA
].nsname
= 0;
lastNA
= (lastNA
+1) % MAXNAMECACHE
;
* Decode the resource record 'rrp' and validate the RR.
* Borrows code almost entirely from doupdate(). is a rather
* non-invasive routine since it just goes thru the same motions
* as doupdate but just marks the array validatelist entry as
* the return code from validate(). This is later used in doupdate
* to cache/not cache the entry. also used in update_msg() to
* delete/keep the record from the outgoing message.
dovalidate(msg
, msglen
, rrp
, zone
, flags
, server
, VCode
)
struct sockaddr_in
*server
;
int class, type
, dlen
, n1
;
register HEADER
*hp
= (HEADER
*) msg
;
dprintf(2, (ddt
, "dovalidate(zone %d, flags %x)\n",
fp_query((char *)msg
,ddt
);
if ((n
= dn_expand((u_char
*)msg
, (u_char
*)msg
+ msglen
, cp
,
(u_char
*)dname
, sizeof(dname
))) < 0) {
dprintf(2, (ddt
, "dovalidate: 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((u_char
*)msg
, (u_char
*)msg
+ msglen
,
cp
, data
, sizeof(data
))) < 0) {
n
= strlen((char *)data
) + 1;
if ((n
= dn_expand((u_char
*)msg
, (u_char
*)msg
+ msglen
,
cp
, data
, sizeof(data
))) < 0) {
cp1
= data
+ (n
= strlen((char *)data
) + 1);
n1
-= 5 * sizeof(u_int32_t
);
if ((n
= dn_expand((u_char
*)msg
, (u_char
*)msg
+ msglen
,
cp1
+= strlen((char *)cp1
) + 1;
bcopy((char *)cp
, (char *)cp1
, n
= 5 * sizeof(u_int32_t
));
bcopy((char *)cp
, data
, sizeof(u_int16_t
));
cp1
= data
+ sizeof(u_int16_t
);
if ((n
= dn_expand((u_char
*)msg
, (u_char
*)msg
+ msglen
,
cp
, cp1
, sizeof(data
) - sizeof(u_int16_t
)))
/* compute end of data */
cp1
+= strlen((char *)cp1
) + 1;
/* compute size of data */
dprintf(3, (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 ??? */
*VCode
= validate(dname
, server
, type
, class,(char *)cp1
, n
"validation failed d:%s, t:%d, c:%d\n",
"validation succeeded d:%s, t:%d, c:%d\n",
/******************************************************************
* This manages a data structure that stores all RRs that we were
* unable to validate. Am not sure exactly what purpose this might
* serve but until such time as we are sure it will not help, let
*****************************************************************/
stick_in_queue(dname
, type
, class, data
)
validateQ
= (TO_Validate
*)malloc(sizeof(TO_Validate
));
validateQ
->class = class;
validateQ
->dname
= malloc((unsigned)strlen(dname
)+1);
strcpy(validateQ
->dname
, dname
);
validateQ
->data
= malloc((unsigned)strlen(data
)+1);
strcpy(validateQ
->data
, data
);
validateQ
->time
= tp
.tv_sec
;
validateQ
->next
= validateQ
->prev
= NULL
;
tempVQ
=(TO_Validate
*)malloc(sizeof(TO_Validate
));
tempVQ
->dname
= malloc((unsigned)strlen(dname
)+1);
strcpy(tempVQ
->dname
, dname
);
tempVQ
->data
= malloc((unsigned)strlen(data
)+1);
strcpy(tempVQ
->data
, data
);
tempVQ
->time
= tp
.tv_sec
;
tempVQ
->next
= currentVQ
->next
;
tempVQ
->prev
= currentVQ
;
if (currentVQ
->next
!= NULL
)
currentVQ
->next
->prev
= tempVQ
;
currentVQ
->next
= tempVQ
;
leasttime
= validateQ
->time
;
for (tempVQ
= validateQ
; tempVQ
!= NULL
; tempVQ
= tempVQ
->next
) {
if (tp
.tv_sec
>= tempVQ
->time
+VQEXPIRY
) {
strcpy(tempVQ
->dname
, dname
);
strcpy(tempVQ
->data
, data
);
tempVQ
->time
= tp
.tv_sec
;
if (tempVQ
->time
< leasttime
) {
leasttime
= tempVQ
->time
;
currentVQ
->class = class;
strcpy(currentVQ
->dname
, dname
);
strcpy(currentVQ
->data
, data
);
currentVQ
->time
= tp
.tv_sec
;
/* removes any INVALID RR's from the msg being returned, updates msglen to
* reflect the new message length.
update_msg(msg
, msglen
, Vlist
, c
)
int qlen
; /* the length of the query section*/
u_int16_t ancount
, nscount
;
u_int16_t new_ancount
, new_nscount
, new_arcount
;
char dname
[MAXDNAME
], qname
[MAXDNAME
];
u_char
**edp
= dnptrs
+ sizeof(dnptrs
)/sizeof(dnptrs
[0]);
u_char
*eom
= msg
+ *msglen
;
u_int16_t type
, class, dlen
;
fprintf(ddt
, "update_msg: msglen:%d, c:%d\n", *msglen
, c
);
fp_query((char *)msg
,ddt
);
/* just making sure we do not do all the work for nothing */
if (Vlist
[i
] == INVALID
) {
/* no invalid records, go about your job */
dprintf(2, (ddt
, "update_msg: NEEDS updating:\n"));
RRlen
= (int *)malloc((unsigned)c
*sizeof(int));
new_ancount
= ancount
= ntohs(hp
->ancount
);
new_nscount
= nscount
= ntohs(hp
->nscount
);
new_arcount
= ntohs(hp
->nscount
);
cp
= msg
+ sizeof(HEADER
);
/* skip the query section */
qlen
= dn_expand(msg
, eom
, cp
, (u_char
*)qname
, sizeof(qname
));
dprintf(2, (ddt
, "dn_skipname() failed, bad record\n"));
qlen
+= 2 * sizeof(u_int16_t
);
for (i
= 0; i
< c
; i
++) {
if (Vlist
[i
] == INVALID
) {
else if (i
< ancount
+nscount
)
RRlen
[i
] = dn_skipname(cp
, msg
+ *msglen
);
"dn_skipname() failed, bad record\n"));
RRlen
[i
] += 2 * sizeof(u_int16_t
) + sizeof(u_int32_t
);
RRlen
[i
] += sizeof(u_int16_t
); /*rdlength*/
RRlen
[i
] += rdlength
; /*rdata field*/
dprintf(3, (ddt
, "RRlen[%d]=%d\n", i
, RRlen
[i
]));
cp
+= rdlength
; /*increment pointer to next RR*/
hp
->ancount
= htons(new_ancount
);
hp
->nscount
= htons(new_nscount
);
hp
->arcount
= htons(new_nscount
);
"newlen:%d, if no RR is INVALID == msglen\n", newlen
));
newmsg
= (u_char
*)calloc(1,newlen
+ MAXDNAME
);
/* bcopy the header, with all the length fields correctly put in */
bcopy((char *)msg
, (char*)newmsg
, sizeof(HEADER
)); /*header copied */
newcp
= newmsg
+sizeof(HEADER
); /*need a pointer in the new buffer */
rembuflen
= newlen
+MAXDNAME
- sizeof(HEADER
); /*buflen we can workin*/
newlen
= sizeof(HEADER
); /* this will now contain the length of msg */
n_new
= dn_comp((u_char
*)qname
, (u_char
*)newcp
, rembuflen
,
(u_char
**)dnptrs
, (u_char
**)edp
);
PUTSHORT(class, newcp
); /*query section complete*/
newlen
+= (n_new
+2*sizeof(u_int16_t
));
rembuflen
-= (n_new
+2*sizeof(u_int16_t
));
/* have to decode and copy every Valid RR from here */
cp
= msg
+sizeof(HEADER
) +qlen
; /*skip header and query section*/
for (i
= 0; i
< c
; i
++) {
if (Vlist
[i
] == INVALID
) {
/* go to next RR if this one is not INVALID */
/* we have a valid record, must put it in the newmsg */
if ((n
= dn_expand((u_char
*)msg
, eom
, cp
,
(u_char
*)dname
, sizeof(dname
))) < 0) {
n_new
= dn_comp((u_char
*)dname
, (u_char
*)newcp
, rembuflen
,
(u_char
**)dnptrs
, (u_char
**)edp
);
"cp:0x%x newcp:0x%x after getting name\n",
"cp:0x%x newcp:0x%x after getting type\n",
"cp:0x%x newcp:0x%x after getting class\n",
"cp:0x%x newcp:0x%x after getting ttl\n",
/* this will probably be modified for newmsg,
* will put this in later, after compression
newlen
+= (n_new
+3*sizeof(u_int16_t
) + sizeof(u_int32_t
));
rembuflen
-= (n_new
+3*sizeof(u_int16_t
)+ sizeof(u_int32_t
));
newcp
+= sizeof(u_int16_t
); /*advance to rdata field*/
dprintf(5, (ddt
, "tempcp:0x%x newcp:0x%x\n",
"update_msg: dname %s type %d class %d ttl %d\n",
dname
, type
, class, ttl
));
/* read off the data section */
PUTSHORT(n
, tempcp
); /*time to put in the dlen*/
bcopy(cp
, newcp
,n
); /*done here*/
dprintf(3, (ddt
, "\tcp:0x%x newcp:0x%x dlen:%d\n",
/*read off name from data section */
if ((n
= dn_expand((u_char
*)msg
, eom
,
cp
, data
, sizeof(data
))) < 0) {
cp
+= n
; /*advance pointer*/
n_new
= dn_comp((u_char
*)data
, (u_char
*)newcp
,
(u_char
**)dnptrs
, (u_char
**)edp
);
PUTSHORT(n_new
,tempcp
); /*put in dlen field*/
newcp
+= n_new
; /*advance new pointer*/
if ((n
= dn_expand((u_char
*)msg
, eom
, cp
,
data
, sizeof(data
))) < 0) {
n_new
= dn_comp((u_char
*)data
, (u_char
*)newcp
,
(u_char
**)dnptrs
, (u_char
**)edp
);
if ((n
= dn_expand((u_char
*)msg
, eom
, cp
,
data
, sizeof(data
))) < 0) {
n_new
= dn_comp((u_char
*)data
, (u_char
*)newcp
,
(u_char
**)dnptrs
, (u_char
**)edp
);
bcopy(cp
, newcp
, n
= 5*sizeof(u_int32_t
));
bcopy(cp
,newcp
,sizeof(u_int16_t
));
newcp
+= sizeof(u_int16_t
);
if ((n
= dn_expand((u_char
*)msg
, eom
, cp
,
data
, sizeof(data
))) < 0) {
n_new
= dn_comp((u_char
*)data
, (u_char
*)newcp
,
(u_char
**)dnptrs
, (u_char
**)edp
);
PUTSHORT(n_new
+sizeof(u_int16_t
), tempcp
);
newlen
+= n_new
+sizeof(u_int16_t
);
rembuflen
-= n_new
+sizeof(u_int16_t
);
dprintf(3, (ddt
, "unknown type %d\n", type
));
"newlen:%d, i:%d newcp:0x%x cp:0x%x\n\n",
bcopy(newmsg
, msg
, newlen
);
"update_msg():newmsg longer than old: n:%d o:%d ???\n",
fp_query((char *)msg
, ddt
);
dprintf(2, (ddt
, "encountered problems: UPDATE_MSG\n"));