📄 queryperf.c
字号:
/* * Copyright (C) 2000, 2001 Nominum, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *//*** *** DNS Query Performance Testing Tool (queryperf.c) *** *** Version $Id: queryperf.c,v 1.1.1.2.2.5.4.3 2004/06/21 00:45:30 marka Exp $ *** *** Stephen Jacob <sj@nominum.com> ***/#include <sys/time.h>#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <limits.h>#include <time.h>#include <unistd.h>#include <netdb.h>#include <netinet/in.h>#include <arpa/nameser.h>#include <resolv.h>#include <math.h>#include <errno.h>#ifndef HAVE_GETADDRINFO#include "missing/addrinfo.h"#endif/* * Configuration defaults */#define DEF_MAX_QUERIES_OUTSTANDING 20#define DEF_QUERY_TIMEOUT 5 /* in seconds */#define DEF_SERVER_TO_QUERY "127.0.0.1"#define DEF_SERVER_PORT "53"#define DEF_BUFFER_SIZE 32 /* in k *//* * Other constants / definitions */#define COMMENT_CHAR ';'#define CONFIG_CHAR '#'#define MAX_PORT 65535#define MAX_INPUT_LEN 512#define MAX_DOMAIN_LEN 255#define MAX_BUFFER_LEN 8192 /* in bytes */#define HARD_TIMEOUT_EXTRA 5 /* in seconds */#define RESPONSE_BLOCKING_WAIT_TIME 0.1 /* in seconds */#define EDNSLEN 11#define FALSE 0#define TRUE 1#define WHITESPACE " \t\n"enum directives_enum { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT };#define DIRECTIVES { "server", "port", "maxqueries", "maxwait" }#define DIR_VALUES { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT }#define QTYPE_STRINGS { \ "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", \ "MR", "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", \ "AAAA", "SRV", "NAPTR", "A6", "AXFR", "MAILB", "MAILA", "*", "ANY" \}#define QTYPE_CODES { \ 1, 2, 3, 4, 5, 6, 7, 8, \ 9, 10, 11, 12, 13, 14, 15, 16, \ 28, 33, 35, 38, 252, 253, 254, 255, 255 \}#define RCODE_STRINGS { \ "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", \ "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", \ "NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", \ "rcode12", "rcode13", "rcode14", "rcode15" \}/* * Data type definitions */#define QUERY_STATUS_MAGIC 0x51535441U /* QSTA */#define VALID_QUERY_STATUS(q) ((q) != NULL && \ (q)->magic == QUERY_STATUS_MAGIC)struct query_status { unsigned int magic; int in_use; unsigned short int id; struct timeval sent_timestamp; char *desc;};/* * Configuration options (global) */unsigned int max_queries_outstanding; /* init 0 */unsigned int query_timeout = DEF_QUERY_TIMEOUT;int ignore_config_changes = FALSE;unsigned int socket_bufsize = DEF_BUFFER_SIZE;int family = AF_UNSPEC;int use_stdin = TRUE;char *datafile_name; /* init NULL */char *server_to_query; /* init NULL */char *server_port; /* init NULL */struct addrinfo *server_ai; /* init NULL */int run_only_once = FALSE;int use_timelimit = FALSE;unsigned int run_timelimit; /* init 0 */int serverset = FALSE, portset = FALSE;int queriesset = FALSE, timeoutset = FALSE;int edns = FALSE, dnssec = FALSE;int countrcodes = FALSE;int rcodecounts[16] = {0};int verbose = FALSE;/* * Other global stuff */int setup_phase = TRUE;FILE *datafile_ptr; /* init NULL */unsigned int runs_through_file; /* init 0 */unsigned int num_queries_sent; /* init 0 */unsigned int num_queries_outstanding; /* init 0 */unsigned int num_queries_timed_out; /* init 0 */struct timeval time_of_program_start;struct timeval time_of_first_query;struct timeval time_of_end_of_run;struct query_status *status; /* init NULL */unsigned int query_status_allocated; /* init 0 */int query_socket = -1;int socket4 = -1, socket6 = -1;static char *rcode_strings[] = RCODE_STRINGS;/* * get_uint16: * Get an unsigned short integer from a buffer (in network order) */static unsigned shortget_uint16(unsigned char *buf) { unsigned short ret; ret = buf[0] * 256 + buf[1]; return (ret);}/* * show_startup_info: * Show name/version */voidshow_startup_info(void) { printf("\n""DNS Query Performance Testing Tool\n""Version: $Id: queryperf.c,v 1.1.1.2.2.5.4.3 2004/06/21 00:45:30 marka Exp $\n""\n");}/* * show_usage: * Print out usage/syntax information */voidshow_usage(void) { fprintf(stderr,"\n""Usage: queryperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]\n"" [-b bufsize] [-t timeout] [-n] [-l limit] [-f family] [-1]\n"" [-e] [-D] [-c] [-v] [-h]\n"" -d specifies the input data file (default: stdin)\n"" -s sets the server to query (default: %s)\n"" -p sets the port on which to query the server (default: %s)\n"" -q specifies the maximum number of queries outstanding (default: %d)\n"" -t specifies the timeout for query completion in seconds (default: %d)\n"" -n causes configuration changes to be ignored\n"" -l specifies how a limit for how long to run tests in seconds (no default)\n"" -1 run through input only once (default: multiple iff limit given)\n"" -b set input/output buffer size in kilobytes (default: %d k)\n"" -f specify address family of DNS transport, inet or inet6 (default: any)\n"" -e enable EDNS 0\n"" -D set the DNSSEC OK bit (implies EDNS)\n"" -c print the number of packets with each rcode\n"" -v verbose: report the RCODE of each response on stdout\n"" -h print this usage\n""\n", DEF_SERVER_TO_QUERY, DEF_SERVER_PORT, DEF_MAX_QUERIES_OUTSTANDING, DEF_QUERY_TIMEOUT, DEF_BUFFER_SIZE);}/* * set_datafile: * Set the datafile to read * * Return -1 on failure * Return a non-negative integer otherwise */intset_datafile(char *new_file) { char *dfname_tmp; if ((new_file == NULL) || (new_file[0] == '\0')) { fprintf(stderr, "Error: null datafile name\n"); return (-1); } if ((dfname_tmp = malloc(strlen(new_file) + 1)) == NULL) { fprintf(stderr, "Error allocating memory for datafile name: " "%s\n", new_file); return (-1); } free(datafile_name); datafile_name = dfname_tmp; strcpy(datafile_name, new_file); use_stdin = FALSE; return (0);}/* * set_input_stdin: * Set the input to be stdin (instead of a datafile) */voidset_input_stdin(void) { use_stdin = TRUE; free(datafile_name); datafile_name = NULL;}/* * set_server: * Set the server to be queried * * Return -1 on failure * Return a non-negative integer otherwise */intset_server(char *new_name) { static struct hostent *server_he; /* If no change in server name, don't do anything... */ if ((server_to_query != NULL) && (new_name != NULL)) if (strcmp(new_name, server_to_query) == 0) return (0); if ((new_name == NULL) || (new_name[0] == '\0')) { fprintf(stderr, "Error: null server name\n"); return (-1); } free(server_to_query); server_to_query = NULL; if ((server_to_query = malloc(strlen(new_name) + 1)) == NULL) { fprintf(stderr, "Error allocating memory for server name: " "%s\n", new_name); return (-1); } strcpy(server_to_query, new_name); return (0);}/* * set_server_port: * Set the port on which to contact the server * * Return -1 if port is invalid * Return a non-negative integer otherwise */intset_server_port(char *new_port) { unsigned int uint_val; if ((is_uint(new_port, &uint_val)) != TRUE) return (-1); if (uint_val && uint_val > MAX_PORT) return (-1); else { if (server_port != NULL && new_port != NULL && strcmp(server_port, new_port) == 0) return (0); free(server_port); server_port = NULL; if ((server_port = malloc(strlen(new_port) + 1)) == NULL) { fprintf(stderr, "Error allocating memory for server port: " "%s\n", new_port); return (-1); } strcpy(server_port, new_port); return (0); }}intset_server_sa(void) { struct addrinfo hints, *res; static struct protoent *proto; int error; if (proto == NULL && (proto = getprotobyname("udp")) == NULL) { fprintf(stderr, "Error: getprotobyname call failed"); return (-1); } memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = proto->p_proto; if ((error = getaddrinfo(server_to_query, server_port, &hints, &res)) != 0) { fprintf(stderr, "Error: getaddrinfo(%s, %s) failed\n", server_to_query, server_port); return (-1); } /* replace the server's addrinfo */ if (server_ai != NULL) freeaddrinfo(server_ai); server_ai = res; return (0);}/* * is_digit: * Tests if a character is a digit * * Return TRUE if it is * Return FALSE if it is not */intis_digit(char d) { if (d < '0' || d > '9') return (FALSE); else return (TRUE);}/* * is_uint: * Tests if a string, test_int, is a valid unsigned integer * * Sets *result to be the unsigned integer if it is valid * * Return TRUE if it is * Return FALSE if it is not */intis_uint(char *test_int, unsigned int *result) { unsigned long int value; char *end; if (test_int == NULL) return (FALSE); if (is_digit(test_int[0]) == FALSE) return (FALSE); value = strtoul(test_int, &end, 10); if ((errno == ERANGE) || (*end != '\0') || (value > UINT_MAX)) return (FALSE); *result = (unsigned int)value; return (TRUE);}/* * set_max_queries: * Set the maximum number of outstanding queries * * Returns -1 on failure * Returns a non-negative integer otherwise */intset_max_queries(unsigned int new_max) { static unsigned int size_qs = sizeof(struct query_status); struct query_status *temp_stat; unsigned int count; if (new_max < 0) { fprintf(stderr, "Unable to change max outstanding queries: " "must be positive and non-zero: %u\n", new_max); return (-1); } if (new_max > query_status_allocated) { temp_stat = realloc(status, new_max * size_qs); if (temp_stat == NULL) { fprintf(stderr, "Error resizing query_status\n"); return (-1); } else { status = temp_stat; /* * Be careful to only initialise between above * the previously allocated space. Note that the * allocation may be larger than the current * max_queries_outstanding. We don't want to * "forget" any outstanding queries! We might * still have some above the bounds of the max. */ count = query_status_allocated; for (; count < new_max; count++) { status[count].in_use = FALSE; status[count].magic = QUERY_STATUS_MAGIC; status[count].desc = NULL; } query_status_allocated = new_max; } } max_queries_outstanding = new_max; return (0);}/* * parse_args: * Parse program arguments and set configuration options * * Return -1 on failure * Return a non-negative integer otherwise */intparse_args(int argc, char **argv) { int c; unsigned int uint_arg_val; while ((c = getopt(argc, argv, "f:q:t:nd:s:p:1l:b:eDcvh")) != -1) { switch (c) { case 'f': if (strcmp(optarg, "inet") == 0) family = AF_INET;#ifdef AF_INET6 else if (strcmp(optarg, "inet6") == 0) family = AF_INET6;#endif else if (strcmp(optarg, "any") == 0) family = AF_UNSPEC; else { fprintf(stderr, "Invalid address family: %s\n", optarg); return (-1); } break; case 'q': if (is_uint(optarg, &uint_arg_val) == TRUE) { set_max_queries(uint_arg_val); queriesset = TRUE; } else { fprintf(stderr, "Option requires a positive " "integer value: -%c %s\n", c, optarg); return (-1); } break; case 't': if (is_uint(optarg, &uint_arg_val) == TRUE) { query_timeout = uint_arg_val; timeoutset = TRUE; } else { fprintf(stderr, "Option requires a positive " "integer value: -%c %s\n", c, optarg); return (-1); } break; case 'n': ignore_config_changes = TRUE; break; case 'd': if (set_datafile(optarg) == -1) { fprintf(stderr, "Error setting datafile " "name: %s\n", optarg); return (-1); } break; case 's': if (set_server(optarg) == -1) { fprintf(stderr, "Error setting server " "name: %s\n", optarg); return (-1); } serverset = TRUE; break; case 'p': if (is_uint(optarg, &uint_arg_val) == TRUE && uint_arg_val < MAX_PORT) { set_server_port(optarg); portset = TRUE; } else { fprintf(stderr, "Option requires a positive " "integer between 0 and %d: -%c %s\n", MAX_PORT - 1, c, optarg); return (-1); } break; case '1': run_only_once = TRUE; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -