text: /**************************************************************************
   * 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 <sys/types.h>
  #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"
  
  #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, qdomain, 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 (domain lives below the qdomain)
   *		return VALID_CACHE;
   *          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, qdomain, server, type, class, data, dlen
  #ifdef NCACHE
  	 ,rcode
  #endif
  	 )
  	char *dname, *qdomain;
  	struct sockaddr_in *server;
  	int type, class;
  	char *data;
  	int dlen;
  #ifdef NCACHE
  	int rcode;
  #endif
  {
  	struct namebuf *np, *dnamep;
  	struct hashbuf *htp;
  	struct databuf *nsp[NSMAX];
  	int count;
  	const char *fname;
  	int exactmatch = 0;
  	struct fwdinfo *fwd;
  
  	nsp[0] = NULL;
  	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 = "";
  	dprintf(5, (ddt,
  		    "validate:namebuf found np:%#lx, d:\"%s\", f:\"%s\"\n",
  		    (u_long)np, dname, fname));
  	/* save the namebuf if we were able to locate the exact dname */
  	if (!strcasecmp(dname, fname)) {
  		dnamep = np;
  		exactmatch = 1;
  	}
  	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"));
  		free_nsp(nsp);
  		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, NAME(*np))) {
        
  			/* 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;
  		free_nsp(nsp);
  		return (INVALID);
      
  	case OK: /*proceed */
  		dprintf(5, (ddt, "validate:found ns records\n"));
  		if (needs_prime_cache)
  			needs_prime_cache = 0;
  		if (samedomain(dname, qdomain) ||
  		    check_addr_ns(nsp, server, dname)) {
  			free_nsp(nsp);
  			return (VALID_CACHE);
  		}
  		/* server is not one of those we know of */
  		/* stick_in_queue(dname, type, class, data); */
  		free_nsp(nsp);
  		return (INVALID);
  	default:
  		free_nsp(nsp);
  		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:
  			case T_LOC:
  #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);
  
  			case T_PX:
  				x = memcmp(dp->d_data, data,
  					INT16SZ);
  				if (x != 0)
  					break;
  				td = data + INT16SZ;
  				tdp = dp->d_data + INT16SZ;
  
  				/* compare first string */
  				x = strncasecmp(td, (char *)tdp,
  						strlen((char *)td) + 1);
  				if (x != 0)
  					break;
  				td += (strlen(td) + 1);
  				tdp += (strlen((char *)tdp) + 1);
  				
  				/* compare second string */
  				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%lx, d:\"%s\"\n", 
  		    inet_ntoa(*saddr), (u_long)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;
  	const char *dname, *fname;
  	int class;
  	int qcomp();
    
  	dprintf(3, (ddt, "check_in_tables(nsp=x%lx, qp=x%x, '%s')\n",
  		    (u_long)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)
  	const char *servername;
  	struct in_addr serveraddr;
  	const char *syslogdname;
  	const 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*)
  					     nhash(nameaddrlist[i].nsname),
  					    (char*)nhash(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 = savestr(servername);
  			return;
  		} 
  	}
  	/* we have to add this one to our cache */
  
  	nameaddrlist[firstNA].nsname = savestr(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, qdomain, server, VCode)
  	u_char *msg, *rrp;
  	int  msglen, zone, flags;
  	char *qdomain;
  	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[MAXDNAME*2 + INT32SZ*5];
  	register HEADER *hp = (HEADER *) msg;
  
  	dprintf(2, (ddt, "dovalidate(zone %d, flags %x)\n",
  		    zone, flags));
  #ifdef DEBUG
  	if (debug >= 10)
  		fp_nquery(msg, msglen, 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:
  	case T_LOC:
  #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;
  
  	case T_PX:
  		/* grab preference */
  		bcopy((char *)cp, data, INT16SZ);
  		cp1 = data + INT16SZ;
  		cp += INT16SZ;
  
  		/* get first name */
  		n = dn_expand(msg, msg + msglen, cp,
  			      (char *)cp1, sizeof(data) - INT16SZ);
  		if (n < 0) {
  			hp->rcode = FORMERR;
  			return (-1);
  		}
  		cp += n;
  		cp1 += (n = strlen((char *)cp1) + 1);
  		n1 = sizeof(data) - n;
  		
  		/* get second name */
  		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;
  		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 = FORMERR;
  		return (-1);
  	}
  
  	*VCode = validate(dname, qdomain, 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));
  		if (!validateQ)
  			panic(errno, "malloc(validateQ)");
  		validateQ->type = type;
  		validateQ->class = class;
  		validateQ->dname = savestr(dname);
  		validateQ->data = savestr(data);	/* XXX no \0 */
  		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));
  		if (!tempVQ)
  			panic(errno, "malloc(tempVQ)");
  		tempVQ->type = type;
  		tempVQ->class = class;
  		tempVQ->dname = savestr(dname);
  		tempVQ->data = savestr(data);	/* XXX no \0 */
  		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
  
  #ifdef BAD_IDEA
  /* 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_nquery(msg, *msglen, ddt);
  	}
  #endif
  	/* just making sure we do not do all the work for nothing */
  	for (i=0;  i<c;  i++) { 
  		if (Vlist[i] == INVALID) {
  			inv = 1;
  			break;
  		}
  	}
  	if (inv != 1) {
  		/* no invalid records, go about your job */
  		return (0);
  	}
  
  	dprintf(2, (ddt, "update_msg: NEEDS updating:\n"));
  
  	RRlen = (int *)malloc((unsigned)c*sizeof(int));
  	if (!RRlen)
  		panic(errno, "malloc(RRlen)");
  	hp = (HEADER *)msg;
  	new_ancount = ancount = ntohs(hp->ancount);
  	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_expand() 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:
  		case T_LOC:
  #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;
  
  		case T_PX:
  			/* grab preference */
  			bcopy(cp, newcp, INT16SZ);
  			cp += INT16SZ;
  			newcp += INT16SZ;
  
  			/* get first 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;
  			newcp += n_new;
  			newlen += n_new+INT16SZ;
  			rembuflen -= n_new+INT16SZ;
  			dlen = n_new+INT16SZ;
  			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;
  			PUTSHORT(dlen, tempcp);
  			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_nquery(msg, *msglen, ddt);
  #endif
  	free((char *)RRlen);
  	return (n);
  badend:
  	dprintf(2, (ddt, "encountered problems: UPDATE_MSG\n"));
  	free((char *)RRlen);
  	return (-1);
  }
  #endif /*BAD_IDEA*/
  
  #endif /*VALIDATE*/
  