/* ns.c
 *
 * Copyright 2003, Michael Robinton <michael@bizsystems.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <sys/types.h>
#include "bswap32.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include "misc_func.h"

/*

    There are more include files specific to the application 
    where the code begins below

 ****************************************************
      The header contains the following fields:

                                           1  1  1  1  1  1
             0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
           |                      ID                       |
           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
           |QR|   Opcode  |AA|TC|RD|RA|        |   RCODE   |
           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+  
           |                    QDCOUNT                    |
           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
           |                    ANCOUNT                    |
           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
           |                    NSCOUNT                    |
           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
           |                    ARCOUNT                    |
           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

	from arpa/nameser_compat.h (ignoring endianness)

  typedef struct {
        unsigned        id :16;         /* query identification number 
                        /* fields in third byte 
        unsigned        qr: 1;          /* response flag 
        unsigned        opcode: 4;      /* purpose of message 
        unsigned        aa: 1;          /* authoritive answer 
        unsigned        tc: 1;          /* truncated message, forces the use of TCP 
        unsigned        rd: 1;          /* recursion desired 
                        /* fields in fourth byte 
        unsigned        ra: 1;          /* recursion available 
        unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) 
        unsigned        ad: 1;          /* authentic data from named 
        unsigned        cd: 1;          /* checking disabled by resolver 
        unsigned        rcode :4;       /* response code 
        unsigned        qdcount :16;    /* number of question entries 
        unsigned        ancount :16;    /* number of answer entries 
        unsigned        nscount :16;    /* number of authority entries 
        unsigned        arcount :16;    /* number of resource entries 
  } HEADER;

  #define NOERROR	0  ns_r_noerror	 No error occurred
  #define FORMERR	1  ns_r_formerr	 Format error
  #define SERVFAIL	2  ns_r_servfail Server failure
  #define NXDOMAIN	3  ns_r_nxdomain Name error
  #define NOTIMP	4  ns_r_notimpl	 Unimplemented
  #define REFUSED	5  ns_r_refused	 Operation refused
  #define YXDOMAIN	6  ns_r_yxdomain Name exists
  #define YXRRSET	7  ns_r_yxrrset	 RRset exists
  #define NXRRSET	8  ns_r_nxrrset	 RRset does not exist
  #define NOTAUTH	9  ns_r_notauth	 Not authoritative for zone
  #define NOTZONE	10 ns_r_notzone	 Zone of record different from zone section

  #define T_A		1   ns_t_a
  #define T_NS		2   ns_t_ns
  #define T_SOA		6   ns_t_soa
  #define T_MX		15  ns_t_mx
  #define T_TXT		16  ns_t_txt
  #define T_AXFR	252 ns_t_axfr
  #define T_ANY		255 ns_t_any

 *
 *
 ***********************************************
 *	Plan
 *
	if (message <= headersize) {
		ignore, do not answer
	}
	if (Query != 0) {
		ignore, do not answer
	}
	if (Opcode != 0 {
		response = NOTIMP
		goto NS_empty
	}
	if (Qcount != 1, NScount > 1 or any of ANcount, ARcount ne 0) {
		response = FORMERR
		goto NS_empty
	}
	if (Class != C_IN) {
		response = REFUSED
		goto NS_empty
	}
	host, domain = parse_addr;
	if (mydomain != domain) {
		response = NXDOMAIN
		goto NS_empty
	}
	AA = 1
	if (host+domain = zone) {
		if (Type == SOA) {
			ANS = SOA
			AUTH = NS
			ADD = NS A records
			goto NS_send
		}
		elsif (Type == A) {
			if (local name == zone name) {
				ANS = A
				AUTH = NS
				ADD = NS A records
					 less any A record above
				goto NS_send
			}
			else {
				goto AUTH SOA
			}
		}
		elsif (Type == NS) {
			ANS = NS
		there is no AUTH
			ADD = NS A records
			goto NS_send
		}
		elsif (Type == MX) {
			ANS = MX
			AUTH = NS
			ADD = MX A records
			      NS A records
			goto NS_send
		}
		elsif (Type == ANY) {
			if (local name == zone name) {
				ANS = A
			}
			ANS = SOA
			ANS = MX
			ANS = NS
		there is no AUTH
			ADD = MX A records
			      NS A records
				 less any A record above
			goto NS_send
		}
		elsif (Type == AXFR) {
			if (blocked) {
				response = REFUSED
				goto empty
			}
			if (UDP) {
				TC = 1
			there is no ANS
				AUTH = NS
				ADD = NS A records
				goto NS_send
			}
			else {
	more......
		}
	}

	if (host NOT numeric) {
		if (Type == A || Type == ANY) {		this is not a zone, no MX, NS, SOA
			if (NS or MX match) {
				ANS = IP - A record
				AUTH = NS
				ADD = NS Arecords
				      less any A record above
				goto NS_send
			}
			else {
				goto AUTH SOA
			}
		}
	}
	elsif (host == isthere) {
		if (Type == ANY) {		this is not a zone, no MX, NS, SOA
			ANS = A
			ANS = TXT	
			AUTH = NS
			ADD = NS A records
			goto NS_send
		}
		elsif (Type == A) {
			ANS = A
			AUTH = NS
			ADD = A records for NS
			goto NS_send
		}
		elsif (Type = TXT) {
			ANS = TXT
			AUTH = NS
			ADD = A records for NS
			goto NS_send
		}
		else {
			AUTH = SOA
			goto NS_send
		}
	}
	else {

		AUTH = SOA
		goto NS_send
	}
	goto NS_send


   ************************************************
	draft-ietf-dnsext-axfr-clarify-05.txt

   3.1. Multiple answers per message

   The zone data in a zone transfer response is a sequence of answer
   RRs.  These RRs are transmitted in the answer section(s) of one or
   more DNS response messages.

   The AXFR protocol definition in RFC1034 does not make a clear
   distinction between response messages and answer RRs.  Historically,
   DNS servers always transmitted a single answer RR per message.  This
   encoding is wasteful due to the overhead of repeatedly sending DNS
   message headers and the loss of domain name compression
   opportunities.  To improve efficiency, some newer servers support a
   mode where multiple RRs are transmitted in a single DNS response
   message.

   A master MAY transmit multiple answer RRs per response message up to
   the largest number that will fit within the 65535 byte limit on TCP
   DNS message size.  In the case of a small zone, this can cause the
   entire transfer to be transmitted in a single response message.

   Slaves MUST accept messages containing any number of answer RRs.  For
   compatibility with old slaves, masters that support sending multiple
   answers per message SHOULD be configurable to revert to the
   historical mode of one answer per message, and the configuration
   SHOULD be settable on a per-slave basis.

3.2. DNS message header contents

   RFC1034 does not specify the contents of the DNS message header of
   the zone transfer response messages.  The header of each message MUST
   be as follows:

       ID      Copy from request
       QR      1
       OPCODE  QUERY
       AA      1, but MAY be 0 when RCODE is not NOERROR
       TC      0
       RD      Copy from request, or 0
       RA      Set according to availability of recursion, or 0
       Z       0
       AD      0
       CD      0
       RCODE   NOERROR on success, error code otherwise

   The slave MUST check the RCODE in each message and abort the transfer
   if it is not NOERROR.  It SHOULD check the ID of the first message
   received and abort the transfer if it does not match the ID of the
   request.  The ID SHOULD be ignored in subsequent messages, and fields
   other than RCODE and ID SHOULD be ignored in all messages, to ensure
   interoperability with certain older implementations which transmit
   incorrect or arbitrary values in these fields.

3.3 see the doc
3.4. The question section

   RFC1034 does not specify whether zone transfer response messages have
   a question section or not.  The initial message of a zone transfer
   response SHOULD have a question section identical to that in the
   request.  Subsequent messages SHOULD NOT have a question section,
   though the final message MAY.  The receiving slave server MUST accept
   any combination of messages with and without a question section.

3.5. The authority section

   The master server MUST transmit messages with an empty authority
   section.  Slaves MUST ignore any authority section contents they may
   receive from masters that do not comply with this requirement.

3.6. The additional section

   The additional section MAY contain additional RRs such as transaction
   signatures.  The slave MUST ignore any unexpected RRs in the
   additional section.  It MUST NOT treat additional section RRs as zone
   data.

4.0 see the doc
5. Transmission order

   RFC1034 states that "The first and last messages must contain the
   data for the top authoritative node of the zone".  This is not
   consistent with existing practice.  All known master implementations
   send, and slave implementations expect to receive, the zone's SOA RR
   as the first and last record of the transfer.

   Therefore, the quoted sentence is hereby superseded by the sentence
   "The first and last RR transmitted must be the SOA record of the
   zone".

   The initial and final SOA record MUST be identical, with the possible
   exception of case and compression.  In particular, they MUST have the
   same serial number.  The slave MUST consider the transfer to be
   complete when, and only when, it has received the message containing
   the second SOA record.

   The transmission order of all other RRs in the zone is undefined.
   Each of them SHOULD be transmitted only once, and slaves MUST ignore
   any duplicate RRs received.

   **************************************************************
	what that means for implementation

	Initial Record
		+-----------------------------------------------+
  Header	| OPCODE=QUERY, RESPONSE, AA			|
		+-----------------------------------------------+
  Question	| same as original				|
		+-----------------------------------------------+
  Answer	| SOA + zero or more records			|
		+-----------------------------------------------+
  Authority	| <empty>					|
		+-----------------------------------------------+
  Additional	| <empty>					|
		+-----------------------------------------------+

	Middle Records
		+-----------------------------------------------+
  Header	| OPCODE=QUERY, RESPONSE, AA			|
		+-----------------------------------------------+
  Question	| <empty>					|
		+-----------------------------------------------+
  Answer	| more records					|
		+-----------------------------------------------+
  Authority	| <empty>					|
		+-----------------------------------------------+
  Additional	| <empty>					|
		+-----------------------------------------------+

	Final Record
		+-----------------------------------------------+
  Header	| OPCODE=QUERY, RESPONSE, AA			|
		+-----------------------------------------------+
  Question	| <empty> (may contain question)		|
		+-----------------------------------------------+
  Answer	| zero or more records + SOA			|
		+-----------------------------------------------+
  Authority	| <empty>					|
		+-----------------------------------------------+
  Additional	| <empty>					|
		+-----------------------------------------------+

 ****************************************************************

  see the stuff in  bind-9.2.1/lib/bind/resolv/res_mkquery.c

 * Inline versions of get/put short/long.  Pointer is advanced
   from	/usr/include/arpa/nameser.h

	NS_GET16(s, cp)
	NS_PUT16(s, cp)

  and from /usr/include/arpa/nameser_compat.h

	#define GETSHORT                NS_GET16
	#define PUTSHORT                NS_PUT16

 */

