1 1.1 christos /* 2 1.1 christos * testcode/perf.c - debug program to estimate name server performance. 3 1.1 christos * 4 1.1 christos * Copyright (c) 2008, NLnet Labs. All rights reserved. 5 1.1 christos * 6 1.1 christos * This software is open source. 7 1.1 christos * 8 1.1 christos * Redistribution and use in source and binary forms, with or without 9 1.1 christos * modification, are permitted provided that the following conditions 10 1.1 christos * are met: 11 1.1 christos * 12 1.1 christos * Redistributions of source code must retain the above copyright notice, 13 1.1 christos * this list of conditions and the following disclaimer. 14 1.1 christos * 15 1.1 christos * Redistributions in binary form must reproduce the above copyright notice, 16 1.1 christos * this list of conditions and the following disclaimer in the documentation 17 1.1 christos * and/or other materials provided with the distribution. 18 1.1 christos * 19 1.1 christos * Neither the name of the NLNET LABS nor the names of its contributors may 20 1.1 christos * be used to endorse or promote products derived from this software without 21 1.1 christos * specific prior written permission. 22 1.1 christos * 23 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 1.1 christos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 1.1 christos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 1.1 christos * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 1.1 christos * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 1.1 christos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 1.1 christos * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 1.1 christos * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 1.1 christos * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 1.1 christos * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 1.1 christos * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 1.1 christos */ 35 1.1 christos 36 1.1 christos /** 37 1.1 christos * \file 38 1.1 christos * 39 1.1 christos * This program estimates DNS name server performance. 40 1.1 christos */ 41 1.1 christos 42 1.1 christos #include "config.h" 43 1.1 christos #ifdef HAVE_GETOPT_H 44 1.1 christos #include <getopt.h> 45 1.1 christos #endif 46 1.1 christos #include <signal.h> 47 1.1 christos #include "util/log.h" 48 1.1 christos #include "util/locks.h" 49 1.1 christos #include "util/net_help.h" 50 1.1 christos #include "util/data/msgencode.h" 51 1.1 christos #include "util/data/msgreply.h" 52 1.1 christos #include "util/data/msgparse.h" 53 1.1 christos #include "sldns/sbuffer.h" 54 1.1 christos #include "sldns/wire2str.h" 55 1.1 christos #include "sldns/str2wire.h" 56 1.1 christos #include <sys/time.h> 57 1.1 christos 58 1.1 christos /** usage information for perf */ 59 1.1 christos static void usage(char* nm) 60 1.1 christos { 61 1.1 christos printf("usage: %s [options] server\n", nm); 62 1.1 christos printf("server: ip address of server, IP4 or IP6.\n"); 63 1.1 christos printf(" If not on port %d add @port.\n", UNBOUND_DNS_PORT); 64 1.1 christos printf("-d sec duration of test in whole seconds (0: wait for ^C)\n"); 65 1.1 christos printf("-a str query to ask, interpreted as a line from qfile\n"); 66 1.1 christos printf("-f fnm query list to read from file\n"); 67 1.1 christos printf(" every line has format: qname qclass qtype [+-]{E}\n"); 68 1.1 christos printf(" where + means RD set, E means EDNS enabled\n"); 69 1.1 christos printf("-q quiet mode, print only final qps\n"); 70 1.1 christos exit(1); 71 1.1 christos } 72 1.1 christos 73 1.1 christos struct perfinfo; 74 1.1 christos struct perfio; 75 1.1 christos 76 1.1 christos /** Global info for perf */ 77 1.1 christos struct perfinfo { 78 1.1 christos /** need to exit */ 79 1.1 christos volatile int exit; 80 1.1 christos /** all purpose buffer (for UDP send and receive) */ 81 1.1 christos sldns_buffer* buf; 82 1.1 christos 83 1.1 christos /** destination */ 84 1.1 christos struct sockaddr_storage dest; 85 1.1 christos /** length of dest socket addr */ 86 1.1 christos socklen_t destlen; 87 1.1 christos 88 1.1 christos /** when did this time slice start */ 89 1.1 christos struct timeval since; 90 1.1 christos /** number of queries received in that time */ 91 1.1 christos size_t numrecv; 92 1.1 christos /** number of queries sent out in that time */ 93 1.1 christos size_t numsent; 94 1.1 christos 95 1.1 christos /** duration of test in seconds */ 96 1.1 christos int duration; 97 1.1 christos /** quiet mode? */ 98 1.1 christos int quiet; 99 1.1 christos 100 1.1 christos /** when did the total test start */ 101 1.1 christos struct timeval start; 102 1.1 christos /** total number recvd */ 103 1.1 christos size_t total_recv; 104 1.1 christos /** total number sent */ 105 1.1 christos size_t total_sent; 106 1.1 christos /** numbers by rcode */ 107 1.1 christos size_t by_rcode[32]; 108 1.1 christos 109 1.1 christos /** number of I/O ports */ 110 1.1 christos size_t io_num; 111 1.1 christos /** I/O ports array */ 112 1.1 christos struct perfio* io; 113 1.1 christos /** max fd value in io ports */ 114 1.1 christos int maxfd; 115 1.1 christos /** readset */ 116 1.1 christos fd_set rset; 117 1.1 christos 118 1.1 christos /** size of querylist */ 119 1.1 christos size_t qlist_size; 120 1.1 christos /** allocated size of qlist array */ 121 1.1 christos size_t qlist_capacity; 122 1.1 christos /** list of query packets (data) */ 123 1.1 christos uint8_t** qlist_data; 124 1.1 christos /** list of query packets (length of a packet) */ 125 1.1 christos size_t* qlist_len; 126 1.1 christos /** index into querylist, for walking the list */ 127 1.1 christos size_t qlist_idx; 128 1.1 christos }; 129 1.1 christos 130 1.1 christos /** I/O port for perf */ 131 1.1 christos struct perfio { 132 1.1 christos /** id number */ 133 1.1 christos size_t id; 134 1.1 christos /** file descriptor of socket */ 135 1.1 christos int fd; 136 1.1 christos /** timeout value */ 137 1.1 christos struct timeval timeout; 138 1.1 christos /** ptr back to perfinfo */ 139 1.1 christos struct perfinfo* info; 140 1.1 christos }; 141 1.1 christos 142 1.1 christos /** number of msec between starting io ports */ 143 1.1 christos #define START_IO_INTERVAL 10 144 1.1 christos /** number of msec timeout on io ports */ 145 1.1 christos #define IO_TIMEOUT 10 146 1.1 christos 147 1.1 christos /** signal handler global info */ 148 1.1 christos static struct perfinfo* sig_info; 149 1.1 christos 150 1.1 christos /** signal handler for user quit */ 151 1.1 christos static RETSIGTYPE perf_sigh(int sig) 152 1.1 christos { 153 1.1 christos log_assert(sig_info); 154 1.1 christos if(!sig_info->quiet) 155 1.1 christos printf("exit on signal %d\n", sig); 156 1.1 christos sig_info->exit = 1; 157 1.1 christos } 158 1.1 christos 159 1.1 christos /** timeval compare, t1 < t2 */ 160 1.1 christos static int 161 1.1 christos perf_tv_smaller(struct timeval* t1, struct timeval* t2) 162 1.1 christos { 163 1.1 christos #ifndef S_SPLINT_S 164 1.1 christos if(t1->tv_sec < t2->tv_sec) 165 1.1 christos return 1; 166 1.1 christos if(t1->tv_sec == t2->tv_sec && 167 1.1 christos t1->tv_usec < t2->tv_usec) 168 1.1 christos return 1; 169 1.1 christos #endif 170 1.1 christos return 0; 171 1.1 christos } 172 1.1 christos 173 1.1 christos /** timeval add, t1 += t2 */ 174 1.1 christos static void 175 1.1 christos perf_tv_add(struct timeval* t1, struct timeval* t2) 176 1.1 christos { 177 1.1 christos #ifndef S_SPLINT_S 178 1.1 christos t1->tv_sec += t2->tv_sec; 179 1.1 christos t1->tv_usec += t2->tv_usec; 180 1.1.1.4 christos while(t1->tv_usec >= 1000000) { 181 1.1 christos t1->tv_usec -= 1000000; 182 1.1 christos t1->tv_sec++; 183 1.1 christos } 184 1.1 christos #endif 185 1.1 christos } 186 1.1 christos 187 1.1 christos /** timeval subtract, t1 -= t2 */ 188 1.1 christos static void 189 1.1 christos perf_tv_subtract(struct timeval* t1, struct timeval* t2) 190 1.1 christos { 191 1.1 christos #ifndef S_SPLINT_S 192 1.1 christos t1->tv_sec -= t2->tv_sec; 193 1.1 christos if(t1->tv_usec >= t2->tv_usec) { 194 1.1 christos t1->tv_usec -= t2->tv_usec; 195 1.1 christos } else { 196 1.1 christos t1->tv_sec--; 197 1.1 christos t1->tv_usec = 1000000-(t2->tv_usec-t1->tv_usec); 198 1.1 christos } 199 1.1 christos #endif 200 1.1 christos } 201 1.1 christos 202 1.1 christos 203 1.1 christos /** setup perf test environment */ 204 1.1 christos static void 205 1.1 christos perfsetup(struct perfinfo* info) 206 1.1 christos { 207 1.1 christos size_t i; 208 1.1 christos if(gettimeofday(&info->start, NULL) < 0) 209 1.1 christos fatal_exit("gettimeofday: %s", strerror(errno)); 210 1.1 christos sig_info = info; 211 1.1 christos if( signal(SIGINT, perf_sigh) == SIG_ERR || 212 1.1 christos #ifdef SIGQUIT 213 1.1 christos signal(SIGQUIT, perf_sigh) == SIG_ERR || 214 1.1 christos #endif 215 1.1 christos #ifdef SIGHUP 216 1.1 christos signal(SIGHUP, perf_sigh) == SIG_ERR || 217 1.1 christos #endif 218 1.1 christos #ifdef SIGBREAK 219 1.1 christos signal(SIGBREAK, perf_sigh) == SIG_ERR || 220 1.1 christos #endif 221 1.1 christos signal(SIGTERM, perf_sigh) == SIG_ERR) 222 1.1 christos fatal_exit("could not bind to signal"); 223 1.1.1.8 christos info->io = (struct perfio*)calloc(info->io_num, sizeof(struct perfio)); 224 1.1 christos if(!info->io) fatal_exit("out of memory"); 225 1.1 christos #ifndef S_SPLINT_S 226 1.1 christos FD_ZERO(&info->rset); 227 1.1 christos #endif 228 1.1 christos info->since = info->start; 229 1.1 christos for(i=0; i<info->io_num; i++) { 230 1.1 christos info->io[i].id = i; 231 1.1 christos info->io[i].info = info; 232 1.1 christos info->io[i].fd = socket( 233 1.1 christos addr_is_ip6(&info->dest, info->destlen)? 234 1.1 christos AF_INET6:AF_INET, SOCK_DGRAM, 0); 235 1.1 christos if(info->io[i].fd == -1) { 236 1.1.1.5 christos fatal_exit("socket: %s", sock_strerror(errno)); 237 1.1 christos } 238 1.1 christos if(info->io[i].fd > info->maxfd) 239 1.1 christos info->maxfd = info->io[i].fd; 240 1.1 christos #ifndef S_SPLINT_S 241 1.1 christos FD_SET(FD_SET_T info->io[i].fd, &info->rset); 242 1.1 christos info->io[i].timeout.tv_usec = ((START_IO_INTERVAL*i)%1000) 243 1.1 christos *1000; 244 1.1 christos info->io[i].timeout.tv_sec = (START_IO_INTERVAL*i)/1000; 245 1.1 christos perf_tv_add(&info->io[i].timeout, &info->since); 246 1.1 christos #endif 247 1.1 christos } 248 1.1 christos } 249 1.1 christos 250 1.1 christos /** cleanup perf test environment */ 251 1.1 christos static void 252 1.1 christos perffree(struct perfinfo* info) 253 1.1 christos { 254 1.1 christos size_t i; 255 1.1 christos if(!info) return; 256 1.1 christos if(info->io) { 257 1.1 christos for(i=0; i<info->io_num; i++) { 258 1.1.1.5 christos sock_close(info->io[i].fd); 259 1.1 christos } 260 1.1 christos free(info->io); 261 1.1 christos } 262 1.1 christos for(i=0; i<info->qlist_size; i++) 263 1.1 christos free(info->qlist_data[i]); 264 1.1 christos free(info->qlist_data); 265 1.1 christos free(info->qlist_len); 266 1.1 christos } 267 1.1 christos 268 1.1 christos /** send new query for io */ 269 1.1 christos static void 270 1.1 christos perfsend(struct perfinfo* info, size_t n, struct timeval* now) 271 1.1 christos { 272 1.1 christos ssize_t r; 273 1.1 christos r = sendto(info->io[n].fd, (void*)info->qlist_data[info->qlist_idx], 274 1.1 christos info->qlist_len[info->qlist_idx], 0, 275 1.1 christos (struct sockaddr*)&info->dest, info->destlen); 276 1.1 christos /*log_hex("send", info->qlist_data[info->qlist_idx], 277 1.1 christos info->qlist_len[info->qlist_idx]);*/ 278 1.1 christos if(r == -1) { 279 1.1.1.5 christos log_err("sendto: %s", sock_strerror(errno)); 280 1.1 christos } else if(r != (ssize_t)info->qlist_len[info->qlist_idx]) { 281 1.1 christos log_err("partial sendto"); 282 1.1 christos } 283 1.1 christos info->qlist_idx = (info->qlist_idx+1) % info->qlist_size; 284 1.1 christos info->numsent++; 285 1.1 christos 286 1.1 christos info->io[n].timeout.tv_sec = IO_TIMEOUT/1000; 287 1.1 christos info->io[n].timeout.tv_usec = (IO_TIMEOUT%1000)*1000; 288 1.1 christos perf_tv_add(&info->io[n].timeout, now); 289 1.1 christos } 290 1.1 christos 291 1.1 christos /** got reply for io */ 292 1.1 christos static void 293 1.1 christos perfreply(struct perfinfo* info, size_t n, struct timeval* now) 294 1.1 christos { 295 1.1 christos ssize_t r; 296 1.1 christos r = recv(info->io[n].fd, (void*)sldns_buffer_begin(info->buf), 297 1.1 christos sldns_buffer_capacity(info->buf), 0); 298 1.1 christos if(r == -1) { 299 1.1.1.5 christos log_err("recv: %s", sock_strerror(errno)); 300 1.1 christos } else { 301 1.1 christos info->by_rcode[LDNS_RCODE_WIRE(sldns_buffer_begin( 302 1.1 christos info->buf))]++; 303 1.1 christos info->numrecv++; 304 1.1 christos } 305 1.1 christos /*sldns_buffer_set_limit(info->buf, r); 306 1.1 christos log_buf(0, "reply", info->buf);*/ 307 1.1 christos perfsend(info, n, now); 308 1.1 christos } 309 1.1 christos 310 1.1 christos /** got timeout for io */ 311 1.1 christos static void 312 1.1 christos perftimeout(struct perfinfo* info, size_t n, struct timeval* now) 313 1.1 christos { 314 1.1 christos /* may not be a dropped packet, this is also used to start 315 1.1 christos * up the sending IOs */ 316 1.1 christos perfsend(info, n, now); 317 1.1 christos } 318 1.1 christos 319 1.1 christos /** print nice stats about qps */ 320 1.1 christos static void 321 1.1 christos stat_printout(struct perfinfo* info, struct timeval* now, 322 1.1 christos struct timeval* elapsed) 323 1.1 christos { 324 1.1 christos /* calculate qps */ 325 1.1 christos double dt, qps = 0; 326 1.1 christos #ifndef S_SPLINT_S 327 1.1 christos dt = (double)(elapsed->tv_sec*1000000 + elapsed->tv_usec) / 1000000; 328 1.1 christos #endif 329 1.1 christos if(dt > 0.001) 330 1.1 christos qps = (double)(info->numrecv) / dt; 331 1.1 christos if(!info->quiet) 332 1.1 christos printf("qps: %g\n", qps); 333 1.1 christos /* setup next slice */ 334 1.1 christos info->since = *now; 335 1.1 christos info->total_sent += info->numsent; 336 1.1 christos info->total_recv += info->numrecv; 337 1.1 christos info->numrecv = 0; 338 1.1 christos info->numsent = 0; 339 1.1 christos } 340 1.1 christos 341 1.1 christos /** wait for new events for performance test */ 342 1.1 christos static void 343 1.1 christos perfselect(struct perfinfo* info) 344 1.1 christos { 345 1.1 christos fd_set rset = info->rset; 346 1.1 christos struct timeval timeout, now; 347 1.1 christos int num; 348 1.1 christos size_t i; 349 1.1 christos if(gettimeofday(&now, NULL) < 0) 350 1.1 christos fatal_exit("gettimeofday: %s", strerror(errno)); 351 1.1 christos /* time to exit? */ 352 1.1 christos if(info->duration > 0) { 353 1.1 christos timeout = now; 354 1.1 christos perf_tv_subtract(&timeout, &info->start); 355 1.1 christos if((int)timeout.tv_sec >= info->duration) { 356 1.1 christos info->exit = 1; 357 1.1 christos return; 358 1.1 christos } 359 1.1 christos } 360 1.1 christos /* time for stats printout? */ 361 1.1 christos timeout = now; 362 1.1 christos perf_tv_subtract(&timeout, &info->since); 363 1.1 christos if(timeout.tv_sec > 0) { 364 1.1 christos stat_printout(info, &now, &timeout); 365 1.1 christos } 366 1.1 christos /* see what is closest port to timeout; or if there is a timeout */ 367 1.1 christos timeout = info->io[0].timeout; 368 1.1 christos for(i=0; i<info->io_num; i++) { 369 1.1 christos if(perf_tv_smaller(&info->io[i].timeout, &now)) { 370 1.1 christos perftimeout(info, i, &now); 371 1.1 christos return; 372 1.1 christos } 373 1.1 christos if(perf_tv_smaller(&info->io[i].timeout, &timeout)) { 374 1.1 christos timeout = info->io[i].timeout; 375 1.1 christos } 376 1.1 christos } 377 1.1 christos perf_tv_subtract(&timeout, &now); 378 1.1 christos 379 1.1 christos num = select(info->maxfd+1, &rset, NULL, NULL, &timeout); 380 1.1 christos if(num == -1) { 381 1.1 christos if(errno == EAGAIN || errno == EINTR) 382 1.1 christos return; 383 1.1 christos log_err("select: %s", strerror(errno)); 384 1.1 christos } 385 1.1 christos 386 1.1 christos /* handle new events */ 387 1.1 christos for(i=0; num && i<info->io_num; i++) { 388 1.1 christos if(FD_ISSET(info->io[i].fd, &rset)) { 389 1.1 christos perfreply(info, i, &now); 390 1.1 christos num--; 391 1.1 christos } 392 1.1 christos } 393 1.1 christos } 394 1.1 christos 395 1.1 christos /** show end stats */ 396 1.1 christos static void 397 1.1 christos perfendstats(struct perfinfo* info) 398 1.1 christos { 399 1.1 christos double dt, qps; 400 1.1 christos struct timeval timeout, now; 401 1.1 christos int i, lost; 402 1.1 christos if(gettimeofday(&now, NULL) < 0) 403 1.1 christos fatal_exit("gettimeofday: %s", strerror(errno)); 404 1.1 christos timeout = now; 405 1.1 christos perf_tv_subtract(&timeout, &info->since); 406 1.1 christos stat_printout(info, &now, &timeout); 407 1.1 christos 408 1.1 christos timeout = now; 409 1.1 christos perf_tv_subtract(&timeout, &info->start); 410 1.1 christos dt = (double)(timeout.tv_sec*1000000 + timeout.tv_usec) / 1000000.0; 411 1.1 christos qps = (double)(info->total_recv) / dt; 412 1.1 christos lost = (int)(info->total_sent - info->total_recv) - (int)info->io_num; 413 1.1 christos if(!info->quiet) { 414 1.1 christos printf("overall time: %g sec\n", 415 1.1 christos (double)timeout.tv_sec + 416 1.1 christos (double)timeout.tv_usec/1000000.); 417 1.1 christos if(lost > 0) 418 1.1 christos printf("Packets lost: %d\n", (int)lost); 419 1.1 christos 420 1.1 christos for(i=0; i<(int)(sizeof(info->by_rcode)/sizeof(size_t)); i++) 421 1.1 christos { 422 1.1 christos if(info->by_rcode[i] > 0) { 423 1.1 christos char rc[16]; 424 1.1 christos sldns_wire2str_rcode_buf(i, rc, sizeof(rc)); 425 1.1 christos printf("%d(%5s): %u replies\n", 426 1.1 christos i, rc, (unsigned)info->by_rcode[i]); 427 1.1 christos } 428 1.1 christos } 429 1.1 christos } 430 1.1 christos printf("average qps: %g\n", qps); 431 1.1 christos } 432 1.1 christos 433 1.1 christos /** perform the performance test */ 434 1.1 christos static void 435 1.1 christos perfmain(struct perfinfo* info) 436 1.1 christos { 437 1.1 christos perfsetup(info); 438 1.1 christos while(!info->exit) { 439 1.1 christos perfselect(info); 440 1.1 christos } 441 1.1 christos perfendstats(info); 442 1.1 christos perffree(info); 443 1.1 christos } 444 1.1 christos 445 1.1 christos /** parse a query line to a packet into buffer */ 446 1.1 christos static int 447 1.1 christos qlist_parse_line(sldns_buffer* buf, char* p) 448 1.1 christos { 449 1.1 christos char nm[1024], cl[1024], tp[1024], fl[1024]; 450 1.1 christos int r; 451 1.1 christos int rec = 1, edns = 0; 452 1.1 christos struct query_info qinfo; 453 1.1 christos nm[0] = 0; cl[0] = 0; tp[0] = 0; fl[0] = 0; 454 1.1 christos r = sscanf(p, " %1023s %1023s %1023s %1023s", nm, cl, tp, fl); 455 1.1 christos if(r != 3 && r != 4) 456 1.1 christos return 0; 457 1.1 christos /*printf("nm='%s', cl='%s', tp='%s', fl='%s'\n", nm, cl, tp, fl);*/ 458 1.1 christos if(strcmp(tp, "IN") == 0 || strcmp(tp, "CH") == 0) { 459 1.1 christos qinfo.qtype = sldns_get_rr_type_by_name(cl); 460 1.1 christos qinfo.qclass = sldns_get_rr_class_by_name(tp); 461 1.1.1.7 christos if((qinfo.qtype == 0 && strcmp(cl, "TYPE0") != 0) || 462 1.1.1.7 christos (qinfo.qclass == 0 && strcmp(tp, "CLASS0") != 0)) { 463 1.1.1.7 christos return 0; 464 1.1.1.7 christos } 465 1.1 christos } else { 466 1.1 christos qinfo.qtype = sldns_get_rr_type_by_name(tp); 467 1.1 christos qinfo.qclass = sldns_get_rr_class_by_name(cl); 468 1.1.1.7 christos if((qinfo.qtype == 0 && strcmp(tp, "TYPE0") != 0) || 469 1.1.1.7 christos (qinfo.qclass == 0 && strcmp(cl, "CLASS0") != 0)) { 470 1.1.1.7 christos return 0; 471 1.1.1.7 christos } 472 1.1 christos } 473 1.1 christos if(fl[0] == '+') rec = 1; 474 1.1 christos else if(fl[0] == '-') rec = 0; 475 1.1 christos else if(fl[0] == 'E') edns = 1; 476 1.1 christos if((fl[0] == '+' || fl[0] == '-') && fl[1] == 'E') 477 1.1 christos edns = 1; 478 1.1 christos qinfo.qname = sldns_str2wire_dname(nm, &qinfo.qname_len); 479 1.1 christos if(!qinfo.qname) 480 1.1 christos return 0; 481 1.1.1.2 christos qinfo.local_alias = NULL; 482 1.1 christos qinfo_query_encode(buf, &qinfo); 483 1.1 christos sldns_buffer_write_u16_at(buf, 0, 0); /* zero ID */ 484 1.1 christos if(rec) LDNS_RD_SET(sldns_buffer_begin(buf)); 485 1.1 christos if(edns) { 486 1.1 christos struct edns_data ed; 487 1.1 christos memset(&ed, 0, sizeof(ed)); 488 1.1 christos ed.edns_present = 1; 489 1.1 christos ed.udp_size = EDNS_ADVERTISED_SIZE; 490 1.1 christos /* Set DO bit in all EDNS datagrams ... */ 491 1.1 christos ed.bits = EDNS_DO; 492 1.1 christos attach_edns_record(buf, &ed); 493 1.1 christos } 494 1.1 christos free(qinfo.qname); 495 1.1 christos return 1; 496 1.1 christos } 497 1.1 christos 498 1.1 christos /** grow query list capacity */ 499 1.1 christos static void 500 1.1 christos qlist_grow_capacity(struct perfinfo* info) 501 1.1 christos { 502 1.1 christos size_t newcap = (size_t)((info->qlist_capacity==0)?16: 503 1.1 christos info->qlist_capacity*2); 504 1.1.1.8 christos uint8_t** d = (uint8_t**)calloc(newcap, sizeof(uint8_t*)); 505 1.1.1.8 christos size_t* l = (size_t*)calloc(newcap, sizeof(size_t)); 506 1.1 christos if(!d || !l) fatal_exit("out of memory"); 507 1.1.1.3 christos if(info->qlist_data && info->qlist_capacity) 508 1.1.1.3 christos memcpy(d, info->qlist_data, sizeof(uint8_t*)* 509 1.1.1.3 christos info->qlist_capacity); 510 1.1.1.3 christos if(info->qlist_len && info->qlist_capacity) 511 1.1.1.3 christos memcpy(l, info->qlist_len, sizeof(size_t)* 512 1.1.1.3 christos info->qlist_capacity); 513 1.1 christos free(info->qlist_data); 514 1.1 christos free(info->qlist_len); 515 1.1 christos info->qlist_data = d; 516 1.1 christos info->qlist_len = l; 517 1.1 christos info->qlist_capacity = newcap; 518 1.1 christos } 519 1.1 christos 520 1.1 christos /** setup query list in info */ 521 1.1 christos static void 522 1.1 christos qlist_add_line(struct perfinfo* info, char* line, int no) 523 1.1 christos { 524 1.1 christos if(!qlist_parse_line(info->buf, line)) { 525 1.1 christos printf("error parsing query %d: %s\n", no, line); 526 1.1 christos exit(1); 527 1.1 christos } 528 1.1 christos sldns_buffer_write_u16_at(info->buf, 0, (uint16_t)info->qlist_size); 529 1.1 christos if(info->qlist_size + 1 > info->qlist_capacity) { 530 1.1 christos qlist_grow_capacity(info); 531 1.1 christos } 532 1.1 christos info->qlist_len[info->qlist_size] = sldns_buffer_limit(info->buf); 533 1.1 christos info->qlist_data[info->qlist_size] = memdup( 534 1.1 christos sldns_buffer_begin(info->buf), sldns_buffer_limit(info->buf)); 535 1.1 christos if(!info->qlist_data[info->qlist_size]) 536 1.1 christos fatal_exit("out of memory"); 537 1.1 christos info->qlist_size ++; 538 1.1 christos } 539 1.1 christos 540 1.1 christos /** setup query list in info */ 541 1.1 christos static void 542 1.1 christos qlist_read_file(struct perfinfo* info, char* fname) 543 1.1 christos { 544 1.1 christos char buf[1024]; 545 1.1 christos char *p; 546 1.1 christos FILE* in = fopen(fname, "r"); 547 1.1 christos int lineno = 0; 548 1.1 christos if(!in) { 549 1.1 christos perror(fname); 550 1.1 christos exit(1); 551 1.1 christos } 552 1.1 christos while(fgets(buf, (int)sizeof(buf), in)) { 553 1.1 christos lineno++; 554 1.1 christos buf[sizeof(buf)-1] = 0; 555 1.1 christos p = buf; 556 1.1 christos while(*p == ' ' || *p == '\t') 557 1.1 christos p++; 558 1.1 christos if(p[0] == 0 || p[0] == '\n' || p[0] == ';' || p[0] == '#') 559 1.1 christos continue; 560 1.1 christos qlist_add_line(info, p, lineno); 561 1.1 christos } 562 1.1 christos printf("Read %s, got %u queries\n", fname, (unsigned)info->qlist_size); 563 1.1 christos fclose(in); 564 1.1 christos } 565 1.1 christos 566 1.1 christos /** getopt global, in case header files fail to declare it. */ 567 1.1 christos extern int optind; 568 1.1 christos /** getopt global, in case header files fail to declare it. */ 569 1.1 christos extern char* optarg; 570 1.1 christos 571 1.1 christos /** main program for perf */ 572 1.1 christos int main(int argc, char* argv[]) 573 1.1 christos { 574 1.1 christos char* nm = argv[0]; 575 1.1 christos int c; 576 1.1 christos struct perfinfo info; 577 1.1 christos #ifdef USE_WINSOCK 578 1.1 christos int r; 579 1.1 christos WSADATA wsa_data; 580 1.1 christos #endif 581 1.1 christos 582 1.1 christos /* defaults */ 583 1.1 christos memset(&info, 0, sizeof(info)); 584 1.1 christos info.io_num = 16; 585 1.1 christos 586 1.1.1.6 christos checklock_start(); 587 1.1 christos log_init(NULL, 0, NULL); 588 1.1 christos log_ident_set("perf"); 589 1.1 christos #ifdef USE_WINSOCK 590 1.1 christos if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) 591 1.1 christos fatal_exit("WSAStartup failed: %s", wsa_strerror(r)); 592 1.1 christos #endif 593 1.1 christos 594 1.1 christos info.buf = sldns_buffer_new(65553); 595 1.1 christos if(!info.buf) fatal_exit("out of memory"); 596 1.1 christos 597 1.1 christos /* parse the options */ 598 1.1 christos while( (c=getopt(argc, argv, "d:ha:f:q")) != -1) { 599 1.1 christos switch(c) { 600 1.1 christos case 'q': 601 1.1 christos info.quiet = 1; 602 1.1 christos break; 603 1.1 christos case 'd': 604 1.1 christos if(atoi(optarg)==0 && strcmp(optarg, "0")!=0) { 605 1.1 christos printf("-d not a number %s", optarg); 606 1.1.1.3 christos exit(1); 607 1.1 christos } 608 1.1 christos info.duration = atoi(optarg); 609 1.1 christos break; 610 1.1 christos case 'a': 611 1.1 christos qlist_add_line(&info, optarg, 0); 612 1.1 christos break; 613 1.1 christos case 'f': 614 1.1 christos qlist_read_file(&info, optarg); 615 1.1 christos break; 616 1.1 christos case '?': 617 1.1 christos case 'h': 618 1.1 christos default: 619 1.1 christos usage(nm); 620 1.1 christos } 621 1.1 christos } 622 1.1 christos argc -= optind; 623 1.1 christos argv += optind; 624 1.1 christos 625 1.1 christos if(argc != 1) { 626 1.1 christos printf("error: pass server IP address on commandline.\n"); 627 1.1 christos usage(nm); 628 1.1 christos } 629 1.1.1.7 christos if(!extstrtoaddr(argv[0], &info.dest, &info.destlen, UNBOUND_DNS_PORT)) { 630 1.1 christos printf("Could not parse ip: %s\n", argv[0]); 631 1.1.1.3 christos exit(1); 632 1.1 christos } 633 1.1 christos if(info.qlist_size == 0) { 634 1.1 christos printf("No queries to make, use -f or -a.\n"); 635 1.1.1.3 christos exit(1); 636 1.1 christos } 637 1.1 christos 638 1.1 christos /* do the performance test */ 639 1.1 christos perfmain(&info); 640 1.1 christos 641 1.1 christos sldns_buffer_free(info.buf); 642 1.1 christos #ifdef USE_WINSOCK 643 1.1 christos WSACleanup(); 644 1.1 christos #endif 645 1.1 christos checklock_stop(); 646 1.1 christos return 0; 647 1.1 christos } 648