/**************************************************************************** 
** File: dns.c
**
** Author: Mike Borella
**
** Comments: Dump DNS header information
**
** $Id: dns.c,v 1.21 2002/01/03 00:04:01 mborella Exp $
**
** 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 Library 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 "dns.h"
#include "ns_labels.h"

#define HOLDER_SIZE 256

/*
 * QR flag map
 */

strmap_t dns_qrflag_map [] =
{
  { DNS_QRFLAG_QUERY,     "query" },
  { DNS_QRFLAG_RESPONSE,  "response" },
  { 0, "" }
};

/*
 * Opcode flag map
 */

strmap_t dns_opcodeflag_map [] =
{
  { DNS_OPCODEFLAG_STANDARD,    "standard" },
  { DNS_OPCODEFLAG_INVERSE,     "inverse" },
  { DNS_OPCODEFLAG_STATUS,      "status" },
  { 0, "" }
};


/*
 * Rcode (return code) flag map
 */

strmap_t dns_rcodeflag_map [] =
{
  { DNS_RCODEFLAG_NOERROR,          "no error" },
  { DNS_RCODEFLAG_FORMATERROR,      "format error" },
  { DNS_RCODEFLAG_SERVERERROR,      "server error" },
  { DNS_RCODEFLAG_NAMEERROR,        "name error" },
  { DNS_RCODEFLAG_NOTIMPLEMENTED,   "not implemented" },
  { DNS_RCODEFLAG_SERVICEREFUSED,   "service refused" },
  { 0, "" }
};

/*
 * Query type map
 */

strmap_t dns_querytype_map [] =
{
  { DNS_QUERYTYPE_A,           "A - IP address" },
  { DNS_QUERYTYPE_NS,          "NS - name server" },
  { DNS_QUERYTYPE_CNAME,       "CNAME - canonical name" },
  { DNS_QUERYTYPE_SOA,         "SOA - start of auth record" },
  { DNS_QUERYTYPE_PTR,         "PTR - pointer record" },
  { DNS_QUERYTYPE_HINFO,       "HINFO - host info" },
  { DNS_QUERYTYPE_MX,          "MX - mail exchange record" },
  { DNS_QUERYTYPE_AAAA,        "AAAA - IPv6 address" },
  { DNS_QUERYTYPE_AXFR,        "AXFR - zone transfer request" },
  { DNS_QUERYTYPE_ANY,         "ANY - all records request" },
  { 0, "" }
};

/*
 * Query class map
 */

strmap_t dns_queryclass_map [] =
{
  { DNS_QUERYCLASS_IP,         "Internet" },
  { 0, "" }
};

extern struct arg_t *my_args;

/*----------------------------------------------------------------------------
**
** dump_dns_questions()
**
** Parse DNS questions and display them
**
**----------------------------------------------------------------------------
*/

void dump_dns_questions(packet_t *pkt, u_int8_t num)
{
  char holder[HOLDER_SIZE];
  u_int16_t query_type, query_class;

  while(num > 0)
    {
      /*
       * Parse the name
       */
      
      parse_ns_labels(pkt, holder);
      
      /*
       * Parse the query type and class
       */
      
      if (get_packet_bytes((u_int8_t *) &query_type, pkt, 2) == 0)
	return;
      if (get_packet_bytes((u_int8_t *) &query_class, pkt, 2) == 0)
	return;
      
      /*
       * Conversions
       */
      
      query_type = ntohs(query_type);
      query_class = ntohs(query_class);
      
      /*
       * Dump the info
       */
      
      if (my_args->m)
	{
	  display_minimal_string(holder);
	  display_minimal_string(" ");
	}
      else
	{
	  display_string("Query", holder); 
	  snprintf(holder, HOLDER_SIZE, "%d (%s)", query_type, 
		  map2str(dns_querytype_map, query_type));
	  display_string("  Query type", holder); 
	  snprintf(holder, HOLDER_SIZE, "%d (%s)", query_class, 
		  map2str(dns_queryclass_map, query_class));
	  display_string("  Query class", holder); 
	}

      num --;
    }
}

