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

replay.c

Go to the documentation of this file.
/*
 * testcode/replay.c - store and use a replay of events for the DNS resolver.
 *
 * Copyright (c) 2007, NLnet Labs. All rights reserved.
 * 
 * This software is open source.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * Neither the name of the NLNET LABS nor the names of its contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * \file
 * Store and use a replay of events for the DNS resolver.
 * Used to test known scenarios to get known outcomes.
 */

#include "config.h"
#include "util/log.h"
#include "util/net_help.h"
#include "testcode/replay.h"
#include "testcode/ldns-testpkts.h"

/** max length of lines in file */
00049 #define MAX_LINE_LEN 10240

/** parse keyword in string. 
 * @param line: if found, the line is advanced to after the keyword.
 * @param keyword: string.
 * @return: true if found, false if not. 
 */
static int 
00057 parse_keyword(char** line, char* keyword)
{
      size_t len = (size_t)strlen(keyword);
      if(strncmp(*line, keyword, len) == 0) {
            *line += len;
            return 1;
      }
      return 0;
}

/** delete moment */
static void
00069 replay_moment_delete(struct replay_moment* mom)
{
      if(!mom)
            return;
      if(mom->match) {
            delete_entry(mom->match);
      }
      free(mom);
}

/** delete range */
static void
00081 replay_range_delete(struct replay_range* rng)
{
      if(!rng)
            return;
      delete_entry(rng->match);
      free(rng);
}

/** strip whitespace from end of string */
static void
00091 strip_end_white(char* p)
{
      size_t i;
      for(i = strlen(p); i > 0; i--) {
            if(isspace((int)p[i-1]))
                  p[i-1] = 0;
            else return;
      }
}

/** 
 * Read a range from file. 
 * @param remain: Rest of line (after RANGE keyword).
 * @param in: file to read from.
 * @param name: name to print in errors.
 * @param lineno: incremented as lines are read.
 * @param line: line buffer.
 * @param ttl: for readentry
 * @param or: for readentry
 * @param prev: for readentry
 * @return: range object to add to list, or NULL on error.
 */
static struct replay_range*
00114 replay_range_read(char* remain, FILE* in, const char* name, int* lineno, 
      char* line, uint32_t* ttl, ldns_rdf** or, ldns_rdf** prev)
{
      struct replay_range* rng = (struct replay_range*)malloc(
            sizeof(struct replay_range));
      off_t pos;
      char *parse;
      struct entry* entry, *last = NULL;
      if(!rng)
            return NULL;
      memset(rng, 0, sizeof(*rng));
      /* read time range */
      if(sscanf(remain, " %d %d", &rng->start_step, &rng->end_step)!=2) {
            log_err("Could not read time range: %s", line);
            free(rng);
            return NULL;
      }
      /* read entries */
      pos = ftello(in);
      while(fgets(line, MAX_LINE_LEN-1, in)) {
            (*lineno)++;
            parse = line;
            while(isspace((int)*parse))
                  parse++;
            if(!*parse || *parse == ';') {
                  pos = ftello(in);
                  continue;
            }
            if(parse_keyword(&parse, "ADDRESS")) {
                  while(isspace((int)*parse))
                        parse++;
                  strip_end_white(parse);
                  if(!extstrtoaddr(parse, &rng->addr, &rng->addrlen)) {
                        log_err("Line %d: could not read ADDRESS: %s", 
                              *lineno, parse);
                        free(rng);
                        return NULL;
                  }
                  pos = ftello(in);
                  continue;
            }
            if(parse_keyword(&parse, "RANGE_END")) {
                  return rng;
            }
            /* set position before line; read entry */
            (*lineno)--;
            fseeko(in, pos, SEEK_SET);
            entry = read_entry(in, name, lineno, ttl, or, prev);
            if(!entry)
                  fatal_exit("%d: bad entry", *lineno);
            entry->next = NULL;
            if(last)
                  last->next = entry;
            else  rng->match = entry;
            last = entry;

            pos = ftello(in);
      }
      replay_range_delete(rng);
      return NULL;
}

/** 
 * Read a replay moment 'STEP' from file. 
 * @param remain: Rest of line (after STEP keyword).
 * @param in: file to read from.
 * @param name: name to print in errors.
 * @param lineno: incremented as lines are read.
 * @param ttl: for readentry
 * @param or: for readentry
 * @param prev: for readentry
 * @return: range object to add to list, or NULL on error.
 */