#include <bdbtarpit.h>
#include "host_info_func.h"
#include "netio_func.h"
#include "ns.h"
#include "misc_func.h"

#include "defines.h"
#include "munge_macros.c"	/* include the macros for "munge_msg"	*/

u_char ns_msgbuf[IP_MAXPACKET];	/* message buffer	*/

/* valid characters for host part of RBL lookup ".0123456789"	*/

u_char validchars[] = {
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0', '.','\0',
	 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
	'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
};

/*	append an IP address to end of text string if qflag is present	*/

struct in_addr in;
char * stdErr_response;
char *
errIP()
{
  extern int qflag;
  extern struct in_addr in;
  extern char mybuffer[], * errormsg;

  if (qflag) {
    sprintf(mybuffer,"%s%s",errormsg,inet_ntoa(in));
    return(mybuffer);
  }
  return(errormsg);
}

/*	verify that the requested IP address
 *	contains only numbers and '.'
 *	returns 1 if not numeric, 0 if numeric
 */

int
not_numericIP(char * ip)
{
  register u_char * vcp = validchars;
  register u_char * cp = ip;
  while(*cp != '\0') {
/* printf("cp=%02X, vcp=%02X\n",*cp,vcp[*cp]); */
	if (! vcp[*cp++])
		return(1);
  }
  return(0);
}

