Logo Search packages:      
Sourcecode: unbound version File versions  Download package

ldns-testpkts.c

Go to the documentation of this file.
/*
 * ldns-testpkts. Data file parse for test packets, and query matching.
 *
 * Data storage for specially crafted replies for testing purposes.
 *
 * (c) NLnet Labs, 2005, 2006, 2007, 2008
 * See the file LICENSE for the license
 */

/**
 * \file
 * This is a debugging aid. It is not efficient, especially
 * with a long config file, but it can give any reply to any query.
 * This can help the developer pre-script replies for queries.
 *
 * You can specify a packet RR by RR with header flags to return.
 *
 * Missing features:
 *          - matching content different from reply content.
 *          - find way to adjust mangled packets?
 */

#include "config.h"
struct sockaddr_storage;
#include <ldns/ldns.h>
#include <errno.h>
#include "ldns-testpkts.h"

/** max line length */
00030 #define MAX_LINE   10240      
/** string to show in warnings and errors */
00032 static const char* prog_name = "ldns-testpkts";

/** logging routine, provided by caller */
void verbose(int lvl, const char* msg, ...);

/** print error and exit */
00038 static void error(const char* msg, ...)
{
      va_list args;
      va_start(args, msg);
      fprintf(stderr, "%s error: ", prog_name);
      vfprintf(stderr, msg, args);
      fprintf(stderr, "\n");
      fflush(stderr);
      va_end(args);
      exit(EXIT_FAILURE);
}

/** return if string is empty or comment */
00051 static bool isendline(char c)
{
      if(c == ';' || c == '#' 
            || c == '\n' || c == 0)
            return true;
      return false;
}

/** true if the string starts with the keyword given. Moves the str ahead. 
 * @param str: before keyword, afterwards after keyword and spaces.
 * @param keyword: the keyword to match
 * @return: true if keyword present. False otherwise, and str unchanged.
*/
00064 static bool str_keyword(const char** str, const char* keyword)
{
      size_t len = strlen(keyword);
      assert(str && keyword);
      if(strncmp(*str, keyword, len) != 0)
            return false;
      *str += len;
      while(isspace((int)**str))
            (*str)++;
      return true;
}

/** Add reply packet to entry */
static struct reply_packet*
00078 entry_add_reply(struct entry* entry) 
{
      struct reply_packet* pkt = (struct reply_packet*)malloc(
            sizeof(struct reply_packet));
      struct reply_packet ** p = &entry->reply_list;
      pkt->next = NULL;
      pkt->packet_sleep = 0;
      pkt->reply = ldns_pkt_new();
      pkt->reply_from_hex = NULL;
      /* link at end */
      while(*p)
            p = &((*p)->next);
      *p = pkt;
      return pkt;
}

/** parse MATCH line */
00095 static void matchline(const char* line, struct entry* e)
{
      const char* parse = line;
      while(*parse) {
            if(isendline(*parse)) 
                  return;
            if(str_keyword(&parse, "opcode")) {
                  e->match_opcode = true;
            } else if(str_keyword(&parse, "qtype")) {
                  e->match_qtype = true;
            } else if(str_keyword(&parse, "qname")) {
                  e->match_qname = true;
            } else if(str_keyword(&parse, "all")) {
                  e->match_all = true;
            } else if(str_keyword(&parse, "ttl")) {
                  e->match_ttl = true;
            } else if(str_keyword(&parse, "DO")) {
                  e->match_do = true;
            } else if(str_keyword(&parse, "UDP")) {
                  e->match_transport = transport_udp;
            } else if(str_keyword(&parse, "TCP")) {
                  e->match_transport = transport_tcp;
            } else if(str_keyword(&parse, "serial")) {
                  e->match_serial = true;
                  if(*parse != '=' && *parse != ':')
                        error("expected = or : in MATCH: %s", line);
                  parse++;
                  e->ixfr_soa_serial = (uint32_t)strtol(parse, (char**)&parse, 10);
                  while(isspace((int)*parse)) 
                        parse++;
            } else {
                  error("could not parse MATCH: '%s'", parse);
            }
      }
}

