/************************************************************************** * ns_validate.c (was security.c in original ISI contribution) * author: anant kumar * contributed: March 17, 1993 * * implements validation procedure for RR's received from a server as a * response to a query. */ #include #include #include #include #include #include #include #include #include #include "named.h" #ifdef VALIDATE static int isvalid __P((struct namebuf *, int, int, char *, int)), check_addr_ns __P((struct databuf **, struct sockaddr_in *, char *)), check_in_tables __P((struct databuf **, struct sockaddr_in *, char *)); #if 0 static void stick_in_queue __P((char *, int, int, char *)); #endif static NAMEADDR nameaddrlist[MAXNAMECACHE]; static int firstNA = 0, lastNA = 0; static TO_Validate *validateQ, *currentVQ; static int VQcount; /***************************************************************** * 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) * return INVALID; * 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) * return VALID_NO_CACHE; * else return INVALID; * * if(we are not authoritative) /findns() returned OK;/ * if (address records for NS's found in cache){ * if("server" = one of the addresses){ * return VALID_CACHE; * }else{ * stick in queue of "to_validate" data; * return (INVALID); * } * else return INVALID; * * 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 * return INVALID. * 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; *****************************************************************************/ int validate(dname, server, type, class, data, dlen #ifdef NCACHE ,rcode #endif ) char *dname; struct sockaddr_in *server; int type; int class; char *data; int dlen; #ifdef NCACHE int rcode; #endif { struct namebuf *np, *dnamep; struct hashbuf *htp; struct databuf *nsp[NSMAX]; int count; char *fname; int exactmatch = 0; struct fwdinfo *fwd; #ifdef DATUMREFCNT nsp[0] = NULL; #endif dprintf(3, (ddt, "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) return(VALID_CACHE); } htp = hashtab; if (priming && (dname[0] == '\0')) np = NULL; else np = nlookup(dname, &htp, &fname, 0); /* we were able to locate namebufs for this domain, or a parent domain, * or ??? */ if (np == NULL) { fname = (char *)malloc(1); fname[0] = '\0'; } dprintf(5, (ddt, "validate:namebuf found np:0x%x, d:\"%s\", f:\"%s\"\n", np, dname, fname)); /* save the namebuf if we were able to locate the exact dname */ if (!strcasecmp(dname, fname)) { dnamep = np; exactmatch = 1; } if (np == NULL && fname != NULL) { free((char *)fname); } switch (findns(&np, class, nsp, &count, 0)) { case NXDOMAIN: /** 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")); #ifdef DATUMREFCNT free_nsp(nsp); #endif if (needs_prime_cache) needs_prime_cache = 0; #ifdef NCACHE if(rcode == NXDOMAIN) { /* 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 */ if (exactmatch) return INVALID; else /* we did not have an exactmatch, the data is * good, we do not NCACHE stuff we are * authoritative for, though. */ return VALID_NO_CACHE; } #endif 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 * doesn't, invalid. */ if (isvalid(np, type, class, data, dlen)) return VALID_NO_CACHE; else return INVALID; } /* 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 */ if (!exactmatch) return INVALID; /* we found the exact name earlier and we are obviously * authoritative so check for data records and see if any * match. */ if (isvalid(dnamep, type, class, data, dlen)) return VALID_NO_CACHE; else return INVALID; case SERVFAIL:/* could not find name server records*/ /* stick_in_queue(dname, type, class, data); */ if (needs_prime_cache) needs_prime_cache = 0; #ifdef DATUMREFCNT free_nsp(nsp); #endif return INVALID; case OK: /*proceed */ dprintf(5, (ddt, "validate:found ns records:calling check_addr_ns\n")); if (needs_prime_cache) needs_prime_cache = 0; if (check_addr_ns(nsp, server, dname)) { #ifdef DATUMREFCNT free_nsp(nsp); #endif return VALID_CACHE; } /* server is not one of those we know of */ /* stick_in_queue(dname, type, class, data); */ #ifdef DATUMREFCNT free_nsp(nsp); #endif return INVALID; default: #ifdef DATUMREFCNT free_nsp(nsp); #endif return INVALID; } /*switch*/ } /*validate*/ /*********************************************************************** * 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. ***********************************************************************/ static int isvalid(np, type, class, data, dlen) struct namebuf *np; int type, class; char *data; int 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 */ return(0); /* we come here only for zone info, * so -ve $ed info can't be */ } continue; } /* 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 * data here. */ if ((data == NULL) || (dlen == 0)) { return 0; } /* XXX: why aren't we just calling db_cmp() ? */ switch (type) { char *td; u_char *tdp; int x; case T_A: case T_WKS: case T_HINFO: case T_UINFO: case T_UID: case T_GID: case T_TXT: case T_X25: case T_ISDN: #ifdef ALLOW_T_UNSPEC case T_UNSPEC: #endif x = memcmp(dp->d_data, data, dlen); dprintf(3, (ddt, "type = %d, GOOD = %d\n", type, x)); if (x == 0) return (1); else break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_NS: case T_PTR: x = strncasecmp((char *)dp->d_data, data, dlen); dprintf(3, (ddt, "type = %d, GOOD = %d\n", type, x)); if (x == 0) return (1); else break; case T_MINFO: case T_SOA: case T_RP: /* compare first string */ x = strncasecmp((char *)dp->d_data, data, strlen(data) + 1); if (x != 0) break; /* move to second string */ td = data + (strlen(data) + 1); tdp = dp->d_data + (strlen((char *)dp->d_data)+1); /* compare second string */ x = strncasecmp(td, (char *)tdp, strlen((char *)td+1)); if (x != 0) break; /* move beyond second string, to * set of words in SOA. * RP and MINFO stuff really * ends here */ td = td + strlen((char *)td) + 1; tdp = tdp + strlen((char *)tdp) + 1; if (type == T_SOA) { x = memcmp(td, (char *)tdp, 5*INT32SZ); if (x != 0) break; } /* everything was equal, wow! * so return a success */ return (1); case T_MX: case T_AFSDB: case T_RT: x = memcmp(dp->d_data, data, INT16SZ); if (x != 0) break; td = data + INT16SZ; tdp = dp->d_data + INT16SZ; x = strncasecmp(td, (char *)tdp, strlen((char *)td) + 1); if (x != 0) break; return (1); default: dprintf(3, (ddt, "unknown type %d\n", type)); return (0); } /* 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! * else we failed */ if ((data == NULL) || (dlen == 0)) { /* negative data, report success */ return 1; } /* positive data, no such RR, validation failed */ return (0); } /****************************************************************** * 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. *******************************************************************/ static int check_addr_ns(nsp, server, dname) struct databuf **nsp; struct sockaddr_in *server; char *dname; { int i, found=0; char sname[MAXDNAME]; struct in_addr *saddr = &(server->sin_addr); struct databuf **nsdp; dprintf(5, (ddt, "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) { if (!bcmp((char *)saddr, (char *)&(nameaddrlist[i].ns_addr), INADDRSZ)) { strcpy(sname, nameaddrlist[i].nsname); found = 1; break; } } if (found) { dprintf(3, (ddt, "check_addr_ns: found address:[%s]\n", inet_ntoa(*saddr))); for (nsdp = nsp; *nsdp != NULL;nsdp++) { dprintf(5, (ddt, "check_addr_ns:names are:%s, %s\n", sname,(*nsdp)->d_data)); if (!strcasecmp(sname,(char *)((*nsdp)->d_data))) { return (1); } } } /* could not find name in my cache of servers, must go through the * whole grind */ 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 * largely. *************************************************************************/ static int check_in_tables(nsp, server, syslogdname) struct databuf *nsp[]; struct sockaddr_in *server; char *syslogdname; { register struct namebuf *np; register struct databuf *dp, *nsdp; struct hashbuf *tmphtp; char *dname, *fname; int class; int qcomp(); dprintf(3, (ddt, "check_in_tables(nsp=x%x,qp=x%x,'%s')\n", nsp, server, syslogdname)); while ((nsdp = *nsp++) != NULL) { class = nsdp->d_class; 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", dname, fname, np)); continue; } /* look for name server addresses */ for (dp = np->n_data; dp != NULL; dp = dp->d_next) { if (stale(dp)) continue; if (dp->d_type != T_A || dp->d_class != class) continue; #ifdef NCACHE if (dp->d_rcode) continue; #endif if (!bcmp((char *)dp->d_data, (char *)&(server->sin_addr), INADDRSZ)) { return (1); } } } 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(). *************************************************************************/ void store_name_addr(servername, serveraddr, syslogdname, sysloginfo) char *servername; struct in_addr *serveraddr; char *syslogdname; char *sysloginfo; { int i; dprintf(3, (ddt, "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) { if (serveraddr->s_addr == nameaddrlist[i].ns_addr.s_addr) { dprintf(5, (ddt, "store_name_addr:found n and a [%s] [%s] in our $\n", inet_ntoa(nameaddrlist[i].ns_addr), inet_ntoa(*serveraddr))); return; } /* if */ } else if (serveraddr->s_addr == nameaddrlist[i].ns_addr.s_addr) { #ifdef BAD_IDEA /* * log this as it needs to be fixed. * replace old name by new, next query likely to have * NS record matching new */ if (!haveComplained( (char*)dhash((u_char*)nameaddrlist[i].nsname, strlen(nameaddrlist[i].nsname)), (char*)dhash((u_char*)servername, strlen(servername)) ) ) { syslog(LOG_INFO, "%s: server name mismatch for [%s]: (%s != %s) (server for %s)", sysloginfo, inet_ntoa(*serveraddr), nameaddrlist[i].nsname, servername, syslogdname); } #endif free(nameaddrlist[i].nsname); nameaddrlist[i].nsname = (char *)malloc((unsigned)strlen(servername)+1); strcpy(nameaddrlist[i].nsname, servername); return; } } /* we have to add this one to our cache */ nameaddrlist[firstNA].nsname = (char *)malloc((unsigned)strlen(servername)+1); strcpy(nameaddrlist[firstNA].nsname, servername); bcopy((char *)serveraddr, (char *)&(nameaddrlist[firstNA].ns_addr), INADDRSZ); 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; if (firstNA == lastNA) { free(nameaddrlist[firstNA].nsname); nameaddrlist[firstNA].nsname = 0; lastNA = (lastNA+1) % MAXNAMECACHE; } return; } /* * 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. */ int dovalidate(msg, msglen, rrp, zone, flags, server, VCode) u_char *msg, *rrp; int msglen, zone, flags; struct sockaddr_in *server; int *VCode; { register u_char *cp; register int n; int class, type, dlen, n1; u_int32_t ttl; char dname[MAXDNAME]; u_char *cp1; u_char data[BUFSIZ]; register HEADER *hp = (HEADER *) msg; dprintf(2, (ddt, "dovalidate(zone %d, flags %x)\n", zone, flags)); #ifdef DEBUG if (debug >= 10) fp_query(msg, ddt); #endif cp = rrp; n = dn_expand(msg, msg + msglen, cp, dname, sizeof dname); if (n < 0) { hp->rcode = FORMERR; return (-1); } cp += n; GETSHORT(type, cp); GETSHORT(class, cp); GETLONG(ttl, cp); GETSHORT(dlen, cp); 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 * database format. */ switch (type) { case T_A: case T_WKS: case T_HINFO: case T_UINFO: case T_UID: case T_GID: case T_TXT: case T_X25: case T_ISDN: #ifdef ALLOW_T_UNSPEC case T_UNSPEC: #endif 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, msg + msglen, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 = data; n = strlen((char *)data) + 1; break; case T_MINFO: case T_SOA: case T_RP: n = dn_expand(msg, msg + msglen, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 = data + (n = strlen((char *)data) + 1); n1 = sizeof(data) - n; if (type == T_SOA) n1 -= 5 * INT32SZ; n = dn_expand(msg, msg + msglen, cp, (char *)cp1, n1); if (n < 0) { hp->rcode = FORMERR; return (-1); } cp += n; cp1 += strlen((char *)cp1) + 1; if (type == T_SOA) { bcopy((char *)cp, (char *)cp1, n = 5 * INT32SZ); cp += n; cp1 += n; } n = cp1 - data; cp1 = data; break; case T_MX: case T_AFSDB: case T_RT: /* grab preference */ bcopy((char *)cp, data, INT16SZ); cp1 = data + INT16SZ; cp += INT16SZ; /* get name */ n = dn_expand(msg, msg + msglen, cp, (char *)cp1, sizeof(data) - INT16SZ); if (n < 0) { hp->rcode = FORMERR; return(-1); } cp += n; /* compute end of data */ cp1 += strlen((char *)cp1) + 1; /* compute size of data */ n = cp1 - data; cp1 = data; break; default: dprintf(3, (ddt, "unknown type %d\n", type)); return ((cp - rrp) + dlen); } if (n > MAXDATA) { dprintf(2, (ddt, "update type %d: %d bytes is too much data\n", type, n)); hp->rcode = NOCHANGE; /* XXX - FORMERR ??? */ return(-1); } *VCode = validate(dname, server, type, class,(char *)cp1, n #ifdef NCACHE ,NOERROR #endif ); if (*VCode == INVALID) { dprintf(2, (ddt, "validation failed d:%s, t:%d, c:%d\n", dname, type, class)); } else { dprintf(2, (ddt, "validation succeeded d:%s, t:%d, c:%d\n", dname, type, class)); } return(cp -rrp); } #if 0 /****************************************************************** * 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 * me do it anyway. *****************************************************************/ static void stick_in_queue(dname, type, class, data) char *dname; int type; int class; char *data; { struct timeval tp; struct _TIMEZONE tzp; TO_Validate *tempVQ; u_long leasttime; if (validateQ == NULL) { validateQ = (TO_Validate *)malloc(sizeof(TO_Validate)); validateQ->type = type; 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); gettimeofday(&tp, &tzp); validateQ->time = tp.tv_sec; VQcount = 1; validateQ->next = validateQ->prev = NULL; currentVQ = validateQ; return; } if (VQcount < MAXVQ) { tempVQ =(TO_Validate *)malloc(sizeof(TO_Validate)); tempVQ->type = type; tempVQ->class = class; tempVQ->dname = malloc((unsigned)strlen(dname)+1); strcpy(tempVQ->dname, dname); tempVQ->data = malloc((unsigned)strlen(data)+1); strcpy(tempVQ->data, data); gettimeofday(&tp,&tzp); tempVQ->time = tp.tv_sec; tempVQ->next = currentVQ->next; tempVQ->prev = currentVQ; if (currentVQ->next != NULL) currentVQ->next->prev = tempVQ; currentVQ->next = tempVQ; currentVQ = tempVQ; VQcount++; return; } gettimeofday(&tp, &tzp); leasttime = validateQ->time; currentVQ = validateQ; for (tempVQ = validateQ; tempVQ != NULL; tempVQ = tempVQ->next) { if (tp.tv_sec >= tempVQ->time +VQEXPIRY) { tempVQ->type = type; tempVQ->class = class; strcpy(tempVQ->dname, dname); strcpy(tempVQ->data, data); tempVQ->time = tp.tv_sec; currentVQ = tempVQ; return; } if (tempVQ->time < leasttime) { leasttime = tempVQ->time; currentVQ = tempVQ; } } currentVQ->type = type; currentVQ->class = class; strcpy(currentVQ->dname, dname); strcpy(currentVQ->data, data); currentVQ->time = tp.tv_sec; return; } #endif /* removes any INVALID RR's from the msg being returned, updates msglen to * reflect the new message length. */ int update_msg(msg, msglen, Vlist, c) u_char *msg; int *msglen; int Vlist[]; int c; { register HEADER *hp; register u_char *cp; int i; int n = 0; u_char *tempcp, *newcp; int *RRlen; int qlen; /* the length of the query section*/ u_int16_t rdlength; u_int16_t ancount, nscount; u_int16_t new_ancount, new_nscount, new_arcount; char dname[MAXDNAME], qname[MAXDNAME]; u_char data[MAXDNAME]; u_char **dpp; u_char *dnptrs[40]; u_char **edp = dnptrs + sizeof(dnptrs)/sizeof(dnptrs[0]); u_char *eom = msg + *msglen; int n_new; int rembuflen, newlen; u_char *newmsg; u_int16_t type, class, dlen; u_int32_t ttl; int inv = 0; #ifdef DEBUG if (debug) { fprintf(ddt, "update_msg: msglen:%d, c:%d\n", *msglen, c); if (debug >= 10) fp_query(msg, ddt); } #endif /* just making sure we do not do all the work for nothing */ for (i=0; iancount); new_nscount = nscount = ntohs(hp->nscount); new_arcount = ntohs(hp->arcount); cp = msg + HFIXEDSZ; newlen = HFIXEDSZ; /* skip the query section */ qlen = dn_expand(msg, eom, cp, qname, sizeof qname); if (qlen <= 0) { dprintf(2, (ddt, "dn_skipname() failed, bad record\n")); goto badend; } cp +=qlen; GETSHORT(type,cp); GETSHORT(class,cp); qlen += 2 * INT16SZ; newlen += qlen; for (i = 0; i < c; i++) { if (Vlist[i] == INVALID) { if (i < ancount) new_ancount--; else if (i < ancount+nscount) new_nscount--; else new_arcount--; } RRlen[i] = dn_skipname(cp, msg + *msglen); if (RRlen[i] <= 0) { dprintf(2, (ddt, "dn_skipname() failed, bad record\n")); goto badend; } RRlen[i] += 2 * INT16SZ + INT32SZ; /*type+class+TTL*/ cp += RRlen[i]; GETSHORT(rdlength, cp); RRlen[i] += INT16SZ; /*rdlength*/ RRlen[i] += rdlength; /*rdata field*/ dprintf(3, (ddt, "RRlen[%d]=%d\n", i, RRlen[i])); if (Vlist[i] != INVALID) newlen += RRlen[i]; cp += rdlength; /*increment pointer to next RR*/ } hp->ancount = htons(new_ancount); hp->nscount = htons(new_nscount); hp->arcount = htons(new_arcount); /* get new buffer */ dprintf(3, (ddt, "newlen:%d, if no RR is INVALID == msglen\n", newlen)); newmsg = (u_char *)calloc(1,newlen + MAXDNAME); if(newmsg == NULL) goto badend; dpp = dnptrs; *dpp++ = newmsg; *dpp = NULL; /* bcopy the header, with all the length fields correctly put in */ bcopy((char *)msg, (char*)newmsg, HFIXEDSZ); /*header copied */ newcp = newmsg +HFIXEDSZ; /*need a pointer in the new buffer */ rembuflen = newlen +MAXDNAME - HFIXEDSZ; /*buflen we can workin*/ newlen = HFIXEDSZ; /* this will now contain the length of msg */ n_new = dn_comp(qname, newcp, rembuflen, dnptrs, edp); if (n_new < 0) goto badend; newcp += n_new; PUTSHORT(type, newcp); PUTSHORT(class, newcp); /*query section complete*/ newlen += (n_new+2*INT16SZ); rembuflen -= (n_new+2*INT16SZ); /* have to decode and copy every Valid RR from here */ cp = msg +HFIXEDSZ +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 */ cp += RRlen[i]; continue; } /* we have a valid record, must put it in the newmsg */ n = dn_expand(msg, eom, cp, dname, sizeof dname); if (n < 0) { hp->rcode = FORMERR; goto badend; } n_new = dn_comp(dname, newcp, rembuflen, dnptrs, edp); if (n_new < 0) goto badend; cp += n; newcp += n_new; dprintf(5, (ddt, "cp:0x%x newcp:0x%x after getting name\n", cp, newcp)); GETSHORT(type, cp); PUTSHORT(type, newcp); dprintf(5, (ddt, "cp:0x%x newcp:0x%x after getting type\n", cp, newcp)); GETSHORT(class, cp); PUTSHORT(class, newcp); dprintf(5, (ddt, "cp:0x%x newcp:0x%x after getting class\n", cp, newcp)); GETLONG(ttl, cp); PUTLONG(ttl, newcp); dprintf(5, (ddt, "cp:0x%x newcp:0x%x after getting ttl\n", cp, newcp)); /* this will probably be modified for newmsg, * will put this in later, after compression */ GETSHORT(dlen, cp); newlen += (n_new+3*INT16SZ + INT32SZ); rembuflen -= (n_new+3*INT16SZ+ INT32SZ); tempcp = newcp; newcp += INT16SZ; /*advance to rdata field*/ dprintf(5, (ddt, "tempcp:0x%x newcp:0x%x\n", tempcp, newcp)); dprintf(3, (ddt, "update_msg: dname %s type %d class %d ttl %d\n", dname, type, class, ttl)); /* read off the data section */ switch (type) { case T_A: case T_WKS: case T_HINFO: case T_UINFO: case T_UID: case T_GID: case T_TXT: case T_X25: case T_ISDN: #ifdef ALLOW_T_UNSPEC case T_UNSPEC: #endif n = dlen; PUTSHORT(n, tempcp); /*time to put in the dlen*/ bcopy(cp, newcp,n); /*done here*/ cp +=n; newcp +=n; newlen += n; rembuflen -= n; dprintf(3, (ddt, "\tcp:0x%x newcp:0x%x dlen:%d\n", cp, newcp, dlen)); break; case T_CNAME: case T_MB: case T_MG: case T_MR: case T_NS: case T_PTR: /*read off name from data section */ n = dn_expand(msg, eom, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; goto badend; } cp += n; /*advance pointer*/ /* fill in new packet */ n_new = dn_comp((char *)data, newcp, rembuflen, dnptrs, edp); if (n_new < 0) goto badend; PUTSHORT(n_new,tempcp); /*put in dlen field*/ newcp += n_new; /*advance new pointer*/ newlen += n_new; rembuflen -= n_new; break; case T_MINFO: case T_SOA: case T_RP: n = dn_expand(msg, eom, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; goto badend; } cp += n; n_new = dn_comp((char *)data, newcp, rembuflen, dnptrs, edp); if (n_new < 0) goto badend; newcp += n_new; newlen += n_new; rembuflen -= n_new; dlen = n_new; n = dn_expand(msg, eom, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; goto badend; } cp += n; n_new = dn_comp((char *)data, newcp, rembuflen, dnptrs, edp); if (n_new < 0) goto badend; newcp += n_new; newlen += n_new; rembuflen -= n_new; dlen += n_new; if (type == T_SOA) { bcopy(cp, newcp, n = 5*INT32SZ); cp += n; newcp += n; newlen +=n; rembuflen -= n; dlen +=n; } PUTSHORT(dlen, tempcp); break; case T_MX: case T_AFSDB: case T_RT: /* grab preference */ bcopy(cp,newcp,INT16SZ); cp += INT16SZ; newcp += INT16SZ; /* get name */ n = dn_expand(msg, eom, cp, (char *)data, sizeof data); if (n < 0) { hp->rcode = FORMERR; goto badend; } cp += n; n_new = dn_comp((char *)data, newcp, rembuflen, dnptrs, edp); if (n_new < 0) goto badend; PUTSHORT(n_new+INT16SZ, tempcp); newcp += n_new; newlen += n_new+INT16SZ; rembuflen -= n_new+INT16SZ; break; default: dprintf(3, (ddt, "unknown type %d\n", type)); goto badend; } dprintf(2, (ddt, "newlen:%d, i:%d newcp:0x%x cp:0x%x\n\n", newlen, i, newcp, cp)); } bcopy(newmsg, msg, newlen); n = *msglen - newlen; if (n < 0) { dprintf(2, (ddt, "update_msg():newmsg longer than old: n:%d o:%d ???\n", newlen, *msglen)); } *msglen = newlen; free((char *)newmsg); #ifdef DEBUG if (debug >= 10) fp_query(msg, ddt); #endif free((char *)RRlen); return(n); badend: dprintf(2, (ddt, "encountered problems: UPDATE_MSG\n")); free((char *)RRlen); return(-1); } #endif /*VALIDATE*/