/*	skip over a name in a dns record, cheaper than dn_expand	*/

char *
name_skip(char * cp)
{
  int i;

  for(i=0;i<MAXDNAME;i++) {
    if ( ((u_char)*(cp + i)) == 0 )
      return((u_char *)(cp +i + 1));
    else if ( ((u_char)*(cp + i)) < 0xC0 )
      continue;
    else 
      return((u_char *)(cp +i + 2));
  }
/* ERROR, should not reach	*/
  return(NULL);
}

/* serial max	= 4294967295
 * rollover	= (1 + max / 2) = 2147483648
 *
 * returns:
 *	 0	s1 = s2
 *	-1	s1 < s2
 *	 1	s1 > s2
 *	>1	undefined
 */
 
#define RollOver 2147483648

int
cmp_serial(u_int32_t s1, u_int32_t s2)
{
  u_int32_t rollover = 2147483647;

  rollover += 1;	/* silence unsigned integer compiler warning	*/
  if (s1 == s2)
    return(0);
  else if ((s1 < s2 && s2 - s1 < rollover) ||
  	   (s1 > s2 && s1 - s2 > rollover))
    return(-1);
  else if ((s1 < s2 && s2 - s1 > rollover) ||
  	   (s1 > s2 && s1 - s2 < rollover))
    return(1);
  return(2);
}