/** parse REPLY line */
00132 static void replyline(const char* line, ldns_pkt *reply)
{
      const char* parse = line;
      while(*parse) {
            if(isendline(*parse)) 
                  return;
                  /* opcodes */
            if(str_keyword(&parse, "QUERY")) {
                  ldns_pkt_set_opcode(reply, LDNS_PACKET_QUERY);
            } else if(str_keyword(&parse, "IQUERY")) {
                  ldns_pkt_set_opcode(reply, LDNS_PACKET_IQUERY);
            } else if(str_keyword(&parse, "STATUS")) {
                  ldns_pkt_set_opcode(reply, LDNS_PACKET_STATUS);
            } else if(str_keyword(&parse, "NOTIFY")) {
                  ldns_pkt_set_opcode(reply, LDNS_PACKET_NOTIFY);
            } else if(str_keyword(&parse, "UPDATE")) {
                  ldns_pkt_set_opcode(reply, LDNS_PACKET_UPDATE);
                  /* rcodes */
            } else if(str_keyword(&parse, "NOERROR")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_NOERROR);
            } else if(str_keyword(&parse, "FORMERR")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_FORMERR);
            } else if(str_keyword(&parse, "SERVFAIL")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_SERVFAIL);
            } else if(str_keyword(&parse, "NXDOMAIN")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_NXDOMAIN);
            } else if(str_keyword(&parse, "NOTIMPL")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_NOTIMPL);
            } else if(str_keyword(&parse, "REFUSED")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_REFUSED);
            } else if(str_keyword(&parse, "YXDOMAIN")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_YXDOMAIN);
            } else if(str_keyword(&parse, "YXRRSET")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_YXRRSET);
            } else if(str_keyword(&parse, "NXRRSET")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_NXRRSET);
            } else if(str_keyword(&parse, "NOTAUTH")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_NOTAUTH);
            } else if(str_keyword(&parse, "NOTZONE")) {
                  ldns_pkt_set_rcode(reply, LDNS_RCODE_NOTZONE);
                  /* flags */
            } else if(str_keyword(&parse, "QR")) {
                  ldns_pkt_set_qr(reply, true);
            } else if(str_keyword(&parse, "AA")) {
                  ldns_pkt_set_aa(reply, true);
            } else if(str_keyword(&parse, "TC")) {
                  ldns_pkt_set_tc(reply, true);
            } else if(str_keyword(&parse, "RD")) {
                  ldns_pkt_set_rd(reply, true);
            } else if(str_keyword(&parse, "CD")) {
                  ldns_pkt_set_cd(reply, true);
            } else if(str_keyword(&parse, "RA")) {
                  ldns_pkt_set_ra(reply, true);
            } else if(str_keyword(&parse, "AD")) {
                  ldns_pkt_set_ad(reply, true);
            } else if(str_keyword(&parse, "DO")) {
                  ldns_pkt_set_edns_udp_size(reply, 4096);
                  ldns_pkt_set_edns_do(reply, true);
            } else {
                  error("could not parse REPLY: '%s'", parse);
            }
      }
}

/** parse ADJUST line */
00197 static void adjustline(const char* line, struct entry* e, 
      struct reply_packet* pkt)
{
      const char* parse = line;
      while(*parse) {
            if(isendline(*parse)) 
                  return;
            if(str_keyword(&parse, "copy_id")) {
                  e->copy_id = true;
            } else if(str_keyword(&parse, "copy_query")) {
                  e->copy_query = true;
            } else if(str_keyword(&parse, "sleep=")) {
                  e->sleeptime = (unsigned int) strtol(parse, (char**)&parse, 10);
                  while(isspace((int)*parse)) 
                        parse++;
            } else if(str_keyword(&parse, "packet_sleep=")) {
                  pkt->packet_sleep = (unsigned int) strtol(parse, (char**)&parse, 10);
                  while(isspace((int)*parse)) 
                        parse++;
            } else {
                  error("could not parse ADJUST: '%s'", parse);
            }
      }
}