/*----------------------------------------------------------------------------
**
** dump_dns_answers()
**
** Parse DNS answers and display them
**
**----------------------------------------------------------------------------
*/
void dump_dns_answers(packet_t *pkt, u_int8_t num, char *answer_type)
{
  char holder[HOLDER_SIZE];
  u_int16_t query_type, query_class;
  u_int32_t ttl;
  u_int16_t rdl;
  u_int8_t resource_data[64];

  while(num > 0)
    {
      /*
       * Parse the name
       */
      
      parse_ns_labels(pkt, holder);
      
      /*
       * Parse the query type and class
       */
      
      if (get_packet_bytes((u_int8_t *) &query_type, pkt, 2) == 0)
	return;
      if (get_packet_bytes((u_int8_t *) &query_class, pkt, 2) == 0)
	return;
      if (get_packet_bytes((u_int8_t *) &ttl, pkt, 4) == 0)
	return;
      if (get_packet_bytes((u_int8_t *) &rdl, pkt, 2) == 0)
	return;
      
      /*
       * Conversions
       */
      
      query_type = ntohs(query_type);
      query_class = ntohs(query_class);
      ttl = ntohl(ttl);
      rdl = ntohs(rdl);

      /*
       * Get the resource data
       */

      switch (query_type)
	{
	case DNS_QUERYTYPE_NS:
	case DNS_QUERYTYPE_CNAME:
	case DNS_QUERYTYPE_SOA:
	case DNS_QUERYTYPE_PTR:
	  parse_ns_labels(pkt, resource_data);
	  break;

	case DNS_QUERYTYPE_A:
	default:
	  if (get_packet_bytes((u_int8_t *) &resource_data, pkt, 
			       rdl) == 0)
	    return;
	  break;
	  
	}

      /*
       * Dump the info
       */
      
      if (my_args->m)
	{
	  if (query_type == DNS_QUERYTYPE_A && !strcmp(answer_type, "Answers"))
	    {
	      display_minimal_ipv4((u_int8_t *) resource_data);
	      display_minimal_string(" ");
	    }
	  if (query_type == DNS_QUERYTYPE_AAAA && 
	      !strcmp(answer_type, "Answers") && rdl == 16 )
	    {
	      display_minimal_ipv6((u_int8_t *) resource_data);
	      display_minimal_string(" ");
	    }
	}
      else
	{
	  display_string(answer_type, holder); 
	  snprintf(holder, HOLDER_SIZE, "%d (%s)", query_type, 
		  map2str(dns_querytype_map, query_type));
	  display_string("  Query type", holder); 
	  snprintf(holder, HOLDER_SIZE, "%d (%s)", query_class, 
		  map2str(dns_queryclass_map, query_class));
	  display_string("  Query class", holder); 
	  display("  TTL", (u_int8_t *) &ttl, 4, DISP_DEC);
	  display("  Resource data length", (u_int8_t *) &rdl, 2, 
		  DISP_DEC);
	  switch(query_type)
	    {
	    case DNS_QUERYTYPE_A:
	      display_ipv4("  Resource data", (u_int8_t *) resource_data);
	      break;

	    case DNS_QUERYTYPE_AAAA:
	      display_ipv6("  Resource data", (u_int8_t *) resource_data);
	      break;

	    case DNS_QUERYTYPE_NS:
	    case DNS_QUERYTYPE_CNAME:
	    case DNS_QUERYTYPE_SOA:
	    case DNS_QUERYTYPE_PTR:
	      display_string("  Resource data", resource_data);
	      break;
	      
	    default:
	      display("  Resource data", (u_int8_t *) resource_data, rdl, 
		      DISP_HEX);	      
	    }
	}
      
      num --;
    }

}