/* return message reponse A_response if response is needed, else NULL
 * sets stdErr_response
 */

u_int32_t *
ns_response(u_char * keydbt_data)
{
  extern DBTPD dbtp;
  extern struct in_addr stdResp, stdRespBeg, stdRespEnd;
  extern int zflag;
  extern char * stdErr_response, * errormsg;
  
  u_int32_t * A_resp;

  stdErr_response = errormsg;
  
  if (*keydbt_data == 0x7F) {
/*	suppress numeric record for 127.0.0.0, it is used internally
 *	127.0.0.1 should never be reported, it is the localhost
 */
    if ((*(keydbt_data +3) & 0xFE) == 0 &&
	*(keydbt_data +2) == 0 &&
	*(keydbt_data +1) == 0)
	    return(NULL);

    else if ((dbtp_get(&dbtp, DBcontrib, (void *)keydbt_data,sizeof(in.s_addr))) == 0) {
	A_resp = (u_int32_t *)dbtp.mgdbt.data;
	stdErr_response = dbtp.mgdbt.data + INADDRSZ + 1;
    }
    else {
	A_resp = &stdResp.s_addr;
	stdErr_response = errIP();
    }
  }
  else if (dbtp_get(&dbtp,DBevidence,(void *)keydbt_data,sizeof(in.s_addr)) == 0) {
    A_resp = &stdResp.s_addr;
    stdErr_response = errIP();
  }
  else if (zflag == 0)
    return(NULL);			/* do not report promiscious contributions	*/
  else if ((dbtp_get(&dbtp, DBcontrib, (void *)keydbt_data,sizeof(in.s_addr))) == 0) {
    A_resp = (u_int32_t *)dbtp.mgdbt.data;
    if (*A_resp < stdRespBeg.s_addr || *A_resp > stdRespEnd.s_addr) {
	A_resp = &stdRespBeg.s_addr;
	stdErr_response = errIP();
    }
    else {
	stdErr_response = dbtp.mgdbt.data + INADDRSZ + 1;
    }
  }
  else {
/*	should not reach this, but will if used without data in either evidence or contrib	*/
    A_resp = &stdRespBeg.s_addr;
    stdErr_response = errIP();
  }
  return(A_resp);
}
/*	verify the message integrity, 
 *	prep message for sending
 *	return -1 on error
 *
 *	We know where the message buffer
 *	so that is not passed. New
 *	message is written over the old one.
 *
 *	input:	write file descriptor (socked fd),
 *		message length,
 *		mode flag
 *  is_tcp = 0	use UDP
 *  is_tcp = 1	normal, put as much in each message as possible
 *  is_tcp = 2  for AXFR, overhead records in first packet,
 *		as much as possible in subsequent packets
 *  is_tcp >=3  for AXFR, overhead records in first packet,
 *		numeric A+TXT records in individual packets
 *		followed by closing SOA packet
 */

