queryperf.c revision e2cf0e8ff9ff37121518af5b34b9e4de7abbb47c
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
1b5a34533410ff4eaff0e5b5b110221a97e29cfcAutomatic Updater * Copyright (C) 2000, 2001 Nominum, Inc.
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews *
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Permission to use, copy, modify, and distribute this software for any
ec5347e2c775f027573ce5648b910361aa926c01Automatic Updater * purpose with or without fee is hereby granted, provided that the above
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * copyright notice and this permission notice appear in all copies.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
dafcb997e390efa4423883dafd100c975c4095d6Mark Andrews * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
ec5347e2c775f027573ce5648b910361aa926c01Automatic Updater/***
ab023a65562e62b85a824509d829b6fad87e00b1Rob Austein *** DNS Query Performance Testing Tool (queryperf.c)
ab023a65562e62b85a824509d829b6fad87e00b1Rob Austein ***
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *** Version $Id: queryperf.c,v 1.5 2002/07/22 02:47:24 marka Exp $
8804fd9936acd703073c4a75072852c38738a990Brian Wellington ***
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *** Stephen Jacob <sj@nominum.com>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington ***/
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <sys/time.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <sys/types.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <sys/socket.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <stdio.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <stdlib.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <string.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <limits.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <time.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <unistd.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <netdb.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <netinet/in.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <arpa/nameser.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <resolv.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <math.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#include <errno.h>
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Configuration defaults
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define DEF_MAX_QUERIES_OUTSTANDING 20
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define DEF_QUERY_TIMEOUT 5 /* in seconds */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define DEF_SERVER_TO_QUERY "localhost"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define DEF_SERVER_PORT 53
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define DEF_BUFFER_SIZE 32 /* in k */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Other constants / definitions
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington#define COMMENT_CHAR ';'
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define CONFIG_CHAR '#'
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define MAX_PORT 65535
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define MAX_INPUT_LEN 512
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define MAX_DOMAIN_LEN 255
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define MAX_BUFFER_LEN 8192 /* in bytes */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define HARD_TIMEOUT_EXTRA 5 /* in seconds */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define RESPONSE_BLOCKING_WAIT_TIME 0.1 /* in seconds */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define EDNSLEN 11
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define FALSE 0
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define TRUE 1
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define WHITESPACE " \t\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonenum directives_enum { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT };
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define DIRECTIVES { "server", "port", "maxqueries", "maxwait" }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define DIR_VALUES { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define QTYPE_STRINGS { \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "MR", "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "AAAA", "AXFR", "MAILB", "MAILA", "*", "ANY" \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington}
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define QTYPE_CODES { \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington 1, 2, 3, 4, 5, 6, 7, 8, \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington 9, 10, 11, 12, 13, 14, 15, 16, \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington 28, 252, 253, 254, 255, 255 \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington}
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define RCODE_STRINGS { \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "rcode12", "rcode13", "rcode14", "rcode15" \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington}
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Data type definitions
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington */
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define QUERY_STATUS_MAGIC 0x51535441U /* QSTA */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define VALID_QUERY_STATUS(q) ((q) != NULL && \
8804fd9936acd703073c4a75072852c38738a990Brian Wellington (q)->magic == QUERY_STATUS_MAGIC)
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonstruct query_status {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington unsigned int magic;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington int in_use;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington unsigned short int id;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington struct timeval sent_timestamp;
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington char *desc;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington};
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Configuration options (global)
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonunsigned int max_queries_outstanding; /* init 0 */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonunsigned int query_timeout = DEF_QUERY_TIMEOUT;
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint ignore_config_changes = FALSE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonunsigned int socket_bufsize = DEF_BUFFER_SIZE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint use_stdin = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonchar *datafile_name; /* init NULL */
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonchar *server_to_query; /* init NULL */
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonunsigned int server_port = DEF_SERVER_PORT;
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonint run_only_once = FALSE;
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonint use_timelimit = FALSE;
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonunsigned int run_timelimit; /* init 0 */
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint serverset = FALSE, portset = FALSE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint queriesset = FALSE, timeoutset = FALSE;
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellingtonint edns = FALSE, dnssec = FALSE;
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellingtonint countrcodes = FALSE;
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellingtonint rcodecounts[16] = {0};
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellingtonint verbose = FALSE;
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington/*
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington * Other global stuff
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington */
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellingtonint setup_phase = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian WellingtonFILE *datafile_ptr; /* init NULL */
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellingtonunsigned int runs_through_file; /* init 0 */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonunsigned int num_queries_sent; /* init 0 */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonunsigned int num_queries_outstanding; /* init 0 */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonunsigned int num_queries_timed_out; /* init 0 */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonstruct timeval time_of_program_start;
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonstruct timeval time_of_first_query;
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonstruct timeval time_of_end_of_run;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonstruct query_status *status; /* init NULL */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonunsigned int query_status_allocated; /* init 0 */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint query_socket; /* init 0 */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonstruct sockaddr_in qaddr;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonstatic char *rcode_strings[] = RCODE_STRINGS;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington/*
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington * get_uint16:
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington * Get an unsigned short integer from a buffer (in network order)
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington */
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonstatic unsigned short
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonget_uint16(unsigned char *buf) {
93d6dfaf66258337985427c86181f01fc51f0bb4Mark Andrews unsigned short ret;
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington ret = buf[0] * 256 + buf[1];
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington return (ret);
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington}
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington/*
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington * show_startup_info:
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington * Show name/version
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington */
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonvoid
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonshow_startup_info(void) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington printf("\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington"DNS Query Performance Testing Tool\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington"Version: $Id: queryperf.c,v 1.5 2002/07/22 02:47:24 marka Exp $\n"
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews"\n");
8804fd9936acd703073c4a75072852c38738a990Brian Wellington}
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
c03bb27f0675a6e60ceea66b451548e8481bc05cMark Andrews/*
e407562a75eb93073bb72089cced150d7ffe4d4fTatuya JINMEI 神明達哉 * show_usage:
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Print out usage/syntax information
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonvoid
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonshow_usage(void) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr,
8804fd9936acd703073c4a75072852c38738a990Brian Wellington"\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington"Usage: queryperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" [-b bufsize] [-t timeout] [-n] [-l limit] [-1]\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -d specifies the input data file (default: stdin)\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -s sets the server to query (default: %s)\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -p sets the port on which to query the server (default: %u)\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -q specifies the maximum number of queries outstanding (default: %d)\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -t specifies the timeout for query completion in seconds (default: %d)\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -n causes configuration changes to be ignored\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -l specifies how a limit for how long to run tests in seconds (no default)\n"
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews" -1 run through input only once (default: multiple iff limit given)\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -b set input/output buffer size in kilobytes (default: %d k)\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -e enable EDNS 0\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -D set the DNSSEC OK bit (implies EDNS)\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -c print the number of packets with each rcode\n"
8804fd9936acd703073c4a75072852c38738a990Brian Wellington" -v verbose: report the RCODE of each response on stdout\n"
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington"\n",
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington DEF_SERVER_TO_QUERY, DEF_SERVER_PORT,
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews DEF_MAX_QUERIES_OUTSTANDING, DEF_QUERY_TIMEOUT,
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews DEF_BUFFER_SIZE);
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews}
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews/*
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews * set_datafile:
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews * Set the datafile to read
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews *
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews * Return -1 on failure
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington * Return a non-negative integer otherwise
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonset_datafile(char *new_file) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington char *dfname_tmp;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington if ((new_file == NULL) || (new_file[0] == '\0')) {
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington fprintf(stderr, "Error: null datafile name\n");
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington return (-1);
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington }
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington if ((dfname_tmp = malloc(strlen(new_file) + 1)) == NULL) {
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington fprintf(stderr, "Error allocating memory for datafile name: "
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "%s\n", new_file);
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews return (-1);
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington free(datafile_name);
2674e1a455d4f71de09b2b60e7a8304b9a305588Mark Andrews datafile_name = dfname_tmp;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington strcpy(datafile_name, new_file);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington use_stdin = FALSE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington return (0);
2674e1a455d4f71de09b2b60e7a8304b9a305588Mark Andrews}
2674e1a455d4f71de09b2b60e7a8304b9a305588Mark Andrews
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * set_input_stdin:
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Set the input to be stdin (instead of a datafile)
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonvoid
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonset_input_stdin(void) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington use_stdin = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington free(datafile_name);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington datafile_name = NULL;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington}
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington/*
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington * set_server:
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington * Set the server to be queried
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington *
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington * Return -1 on failure
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington * Return a non-negative integer otherwise
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonset_server(char *new_name) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington static struct hostent *server_he;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington /* If no change in server name, don't do anything... */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if ((server_to_query != NULL) && (new_name != NULL))
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (strcmp(new_name, server_to_query) == 0)
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (0);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if ((new_name == NULL) || (new_name[0] == '\0')) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr, "Error: null server name\n");
368b37b616234fce3d23099eb180f1dd38e1fb62Mark Andrews return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
b7cd261f2fca2c7138cdc6ae8ee434e9c0031303Brian Wellington free(server_to_query);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington server_to_query = NULL;
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if ((server_to_query = malloc(strlen(new_name) + 1)) == NULL) {
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews fprintf(stderr, "Error allocating memory for server name: "
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews "%s\n", new_name);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
e407562a75eb93073bb72089cced150d7ffe4d4fTatuya JINMEI 神明達哉 if ((server_he = gethostbyname(new_name)) == NULL) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr, "Error: gethostbyname(\"%s\") failed\n",
8804fd9936acd703073c4a75072852c38738a990Brian Wellington new_name);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington strcpy(server_to_query, new_name);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington qaddr.sin_addr = *((struct in_addr *)server_he->h_addr);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (0);
368b37b616234fce3d23099eb180f1dd38e1fb62Mark Andrews}
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * set_server_port:
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Set the port on which to contact the server
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Return -1 if port is invalid
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Return a non-negative integer otherwise
e407562a75eb93073bb72089cced150d7ffe4d4fTatuya JINMEI 神明達哉 */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonset_server_port(unsigned int new_port) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (new_port > MAX_PORT)
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington else {
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews server_port = new_port;
a56f5ada432128085e4a06815328023ee0c9610dMark Andrews qaddr.sin_port = htons(server_port);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (0);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington}
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * is_digit:
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Tests if a character is a digit
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington *
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington * Return TRUE if it is
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Return FALSE if it is not
86c3cd20dd2aff4b1c52ce13d8587abd8d2bd16cBrian Wellington */
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonint
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellingtonis_digit(char d) {
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington if (d < '0' || d > '9')
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington return (FALSE);
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington else
42b48d11ca7b296324d7a8a98cdbf0070b0deb1dMark Andrews return (TRUE);
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington}
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * is_uint:
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Tests if a string, test_int, is a valid unsigned integer
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Sets *result to be the unsigned integer if it is valid
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Return TRUE if it is
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Return FALSE if it is not
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonis_uint(char *test_int, unsigned int *result) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington unsigned long int value;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington char *end;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (test_int == NULL)
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (FALSE);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (is_digit(test_int[0]) == FALSE)
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (FALSE);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington value = strtoul(test_int, &end, 10);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if ((errno == ERANGE) || (*end != '\0') || (value > UINT_MAX))
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (FALSE);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *result = (unsigned int)value;
78838d3e0cd62423c23de5503910e01884d2104bBrian Wellington return (TRUE);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington}
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * set_max_queries:
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Set the maximum number of outstanding queries
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Returns -1 on failure
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Returns a non-negative integer otherwise
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonset_max_queries(unsigned int new_max) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington static unsigned int size_qs = sizeof(struct query_status);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington struct query_status *temp_stat;
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington unsigned int count;
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington if (new_max < 0) {
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington fprintf(stderr, "Unable to change max outstanding queries: "
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington "must be positive and non-zero: %u\n", new_max);
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington return (-1);
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington }
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (new_max > query_status_allocated) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington temp_stat = realloc(status, new_max * size_qs);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (temp_stat == NULL) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr, "Error resizing query_status\n");
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington } else {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington status = temp_stat;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington /*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Be careful to only initialise between above
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * the previously allocated space. Note that the
f3ca27e9fe307b55e35ea8d7b37351650630e5a3Andreas Gustafsson * allocation may be larger than the current
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * max_queries_outstanding. We don't want to
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * "forget" any outstanding queries! We might
df925e6c66d45d960fbac0383169763967d2111cEvan Hunt * still have some above the bounds of the max.
df925e6c66d45d960fbac0383169763967d2111cEvan Hunt */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington count = query_status_allocated;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington for (; count < new_max; count++) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington status[count].in_use = FALSE;
f3ca27e9fe307b55e35ea8d7b37351650630e5a3Andreas Gustafsson status[count].magic = QUERY_STATUS_MAGIC;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington status[count].desc = NULL;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington query_status_allocated = new_max;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington max_queries_outstanding = new_max;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (0);
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington}
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington
13090db2b1c210b8386793ff0cbbb0b4348c1ecaBrian Wellington/*
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * parse_args:
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Parse program arguments and set configuration options
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Return -1 on failure
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Return a non-negative integer otherwise
8804fd9936acd703073c4a75072852c38738a990Brian Wellington */
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonint
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonparse_args(int argc, char **argv) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington int c;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington unsigned int uint_arg_val;
b7cd261f2fca2c7138cdc6ae8ee434e9c0031303Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington while ((c = getopt(argc, argv, "q:t:nd:s:p:1l:b:eDcv")) != -1) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington switch (c) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington case 'q':
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (is_uint(optarg, &uint_arg_val) == TRUE) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington set_max_queries(uint_arg_val);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington queriesset = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington } else {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr, "Option requires a positive "
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "integer value: -%c %s\n",
8804fd9936acd703073c4a75072852c38738a990Brian Wellington c, optarg);
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington break;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington case 't':
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (is_uint(optarg, &uint_arg_val) == TRUE) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington query_timeout = uint_arg_val;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington timeoutset = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington } else {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr, "Option requires a positive "
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "integer value: -%c %s\n",
8804fd9936acd703073c4a75072852c38738a990Brian Wellington c, optarg);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington break;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington case 'n':
8804fd9936acd703073c4a75072852c38738a990Brian Wellington ignore_config_changes = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington break;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
df925e6c66d45d960fbac0383169763967d2111cEvan Hunt case 'd':
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (set_datafile(optarg) == -1) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr, "Error setting datafile "
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "name: %s\n", optarg);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington break;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington case 's':
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (set_server(optarg) == -1) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr, "Error setting server "
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "name: %s\n", optarg);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington serverset = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington break;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington case 'p':
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (is_uint(optarg, &uint_arg_val) == TRUE &&
8804fd9936acd703073c4a75072852c38738a990Brian Wellington uint_arg_val < MAX_PORT)
8804fd9936acd703073c4a75072852c38738a990Brian Wellington {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington set_server_port(uint_arg_val);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington portset = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington } else {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr, "Option requires a positive "
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "integer between 0 and %d: -%c %s\n",
8804fd9936acd703073c4a75072852c38738a990Brian Wellington MAX_PORT - 1, c, optarg);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (-1);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington }
8804fd9936acd703073c4a75072852c38738a990Brian Wellington break;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington
8804fd9936acd703073c4a75072852c38738a990Brian Wellington case '1':
8804fd9936acd703073c4a75072852c38738a990Brian Wellington run_only_once = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington break;
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington
e3e3bafa138a20558a2253470effc01702fc6dfdBrian Wellington case 'l':
8804fd9936acd703073c4a75072852c38738a990Brian Wellington if (is_uint(optarg, &uint_arg_val) == TRUE) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington use_timelimit = TRUE;
8804fd9936acd703073c4a75072852c38738a990Brian Wellington run_timelimit = uint_arg_val;
df925e6c66d45d960fbac0383169763967d2111cEvan Hunt } else {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington fprintf(stderr, "Option requires a positive "
8804fd9936acd703073c4a75072852c38738a990Brian Wellington "integer: -%c %s\n",
8804fd9936acd703073c4a75072852c38738a990Brian Wellington c, optarg);
return (-1);
}
break;
case 'b':
if (is_uint(optarg, &uint_arg_val) == TRUE) {
socket_bufsize = uint_arg_val;
} else {
fprintf(stderr, "Option requires a positive "
"integer: -%c %s\n",
c, optarg);
return (-1);
}
break;
case 'e':
edns = TRUE;
break;
case 'D':
dnssec = TRUE;
edns = TRUE;
break;
case 'c':
countrcodes = TRUE;
break;
case 'v':
verbose = 1;
break;
default:
fprintf(stderr, "Invalid option: -%c\n", optopt);
return (-1);
}
}
if (run_only_once == FALSE && use_timelimit == FALSE)
run_only_once = TRUE;
return (0);
}
/*
* open_datafile:
* Open the data file ready for reading
*
* Return -1 on failure
* Return non-negative integer on success
*/
int
open_datafile(void) {
if (use_stdin == TRUE) {
datafile_ptr = stdin;
return (0);
} else {
if ((datafile_ptr = fopen(datafile_name, "r")) == NULL) {
fprintf(stderr, "Error: unable to open datafile: %s\n",
datafile_name);
return (-1);
} else
return (0);
}
}
/*
* close_datafile:
* Close the data file if any is open
*
* Return -1 on failure
* Return non-negative integer on success, including if not needed
*/
int
close_datafile(void) {
if ((use_stdin == FALSE) && (datafile_ptr != NULL)) {
if (fclose(datafile_ptr) != 0) {
fprintf(stderr, "Error: unable to close datafile\n");
return (-1);
}
}
return (0);
}
/*
* open_socket:
* Open a socket for the queries
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
open_socket(void) {
int sock;
struct protoent *proto;
struct sockaddr_in bind_addr;
int ret;
int bufsize;
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = htons(0); /* Have bind allocate a random port */
bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bzero(&(bind_addr.sin_zero), 8);
if ((proto = getprotobyname("udp")) == NULL) {
fprintf(stderr, "Error: getprotobyname call failed");
return (-1);
}
if ((sock = socket(PF_INET, SOCK_DGRAM, proto->p_proto)) == -1) {
fprintf(stderr, "Error: socket call failed");
return (-1);
}
if (bind(sock, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr))
== -1) {
fprintf(stderr, "Error: bind call failed");
return (-1);
}
bufsize = 1024 * socket_bufsize;
ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
(char *) &bufsize, sizeof(bufsize));
if (ret < 0)
fprintf(stderr, "Warning: setsockbuf(SO_RCVBUF) failed\n");
ret = setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
(char *) &bufsize, sizeof(bufsize));
if (ret < 0)
fprintf(stderr, "Warning: setsockbuf(SO_SNDBUF) failed\n");
query_socket = sock;
return (0);
}
/*
* close_socket:
* Close the query socket
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
close_socket(void) {
if (query_socket != 0) {
if (close(query_socket) != 0) {
fprintf(stderr, "Error: unable to close socket\n");
return (-1);
}
}
query_socket = 0;
return (0);
}
/*
* setup:
* Set configuration options from command line arguments
* Open datafile ready for reading
*
* Return -1 on failure
* Return non-negative integer on success
*/
int
setup(int argc, char **argv) {
qaddr.sin_family = AF_INET;
qaddr.sin_port = htons(0);
qaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bzero(&(qaddr.sin_zero), 8);
set_input_stdin();
if (set_max_queries(DEF_MAX_QUERIES_OUTSTANDING) == -1) {
fprintf(stderr, "%s: Unable to set default max outstanding "
"queries\n", argv[0]);
return (-1);
}
if (set_server(DEF_SERVER_TO_QUERY) == -1) {
fprintf(stderr, "%s: Error setting default server name\n",
argv[0]);
return (-1);
}
if (set_server_port(DEF_SERVER_PORT) == -1) {
fprintf(stderr, "%s: Error setting default server port\n",
argv[0]);
return (-1);
}
if (parse_args(argc, argv) == -1) {
show_usage();
return (-1);
}
if (open_datafile() == -1)
return (-1);
if (open_socket() == -1)
return (-1);
return (0);
}
/*
* set_timenow:
* Set a timeval struct to indicate the current time
*/
void
set_timenow(struct timeval *tv) {
if (gettimeofday(tv, NULL) == -1) {
fprintf(stderr, "Error in gettimeofday(). Using inaccurate "
"time() instead\n");
tv->tv_sec = time(NULL);
tv->tv_usec = 0;
}
}
/*
* difftv:
* Find the difference in seconds between two timeval structs.
*
* Return the difference between tv1 and tv2 in seconds in a double.
*/
double
difftv(struct timeval tv1, struct timeval tv2) {
long diff_sec, diff_usec;
double diff;
diff_sec = tv1.tv_sec - tv2.tv_sec;
diff_usec = tv1.tv_usec - tv2.tv_usec;
diff = (double)diff_sec + ((double)diff_usec / 1000000.0);
return diff;
}
/*
* timelimit_reached:
* Have we reached the time limit (if any)?
*
* Returns FALSE if there is no time limit or if we have not reached it
* Returns TRUE otherwise
*/
int
timelimit_reached(void) {
struct timeval time_now;
set_timenow(&time_now);
if (use_timelimit == FALSE)
return (FALSE);
if (setup_phase == TRUE) {
if (difftv(time_now, time_of_program_start)
< (double)(run_timelimit + HARD_TIMEOUT_EXTRA))
return (FALSE);
else
return (TRUE);
} else {
if (difftv(time_now, time_of_first_query)
< (double)run_timelimit)
return (FALSE);
else
return (TRUE);
}
}
/*
* keep_sending:
* Should we keep sending queries or stop here?
*
* Return TRUE if we should keep on sending queries
* Return FALSE if we should stop
*
* Side effects:
* Rewinds the input and clears reached_end_input if we have reached the
* end of the input, but we are meant to run through it multiple times
* and have not hit the time limit yet (if any is set).
*/
int
keep_sending(int *reached_end_input) {
static int stop = FALSE;
if (stop == TRUE)
return (FALSE);
if ((*reached_end_input == FALSE) && (timelimit_reached() == FALSE))
return (TRUE);
else if ((*reached_end_input == TRUE) && (run_only_once == FALSE)
&& (timelimit_reached() == FALSE)) {
rewind(datafile_ptr);
*reached_end_input = FALSE;
runs_through_file++;
return (TRUE);
} else {
if (*reached_end_input == TRUE)
runs_through_file++;
stop = TRUE;
return (FALSE);
}
}
/*
* queries_outstanding:
* How many queries are outstanding?
*
* Returns the number of outstanding queries
*/
unsigned int
queries_outstanding(void) {
return (num_queries_outstanding);
}
/*
* next_input_line:
* Get the next non-comment line from the input file
*
* Put text in line, up to max of n chars. Skip comment lines.
* Skip empty lines.
*
* Return line length on success
* Return 0 if cannot read a non-comment line (EOF or error)
*/
int
next_input_line(char *line, int n) {
char *result;
do {
result = fgets(line, n, datafile_ptr);
} while ((result != NULL) &&
((line[0] == COMMENT_CHAR) || (line[0] == '\n')));
if (result == NULL)
return (0);
else
return (strlen(line));
}
/*
* identify_directive:
* Gives us a numerical value equivelant for a directive string
*
* Returns the value for the directive
* Returns -1 if not a valid directive
*/
int
identify_directive(char *dir) {
static char *directives[] = DIRECTIVES;
static int dir_values[] = DIR_VALUES;
unsigned int index, num_directives;
num_directives = sizeof(directives) / sizeof(directives[0]);
if (num_directives > (sizeof(dir_values) / sizeof(int)))
num_directives = sizeof(dir_values) / sizeof(int);
for (index = 0; index < num_directives; index++) {
if (strcmp(dir, directives[index]) == 0)
return (dir_values[index]);
}
return (-1);
}
/*
* update_config:
* Update configuration options from a line from the input file
*/
void
update_config(char *config_change_desc) {
char *directive, *config_value, *trailing_garbage;
char conf_copy[MAX_INPUT_LEN + 1];
unsigned int uint_val;
int directive_number;
int check;
if (ignore_config_changes == TRUE) {
fprintf(stderr, "Ignoring configuration change: %s",
config_change_desc);
return;
}
strcpy(conf_copy, config_change_desc);
++config_change_desc;
if (*config_change_desc == '\0') {
fprintf(stderr, "Invalid config: No directive present: %s\n",
conf_copy);
return;
}
if (index(WHITESPACE, *config_change_desc) != NULL) {
fprintf(stderr, "Invalid config: Space before directive or "
"no directive present: %s\n", conf_copy);
return;
}
directive = strtok(config_change_desc, WHITESPACE);
config_value = strtok(NULL, WHITESPACE);
trailing_garbage = strtok(NULL, WHITESPACE);
if ((directive_number = identify_directive(directive)) == -1) {
fprintf(stderr, "Invalid config: Bad directive: %s\n",
conf_copy);
return;
}
if (config_value == NULL) {
fprintf(stderr, "Invalid config: No value present: %s\n",
conf_copy);
return;
}
if (trailing_garbage != NULL) {
fprintf(stderr, "Config warning: "
"trailing garbage: %s\n", conf_copy);
}
switch(directive_number) {
case V_SERVER:
if (serverset && (setup_phase == TRUE)) {
fprintf(stderr, "Config change overriden by command "
"line: %s\n", directive);
return;
}
if (set_server(config_value) == -1)
fprintf(stderr, "Set server error: unable to change "
"the server name to '%s'\n", config_value);
break;
case V_PORT:
if (portset && (setup_phase == TRUE)) {
fprintf(stderr, "Config change overriden by command "
"line: %s\n", directive);
return;
}
check = is_uint(config_value, &uint_val);
if ((check == TRUE) && (uint_val > 0)) {
if (set_server_port(uint_val) == -1) {
fprintf(stderr, "Invalid config: Bad value for"
" %s: %s\n", directive, config_value);
}
} else
fprintf(stderr, "Invalid config: Bad value for "
"%s: %s\n", directive, config_value);
break;
case V_MAXQUERIES:
if (queriesset && (setup_phase == TRUE)) {
fprintf(stderr, "Config change overriden by command "
"line: %s\n", directive);
return;
}
check = is_uint(config_value, &uint_val);
if ((check == TRUE) && (uint_val > 0)) {
set_max_queries(uint_val);
} else
fprintf(stderr, "Invalid config: Bad value for "
"%s: %s\n", directive, config_value);
break;
case V_MAXWAIT:
if (timeoutset && (setup_phase == TRUE)) {
fprintf(stderr, "Config change overriden by command "
"line: %s\n", directive);
return;
}
check = is_uint(config_value, &uint_val);
if ((check == TRUE) && (uint_val > 0)) {
query_timeout = uint_val;
} else
fprintf(stderr, "Invalid config: Bad value for "
"%s: %s\n", directive, config_value);
break;
default:
fprintf(stderr, "Invalid config: Bad directive: %s\n",
directive);
break;
}
}
/*
* parse_query:
* Parse a query line from the input file
*
* Set qname to be the domain to query (up to a max of qnlen chars)
* Set qtype to be the type of the query
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
parse_query(char *input, char *qname, int qnlen, int *qtype) {
static char *qtype_strings[] = QTYPE_STRINGS;
static int qtype_codes[] = QTYPE_CODES;
int num_types, index;
int found = FALSE;
char incopy[MAX_INPUT_LEN + 1];
char *domain_str, *type_str;
num_types = sizeof(qtype_strings) / sizeof(qtype_strings[0]);
if (num_types > (sizeof(qtype_codes) / sizeof(int)))
num_types = sizeof(qtype_codes) / sizeof(int);
strcpy(incopy, input);
domain_str = strtok(incopy, WHITESPACE);
type_str = strtok(NULL, WHITESPACE);
if ((domain_str == NULL) || (type_str == NULL)) {
fprintf(stderr, "Invalid query input format: %s\n", input);
return (-1);
}
if (strlen(domain_str) > qnlen) {
fprintf(stderr, "Query domain too long: %s\n", domain_str);
return (-1);
}
for (index = 0; (index < num_types) && (found == FALSE); index++) {
if (strcasecmp(type_str, qtype_strings[index]) == 0) {
*qtype = qtype_codes[index];
found = TRUE;
}
}
if (found == FALSE) {
fprintf(stderr, "Query type not understood: %s\n", type_str);
return (-1);
}
strcpy(qname, domain_str);
return (0);
}
/*
* dispatch_query:
* Send the query packet for the entry
*
* Return -1 on failure
* Return a non-negative integer otherwise
*/
int
dispatch_query(unsigned short int id, char *dom, int qt) {
static u_char packet_buffer[PACKETSZ + 1];
static socklen_t sockaddrlen = sizeof(struct sockaddr);
int buffer_len = PACKETSZ;
int bytes_sent;
unsigned short int net_id = htons(id);
char *id_ptr = (char *)&net_id;
buffer_len = res_mkquery(QUERY, dom, C_IN, qt, NULL, 0,
NULL, packet_buffer, PACKETSZ);
if (buffer_len == -1) {
fprintf(stderr, "Failed to create query packet: %s %d\n",
dom, qt);
return (-1);
}
if (edns) {
unsigned char *p;
if (buffer_len + EDNSLEN >= PACKETSZ) {
fprintf(stderr, "Failed to add OPT to query packet\n");
return (-1);
}
packet_buffer[11] = 1;
p = &packet_buffer[buffer_len];
*p++ = 0; /* root name */
*p++ = 0;
*p++ = 41; /* OPT */
*p++ = 16;
*p++ = 0; /* UDP payload size (4K) */
*p++ = 0; /* extended rcode */
*p++ = 0; /* version */
if (dnssec)
*p++ = 0x80; /* upper flag bits - DO set */
else
*p++ = 0; /* upper flag bits */
*p++ = 0; /* lower flag bit */
*p++ = 0;
*p++ = 0; /* rdlen == 0 */
buffer_len += EDNSLEN;
}
packet_buffer[0] = id_ptr[0];
packet_buffer[1] = id_ptr[1];
bytes_sent = sendto(query_socket, packet_buffer, buffer_len, 0,
(struct sockaddr *)&qaddr, sockaddrlen);
if (bytes_sent == -1) {
fprintf(stderr, "Failed to send query packet: %s %d\n",
dom, qt);
return (-1);
}
if (bytes_sent != buffer_len)
fprintf(stderr, "Warning: incomplete packet sent: %s %d\n",
dom, qt);
return (0);
}
/*
* send_query:
* Send a query based on a line of input
*/
void
send_query(char *query_desc) {
static unsigned short int use_query_id = 0;
static int qname_len = MAX_DOMAIN_LEN;
static char domain[MAX_DOMAIN_LEN + 1];
int query_type;
unsigned int count;
use_query_id++;
if (parse_query(query_desc, domain, qname_len, &query_type) == -1) {
fprintf(stderr, "Error parsing query: %s\n", query_desc);
return;
}
if (dispatch_query(use_query_id, domain, query_type) == -1) {
fprintf(stderr, "Error sending query: %s\n", query_desc);
return;
}
if (setup_phase == TRUE) {
set_timenow(&time_of_first_query);
setup_phase = FALSE;
printf("[Status] Sending queries\n");
}
/* Find the first slot in status[] that is not in use */
for(count = 0; (status[count].in_use == TRUE)
&& (count < max_queries_outstanding); count++);
if (status[count].in_use == TRUE) {
fprintf(stderr, "Unexpected error: We have run out of "
"status[] space!\n");
return;
}
/* Register the query in status[] */
status[count].in_use = TRUE;
status[count].id = use_query_id;
if (verbose)
status[count].desc = strdup(query_desc);
set_timenow(&status[count].sent_timestamp);
num_queries_sent++;
num_queries_outstanding++;
}
/*
* data_available:
* Is there data available on the given file descriptor?
*
* Return TRUE if there is
* Return FALSE otherwise
*/
int
data_available(int fd, double wait) {
fd_set read_fds;
struct timeval tv;
int retval;
/* Set list of file descriptors */
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
if ((wait > 0.0) && (wait < (double)LONG_MAX)) {
tv.tv_sec = (long)floor(wait);
tv.tv_usec = (long)(1000000.0 * (wait - floor(wait)));
} else {
tv.tv_sec = 0;
tv.tv_usec = 0;
}
retval = select(fd + 1, &read_fds, NULL, NULL, &tv);
if (FD_ISSET(fd, &read_fds))
return (TRUE);
else
return (FALSE);
}
/*
* register_response:
* Register receipt of a query
*
* Removes (sets in_use = FALSE) the record for the given query id in
* status[] if any exists.
*/
void
register_response(unsigned short int id, unsigned int rcode) {
unsigned int ct = 0;
int found = FALSE;
for(; (ct < query_status_allocated) && (found == FALSE); ct++) {
if ((status[ct].in_use == TRUE) && (status[ct].id == id)) {
status[ct].in_use = FALSE;
num_queries_outstanding--;
found = TRUE;
if (status[ct].desc) {
printf("> %s %s\n", rcode_strings[rcode],
status[ct].desc);
free(status[ct].desc);
}
if (countrcodes)
rcodecounts[rcode]++;
}
}
if (found == FALSE)
fprintf(stderr, "Warning: Received a response with an "
"unexpected (maybe timed out) id: %u\n", id);
}
/*
* process_single_response:
* Receive from the given socket & process an invididual response packet.
* Remove it from the list of open queries (status[]) and decrement the
* number of outstanding queries if it matches an open query.
*/
void
process_single_response(int sockfd) {
static struct sockaddr_in from_addr;
static unsigned char in_buf[MAX_BUFFER_LEN];
int numbytes, addr_len, resp_id;
int flags;
addr_len = sizeof(struct sockaddr);
if ((numbytes = recvfrom(sockfd, in_buf, MAX_BUFFER_LEN,
0, (struct sockaddr *)&from_addr, &addr_len)) == -1) {
fprintf(stderr, "Error receiving datagram\n");
return;
}
resp_id = get_uint16(in_buf);
flags = get_uint16(in_buf + 2);
register_response(resp_id, flags & 0xF);
}
/*
* process_responses:
* Go through any/all received responses and remove them from the list of
* open queries (set in_use = FALSE for their entry in status[]), also
* decrementing the number of outstanding queries.
*/
void
process_responses(void) {
double first_packet_wait = RESPONSE_BLOCKING_WAIT_TIME;
unsigned int outstanding = queries_outstanding();
/*
* Don't block waiting for packets at all if we aren't looking for
* any responses or if we are now able to send new queries.
*/
if ((outstanding == 0) || (outstanding < max_queries_outstanding)) {
first_packet_wait = 0.0;
}
if (data_available(query_socket, first_packet_wait) == TRUE) {
process_single_response(query_socket);
while (data_available(query_socket, 0.0) == TRUE)
process_single_response(query_socket);
}
}
/*
* retire_old_queries:
* Go through the list of open queries (status[]) and remove any queries
* (i.e. set in_use = FALSE) which are older than the timeout, decrementing
* the number of queries outstanding for each one removed.
*/
void
retire_old_queries(void) {
unsigned int count = 0;
struct timeval curr_time;
set_timenow(&curr_time);
for(; count < query_status_allocated; count++) {
if ((status[count].in_use == TRUE)
&& (difftv(curr_time, status[count].sent_timestamp)
>= (double)query_timeout)) {
status[count].in_use = FALSE;
num_queries_outstanding--;
num_queries_timed_out++;
if (status[count].desc) {
printf("> T %s\n", status[count].desc);
free(status[count].desc);
} else {
printf("[Timeout] Query timed out: msg id %u\n",
status[count].id);
}
}
}
}
/*
* print_statistics:
* Print out statistics based on the results of the test
*/
void
print_statistics(void) {
unsigned int num_queries_completed;
double per_lost, per_completed;
double run_time, queries_per_sec;
struct timeval start_time;
num_queries_completed = num_queries_sent - num_queries_timed_out;
if (num_queries_completed == 0) {
per_lost = 0.0;
per_completed = 0.0;
} else {
per_lost = 100.0 * (double)num_queries_timed_out
/ (double)num_queries_sent;
per_completed = 100.0 - per_lost;
}
if (num_queries_sent == 0) {
start_time.tv_sec = time_of_program_start.tv_sec;
start_time.tv_usec = time_of_program_start.tv_usec;
run_time = 0.0;
queries_per_sec = 0.0;
} else {
start_time.tv_sec = time_of_first_query.tv_sec;
start_time.tv_usec = time_of_first_query.tv_usec;
run_time = difftv(time_of_end_of_run, time_of_first_query);
queries_per_sec = (double)num_queries_completed / run_time;
}
printf("\n");
printf("Statistics:\n");
printf("\n");
printf(" Parse input file: %s\n",
((run_only_once == TRUE) ? "once" : "multiple times"));
if (use_timelimit)
printf(" Run time limit: %u seconds\n", run_timelimit);
if (run_only_once == FALSE)
printf(" Ran through file: %u times\n",
runs_through_file);
else
printf(" Ended due to: reaching %s\n",
((runs_through_file == 0) ? "time limit"
: "end of file"));
printf("\n");
printf(" Queries sent: %u queries\n", num_queries_sent);
printf(" Queries completed: %u queries\n", num_queries_completed);
printf(" Queries lost: %u queries\n", num_queries_timed_out);
printf("\n");
if (countrcodes) {
unsigned int i;
for (i = 0; i < 16; i++) {
if (rcodecounts[i] == 0)
continue;
printf(" Returned %8s: %u queries\n",
rcode_strings[i], rcodecounts[i]);
}
printf("\n");
}
printf(" Percentage completed: %6.2lf%%\n", per_completed);
printf(" Percentage lost: %6.2lf%%\n", per_lost);
printf("\n");
printf(" Started at: %s", ctime(&start_time.tv_sec));
printf(" Finished at: %s",
ctime(&time_of_end_of_run.tv_sec));
printf(" Ran for: %.6lf seconds\n", run_time);
printf("\n");
printf(" Queries per second: %.6lf qps\n", queries_per_sec);
printf("\n");
}
/*
* dnsqtest Program Mainline
*/
int
main(int argc, char **argv) {
int got_eof = FALSE;
int input_length = MAX_INPUT_LEN;
char input_line[MAX_INPUT_LEN + 1];
set_timenow(&time_of_program_start);
time_of_first_query.tv_sec = 0;
time_of_first_query.tv_usec = 0;
time_of_end_of_run.tv_sec = 0;
time_of_end_of_run.tv_usec = 0;
input_line[0] = '\0';
show_startup_info();
if (setup(argc, argv) == -1)
return (-1);
printf("[Status] Processing input data\n");
while (keep_sending(&got_eof) == TRUE || queries_outstanding() > 0) {
while (keep_sending(&got_eof) == TRUE
&& queries_outstanding() < max_queries_outstanding) {
int len = next_input_line(input_line, input_length);
if (len == 0) {
got_eof = TRUE;
} else {
/* Zap the trailing newline */
if (input_line[len - 1] == '\n')
input_line[len - 1] = '\0';
/*
* TODO: Should test if we got a whole line
* and flush to the next \n in input if not
* here... Add this later. Only do the next
* few lines if we got a whole line, else
* print a warning. Alternative: Make the
* max line size really big. BAD! :)
*/
if (input_line[0] == CONFIG_CHAR)
update_config(input_line);
else {
send_query(input_line);
}
}
}
retire_old_queries();
process_responses();
}
set_timenow(&time_of_end_of_run);
printf("[Status] Testing complete\n");
close_socket();
close_datafile();
print_statistics();
return (0);
}