/*----------------------------------------------------------------------------
**
** dump_dns()
**
** Parse DNS packet and dump fields
**
**----------------------------------------------------------------------------
*/

void dump_dns(packet_t *pkt)
{
  dns_header_t dns;
  u_int8_t f_qr, f_opcode, f_aa, f_tc, f_rd, f_ra, f_zero, f_rcode;
  char holder[HOLDER_SIZE];

  /* Set the layer */
  set_layer(LAYER_APPLICATION);

  /*
   * Mark the beginning of the DNS portion so that labels can be stored
   * properly
   */

  set_packet_mark(pkt);

  /*
   * Reset the DNS labels structures for a new DNS packet
   */

  reset_nslabels();

  /*
   * Get the header
   */

  if (get_packet_bytes((u_int8_t *) &dns, pkt, 12) == 0)
    return;
  
  /*
   * Conversions
   */

  dns.id = ntohs(dns.id);
  dns.number_questions = ntohs(dns.number_questions);
  dns.number_answers = ntohs(dns.number_answers);
  dns.number_authority = ntohs(dns.number_authority);
  dns.number_additional = ntohs(dns.number_additional);
  f_qr = dns.flag_qr;
  f_opcode = dns.flag_opcode;
  f_aa = dns.flag_aa;
  f_tc = dns.flag_tc;
  f_rd = dns.flag_rd;
  f_ra = dns.flag_ra;
  f_zero = dns.flag_zero;
  f_rcode = dns.flag_rcode;
  
  /*
   * Print it
   */

  if (my_args->m)
    {
      display_minimal_string("| DNS ");
      display_minimal_string(map2str(dns_qrflag_map, f_qr));
      display_minimal_string(" ");
    }
  else
    {
      /* announcement */
      display_header_banner("DNS Header");
	      
      /* identification */
      display("Identification", (u_int8_t *) &dns.id, 2, DISP_DEC);

      /* flags */
      snprintf(holder, HOLDER_SIZE, "%d (%s)", f_qr, 
	       map2str(dns_qrflag_map, f_qr));
      display_string("Flag query/response", holder);
      snprintf(holder, HOLDER_SIZE, "%d (%s)", f_opcode, 
	      map2str(dns_opcodeflag_map, f_opcode));
      display_string("Flag opcode", holder);
      display("Flag auth answer", (u_int8_t *) &f_aa, 1, DISP_BINNLZ);
      display("Flag trunctated", (u_int8_t *) &f_tc, 1, DISP_BINNLZ);
      display("Flag recursion desired", (u_int8_t *) &f_ra, 1, DISP_BINNLZ);
      display("Flag recursion available", (u_int8_t *) &f_ra, 1, DISP_BINNLZ);
      display("Flag zero", (u_int8_t *) &f_zero, 1, DISP_BINNLZ);
      snprintf(holder, HOLDER_SIZE, "%d (%s)", f_rcode, 
	      map2str(dns_rcodeflag_map, f_rcode));      
      display_string("Flag return code", holder);

      /* numbers of questions and answers */
      display("# of questions", (u_int8_t *) &dns.number_questions, 2, 
	      DISP_DEC);
      display("# of answers", (u_int8_t *) &dns.number_answers, 2, 
	      DISP_DEC);
      display("# of authorization RRs", (u_int8_t *) &dns.number_authority, 2, 
	      DISP_DEC);
      display("# of additional RRs", (u_int8_t *) &dns.number_additional, 2, 
	      DISP_DEC);
    }

  /*
   * Parse the question and answers
   */

  dump_dns_questions(pkt, dns.number_questions);
  dump_dns_answers(pkt, dns.number_answers, "Answers");
  dump_dns_answers(pkt, dns.number_authority, "Auth answers");
  dump_dns_answers(pkt, dns.number_additional, "Addtl answers");

  /* dump the hex buffer */
  hexbuffer_flush();

}