int
munge_msg(int fd, size_t msglen, int is_tcp)
{
  extern u_char ns_msgbuf[];
  extern char * dbhome;
  extern char * errormsg;
  extern char * local_name;
  extern char * contact;
  extern int local_name_len;
  extern char * zone_name;
  extern int zone_name_len;
  extern int zoneEQlocal;
  extern DBTPD dbtp;
  extern struct in_addr stdResp, stdRespBeg, stdRespEnd, serial_rec;
  extern u_int32_t refresh, retry, expire, minimum, soa_ttl, localip[];
  extern int h_name_ctr, mxmark[], visibleMAXeth;
  extern int bflag, datalog, zflag, run, dflag;
  extern struct sockaddr_in client;
  extern struct sockaddr * cin_ptr;
  extern char mybuffer[], * rtn;
  extern int fdUDP, fdTCPlisten, fdTCP;
  extern char str30[], * stdErr_response;
  extern pid_t parent;
  
  register HEADER * hp = (HEADER *) ns_msgbuf;
  register u_char * cp;
  u_char * dnptrs[20], * rdlptr;		/* for dn_expand-comp	*/
  u_char **lastdnptr, **dpp;
  u_char dnbuf[MAXDNAME];			/* maximum expanded sz	*/
  int rcode;
  u_char * eom, * Txt_resp;
  int type, class, n, i, qnlen, AA = 0, TC = 0, ci;
  char * notThisA, * Hptr, hostpart[MAXDNAME], * cHptr;
  u_int32_t serial, ixfr_ser, axfrc = 0, * Aptr, * Astart, * A_resp;
  u_short len;
  struct in_addr rev;
  u_int ancount = 0, arcount = 0, nscount = 0, ixfrcnt;
  u_int tmsgcnt = 0, tancnt = 0, tarcnt = 0, tnscnt = 0, tbytcnt = 0;	/* statistics	*/
  size_t origqlen = msglen;
  
  do {	/* check for valid message for this zone	*/
/*	check header	*/
    if (msglen < sizeof(HEADER))	/* short message, ignore	*/
  	return(-1);

    if (hp->qr == 1)			/* response bit should be 0	*/
  	return(-1);			/* ignore			*/

    if (hp->opcode != 0) {		/* opcode should be Query	*/
	rcode = NOTIMP;
	break;
    }
    
/*	should be a single question and nothing more	*/
    if (ntohs(hp->qdcount) != 1 || hp->ancount || (ixfrcnt = ntohs(hp->nscount)) > 1 || hp->arcount) {
	rcode = FORMERR;
	break;
    }
/*	check question
 *
  The question section is used to carry the "question" in most queries,
  i.e., the parameters that define what is being asked.  The section
  contains QDCOUNT (usually 1) entries, each of the following format:

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                     QNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QTYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QCLASS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

    where:

    QNAME	a domain name represented as a sequence of labels, where
		each label consists of a length octet followed by that
		number of octets.  The domain name terminates with the
		zero length octet for the null label of the root.  Note
		that this field may be an odd number of octets; no
		padding is used.

 *
 */
  
    cp	= ns_msgbuf + HFIXEDSZ;
    eom	= ns_msgbuf + msglen;
/*	initialize send buffer	*/
    dpp = dnptrs;
    *dpp++ = ns_msgbuf;
    *dpp++ = ns_msgbuf + HFIXEDSZ;	/* question name	*/
    *dpp++ = NULL;
    lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];

/*	get name, stash it in dnbuf	*/
    if ((n = dn_expand(ns_msgbuf, eom, cp, dnbuf, MAXDNAME)) < 0) {
	rcode = FORMERR;
	break;
    }
    cp += n;
    GETSHORT(type, cp);
    GETSHORT(class, cp);

/* ************************************	*
 *	cp now points to ANSWER sect	*
 * ************************************	*
 */

    if (cp > eom) {
	rcode = FORMERR;
	break;
    }

    origqlen = cp - ns_msgbuf;		/* set error size to just question	*/

    if (class != C_IN) {
	rcode = REFUSED;
	break;
    }
    qnlen = strlen(dnbuf);		/* length of question name	*/
    if (qnlen < zone_name_len) {	/* too short, not this zone	*/
  	rcode = NXDOMAIN;
  	break;
    }
/*	name ends in zone name	*/
    if (strcasecmp((u_char *)(dnbuf - zone_name_len + qnlen), zone_name)) {
  	rcode = NXDOMAIN;		/* no match, not this zone	*/
	break;
    }
/*	name != zone name and zone name is not preceeded by '.'	*/
    if (qnlen != zone_name_len && *(u_char *)(dnbuf - zone_name_len + qnlen -1) != '.') {
    	rcode = NXDOMAIN;		
	break;
    }
    
    AA = 1;			/* we are authorative for this zone	*/
    rcode = NOERROR;
    break;
  } while(1);

  if (rcode != NOERROR) {
NS_errorExit:
    cp = ns_msgbuf + origqlen;	/* send back original message	*/
    goto NS_send;
  }

  if (datalog > 1 && cin_ptr != NULL) {			/* verbose logging	*/
    sprintf(mybuffer,"query type %d from %s",(u_char)type, inet_ntoa(client.sin_addr));
    LogPrint(mybuffer);
  }
  
  if (qnlen == zone_name_len) {	/* query is for zone name	*/
    switch (type) {
      case T_SOA:
    NS_send_SOA:
	RR_SOA(ancount);
/* AUTH	*/
	ADD_NS_names(nscount);
/* ADD	*/	
	ADD_NS_A(arcount);
	goto NS_send;
	break;
	
      case T_A:
      	if (zoneEQlocal) {
	  notThisA = local_name;
      	  ADD_local_A(ancount);
/* AUTH */
	  ADD_NS_names(nscount);
/* ADD  */
	  ADD_NS_A_without(notThisA,arcount);
	  goto NS_send;
	  break;
	} else {
/* AUTHority only	*/
	  goto NS_AuthOnly;
	  break;
	}
	
      case T_NS:
      	ADD_NS_names(ancount);
/* ADD  there is no authority section	*/
      	ADD_NS_A(arcount);
	goto NS_send;
      	break;

      case T_MX:
	ADD_MX_names(ancount);
/* AUTH */
	ADD_NS_names(nscount);
/* ADD  */
	ADD_ALL_A(arcount);
	goto NS_send;
	break;

      case T_ANY:
	notThisA = NULL;		/* flag to mark A record if used	*/
      	if (zoneEQlocal) {
	  notThisA = local_name;
	  ADD_local_A(ancount);
	}
	RR_SOA(ancount);
	ADD_NS_names(ancount);
	ADD_MX_names(ancount);
/* ADD  there is no authority section   */
	if (notThisA != NULL) {
	  ADD_ALL_A_without(notThisA,arcount);
	} else {
	  ADD_ALL_A(arcount);
	}
	goto NS_send;
	break;
	
      case T_IXFR:
	if (bflag)		/* if AXFR is blocked	*/
	  goto NS_AXFR_refuse;
	else if (ixfrcnt != 1) {
	  rcode = FORMERR;
	  goto NS_errorExit;
	}
/* if the serial record is missing, force the transfer	*/
	else if ((n = dbtp_get(&dbtp,DBtarpit,(void *)&serial_rec.s_addr,sizeof(serial_rec.s_addr))) == 0) {
	  serial = *(u_int32_t *)dbtp.mgdbt.data;
	  cp = name_skip(cp)
		 + INT16SZ			/* TYPE			*/
		 + INT16SZ			/* CLASS		*/
		 + INT32SZ			/* TTL			*/
		 + INT16SZ;			/* RDLENGTH		*/
	  cp = name_skip(cp);			/* skip past MNAME	*/
	  cp = name_skip(cp);			/* skip past RNAME	*/
	  ixfr_ser = ntohl(*(u_int32_t *)cp);
	  cp = ns_msgbuf + origqlen;		/* restore pointer to question end		*/	
	  i = cmp_serial(serial,ixfr_ser);
	  if (i < 1) {
	    RR_SOA(ancount);			/* send single SOA if IXFR is not needed	*/
	    goto NS_send;
	  }
	}
/* fall into AXFR	*/

      case T_AXFR:

	if (bflag) {		/* if AXFR is blocked	*/
  NS_AXFR_refuse:
	  rcode = REFUSED;
	  goto NS_errorExit;
	}
	else if (is_tcp == 0) {
	  TC = 1;
/* AUTH  there is no answer section
 *	  ADD_NS_names(nscount);
 *	  ADD_NS_A(arcount);
 *	  goto NS_send;
 * Just send a FORMERR
 */
 	  rcode = FORMERR;
 	  goto NS_errorExit;
	  break;
	}
	else {
/* ************************************************************	*
 *	This section is pretty much independent of the rest
 *	of this routine and requires its own initialization
 *	except for the first pass through the while loop.
 *	Counter 'axfrc' denotes the progress by marking that
 *	the first SOA has been sent and tracking subsequent db 
 *	accesses to retrieve numeric records
 * ************************************************************	*
 */

	  while(1) {
	    ancount = 0;
/* prep header		*/
	    if (axfrc == 0) {		/* first time, initialization completed already, cp correct	*/
	      axfrc = 1;
	/* question remains in header as does qdcount	*/
	      RR_SOA(ancount);		/* first element is SOA	*/
	      notThisA = NULL;		/* flag to mark A record if used	*/
	      if (zoneEQlocal) {
		notThisA = local_name;
	        ADD_local_A(ancount);
	      }
	      ADD_NS_names(ancount);
	      ADD_MX_names(ancount);
	      for(ci=0;ci<h_name_ctr;ci++) {
		cHptr = current_name(ci);
/*	name ends in zone name and is not local name	*/
		if (((len = strlen(cHptr)) >= zone_name_len) &&
		    (strcasecmp((u_char *)(cHptr - zone_name_len + len), zone_name) == 0) &&
		     strcasecmp((u_char *)cHptr,local_name)) {
		  Aptr = Astart = current_Astart(ci);
		  do {
		    RR_A(cHptr,Aptr,ancount);
		  } while((Aptr = next_A_record(Astart,Aptr)) != NULL);
		}
	      }
	      if (is_tcp > 1)
	      	goto NS_AXFR_send;
	      else
	      	goto NS_AXFR_more;
	    }
/* **************************************************** *
 *	overhead records complete, add numeric records	*
 * ****************************************************	*
 *	axfrc is > 0
 */
	    hp->qdcount = 0;		/* no question for subsequent packets	*/
	    cp  = ns_msgbuf + HFIXEDSZ;
/*	initialize send buffer	*/
	    dpp = dnptrs;
	    *dpp++ = ns_msgbuf;
	    *dpp++ = NULL;
	    lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];

	NS_AXFR_more:

	    do {

	NS_AXFR_next:
		if (dbtp_getrecno(&dbtp,DBtarpit, axfrc++))
			goto NS_AXFR_last_SOA;

/*	suppress numeric record for 127.0.0.0, it is used internally
	127.0.0.1 should never be reported, it is the localhost		*/

		in.s_addr = *(u_int32_t *)dbtp.keydbt.data;	/* propagate address to (in) for errIP() use	*/

		if ((A_resp = ns_response((u_char *)dbtp.keydbt.data)) == NULL)
			goto NS_AXFR_next;		/* do not report promiscious contributions      */

		bswap32((u_char *)&rev.s_addr,(u_char *)dbtp.keydbt.data);
		sprintf(dnbuf,"%s.%s",inet_ntoa(rev),zone_name);
		RR_A(dnbuf,A_resp,ancount);
		RR_TXT(dnbuf,stdErr_response,ancount);
	    } while (is_tcp < 3 && cp - ns_msgbuf < MSGsize);
/* prep header		*/
	NS_AXFR_send:
	    msglen = hp->opcode;	/* temporary storage	*/
	    CLEAR_FLAGS();		/* clear flags	*/
	    hp->qr	= 1;
	    hp->opcode	= msglen;
	    hp->aa	= AA;
	    hp->rcode	= NOERROR;
	    hp->ancount = htons(ancount);
	    hp->nscount	= 0;
	    hp->arcount	= 0;
	    msglen = cp - ns_msgbuf;
	    len = htons((u_short)msglen);
	/* collect statistics	*/
	    tancnt += ancount;
	    tbytcnt += msglen + sizeof(len);
	    tmsgcnt += 1;
	    writen(fd,(u_char *)&len,sizeof(len),is_tcp);
	    writen(fd, ns_msgbuf, msglen,is_tcp);
	  } /* end while 1 */

	NS_AXFR_last_SOA:
	  RR_SOA(ancount);
	  goto NS_send;
	  break;
	}
      default:
	rcode = NOTIMP;
	goto NS_errorExit;
      	break;

    }	/* end switch		*/
  }	/* end if zone request	*/