static struct replay_moment*
00188 replay_moment_read(char* remain, FILE* in, const char* name, int* lineno, 
      uint32_t* ttl, ldns_rdf** or, ldns_rdf** prev)
{
      struct replay_moment* mom = (struct replay_moment*)malloc(
            sizeof(struct replay_moment));
      int skip = 0;
      int readentry = 0;
      if(!mom)
            return NULL;
      memset(mom, 0, sizeof(*mom));
      if(sscanf(remain, " %d%n", &mom->time_step, &skip) != 1) {
            log_err("%d: cannot read number: %s", *lineno, remain);
            free(mom);
            return NULL;
      }
      remain += skip;
      while(isspace((int)*remain))
            remain++;
      if(parse_keyword(&remain, "NOTHING")) {
            mom->evt_type = repevt_nothing;
      } else if(parse_keyword(&remain, "QUERY")) {
            mom->evt_type = repevt_front_query;
            readentry = 1;
            if(!extstrtoaddr("127.0.0.1", &mom->addr, &mom->addrlen))
                  fatal_exit("internal error");
      } else if(parse_keyword(&remain, "CHECK_ANSWER")) {
            mom->evt_type = repevt_front_reply;
            readentry = 1;
      } else if(parse_keyword(&remain, "CHECK_OUT_QUERY")) {
            mom->evt_type = repevt_back_query;
            readentry = 1;
      } else if(parse_keyword(&remain, "REPLY")) {
            mom->evt_type = repevt_back_reply;
            readentry = 1;
      } else if(parse_keyword(&remain, "TIMEOUT")) {
            mom->evt_type = repevt_timeout;
      } else if(parse_keyword(&remain, "ERROR")) {
            mom->evt_type = repevt_error;
      } else {
            log_err("%d: unknown event type %s", *lineno, remain);
            free(mom);
            return NULL;
      }
      while(isspace((int)*remain))
            remain++;
      if(parse_keyword(&remain, "ADDRESS")) {
            while(isspace((int)*remain))
                  remain++;
            if(strlen(remain) > 0) /* remove \n */
                  remain[strlen(remain)-1] = 0;
            printf("remain '%s'\n", remain);
            if(!extstrtoaddr(remain, &mom->addr, &mom->addrlen)) {
                  log_err("line %d: could not parse ADDRESS: %s", 
                        *lineno, remain);
                  free(mom);
                  return NULL;
            }
      } 

      if(readentry) {
            mom->match = read_entry(in, name, lineno, ttl, or, prev);
            if(!mom->match) {
                  free(mom);
                  return NULL;
            }
      }

      return mom;
}

/** makes scenario with title on rest of line */
static struct replay_scenario*
00260 make_scenario(char* line)
{
      struct replay_scenario* scen;
      while(isspace((int)*line))
            line++;
      if(!*line) {
            log_err("scenario: no title given");
            return NULL;
      }
      scen = (struct replay_scenario*)malloc(sizeof(struct replay_scenario));
      if(!scen)
            return NULL;
      memset(scen, 0, sizeof(*scen));
      scen->title = strdup(line);
      if(!scen->title) {
            free(scen);
            return NULL;
      }
      return scen;
}

struct replay_scenario* 
00282 replay_scenario_read(FILE* in, const char* name, int* lineno)
{
      char line[MAX_LINE_LEN];
      char *parse;
      struct replay_scenario* scen = NULL;
      uint32_t ttl = 3600;
      ldns_rdf* or = NULL;
      ldns_rdf* prev = NULL;
      line[MAX_LINE_LEN-1]=0;

      while(fgets(line, MAX_LINE_LEN-1, in)) {
            parse=line;
            (*lineno)++;
            while(isspace((int)*parse))
                  parse++;
            if(!*parse) 
                  continue; /* empty line */
            if(parse_keyword(&parse, ";"))
                  continue; /* comment */
            if(parse_keyword(&parse, "SCENARIO_BEGIN")) {
                  scen = make_scenario(parse);
                  if(!scen)
                        fatal_exit("%d: could not make scen", *lineno);
                  continue;
            } 
            if(!scen)
                  fatal_exit("%d: expected SCENARIO", *lineno);
            if(parse_keyword(&parse, "RANGE_BEGIN")) {
                  struct replay_range* newr = replay_range_read(parse, 
                        in, name, lineno, line, &ttl, &or, &prev);
                  if(!newr)
                        fatal_exit("%d: bad range", *lineno);
                  newr->next_range = scen->range_list;
                  scen->range_list = newr;
            } else if(parse_keyword(&parse, "STEP")) {
                  struct replay_moment* mom = replay_moment_read(parse, 
                        in, name, lineno, &ttl, &or, &prev);
                  if(!mom)
                        fatal_exit("%d: bad moment", *lineno);
                  if(scen->mom_last && 
                        scen->mom_last->time_step >= mom->time_step)
                        fatal_exit("%d: time goes backwards", *lineno);
                  if(scen->mom_last)
                        scen->mom_last->mom_next = mom;
                  else  scen->mom_first = mom;
                  scen->mom_last = mom;
            } else if(parse_keyword(&parse, "SCENARIO_END")) {
                  struct replay_moment *p = scen->mom_first;
                  int num = 0;
                  while(p) {
                        num++;
                        p = p->mom_next;
                  }
                  log_info("Scenario has %d steps", num);
                  ldns_rdf_deep_free(or);
                  ldns_rdf_deep_free(prev);
                  return scen;
            }
      }
      ldns_rdf_deep_free(or);
      ldns_rdf_deep_free(prev);
      replay_scenario_delete(scen);
      return NULL;
}

void 
00348 replay_scenario_delete(struct replay_scenario* scen)
{
      struct replay_moment* mom, *momn;
      struct replay_range* rng, *rngn;
      if(!scen)
            return;
      if(scen->title)
            free(scen->title);
      mom = scen->mom_first;
      while(mom) {
            momn = mom->mom_next;
            replay_moment_delete(mom);
            mom = momn;
      }
      rng = scen->range_list;
      while(rng) {
            rngn = rng->next_range;
            replay_range_delete(rng);
            rng = rngn;
      }
      free(scen);
}

Generated by  Doxygen 1.6.0   Back to index