/** create new entry */
00223 static struct entry* new_entry()
{
      struct entry* e = LDNS_MALLOC(struct entry);
      memset(e, 0, sizeof(e));
      e->match_opcode = false;
      e->match_qtype = false;
      e->match_qname = false;
      e->match_all = false;
      e->match_ttl = false;
      e->match_do = false;
      e->match_serial = false;
      e->ixfr_soa_serial = 0;
      e->match_transport = transport_any;
      e->reply_list = NULL;
      e->copy_id = false;
      e->copy_query = false;
      e->sleeptime = 0;
      e->next = NULL;
      return e;
}

/**
 * Converts a hex string to binary data
 * @param hexstr: string of hex.
 * @param len: is the length of the string
 * @param buf: is the buffer to store the result in
 * @param offset: is the starting position in the result buffer
 * @param buf_len: is the length of buf.
 *
 * This function returns the length of the result
 */
static size_t
00255 hexstr2bin(char *hexstr, int len, uint8_t *buf, size_t offset, size_t buf_len)
{
      char c;
      int i; 
      uint8_t int8 = 0;
      int sec = 0;
      size_t bufpos = 0;
      
      if (len % 2 != 0) {
            return 0;
      }

      for (i=0; i<len; i++) {
            c = hexstr[i];

            /* case insensitive, skip spaces */
            if (c != ' ') {
                  if (c >= '0' && c <= '9') {
                        int8 += c & 0x0f;  
                  } else if (c >= 'a' && c <= 'z') {
                        int8 += (c & 0x0f) + 9;   
                  } else if (c >= 'A' && c <= 'Z') {
                        int8 += (c & 0x0f) + 9;   
                  } else {
                        return 0;
                  }
                   
                  if (sec == 0) {
                        int8 = int8 << 4;
                        sec = 1;
                  } else {
                        if (bufpos + offset + 1 <= buf_len) {
                              buf[bufpos+offset] = int8;
                              int8 = 0;
                              sec = 0; 
                              bufpos++;
                        } else {
                              fprintf(stderr, "Buffer too small in hexstr2bin");
                        }
                  }
            }
        }
        return bufpos;
}

/** convert hex buffer to binary buffer */
static ldns_buffer *
00302 data_buffer2wire(ldns_buffer *data_buffer)
{
      ldns_buffer *wire_buffer = NULL;
      int c;
      
      /* stat hack
       * 0 = normal
       * 1 = comment (skip to end of line)
       * 2 = unprintable character found, read binary data directly
       */
      size_t data_buf_pos = 0;
      int state = 0;
      uint8_t *hexbuf;
      int hexbufpos = 0;
      size_t wirelen;
      uint8_t *data_wire = (uint8_t *) ldns_buffer_export(data_buffer);
      uint8_t *wire = LDNS_XMALLOC(uint8_t, LDNS_MAX_PACKETLEN);
      
      hexbuf = LDNS_XMALLOC(uint8_t, LDNS_MAX_PACKETLEN);
      for (data_buf_pos = 0; data_buf_pos < ldns_buffer_position(data_buffer); data_buf_pos++) {
            c = (int) data_wire[data_buf_pos];
            
            if (state < 2 && !isascii(c)) {
                  /*verbose("non ascii character found in file: (%d) switching to raw mode\n", c);*/
                  state = 2;
            }
            switch (state) {
                  case 0:
                        if (  (c >= '0' && c <= '9') ||
                              (c >= 'a' && c <= 'f') ||
                              (c >= 'A' && c <= 'F') )
                        {
                              hexbuf[hexbufpos] = (uint8_t) c;
                              hexbufpos++;
                        } else if (c == ';') {
                              state = 1;
                        } else if (c == ' ' || c == '\t' || c == '\n') {
                              /* skip whitespace */
                        } 
                        break;
                  case 1:
                        if (c == '\n' || c == EOF) {
                              state = 0;
                        }
                        break;
                  case 2:
                        hexbuf[hexbufpos] = (uint8_t) c;
                        hexbufpos++;
                        break;
                  default:
                        error("unknown state while reading");
                        LDNS_FREE(hexbuf);
                        return 0;
                        break;
            }
      }

      if (hexbufpos >= LDNS_MAX_PACKETLEN) {
            /*verbose("packet size reached\n");*/
      }
      
      /* lenient mode: length must be multiple of 2 */
      if (hexbufpos % 2 != 0) {
            hexbuf[hexbufpos] = (uint8_t) '0';
            hexbufpos++;
      }

      if (state < 2) {
            wirelen = hexstr2bin((char *) hexbuf, hexbufpos, wire, 0, LDNS_MAX_PACKETLEN);
            wire_buffer = ldns_buffer_new(wirelen);
            ldns_buffer_new_frm_data(wire_buffer, wire, wirelen);
      } else {
            error("Incomplete hex data, not at byte boundary\n");
      }
      LDNS_FREE(wire);
      LDNS_FREE(hexbuf);
      return wire_buffer;
}     