/*	parse out the host name	and lay
 *	a '\0' on the '.' separating zone name	*/
  memcpy(hostpart,dnbuf,(size_t)(qnlen - zone_name_len));
  *(char *)(hostpart + qnlen - zone_name_len - 1) = '\0';

/* iterator i, pointer Hptr are used in the macros and are replaced here by ci and cHptr	*/
  if (not_numericIP(hostpart)) {		/* if non numeric IP	*/
    if (type == T_A) {				/* only A records are available for our names	*/
      for(ci=0;ci<h_name_ctr;ci++) {
        cHptr = current_name(ci);
        if(strcasecmp(cHptr,dnbuf))		/* check for name as one of our NS or MX hosts	*/
      		continue;
        Aptr = Astart = current_Astart(ci);
        do {
      	  RR_A(cHptr,Aptr,ancount);
        } while((Aptr = next_A_record(Astart,Aptr)) != NULL);
        ADD_NS_names(nscount);
        ADD_NS_A_without(cHptr,arcount);
        break;
      }
      if (ci < h_name_ctr)
	  goto NS_send;		/* send if A record found	*/
      goto NS_AuthOnly;
    } else {
  NS_AuthOnly:
      rcode = NXDOMAIN;
      RR_SOA(nscount);
      goto NS_send;
    }
  }

  if (inet_aton(hostpart, &rev) == 0)		/* convert to network address	*/
	goto NS_AuthOnly;			/* punt on invalid address	*/
  bswap32((u_char *)&in.s_addr,(u_char *)&rev.s_addr); /* un-reverse IP address	*/
