1630. [contrib] queryperf: add support for IPv6 transport.

This commit is contained in:
Tatuya JINMEI 神明達哉
2004-05-12 07:04:58 +00:00
parent 392cd518b3
commit f620c5e527
5 changed files with 3128 additions and 825 deletions

View File

@@ -1,15 +1,23 @@
# Copyright (C) 2000, 2001 Nominum, Inc. All Rights Reserved.
CC = @CC@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
DEFS = @DEFS@
queryperf: queryperf.o
$(CC) $(CFLAGS) $(DEFS) queryperf.o $(LIBS) -lm -o queryperf
queryperf: queryperf.o $(LIBOBJS)
$(CC) $(CFLAGS) $(DEFS) queryperf.o $(LIBOBJS) $(LIBS) -lm -o queryperf
queryperf.o: queryperf.c
$(CC) $(CFLAGS) $(DEFS) -c queryperf.c
# under missing subdir
getaddrinfo.o: ./missing/getaddrinfo.c
$(CC) $(CFLAGS) -c ./missing/$*.c
getnameinfo.o: ./missing/getnameinfo.c
$(CC) $(CFLAGS) -c ./missing/$*.c
clean:
rm -f *.o queryperf

File diff suppressed because it is too large Load Diff

View File

@@ -36,9 +36,31 @@ AC_DEFUN(AC_TYPE_SOCKLEN_T,
fi
])
AC_SEARCH_LIBS(res_mkquery, resolv bind);
AC_DEFUN(AC_SA_LEN,
[AC_CACHE_CHECK([for sa_len], ac_cv_sa_len,
[
AC_TRY_COMPILE(
[#include <sys/types.h>
#include <sys/socket.h>],
[struct sockaddr sa; sa.sa_len = 0;],
ac_cv_sa_len=yes,
ac_cv_sa_len=no)
])
if test $ac_cv_sa_len = yes; then
AC_DEFINE(HAVE_SA_LEN)
fi
])
AC_SEARCH_LIBS(res_mkquery, resolv bind)
AC_CHECK_LIB(socket, socket)
AC_CHECK_LIB(nsl, inet_ntoa)
AC_CHECK_FUNCS(gethostbyname2)
AC_CHECK_FUNC(getaddrinfo,
AC_DEFINE(HAVE_GETADDRINFO), AC_LIBOBJ(getaddrinfo))
AC_CHECK_FUNC(getnameinfo,
AC_DEFINE(HAVE_GETNAMEINFO), AC_LIBOBJ(getnameinfo))
AC_TYPE_SOCKLEN_T
AC_SA_LEN
AC_OUTPUT(Makefile)

View File

@@ -18,7 +18,7 @@
/***
*** DNS Query Performance Testing Tool (queryperf.c)
***
*** Version $Id: queryperf.c,v 1.8 2003/03/26 06:07:54 marka Exp $
*** Version $Id: queryperf.c,v 1.9 2004/05/12 07:04:58 jinmei Exp $
***
*** Stephen Jacob <sj@nominum.com>
***/
@@ -39,14 +39,18 @@
#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 "localhost"
#define DEF_SERVER_PORT 53
#define DEF_SERVER_TO_QUERY "127.0.0.1"
#define DEF_SERVER_PORT "53"
#define DEF_BUFFER_SIZE 32 /* in k */
/*
@@ -116,11 +120,13 @@ 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 */
unsigned int server_port = DEF_SERVER_PORT;
char *server_port; /* init NULL */
struct addrinfo *server_ai; /* init NULL */
int run_only_once = FALSE;
int use_timelimit = FALSE;
@@ -154,8 +160,8 @@ struct timeval time_of_end_of_run;
struct query_status *status; /* init NULL */
unsigned int query_status_allocated; /* init 0 */
int query_socket; /* init 0 */
struct sockaddr_in qaddr;
int query_socket = -1;
int socket4 = -1, socket6 = -1;
static char *rcode_strings[] = RCODE_STRINGS;
@@ -180,7 +186,7 @@ void
show_startup_info(void) {
printf("\n"
"DNS Query Performance Testing Tool\n"
"Version: $Id: queryperf.c,v 1.8 2003/03/26 06:07:54 marka Exp $\n"
"Version: $Id: queryperf.c,v 1.9 2004/05/12 07:04:58 jinmei Exp $\n"
"\n");
}
@@ -193,17 +199,18 @@ show_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] [-1]\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: %u)\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"
@@ -287,14 +294,7 @@ set_server(char *new_name) {
return (-1);
}
if ((server_he = gethostbyname(new_name)) == NULL) {
fprintf(stderr, "Error: gethostbyname(\"%s\") failed\n",
new_name);
return (-1);
}
strcpy(server_to_query, new_name);
qaddr.sin_addr = *((struct in_addr *)server_he->h_addr);
return (0);
}
@@ -307,16 +307,64 @@ set_server(char *new_name) {
* Return a non-negative integer otherwise
*/
int
set_server_port(unsigned int new_port) {
if (new_port > MAX_PORT)
set_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 {
server_port = new_port;
qaddr.sin_port = htons(server_port);
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);
}
}
int
set_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
@@ -425,8 +473,23 @@ parse_args(int argc, char **argv) {
int c;
unsigned int uint_arg_val;
while ((c = getopt(argc, argv, "q:t:nd:s:p:1l:b:eDcvh")) != -1) {
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);
@@ -476,7 +539,7 @@ parse_args(int argc, char **argv) {
if (is_uint(optarg, &uint_arg_val) == TRUE &&
uint_arg_val < MAX_PORT)
{
set_server_port(uint_arg_val);
set_server_port(optarg);
portset = TRUE;
} else {
fprintf(stderr, "Option requires a positive "
@@ -582,39 +645,54 @@ close_datafile(void) {
/*
* open_socket:
* Open a socket for the queries
* Open a socket for the queries. When we have an active socket already,
* close it and open a new one.
*
* Return -1 on failure
* Return a non-negative integer otherwise
* Return the socket identifier
*/
int
open_socket(void) {
int sock;
struct protoent *proto;
struct sockaddr_in bind_addr;
int ret;
int bufsize;
struct addrinfo hints, *res;
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);
memset(&hints, 0, sizeof(hints));
hints.ai_family = server_ai->ai_family;
hints.ai_socktype = server_ai->ai_socktype;
hints.ai_protocol = server_ai->ai_protocol;
hints.ai_flags = AI_PASSIVE;
if ((proto = getprotobyname("udp")) == NULL) {
fprintf(stderr, "Error: getprotobyname call failed");
if ((ret = getaddrinfo(NULL, "0", &hints, &res)) != 0) {
fprintf(stderr,
"Error: getaddrinfo for bind socket failed: %s\n",
gai_strerror(ret));
return (-1);
}
if ((sock = socket(PF_INET, SOCK_DGRAM, proto->p_proto)) == -1) {
if ((sock = socket(res->ai_family, SOCK_DGRAM,
res->ai_protocol)) == -1) {
fprintf(stderr, "Error: socket call failed");
return (-1);
goto fail;
}
if (bind(sock, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr))
== -1) {
fprintf(stderr, "Error: bind call failed");
return (-1);
#if defined(AF_INET6) && defined(IPV6_V6ONLY)
if (res->ai_family == AF_INET6) {
int on = 1;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
&on, sizeof(on)) == -1) {
fprintf(stderr,
"Warning: setsockopt(IPV6_V6ONLY) failed\n");
}
}
#endif
if (bind(sock, res->ai_addr, res->ai_addrlen) == -1)
fprintf(stderr, "Error: bind call failed");
freeaddrinfo(res);
bufsize = 1024 * socket_bufsize;
@@ -628,32 +706,80 @@ open_socket(void) {
if (ret < 0)
fprintf(stderr, "Warning: setsockbuf(SO_SNDBUF) failed\n");
query_socket = sock;
return (0);
return (sock);
fail:
if (res)
freeaddrinfo(res);
return (-1);
}
/*
* close_socket:
* Close the query socket
* Close the query socket(s)
*
* 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");
if (socket4 != -1) {
if (close(socket4) != 0) {
fprintf(stderr,
"Error: unable to close IPv4 socket\n");
return (-1);
}
}
query_socket = 0;
if (socket6 != -1) {
if (close(socket6) != 0) {
fprintf(stderr,
"Error: unable to close IPv6 socket\n");
return (-1);
}
}
query_socket = -1;
return (0);
}
/*
* change_socket:
* Choose an appropriate socket according to the address family of the
* current server. Open a new socket if necessary.
*
* Return -1 on failure
* Return the socket identifier
*/
int
change_socket(void) {
int s, *sockp;
switch (server_ai->ai_family) {
case AF_INET:
sockp = &socket4;
break;
#ifdef AF_INET6
case AF_INET6:
sockp = &socket6;
break;
#endif
default:
fprintf(stderr, "unexpected address family: %d\n",
server_ai->ai_family);
exit(1);
}
if (*sockp == -1) {
if ((s = open_socket()) == -1)
return (-1);
*sockp = s;
}
return (*sockp);
}
/*
* setup:
* Set configuration options from command line arguments
@@ -664,11 +790,6 @@ close_socket(void) {
*/
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) {
@@ -697,7 +818,10 @@ setup(int argc, char **argv) {
if (open_datafile() == -1)
return (-1);
if (open_socket() == -1)
if (set_server_sa() == -1)
return (-1);
if ((query_socket = change_socket()) == -1)
return (-1);
return (0);
@@ -733,7 +857,7 @@ difftv(struct timeval tv1, struct timeval tv2) {
diff = (double)diff_sec + ((double)diff_usec / 1000000.0);
return diff;
return (diff);
}
/*
@@ -875,6 +999,7 @@ update_config(char *config_change_desc) {
unsigned int uint_val;
int directive_number;
int check;
int old_af;
if (ignore_config_changes == TRUE) {
fprintf(stderr, "Ignoring configuration change: %s",
@@ -928,9 +1053,29 @@ update_config(char *config_change_desc) {
return;
}
if (set_server(config_value) == -1)
if (set_server(config_value) == -1) {
fprintf(stderr, "Set server error: unable to change "
"the server name to '%s'\n", config_value);
return;
}
old_af = server_ai->ai_family;
if (set_server_sa() == -1) {
fprintf(stderr, "Set server error: unable to resolve "
"a new server '%s'\n",
config_value);
return;
}
if (old_af != server_ai->ai_family) {
if ((query_socket = change_socket()) == -1) {
/* XXX: this is fatal */
fprintf(stderr, "Set server error: "
"unable to open a new socket "
"for '%s'\n", config_value);
exit(1);
}
}
break;
case V_PORT:
@@ -943,9 +1088,15 @@ update_config(char *config_change_desc) {
check = is_uint(config_value, &uint_val);
if ((check == TRUE) && (uint_val > 0)) {
if (set_server_port(uint_val) == -1) {
if (set_server_port(config_value) == -1) {
fprintf(stderr, "Invalid config: Bad value for"
" %s: %s\n", directive, config_value);
} else {
if (set_server_sa() == -1) {
fprintf(stderr,
"Failed to set a new port\n");
return;
}
}
} else
fprintf(stderr, "Invalid config: Bad value for "
@@ -1098,7 +1249,7 @@ dispatch_query(unsigned short int id, char *dom, int qt) {
packet_buffer[1] = id_ptr[1];
bytes_sent = sendto(query_socket, packet_buffer, buffer_len, 0,
(struct sockaddr *)&qaddr, sockaddrlen);
server_ai->ai_addr, server_ai->ai_addrlen);
if (bytes_sent == -1) {
fprintf(stderr, "Failed to send query packet: %s %d\n",
dom, qt);
@@ -1121,6 +1272,7 @@ 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];
char serveraddr[NI_MAXHOST];
int query_type;
unsigned int count;
@@ -1132,14 +1284,30 @@ send_query(char *query_desc) {
}
if (dispatch_query(use_query_id, domain, query_type) == -1) {
fprintf(stderr, "Error sending query: %s\n", query_desc);
char *addrstr;
if (getnameinfo(server_ai->ai_addr, server_ai->ai_addrlen,
serveraddr, sizeof(serveraddr), NULL, 0,
NI_NUMERICHOST) == 0) {
addrstr = serveraddr;
} else
addrstr = "???"; /* XXX: this should not happen */
fprintf(stderr, "Error sending query to %s: %s\n",
addrstr, query_desc);
return;
}
if (setup_phase == TRUE) {
set_timenow(&time_of_first_query);
setup_phase = FALSE;
printf("[Status] Sending queries\n");
if (getnameinfo(server_ai->ai_addr, server_ai->ai_addrlen,
serveraddr, sizeof(serveraddr), NULL, 0,
NI_NUMERICHOST) != 0) {
fprintf(stderr, "Error printing server address\n");
return;
}
printf("[Status] Sending queries (beginning with %s)\n",
serveraddr);
}
/* Find the first slot in status[] that is not in use */
@@ -1163,39 +1331,6 @@ send_query(char *query_desc) {
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
@@ -1236,15 +1371,18 @@ register_response(unsigned short int id, unsigned int rcode) {
*/
void
process_single_response(int sockfd) {
static struct sockaddr_in from_addr;
struct sockaddr_storage from_addr_ss;
struct sockaddr *from_addr;
static unsigned char in_buf[MAX_BUFFER_LEN];
int numbytes, addr_len, resp_id;
int flags;
addr_len = sizeof(struct sockaddr);
memset(&from_addr_ss, 0, sizeof(from_addr_ss));
from_addr = (struct sockaddr *)&from_addr_ss;
addr_len = sizeof(from_addr_ss);
if ((numbytes = recvfrom(sockfd, in_buf, MAX_BUFFER_LEN,
0, (struct sockaddr *)&from_addr, &addr_len)) == -1) {
0, from_addr, &addr_len)) == -1) {
fprintf(stderr, "Error receiving datagram\n");
return;
}
@@ -1255,6 +1393,55 @@ process_single_response(int sockfd) {
register_response(resp_id, flags & 0xF);
}
/*
* data_available:
* Is there data available on the given file descriptor?
*
* Return TRUE if there is
* Return FALSE otherwise
*/
int
data_available(double wait) {
fd_set read_fds;
struct timeval tv;
int retval;
int available = FALSE;
int maxfd = -1;
/* Set list of file descriptors */
FD_ZERO(&read_fds);
if (socket4 != -1) {
FD_SET(socket4, &read_fds);
maxfd = socket4;
}
if (socket6 != -1) {
FD_SET(socket6, &read_fds);
if (maxfd == -1 || maxfd < socket6)
maxfd = socket6;
}
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(maxfd + 1, &read_fds, NULL, NULL, &tv);
if (socket4 != -1 && FD_ISSET(socket4, &read_fds)) {
available = TRUE;
process_single_response(socket4);
}
if (socket6 != -1 && FD_ISSET(socket6, &read_fds)) {
available = TRUE;
process_single_response(socket6);
}
return (available);
}
/*
* process_responses:
* Go through any/all received responses and remove them from the list of
@@ -1274,11 +1461,9 @@ process_responses(void) {
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);
if (data_available(first_packet_wait) == TRUE) {
while (data_available(0.0) == TRUE)
;
}
}
@@ -1393,9 +1578,10 @@ print_statistics(void) {
printf("\n");
printf(" Started at: %s", ctime(&start_time.tv_sec));
printf(" Started at: %s",
ctime((const time_t *)&start_time.tv_sec));
printf(" Finished at: %s",
ctime(&time_of_end_of_run.tv_sec));
ctime((const time_t *)&time_of_end_of_run.tv_sec));
printf(" Ran for: %.6lf seconds\n", run_time);
printf("\n");