/** parse ORIGIN */
static void 
00383 get_origin(const char* name, int lineno, ldns_rdf** origin, char* parse)
{
      /* snip off rest of the text so as to make the parse work in ldns */
      char* end;
      char store;
      ldns_status status;

      ldns_rdf_free(*origin);
      *origin = NULL;

      end=parse;
      while(!isspace((int)*end) && !isendline(*end))
            end++;
      store = *end;
      *end = 0;
      verbose(3, "parsing '%s'\n", parse);
      status = ldns_str2rdf_dname(origin, parse);
      *end = store;
      if (status != LDNS_STATUS_OK)
            error("%s line %d:\n\t%s: %s", name, lineno,
            ldns_get_errorstr_by_id(status), parse);
}

/* Reads one entry from file. Returns entry or NULL on error. */
struct entry*
00408 read_entry(FILE* in, const char* name, int *lineno, uint32_t* default_ttl, 
      ldns_rdf** origin, ldns_rdf** prev_rr)
{
      struct entry* current = NULL;
      char line[MAX_LINE];
      const char* parse;
      ldns_pkt_section add_section = LDNS_SECTION_QUESTION;
      struct reply_packet *cur_reply = NULL;
      bool reading_hex = false;
      ldns_buffer* hex_data_buffer = NULL;

      while(fgets(line, (int)sizeof(line), in) != NULL) {
            line[MAX_LINE-1] = 0;
            parse = line;
            (*lineno) ++;
            
            while(isspace((int)*parse))
                  parse++;
            /* test for keywords */
            if(isendline(*parse))
                  continue; /* skip comment and empty lines */
            if(str_keyword(&parse, "ENTRY_BEGIN")) {
                  if(current) {
                        error("%s line %d: previous entry does not ENTRY_END", 
                              name, *lineno);
                  }
                  current = new_entry();
                  current->lineno = *lineno;
                  cur_reply = entry_add_reply(current);
                  continue;
            } else if(str_keyword(&parse, "$ORIGIN")) {
                  get_origin(name, *lineno, origin, (char*)parse);
                  continue;
            } else if(str_keyword(&parse, "$TTL")) {
                  *default_ttl = (uint32_t)atoi(parse);
                  continue;
            }

            /* working inside an entry */
            if(!current) {
                  error("%s line %d: expected ENTRY_BEGIN but got %s", 
                        name, *lineno, line);
            }
            if(str_keyword(&parse, "MATCH")) {
                  matchline(parse, current);
            } else if(str_keyword(&parse, "REPLY")) {
                  replyline(parse, cur_reply->reply);
            } else if(str_keyword(&parse, "ADJUST")) {
                  adjustline(parse, current, cur_reply);
            } else if(str_keyword(&parse, "EXTRA_PACKET")) {
                  cur_reply = entry_add_reply(current);
            } else if(str_keyword(&parse, "SECTION")) {
                  if(str_keyword(&parse, "QUESTION"))
                        add_section = LDNS_SECTION_QUESTION;
                  else if(str_keyword(&parse, "ANSWER"))
                        add_section = LDNS_SECTION_ANSWER;
                  else if(str_keyword(&parse, "AUTHORITY"))
                        add_section = LDNS_SECTION_AUTHORITY;
                  else if(str_keyword(&parse, "ADDITIONAL"))
                        add_section = LDNS_SECTION_ADDITIONAL;
                  else error("%s line %d: bad section %s", name, *lineno, parse);
            } else if(str_keyword(&parse, "HEX_ANSWER_BEGIN")) {
                  hex_data_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN);
                  reading_hex = true;
            } else if(str_keyword(&parse, "HEX_ANSWER_END")) {
                  if (!reading_hex) {
                        error("%s line %d: HEX_ANSWER_END read but no HEX_ANSWER_BEGIN keyword seen", name, *lineno);
                  }
                  reading_hex = false;
                  cur_reply->reply_from_hex = data_buffer2wire(hex_data_buffer);
                  ldns_buffer_free(hex_data_buffer);
            } else if(str_keyword(&parse, "ENTRY_END")) {
                  return current;
            } else if(reading_hex) {
                  ldns_buffer_printf(hex_data_buffer, line);
            } else {
                  /* it must be a RR, parse and add to packet. */
                  ldns_rr* n = NULL;
                  ldns_status status;
                  status = ldns_rr_new_frm_str(&n, parse, *default_ttl, 
                        *origin, prev_rr);
                  if (status != LDNS_STATUS_OK)
                        error("%s line %d:\n\t%s: %s", name, *lineno,
                              ldns_get_errorstr_by_id(status), parse);
                  ldns_pkt_push_rr(cur_reply->reply, add_section, n);
            }

      }
      if (reading_hex) {
            error("%s: End of file reached while still reading hex, "
                  "missing HEX_ANSWER_END\n", name);
      }
      if(current) {
            error("%s: End of file reached while reading entry. "
                  "missing ENTRY_END\n", name);
      }
      return 0;
}