/*  in.s_addr = bswap_32(rev.s_addr);		un-reverse IP address	*/

/* dbtp_get returns 0 on success	*/
  if (dbtp_get(&dbtp, DBtarpit, (void *)(&in.s_addr), sizeof(in.s_addr)))	/* check if in tarpit		*/
  	goto NS_AuthOnly;

  if ((A_resp = ns_response((u_char *)(dbtp.keydbt.data))) == NULL)
  	goto NS_AuthOnly;			/* do not report promiscious contributions      */

  switch(type)
  {
    case T_ANY:
      RR_A(dnbuf,A_resp,ancount);
      RR_TXT(dnbuf,stdErr_response,ancount);
/* AUTH */
  NS_fin:
      ADD_NS_names(nscount);
/* ADD  */
      ADD_NS_A(arcount);
      goto NS_send;
      break;

    case T_A:
      RR_A(dnbuf,A_resp,ancount);
      goto NS_fin;
      break;
      
    case T_TXT:
      RR_TXT(dnbuf,stdErr_response,ancount);
      goto NS_fin;
      break;
      
    default:
	rcode = NOTIMP;
	goto NS_errorExit;
	break;
  }

  NS_send:
    msglen = hp->opcode;	/* temporary storage	*/
    CLEAR_FLAGS();		/* clear flags	*/
    hp->qr	= 1;
    hp->opcode	= msglen;
    hp->aa	= AA;
    hp->tc	= TC;
    hp->rcode	= rcode;
    hp->ancount	= htons(ancount);
    hp->nscount	= htons(nscount);
    hp->arcount	= htons(arcount);

  /* collect statistics	*/
  tancnt += ancount;
  tnscnt += nscount;
  tarcnt += arcount;
  tmsgcnt += 1;
  msglen = cp - ns_msgbuf;
  if (msglen > PACKETSZ && is_tcp == 0) {
    hp->tc = 1;
    msglen = PACKETSZ;
  }
  if (is_tcp) {		/* this is TCP, must prepend message length	*/
    len = htons((u_short)msglen);
    tbytcnt += sizeof(len);
    writen(fd,(u_char *)&len,sizeof(len),is_tcp);
  }
  tbytcnt += msglen;
  if (datalog > 1 && cin_ptr != NULL) {
    if (is_tcp)
      strcpy(mybuffer,"TCP ");
    else
      strcpy(mybuffer,"UDP ");
    sprintf((char *)(mybuffer +4),"type %d, to %s, answer %d, authority %d, additional %d, bytes %d, messages %d",
    	(u_char)type,inet_ntoa(client.sin_addr),tancnt,tnscnt,tarcnt,tbytcnt,tmsgcnt); 
    LogPrint(mybuffer);
  }
  return(writen(fd, ns_msgbuf, msglen, is_tcp));
}