/* reads the canned reply file and returns a list of structs */
struct entry* 
00509 read_datafile(const char* name)
{
      struct entry* list = NULL;
      struct entry* last = NULL;
      struct entry* current = NULL;
      FILE *in;
      int lineno = 0;
      uint32_t default_ttl = 0;
      ldns_rdf* origin = NULL;
      ldns_rdf* prev_rr = NULL;
      int entry_num = 0;

      if((in=fopen(name, "r")) == NULL) {
            error("could not open file %s: %s", name, strerror(errno));
      }

      while((current = read_entry(in, name, &lineno, &default_ttl, 
            &origin, &prev_rr)))
      {
            if(last)
                  last->next = current;
            else  list = current;
            last = current;
            entry_num ++;
      }
      verbose(1, "%s: Read %d entries\n", prog_name, entry_num);

      fclose(in);
      ldns_rdf_deep_free(origin);
      ldns_rdf_deep_free(prev_rr);
      return list;
}

/** get qtype from rr */
00543 static ldns_rr_type get_qtype(ldns_pkt* p)
{
      if(!ldns_rr_list_rr(ldns_pkt_question(p), 0))
            return 0;
      return ldns_rr_get_type(ldns_rr_list_rr(ldns_pkt_question(p), 0));
}

/** returns owner from rr */
00551 static ldns_rdf* get_owner(ldns_pkt* p)
{
      if(!ldns_rr_list_rr(ldns_pkt_question(p), 0))
            return NULL;
      return ldns_rr_owner(ldns_rr_list_rr(ldns_pkt_question(p), 0));
}

/** get authority section SOA serial value */
00559 static uint32_t get_serial(ldns_pkt* p)
{
      ldns_rr *rr = ldns_rr_list_rr(ldns_pkt_authority(p), 0);
      ldns_rdf *rdf;
      uint32_t val;
      if(!rr) return 0;
      rdf = ldns_rr_rdf(rr, 2);
      if(!rdf) return 0;
      val = ldns_rdf2native_int32(rdf);
      verbose(3, "found serial %u in msg. ", (int)val);
      return val;
}

/** match two rr lists */
static int
00574 match_list(ldns_rr_list* q, ldns_rr_list *p, bool mttl)
{
      size_t i;
      if(ldns_rr_list_rr_count(q) != ldns_rr_list_rr_count(p))
            return 0;
      for(i=0; i<ldns_rr_list_rr_count(q); i++)
      {
            if(ldns_rr_compare(ldns_rr_list_rr(q, i), 
                  ldns_rr_list_rr(p, i)) != 0) {
                  verbose(3, "rr %d different", i);
                  return 0;
            }
            if(mttl && ldns_rr_ttl(ldns_rr_list_rr(q, i)) !=
                  ldns_rr_ttl(ldns_rr_list_rr(p, i))) {
                  verbose(3, "rr %d ttl different", i);
                  return 0;
            }
      }
      return 1;
}

/** compare two booleans */
static int
00597 cmp_bool(int x, int y)
{
      if(!x && !y) return 0;
      if(x && y) return 0;
      if(!x) return -1;
      return 1;
}

/** match all of the packet */
static int
00607 match_all(ldns_pkt* q, ldns_pkt* p, bool mttl)
{
      if(ldns_pkt_get_opcode(q) != ldns_pkt_get_opcode(p)) 
      { verbose(3, "allmatch: opcode different"); return 0;}
      if(ldns_pkt_get_rcode(q) != ldns_pkt_get_rcode(p))
      { verbose(3, "allmatch: rcode different"); return 0;}
      if(ldns_pkt_id(q) != ldns_pkt_id(p))
      { verbose(3, "allmatch: id different"); return 0;}
      if(cmp_bool(ldns_pkt_qr(q), ldns_pkt_qr(p)) != 0)
      { verbose(3, "allmatch: qr different"); return 0;}
      if(cmp_bool(ldns_pkt_aa(q), ldns_pkt_aa(p)) != 0)
      { verbose(3, "allmatch: aa different"); return 0;}
      if(cmp_bool(ldns_pkt_tc(q), ldns_pkt_tc(p)) != 0)
      { verbose(3, "allmatch: tc different"); return 0;}
      if(cmp_bool(ldns_pkt_rd(q), ldns_pkt_rd(p)) != 0)
      { verbose(3, "allmatch: rd different"); return 0;}
      if(cmp_bool(ldns_pkt_cd(q), ldns_pkt_cd(p)) != 0)
      { verbose(3, "allmatch: cd different"); return 0;}
      if(cmp_bool(ldns_pkt_ra(q), ldns_pkt_ra(p)) != 0)
      { verbose(3, "allmatch: ra different"); return 0;}
      if(cmp_bool(ldns_pkt_ad(q), ldns_pkt_ad(p)) != 0)
      { verbose(3, "allmatch: ad different"); return 0;}
      if(ldns_pkt_qdcount(q) != ldns_pkt_qdcount(p))
      { verbose(3, "allmatch: qdcount different"); return 0;}
      if(ldns_pkt_ancount(q) != ldns_pkt_ancount(p))
      { verbose(3, "allmatch: ancount different"); return 0;}
      if(ldns_pkt_nscount(q) != ldns_pkt_nscount(p))
      { verbose(3, "allmatch: nscount different"); return 0;}
      if(ldns_pkt_arcount(q) != ldns_pkt_arcount(p))
      { verbose(3, "allmatch: arcount different"); return 0;}
      if(!match_list(ldns_pkt_question(q), ldns_pkt_question(p), mttl))
      { verbose(3, "allmatch: qd section different"); return 0;}
      if(!match_list(ldns_pkt_answer(q), ldns_pkt_answer(p), mttl))
      { verbose(3, "allmatch: an section different"); return 0;}
      if(!match_list(ldns_pkt_authority(q), ldns_pkt_authority(p), mttl))
      { verbose(3, "allmatch: ar section different"); return 0;}
      if(!match_list(ldns_pkt_additional(q), ldns_pkt_additional(p), mttl)) 
      { verbose(3, "allmatch: ns section different"); return 0;}
      return 1;
}

/* finds entry in list, or returns NULL */
struct entry* 
00650 find_match(struct entry* entries, ldns_pkt* query_pkt,
      enum transport_type transport)
{
      struct entry* p = entries;
      ldns_pkt* reply = NULL;
      for(p=entries; p; p=p->next) {
            verbose(3, "comparepkt: ");
            reply = p->reply_list->reply;
            if(p->match_opcode && ldns_pkt_get_opcode(query_pkt) != 
                  ldns_pkt_get_opcode(reply)) {
                  verbose(3, "bad opcode\n");
                  continue;
            }
            if(p->match_qtype && get_qtype(query_pkt) != get_qtype(reply)) {
                  verbose(3, "bad qtype\n");
                  continue;
            }
            if(p->match_qname) {
                  if(!get_owner(query_pkt) || !get_owner(reply) ||
                        ldns_dname_compare(
                        get_owner(query_pkt), get_owner(reply)) != 0) {
                        verbose(3, "bad qname\n");
                        continue;
                  }
            }
            if(p->match_serial && get_serial(query_pkt) != p->ixfr_soa_serial) {
                        verbose(3, "bad serial\n");
                        continue;
            }
            if(p->match_do && !ldns_pkt_edns_do(query_pkt)) {
                  verbose(3, "no DO bit set\n");
                  continue;
            }
            if(p->match_transport != transport_any && p->match_transport != transport) {
                  verbose(3, "bad transport\n");
                  continue;
            }
            if(p->match_all && !match_all(query_pkt, reply, p->match_ttl)) {
                  verbose(3, "bad allmatch\n");
                  continue;
            }
            verbose(3, "match!\n");
            return p;
      }
      return NULL;
}

void
00698 adjust_packet(struct entry* match, ldns_pkt* answer_pkt, ldns_pkt* query_pkt)
{
      /* copy & adjust packet */
      if(match->copy_id)
            ldns_pkt_set_id(answer_pkt, ldns_pkt_id(query_pkt));
      if(match->copy_query) {
            ldns_rr_list* list = ldns_pkt_get_section_clone(query_pkt,
                  LDNS_SECTION_QUESTION);
            ldns_rr_list_deep_free(ldns_pkt_question(answer_pkt));
            ldns_pkt_set_question(answer_pkt, list);
      }
      if(match->sleeptime > 0) {
            verbose(3, "sleeping for %d seconds\n", match->sleeptime);
            sleep(match->sleeptime);
      }
}

/*
 * Parses data buffer to a query, finds the correct answer 
 * and calls the given function for every packet to send.
 */
void
00720 handle_query(uint8_t* inbuf, ssize_t inlen, struct entry* entries, int* count,
      enum transport_type transport, void (*sendfunc)(uint8_t*, size_t, void*),
      void* userdata, FILE* verbose_out)
{
      ldns_status status;
      ldns_pkt *query_pkt = NULL;
      ldns_pkt *answer_pkt = NULL;
      struct reply_packet *p;
      ldns_rr *query_rr = NULL;
      uint8_t *outbuf = NULL;
      size_t answer_size = 0;
      struct entry* entry = NULL;
      ldns_rdf *stop_command = ldns_dname_new_frm_str("server.stop.");

      status = ldns_wire2pkt(&query_pkt, inbuf, (size_t)inlen);
      if (status != LDNS_STATUS_OK) {
            verbose(1, "Got bad packet: %s\n", ldns_get_errorstr_by_id(status));
            ldns_rdf_free(stop_command);
            return;
      }
      
      query_rr = ldns_rr_list_rr(ldns_pkt_question(query_pkt), 0);
      verbose(1, "query %d: id %d: %s %d bytes: ", ++(*count), (int)ldns_pkt_id(query_pkt), 
            (transport==transport_tcp)?"TCP":"UDP", inlen);
      if(verbose_out) ldns_rr_print(verbose_out, query_rr);
      if(verbose_out) ldns_pkt_print(verbose_out, query_pkt);

      if (ldns_rr_get_type(query_rr) == LDNS_RR_TYPE_TXT &&
          ldns_rr_get_class(query_rr) == LDNS_RR_CLASS_CH &&
          ldns_dname_compare(ldns_rr_owner(query_rr), stop_command) == 0) {
            exit(0);
        }
      
      /* fill up answer packet */
      entry = find_match(entries, query_pkt, transport);
      if(!entry || !entry->reply_list) {
            verbose(1, "no answer packet for this query, no reply.\n");
            ldns_pkt_free(query_pkt);
            ldns_rdf_free(stop_command);
            return;
      }
      for(p = entry->reply_list; p; p = p->next)
      {
            verbose(3, "Answer pkt:\n");
            if (p->reply_from_hex) {
                  /* try to parse the hex packet, if it can be
                   * parsed, we can use adjust rules. if not,
                   * send packet literally */
                  status = ldns_buffer2pkt_wire(&answer_pkt, p->reply_from_hex);
                  if (status == LDNS_STATUS_OK) {
                        adjust_packet(entry, answer_pkt, query_pkt);
                        if(verbose_out) ldns_pkt_print(verbose_out, answer_pkt);
                        status = ldns_pkt2wire(&outbuf, answer_pkt, &answer_size);
                        verbose(2, "Answer packet size: %u bytes.\n", (unsigned int)answer_size);
                        if (status != LDNS_STATUS_OK) {
                              verbose(1, "Error creating answer: %s\n", ldns_get_errorstr_by_id(status));
                              ldns_pkt_free(query_pkt);
                              ldns_rdf_free(stop_command);
                              return;
                        }
                        ldns_pkt_free(answer_pkt);
                        answer_pkt = NULL;
                  } else {
                        verbose(3, "Could not parse hex data (%s), sending hex data directly.\n", ldns_get_errorstr_by_id(status));
                        /* still try to adjust ID */
                        answer_size = ldns_buffer_capacity(p->reply_from_hex);
                        outbuf = LDNS_XMALLOC(uint8_t, answer_size);
                        memcpy(outbuf, ldns_buffer_export(p->reply_from_hex), answer_size);
                        if(entry->copy_id) {
                              ldns_write_uint16(outbuf, 
                                    ldns_pkt_id(query_pkt));
                        }
                  }
            } else {
                  answer_pkt = ldns_pkt_clone(p->reply);
                  adjust_packet(entry, answer_pkt, query_pkt);
                  if(verbose_out) ldns_pkt_print(verbose_out, answer_pkt);
                  status = ldns_pkt2wire(&outbuf, answer_pkt, &answer_size);
                  verbose(1, "Answer packet size: %u bytes.\n", (unsigned int)answer_size);
                  if (status != LDNS_STATUS_OK) {
                        verbose(1, "Error creating answer: %s\n", ldns_get_errorstr_by_id(status));
                        ldns_pkt_free(query_pkt);
                        ldns_rdf_free(stop_command);
                        return;
                  }
                  ldns_pkt_free(answer_pkt);
                  answer_pkt = NULL;
            }
            if(p->packet_sleep) {
                  verbose(3, "sleeping for next packet %d secs\n", 
                        p->packet_sleep);
                  sleep(p->packet_sleep);
                  verbose(3, "wakeup for next packet "
                        "(slept %d secs)\n", p->packet_sleep);
            }
            sendfunc(outbuf, answer_size, userdata);
            LDNS_FREE(outbuf);
            outbuf = NULL;
            answer_size = 0;
      }
      ldns_pkt_free(query_pkt);
      ldns_rdf_free(stop_command);
}

/** delete the list of reply packets */
00825 void delete_replylist(struct reply_packet* replist)
{
      struct reply_packet *p=replist, *np;
      while(p) {
            np = p->next;
            ldns_pkt_free(p->reply);
            ldns_buffer_free(p->reply_from_hex);
            free(p);
            p=np;
      }
}

00837 void delete_entry(struct entry* list)
{
      struct entry *p=list, *np;
      while(p) {
            np = p->next;
            delete_replylist(p->reply_list);
            free(p);
            p = np;
      }
}

Generated by  Doxygen 1.6.0   Back to index