├── Makefile ├── README.md ├── a.out ├── conf.c ├── conf.h ├── dns_param.h ├── dnsperf.c ├── dnsperf.conf ├── events.c ├── events.h ├── list.h ├── sock.c └── sock.h /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CC = gcc 3 | CFLAGS = -g -Wall 4 | LIBS = -lresolv 5 | shell = /bin/sh 6 | ECHO = /bin/echo 7 | DEFINES = -DHAVE_EPOLL 8 | INC = -I ./ 9 | 10 | ifeq ($(shell test -f /usr/include/sys/event.h && echo yes), yes) 11 | DEFINES = -DHAVE_KQUEUE 12 | @echo "Use Kqueue" 13 | endif 14 | 15 | all: dnsperf 16 | 17 | dnsperf: dnsperf.o events.o sock.o 18 | $(CC) $(CFLAGS) $(DEFINES) -o $@ $^ $(LIBS) $(INC) 19 | 20 | dnsperf.o: dnsperf.c 21 | $(CC) $(CFLAGS) $(DEFINES) -c $^ $(INC) 22 | 23 | events.o: events.c 24 | $(CC) $(CFLAGS) $(DEFINES) -c $^ $(INC) 25 | 26 | sock.o: sock.c 27 | $(CC) $(CFLAGS) $(DEFINES) -c $^ $(INC) 28 | 29 | clean: 30 | rm -f *.o dnsperf 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dnsperf 2 | ====== 3 | A DNS performance tool. 4 | 5 | ### Introduction 6 | Dnsperf is a single-process dns load testing and benchmarking utility. It was designed to measure the performance of your 7 | DNS Server or Local DNS Server by send a configured number of queries. 8 | Performance measures contains elapsed time, its transation rate, its concurrrency and the percentage of successful queries. 9 | These measures are reported at the end of each testing. 10 | 11 | ### Usage 12 | Dnsperf supports the following command line options: 13 | 14 | **-s** 15 |     Specifies the DNS server's IP address. The default IP is `127.0.0.1`. 16 | **-p** 17 |     Specifies the DNS server's port. The default Port is `53`. 18 | **-d** 19 |     Specifies the input data file. Input data file contains `query domain` and `query type`. 20 | **-t** 21 |     Specifies the timeout for query completion in millisecond. The default timeout is `3000ms`. 22 | **-Q** 23 |     Specifies the max number of queries to be send. The default number is `1000`. 24 | **-c** 25 |     Specifies the number of concurrent queries. The default number is `100`. Dnsperf will randomly pick a `query domain` from data file as QNAME. 26 | **-l** 27 |     Specifies how long to run tests in seconds. The default number is infinite. 28 | **-e** 29 |     This will sets the real client IP in query string following the rules defined in [edns-client-subnet]. 30 | 31 | **-i** 32 |     Specifies interval of queries in seconds. The default number is zero. This option is not supported currently. 33 | **-P** 34 |     Specifies the transport layer protocol to send DNS queries, `udp` or `tcp`. As we know, although UDP is the suggested protocol, DNS queries can be send either by UDP or TCP. The default is `udp`. `tcp` is not supported currently, and it is coming soon. 35 | **-f** 36 |     Specify address family of DNS transport, `inet` or `inet6`. The default is `inet`. `inet6` is not supported currently. 37 | **-v** 38 |     Verbose: report the RCODE of each response on stdout. 39 | **-h** 40 |     Print the usage of dnsperf. 41 | 42 | ### Data file format 43 | An example of data file format is shown in file `a.out` in project directory. 44 | In the file, the line begin with `#` is recgonized as comment. Each useful line contains two columns. The first column is the `domain name` to be queried, and the second column is the `query type`. 45 | The `query type` currently supported includes: `A`,`NS`,`MD`,`MF`,`CNAME`,`SOA`,`MB`,`MG`,`MR`,`NULL`,`WKS`,`PTR`,`HINFO`,`MINFO`,`MX`,`TXT`,`AAAA`,`SRV`,`NAPTR`,`A6`,`ASFR`,`MAILB`,`MAILA`,`ANY`. 46 | 47 | ### Performance Statistics 48 | Performance statistics will displayed on your `stdin` after testing. The following is a sample outputs. 49 | ```sh 50 | DNS Performance Testing Tool 51 | 52 | [Status] Processing query data 53 | [Status] Sending queries to 127.0.0.1:53 54 | time up 55 | [Status]DNS Query Performance Testing Finish 56 | [Result]Queries sent: 35650 57 | [Result]Queries completed: 35578 58 | [Result]Complete percentage: 99.80% 59 | 60 | [Result]Elapsed time(s): 1.00000 61 | 62 | [Result]Queries Per Second: 35650.0000 63 | ``` 64 | The outputs is easy to comprehend. 65 | 66 | ### Author 67 | Cobblau, 68 | 69 | [edns-client-subnet]: http://www.afasterinternet.com/ietfdraft.htm -------------------------------------------------------------------------------- /a.out: -------------------------------------------------------------------------------- 1 | # This is a comment and is ommited 2 | # The columns after column 2 will be ommited if one line contains more than 3 colums. 3 | www.taobao.com A 4 | img01.taobaocdn.com CNAME 5 | -------------------------------------------------------------------------------- /conf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A DNS Performance tool. 3 | * 4 | * Copyright (C) 2014 Cobblau 5 | * 6 | * dnsperf is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * dnsperf is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | 21 | /* 22 | * 23 | * NOTE: This file is not used currently. 24 | * 25 | */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "conf.h" 37 | 38 | 39 | static int conf_read_file(char *file_name, char *buf, int len); 40 | static int conf_parse(conf_t *cf, char *buf, int len); 41 | static conf_command_t *conf_find_command(conf_command_t *commands, char *p, int len); 42 | static int conf_get_int(void *conf, unsigned int offset, char *s, int len); 43 | static int conf_get_on_off(void *conf, unsigned int offset, char *s, int len); 44 | static int conf_get_string(void *conf, unsigned int offset, char *s, int len); 45 | static int conf_get_iarray(void *conf, unsigned int offset, char *s, int len); 46 | 47 | 48 | static int my_atoi(char *p, int len); 49 | static char *my_substring(char *pos, char *last, char **start, int *len); 50 | 51 | 52 | static conf_command_t main_commands[] = { 53 | { "name_server", 54 | conf_get_string, 55 | offsetof(conf_t, name_server) }, 56 | 57 | { "port", 58 | conf_get_int, 59 | offsetof(conf_t, port) }, 60 | 61 | { "timeout", 62 | conf_get_int, 63 | offsetof(conf_t, timeout) }, 64 | 65 | { "max_query", 66 | conf_get_int, 67 | offsetof(conf_t, max_query) }, 68 | 69 | { "concurrent_query", 70 | conf_get_int, 71 | offsetof(conf_t, concurrent_query) }, 72 | 73 | { "running_time", 74 | conf_get_int, 75 | offsetof(conf_t, running_time) }, 76 | 77 | { "protocol", 78 | conf_get_string, 79 | offsetof(conf_t, protocol) }, 80 | 81 | { "address_family", 82 | conf_get_string, 83 | offsetof(conf_t, addr_family) }, 84 | 85 | { "verbose", 86 | conf_get_on_off, 87 | offsetof(conf_t, verbose) }, 88 | 89 | { "", NULL, 0 } 90 | }; 91 | 92 | int 93 | conf_parse_file(char *conf_file_name, conf_t **conf) 94 | { 95 | int len; 96 | char buf[8192] = {0}; 97 | conf_t *cf; 98 | 99 | if ((cf = malloc(sizeof(conf_t))) == NULL) { 100 | return -1; 101 | } 102 | 103 | len = conf_read_file(conf_file_name, buf, 8192); 104 | if (len == -1) { 105 | return -1; 106 | } 107 | 108 | if (conf_parse(cf, buf, len) == -1) { 109 | return -1; 110 | } 111 | 112 | *conf = cf; 113 | return 0; 114 | } 115 | 116 | static int 117 | conf_read_file(char *file_name, char *buf, int len) 118 | { 119 | int fd; 120 | int n, size; 121 | 122 | fd = open(file_name, O_RDONLY, 0); 123 | if (fd == -1) { 124 | return -1; 125 | } 126 | 127 | n = size = 0; 128 | for (;;) { 129 | n = read(fd, buf + size, len); 130 | 131 | if (n == 0) { 132 | break; 133 | } 134 | 135 | size += n; 136 | len -= n; 137 | 138 | if (len <= 0) { 139 | /* config file is too large */ 140 | return -1; 141 | } 142 | } 143 | 144 | close(fd); 145 | return size; 146 | } 147 | 148 | static int 149 | conf_parse(conf_t *cf, char *buf, int len) 150 | { 151 | conf_command_t *cmd; 152 | char *begin, *end; 153 | char *p, *p1, *p2, ch; 154 | int skip; 155 | 156 | p1 = p2 = NULL; 157 | cmd = NULL; 158 | begin = buf; 159 | end = buf + len; 160 | 161 | enum { 162 | directive_start = 0, 163 | directive_name, 164 | directive_arg 165 | } state; 166 | 167 | state = directive_start; 168 | skip = 0; 169 | 170 | for (p = begin; p < end; p++) { 171 | ch = *p; 172 | 173 | if (ch == '#') { 174 | skip = 1; 175 | continue; 176 | } 177 | 178 | if (skip) { 179 | if (ch == '\n') { 180 | skip = 0; 181 | } 182 | continue; 183 | } 184 | 185 | switch (state) { 186 | case directive_start: 187 | if (is_digit(ch) || is_letter(ch) || ch == '_') { 188 | p1 = p; 189 | state = directive_name; 190 | break; 191 | } 192 | 193 | if (!is_blank(ch)) { 194 | return -1; 195 | } 196 | 197 | break; 198 | 199 | case directive_name: 200 | 201 | if (is_blank(ch)) { 202 | p2 = p; 203 | 204 | cmd = conf_find_command(main_commands, p1, p2 - p1); 205 | if (cmd == NULL) { 206 | fprintf(stderr, "CONF: not found conf command %s", p1); 207 | return -1; 208 | } 209 | 210 | p1 = p; 211 | state = directive_arg; 212 | break; 213 | } 214 | 215 | if (!is_digit(ch) && !is_letter(ch) && ch != '_') { 216 | fprintf(stderr, "CONF: invalid character(%c) in command", ch); 217 | return -1; 218 | } 219 | 220 | break; 221 | 222 | case directive_arg: 223 | 224 | if (ch == ';') { 225 | p2 = p; 226 | 227 | while(is_blank(*p1)) p1++; 228 | 229 | if (cmd->handler(cf, cmd->offset, p1, p2 - p1) != 0) { 230 | fprintf(stderr, "CONF: get value error!"); 231 | return -1; 232 | } 233 | 234 | state = directive_start; 235 | } 236 | 237 | break; 238 | 239 | } 240 | } 241 | 242 | if (state != directive_start) { 243 | fprintf(stderr, "CONF: command not complete"); 244 | return -1; 245 | } 246 | 247 | return 0; 248 | } 249 | 250 | static conf_command_t * 251 | conf_find_command(conf_command_t *commands, char *p, int len) 252 | { 253 | int i; 254 | conf_command_t *cmd; 255 | 256 | for (i = 0; ; i++) { 257 | cmd = main_commands + i; 258 | if (cmd->handler == NULL) { 259 | break; 260 | } 261 | 262 | if (strncmp(cmd->name, p, len) == 0) { 263 | return cmd; 264 | } 265 | } 266 | 267 | return NULL; 268 | } 269 | 270 | static int 271 | conf_get_int(void *conf, unsigned int offset, char *s, int len) 272 | { 273 | char *p; 274 | int v; 275 | 276 | p = (char *) conf; 277 | 278 | v = my_atoi(s, len); 279 | 280 | *(int *) (p + offset) = v; 281 | 282 | return 0; 283 | } 284 | 285 | static int 286 | conf_get_on_off(void *conf, unsigned int offset, char *s, int len) 287 | { 288 | char *p; 289 | 290 | p = (char *) conf; 291 | 292 | if (strncasecmp(s, "off", 3) == 0) { 293 | *(int *) (p + offset) = 0; 294 | return 0; 295 | } 296 | 297 | if (strncasecmp(s, "on", 2) == 0) { 298 | *(int *) (p + offset) = 1; 299 | return 0; 300 | } 301 | 302 | return -1; 303 | } 304 | 305 | static int 306 | conf_get_string(void *conf, unsigned int offset, char *s, int len) 307 | { 308 | char *p, *v; 309 | 310 | 311 | p = (char *) conf; 312 | 313 | if (len == 0) { 314 | *(char **) (p + offset) = NULL; 315 | return 0; 316 | } 317 | 318 | if((v = calloc(1, sizeof(char) * (len + 1))) == NULL) { 319 | return -1; 320 | } 321 | memcpy(v, s, len); 322 | 323 | *(char **) (p + offset) = v; 324 | 325 | return 0; 326 | } 327 | 328 | static int 329 | conf_get_iarray(void *conf, unsigned int offset, char *s, int len) 330 | { 331 | char *p, *c, *start; 332 | int l, value; 333 | struct _list *rcodes; 334 | 335 | if ((rcodes = alloc_list(NULL, NULL)) == NULL) { 336 | return -1; 337 | } 338 | 339 | for (p = s; p < s + len; ) { 340 | p = my_substring(p, s + len, &start, &l); 341 | 342 | if ((value = my_atoi(start, l)) == -1) { 343 | goto failure; 344 | } 345 | 346 | list_add2(rcodes, (void *) value); 347 | } 348 | 349 | c = (char *) conf; 350 | 351 | *(struct _list **) (c + offset) = rcodes; 352 | 353 | return 0; 354 | 355 | failure: 356 | free(rcodes); 357 | return -1; 358 | } 359 | 360 | static int 361 | my_atoi(char *s, int n) 362 | { 363 | int value; 364 | 365 | if (n == 0) { 366 | return -1; 367 | } 368 | 369 | for (value = 0; n--; s++) { 370 | if (*s < '0' || *s > '9') { 371 | return -1; 372 | } 373 | 374 | value = value * 10 + (*s - '0'); 375 | } 376 | 377 | return value < 0 ? -1 : value; 378 | } 379 | 380 | static char * 381 | my_substring(char *pos, char *last, char **start, int *len) 382 | { 383 | char *p; 384 | 385 | for (p = pos; p < last; p++) { 386 | if (*p == ' ' || *p == '\t' || *p == '\n') { 387 | continue; 388 | } 389 | break; 390 | } 391 | 392 | *start = p; 393 | 394 | for ( ; p < last; p++) { 395 | if (*p == ' ' || *p == '\t' || *p == '\n') { 396 | break; 397 | } 398 | } 399 | 400 | *len = p - *start; 401 | 402 | return p; 403 | } 404 | 405 | 406 | conf_t * 407 | conf_create() 408 | { 409 | conf_t *main_conf; 410 | 411 | if((main_conf = malloc(sizeof(conf_t))) == NULL) { 412 | return NULL; 413 | } 414 | 415 | main_conf->name_server = NULL; 416 | main_conf->port = 53; 417 | main_conf->timeout = 3000; 418 | main_conf->max_query = 1000; 419 | main_conf->concurrent_query = 100; 420 | main_conf->running_time = 1000000; 421 | main_conf->protocol = NULL; 422 | main_conf->addr_family = NULL; 423 | main_conf->verbose = 0; 424 | 425 | return main_conf; 426 | } 427 | 428 | int 429 | conf_destroy(conf_t *cf) 430 | { 431 | free(cf->name_server); 432 | free(cf->protocol); 433 | free(cf->addr_family); 434 | 435 | free(cf); 436 | 437 | return 0; 438 | } 439 | -------------------------------------------------------------------------------- /conf.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONF_H_ 2 | #define _CONF_H_ 3 | 4 | 5 | #define DEFAULT_CONF_FILE "./dnsperf.conf" 6 | 7 | #define is_digit(c) ((c) >= '0' && (c) <= '9') 8 | #define is_letter(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) 9 | #define is_blank(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r') 10 | 11 | typedef int (*conf_get_f) (void *conf, unsigned int offset, char *s, int len); 12 | 13 | typedef struct { 14 | char *name; 15 | conf_get_f handler; 16 | unsigned int offset; 17 | } conf_command_t; 18 | 19 | typedef struct { 20 | char *name_server; 21 | unsigned int port; 22 | unsigned int timeout; 23 | unsigned int max_query; 24 | unsigned int concurrent_query; 25 | unsigned int running_time; 26 | char *protocol; 27 | char *addr_family; 28 | unsigned verbose; 29 | } conf_t; 30 | 31 | conf_t *conf_create(); 32 | int conf_parse_file(char *conf_file_name, conf_t **conf); 33 | int conf_destroy(conf_t *cf); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /dns_param.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _DNS_PARAM_H_ 3 | #define _DNS_PARAM_H_ 4 | 5 | 6 | typedef enum { /* OP Code */ 7 | DNS_OPCODE_UNKNOWN = -1, 8 | DNS_OPCODE_QUERY = 0, 9 | DNS_OPCODE_IQUERY = 1, /* inverse query. RFC3425 */ 10 | DNS_OPCODE_STSTUS = 2, /* DNS status query */ 11 | DNS_OPCODE_NOTIFY = 4, 12 | DNS_OPCODE_UPDATE = 5 13 | } dns_qcode_t; 14 | 15 | typedef enum{ /* Query Classes */ 16 | DNS_CLASS_UNKNOWN = -1, /* Unknown */ 17 | DNS_CLASS_IN = 1, /* Internet */ 18 | DNS_CLASS_CHAOS = 3, /* CHAOS (obsolete) */ 19 | DNS_CLASS_HESIOD = 4, /* HESIOD (obsolete) */ 20 | DNS_CLASS_NONE = 254, /* NONE (RFC 2136) */ 21 | DNS_CLASS_ANY = 255 /* ANY */ 22 | } dns_class_t; 23 | 24 | typedef enum{ /* Query Code */ 25 | DNS_RCODE_UNKNOWN = -1, 26 | DNS_RCODE_NOERROR = 0, 27 | DNS_RCODE_FORMERR = 1, 28 | DNS_RCODE_SERVFAIL = 2, 29 | DNS_RCODE_NXDOMAIN = 3, 30 | DNS_RCODE_NOTIMP = 4, 31 | DNS_RCODE_REFUSED = 5, 32 | /** 33 | * Rcode from RFC2136 34 | */ 35 | DNS_RCODE_YXDOMAIN = 6, /* name exists when it should not */ 36 | DNS_RCODE_YXRRSET = 7, /* RR set exists when it should not*/ 37 | DNS_RCODE_NXRRSET = 8, /* RR set that should exist does not */ 38 | DNS_RCODE_NOTAUTH = 9, /* server not authoritive for zone */ 39 | DNS_RCODE_NOTZONE = 10, /* name not contained in zone */ 40 | 41 | DNS_RCODE_BADVERS = 16, /* bad opt version number. RFC2671 */ 42 | DNS_RCODE_BADSIG = 16, /* TSIG signature failure. RFC2845 */ 43 | DNS_RCODE_BADKEY = 17, /* key not recognized. RFC2845 */ 44 | DNS_RCODE_BADTIME = 18, /* signature out-of-time window. RFC2845 */ 45 | DNS_RCODE_BADMODE = 19, /* bad tkey mode. RFC2930 */ 46 | DNS_RCODE_BADNAME = 20, /* duplicate key name. RFC2930 */ 47 | DNS_RCODE_BADALG = 21 /* algorithm not supported. RFC2930 */ 48 | } dns_rcode_t; 49 | 50 | 51 | typedef enum { /* Query types */ 52 | DNS_RR_UNKNOWN = -1, /* Unknown */ 53 | 54 | DNS_RR_NONE = 0, /* None/invalid */ 55 | DNS_RR_A = 1, /* Address */ 56 | DNS_RR_NS = 2, /* Nameserver */ 57 | DNS_RR_MD = 3, /* Mail dest */ 58 | DNS_RR_MF = 4, /* Mail forwarder */ 59 | DNS_RR_CNAME = 5, /* Canonical name */ 60 | DNS_RR_SOA = 6, /* Start of authority */ 61 | DNS_RR_MB = 7, /* Mailbox name */ 62 | DNS_RR_MG = 8, /* Mail group */ 63 | DNS_RR_MR = 9, /* Mail rename */ 64 | DNS_RR_NULL = 10, /* Null */ 65 | DNS_RR_WKS = 11, /* Well known service */ 66 | DNS_RR_PTR = 12, /* IP -> fqdn mapping */ 67 | DNS_RR_HINFO = 13, /* Host info */ 68 | DNS_RR_MINFO = 14, /* Mailbox info */ 69 | DNS_RR_MX = 15, /* Mail routing info */ 70 | DNS_RR_TXT = 16, /* Text */ 71 | DNS_RR_RP = 17, /* Responsible person */ 72 | DNS_RR_AFSDB = 18, /* AFS cell database */ 73 | DNS_RR_X25 = 19, /* X_25 calling address */ 74 | DNS_RR_ISDN = 20, /* ISDN calling address */ 75 | DNS_RR_RT = 21, /* Router */ 76 | DNS_RR_NSAP = 22, /* NSAP address */ 77 | DNS_RR_NSAP_PTR = 23, /* Reverse NSAP lookup (depreciated) */ 78 | DNS_RR_SIG = 24, /* Security signature */ 79 | DNS_RR_KEY = 25, /* Security key */ 80 | DNS_RR_PX = 26, /* X.400 mail mapping */ 81 | DNS_RR_GPOS = 27, /* Geographical position (withdrawn) */ 82 | DNS_RR_AAAA = 28, /* IPv6 Address */ 83 | DNS_RR_LOC = 29, /* Location info */ 84 | DNS_RR_NXT = 30, /* Next domain (security) */ 85 | DNS_RR_EID = 31, /* Endpoint identifier */ 86 | DNS_RR_NIMLOC = 32, /* Nimrod Locator */ 87 | DNS_RR_SRV = 33, /* Server */ 88 | DNS_RR_ATMA = 34, /* ATM Address */ 89 | DNS_RR_NAPTR = 35, /* Naming Authority Pointer */ 90 | DNS_RR_KX = 36, /* Key Exchange */ 91 | DNS_RR_CERT = 37, /* Certification record */ 92 | DNS_RR_A6 = 38, /* IPv6 address (deprecates AAAA) */ 93 | DNS_RR_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ 94 | DNS_RR_SINK = 40, /* Kitchen sink (experimentatl) */ 95 | DNS_RR_OPT = 41, /* EDNS0 option (meta-RR) */ 96 | DNS_RR_APL = 42, /* (RFC3123)*/ 97 | DNS_RR_DS = 43, /* Delegation Signer */ 98 | DNS_RR_SSHFP = 44, /* SSH Key Fingerprint */ 99 | DNS_RR_IPSECKEY = 45, /* IPSECKEY */ 100 | DNS_RR_RRIG = 46, /* RRSIG */ 101 | DNS_RR_NSEC = 47, /* NSEC */ 102 | DNS_RR_DNSKEY = 48, /* DNSKEY */ 103 | DNS_RR_DHCID = 49, /* DNCID */ 104 | DNS_RR_NSEC3 = 50, /* NSEC3 */ 105 | DNS_RR_NSEC3PARAM = 51, /* NSEC3PARAM */ 106 | DNS_RR_TLSA = 52, /* TLSA */ 107 | DNS_RR_HIP = 55, /* Host Identity Protocol */ 108 | DNS_RR_TKEY = 249, /* Transaction Key */ 109 | DNS_RR_TSIG = 250, /* Transaction signature */ 110 | DNS_RR_IXFR = 251, /* Incremental zone transfer */ 111 | DNS_RR_AXFR = 252, /* Zone transfer */ 112 | DNS_RR_MAILB = 253, /* Transfer mailbox records */ 113 | DNS_RR_MAILA = 254, /* Transfer mail agent records */ 114 | DNS_RR_ANY = 255, /* A request for all records */ 115 | DNS_RR_URI = 256, 116 | DNS_RR_CAA = 257, /* Certification Authority Restriction */ 117 | DNS_RR_TA = 32768, /* DNSSEC Trust Authorities */ 118 | DNS_RR_DLV = 32769 /* DNSSEC Lookaside Validation */ 119 | } dns_rrtype_t; 120 | 121 | /* DNS EDNS0 Option Codes (OPT) */ 122 | typedef enum { 123 | DNS_OPTTYPE_LLQ = 1, 124 | DNS_OPTTYPE_UL = 2, 125 | DNS_OPTTYPE_NSID = 3, 126 | DNS_OPTTYPE_DAU = 4, 127 | DNS_OPTTYPE_DHU = 5, 128 | DNS_OPTTYPE_N3U = 6, 129 | DNS_OPTTYPE_CLIENT_SUBNET = 8 130 | } dns_opttype_t; 131 | 132 | #define DNS_MESSAGE_HEADER_LEN 12 133 | 134 | /* DNS Header Flags */ 135 | #define DNS_HEADER_FLAG_QR 0x8000U /* Query-Response bit */ 136 | #define DNS_HEADER_FLAG_AA 0x0400U /* Authoritative Answer */ 137 | #define DNS_HEADER_FLAG_TC 0x0200U /* Truncated Response */ 138 | #define DNS_HEADER_FLAG_RD 0x0100U /* Recursion Desired */ 139 | #define DNS_HEADER_FLAG_RA 0x0080U /* Recursion Allowed */ 140 | #define DNS_HEADER_FLAG_AD 0x0020U /* Authentic Data. Used by DNSSEC */ 141 | #define DNS_HEADER_FLAG_CD 0x0010U /* Checking Disabled. Used by DNSSEC */ 142 | 143 | /* EDNS Header Flags */ 144 | #define DNS_EDNS_MESSAGEFLAGS_DO 0x8000U /* DNSSEC answer OK */ 145 | 146 | /* Section types */ 147 | #define DNS_SECTION_ANY (-1) 148 | #define DNS_SECTION_QUESTION 0 149 | #define DNS_SECTION_ANSWER 1 150 | #define DNS_SECTION_AUTHORITY 2 151 | #define DNS_SECTION_ADDITIONAL 3 152 | #define DNS_SECTION_MAX 4 153 | 154 | #define DNS_PSEUDOSECTION_ANY (-1) 155 | #define DNS_PSEUDOSECTION_OPT 0 156 | #define DNS_PSEUDOSECTION_TSIG 1 157 | #define DNS_PSEUDOSECTION_SIG0 2 158 | #define DNS_PSEUDOSECTION_MAX 3 159 | 160 | #define DNS_NAME_COMPRESS_LABEL 0x00U 161 | #define DNS_NAME_COMPRESS_POINTER 0xC0U 162 | 163 | #define DNS_MAX_NAME_LEN 255 164 | #define DNS_MAX_LABEL_LEN 63 165 | 166 | #endif 167 | -------------------------------------------------------------------------------- /dnsperf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A DNS Performance tool. 3 | * 4 | * Copyright (C) 2014 Cobblau 5 | * 6 | * dnsperf is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * dnsperf is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | 38 | /* 39 | * Defines. 40 | */ 41 | typedef struct timeval timeval_t; 42 | 43 | #define TRUE 1 44 | #define FALSE 0 45 | 46 | #define UDP 1 47 | #define TCP 2 48 | 49 | #define DEFAULT_SERVER "127.0.0.1" 50 | #define DEFAULT_PORT "53" 51 | #define DEFAULT_TIMEOUT "3000" /* ms */ 52 | #define DEFAULT_QUERY_NUM "1000" 53 | #define DEFAULT_C_QUERY_NUM "100" 54 | 55 | #define MAX_DOMAIN_LEN 255 56 | 57 | 58 | /* query states */ 59 | #define F_UNUSED 0 /* unused */ 60 | #define F_CONNECTING 1 /* connect() in progress */ 61 | #define F_SENDING 2 /* writing */ 62 | #define F_READING 4 /* reading */ 63 | #define F_DONE 8 /* all done */ 64 | 65 | typedef struct data_s { 66 | unsigned int qtype; 67 | unsigned int len; /* domain's len */ 68 | char domain[MAX_DOMAIN_LEN]; 69 | } data_t; 70 | 71 | typedef struct query_s { 72 | dns_perf_event_ops_t ops; 73 | 74 | int id; 75 | int fd; /* socket fd */ 76 | 77 | u_char send_buf[PACKETSZ]; 78 | int send_len; 79 | int send_pos; 80 | 81 | /* DNS respond maybe bigger than PACKETSZ, but we only read PACKETSZ bytes */ 82 | u_char recv_buf[PACKETSZ]; 83 | int recv_pos; 84 | 85 | unsigned int state; 86 | timeval_t sands; 87 | 88 | data_t *data; 89 | } query_t; 90 | 91 | 92 | 93 | /* 94 | * Global vars. 95 | */ 96 | char *g_name_server; 97 | unsigned int g_name_server_port; 98 | char *g_data_file_name; 99 | char *g_real_client; 100 | unsigned int g_timeout; 101 | unsigned int g_perf_time; 102 | unsigned int g_query_number; 103 | unsigned int g_concurrent_query; 104 | unsigned int g_interval; 105 | int g_layer4_protocol = UDP; 106 | int g_net_family = AF_INET; 107 | int g_print_rcode_num; 108 | int g_report_rcode; 109 | 110 | timeval_t g_query_start; 111 | timeval_t g_query_end; 112 | 113 | /* Stores read from data `g_data_file_handler' */ 114 | data_t *g_data_array; 115 | int g_data_array_len; 116 | query_t *g_query_array; /* len = g_concurrent_query */ 117 | 118 | /* epoll vars */ 119 | int g_epoll_fd; 120 | struct epoll_event *g_epoll_events; 121 | 122 | 123 | /* statistics */ 124 | unsigned int g_send_number; 125 | unsigned int g_recv_number; 126 | unsigned int g_success_number; /* 0 */ 127 | unsigned int g_formerr_number; /* 1 */ 128 | unsigned int g_serverr_number; /* 2 */ 129 | unsigned int g_nxdomain_number; /* 3 */ 130 | unsigned int g_notimp_number; /* 4 */ 131 | unsigned int g_refuse_number; /* 5 */ 132 | unsigned int g_other_number; /* other rcode */ 133 | 134 | 135 | int g_stop; /* 1: running 0: stop */ 136 | 137 | 138 | /* 139 | * Functions. 140 | */ 141 | void dns_perf_show_info() 142 | { 143 | printf("\nDNS Performance Testing Tool\n\n"); 144 | } 145 | 146 | void dns_perf_show_usage() 147 | { 148 | fprintf(stderr,"\n" 149 | "Usage: dnsperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]\n" 150 | " [-t timeout] [-Q max queries] [-c concurrent queries]\n" 151 | " [-l running time] [-e real client ip] [-P udp|tcp]\n" 152 | " [-f family] [-T qps] [-c] [-v] [-h]\n\n" 153 | " -d specifies the input data file (default: stdin)\n" 154 | " -s sets the dns server's address (default: %s)\n" 155 | " -p sets the dns server's port (default: %s)\n" 156 | " -t specifies the timeout for query completion in millisecond (default: %s)\n" 157 | " -Q specifies the maximum number of queries to be send (default: %s)\n" 158 | " -c specifies the number of concurrent queries (default: %s)\n" 159 | " dns_perf will randomly pick from data file \n" 160 | " -l specifies how long to run tests in seconds (no default)\n" 161 | " -i Specifies interval of queries in seconds. The default number is zero.\n" 162 | " -e This will sets the real client IP in query string following the rules \n" 163 | " defined in edns-client-subnet\n" 164 | " -P specifies the transport layer protocol to send DNS quires,\n" 165 | " udp or tcp (default: udp)\n" 166 | " -f specify address family of DNS transport, inet or inet6 (default: inet)\n" 167 | " -v verbose: report the RCODE of each response on stdout\n" 168 | " -h print this usage\n" 169 | "\n", 170 | DEFAULT_SERVER, DEFAULT_PORT, DEFAULT_TIMEOUT, DEFAULT_QUERY_NUM, 171 | DEFAULT_C_QUERY_NUM); 172 | } 173 | 174 | /* 175 | * Set the nameserver's address to be queried. 176 | */ 177 | int dns_perf_set_str(char **dst, char *src) 178 | { 179 | if ((*dst != NULL) && (src != NULL)) { 180 | if (strcmp(src, *dst) == 0) { 181 | return 0; 182 | } 183 | } 184 | 185 | if (src == NULL || src[0] == '\0') { 186 | return -1; 187 | } 188 | 189 | free(*dst); 190 | 191 | if ((*dst = malloc(strlen(src) + 1)) == NULL) { 192 | fprintf(stderr, "Error allocating memory for server name: %s\n", src); 193 | return -1; 194 | } 195 | 196 | memset(*dst, '\0', strlen(src) + 1); 197 | memcpy(*dst, src, strlen(src)); 198 | 199 | return 0; 200 | } 201 | 202 | int dns_perf_set_uint(unsigned int *dst, char *src) 203 | { 204 | unsigned int val; 205 | 206 | val = atol(src); 207 | if (val <= 0 && val > 65535) { 208 | return -1; 209 | } 210 | 211 | *dst = val; 212 | 213 | return 0; 214 | } 215 | 216 | void sig_handler(int signo) 217 | { 218 | switch (signo) { 219 | case SIGINT: 220 | case SIGTERM: 221 | g_stop = 1; 222 | break; 223 | 224 | default: 225 | break; 226 | } 227 | } 228 | 229 | timeval_t dns_perf_timer_sub(timeval_t a, timeval_t b) 230 | { 231 | timeval_t ret; 232 | 233 | memset(&ret, 0, sizeof(timeval_t)); 234 | 235 | ret.tv_usec = a.tv_usec - b.tv_usec; 236 | ret.tv_sec = a.tv_sec - b.tv_sec; 237 | 238 | if (ret.tv_usec < 0) { 239 | ret.tv_usec += 1000000; 240 | ret.tv_sec--; 241 | } 242 | 243 | return ret; 244 | } 245 | 246 | int dns_perf_timer_cmp(timeval_t a, timeval_t b) 247 | { 248 | if (a.tv_sec > b.tv_sec) 249 | return 1; 250 | if (a.tv_sec < b.tv_sec) 251 | return -1; 252 | if (a.tv_usec > b.tv_usec) 253 | return 1; 254 | if (a.tv_usec < b.tv_usec) 255 | return -1; 256 | return 0; 257 | } 258 | 259 | 260 | timeval_t dns_perf_timer_add_long(timeval_t a, long b) 261 | { 262 | timeval_t ret; 263 | 264 | memset(&ret, 0, sizeof(timeval_t)); 265 | 266 | ret.tv_usec = a.tv_usec + b % 1000000; 267 | ret.tv_sec = a.tv_sec + b / 1000000; 268 | 269 | if (ret.tv_usec >= 1000000) { 270 | ret.tv_sec++; 271 | ret.tv_usec -= 1000000; 272 | } 273 | 274 | return ret; 275 | } 276 | 277 | 278 | int dns_perf_valid_qtype(char *qtype) 279 | { 280 | static char *qtypes[] = {"A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", 281 | "MR", "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", 282 | "AAAA", "SRV", "NAPTR", "A6", "AXFR", "MAILB", "MAILA", "*", "ANY"}; 283 | 284 | static int qtype_codes[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 285 | 15, 16, 28, 33, 35, 38, 252, 253, 254, 255, 255}; 286 | 287 | int qtype_len = sizeof(qtypes) / sizeof(qtypes[0]); 288 | int i; 289 | 290 | for (i = 0; i < qtype_len; i++) { 291 | if (strcasecmp(qtypes[i], qtype) == 0) { 292 | return qtype_codes[i]; 293 | } 294 | } 295 | 296 | return -1; 297 | } 298 | 299 | 300 | int dns_perf_parse_args(int argc, char **argv) 301 | { 302 | int queryset, perfset;; 303 | int c; 304 | 305 | while((c = getopt(argc, argv, "d:s:p:t:l:Q:q:i:P:f:T:c:e:vh")) != -1) { 306 | 307 | switch (c) { 308 | case 'd': 309 | if (dns_perf_set_str(&g_data_file_name, optarg) == -1) { 310 | fprintf(stderr, "Error setting datafile %s\n", optarg); 311 | return -1; 312 | } 313 | break; 314 | 315 | case 's': 316 | if (dns_perf_set_str(&g_name_server, optarg) == -1) { 317 | fprintf(stderr, "Error setting name_server %s\n", optarg); 318 | return -1; 319 | } 320 | break; 321 | 322 | case 'p': 323 | if (dns_perf_set_uint(&g_name_server_port, optarg) == -1) { 324 | fprintf(stderr, "Error setting name_server's port %s\n", optarg); 325 | return -1; 326 | } 327 | break; 328 | 329 | case 't': 330 | if (dns_perf_set_uint(&g_timeout, optarg) == -1) { 331 | fprintf(stderr, "Error setting timeout %s\n", optarg); 332 | return -1; 333 | } 334 | break; 335 | 336 | case 'l': 337 | if (dns_perf_set_uint(&g_perf_time, optarg) == -1) { 338 | fprintf(stderr, "Error setting query time %s\n", optarg); 339 | return -1; 340 | } 341 | perfset = TRUE; 342 | break; 343 | 344 | case 'Q': 345 | if (dns_perf_set_uint(&g_query_number, optarg) == -1) { 346 | fprintf(stderr, "Error setting query number %s\n", optarg); 347 | return -1; 348 | } 349 | queryset = TRUE; 350 | break; 351 | 352 | case 'c': 353 | if (dns_perf_set_uint(&g_concurrent_query, optarg) == -1) { 354 | fprintf(stderr, "Error setting concurrent query num %s\n", optarg); 355 | return -1; 356 | } 357 | break; 358 | 359 | case 'i': 360 | if (dns_perf_set_uint(&g_interval, optarg) == -1) { 361 | fprintf(stderr, "Error setting interval number %s\n", optarg); 362 | return -1; 363 | } 364 | break; 365 | 366 | case 'P': 367 | if (strcmp(optarg, "udp") == 0) { 368 | g_layer4_protocol = UDP; 369 | } else if (strcmp(optarg, "tcp") == 0) { 370 | g_layer4_protocol = TCP; 371 | } else { 372 | fprintf(stderr, "Invalid transport protocol: %s\n", optarg); 373 | return -1; 374 | } 375 | break; 376 | 377 | case 'f': 378 | if (strcmp(optarg, "inet") == 0) { 379 | g_net_family = AF_INET; 380 | } else if (strcmp(optarg, "inet6") == 0) { 381 | g_net_family = AF_INET6; 382 | } else { 383 | fprintf(stderr, "Invalid address family: %s\n", optarg); 384 | return -1; 385 | } 386 | break; 387 | 388 | case 'e': 389 | if (dns_perf_set_str(&g_real_client, optarg) == -1) { 390 | fprintf(stderr, "Error setting edns client ip %s\n", optarg); 391 | return -1; 392 | } 393 | break; 394 | 395 | 396 | case 'v': 397 | g_report_rcode = TRUE; 398 | break; 399 | 400 | case 'h': 401 | return -1; 402 | 403 | default: 404 | fprintf(stderr, "Invalid option: %s\n", optarg); 405 | return -1; 406 | } 407 | } 408 | 409 | if (queryset == TRUE && perfset == TRUE) { 410 | fprintf(stderr, "-Q and -l is exclusive, please set only one\n"); 411 | return -1; 412 | } 413 | 414 | if (g_perf_time != 0) { 415 | g_query_number = 100000000; 416 | } 417 | 418 | return 0; 419 | } 420 | 421 | 422 | /* 423 | * dns_perf_data_array_init: 424 | * fill 'g_query_array' with information read from 'g_data_file_handler' 425 | */ 426 | int dns_perf_data_array_init() 427 | { 428 | FILE *file; 429 | char buf[1024], domain[255], qtype[10]; 430 | int len = 0, qtype_n; 431 | data_t *d; 432 | 433 | if (g_data_file_name == NULL) { 434 | return -1; 435 | } 436 | 437 | 438 | if ((file = fopen(g_data_file_name, "r")) == NULL) { 439 | return -1; 440 | } 441 | 442 | /* Calculate how many useful lines */ 443 | while(fgets(buf, 1024, file) != 0) { 444 | if (buf[0] == '#' || buf[0] == '\n') { 445 | continue; 446 | } 447 | 448 | len++; 449 | } 450 | 451 | if ((g_data_array = calloc(len, sizeof(data_t))) == NULL) { 452 | fprintf(stderr, "Malloc memory error"); 453 | goto finish; 454 | } 455 | 456 | rewind(file); 457 | g_data_array_len = 0; 458 | while(fgets(buf, 1024, file) != 0) { 459 | if (buf[0] == '#' || buf[0] == '\n') { 460 | continue; 461 | } 462 | 463 | if (sscanf(buf, "%s %s", domain, qtype) == EOF) { 464 | fprintf(stderr, "Error string in data file:%s\n", buf); 465 | goto finish; 466 | } 467 | 468 | if (strlen(domain) > MAX_DOMAIN_LEN) { 469 | fprintf(stderr, "Error domain name too long:%s\n", domain); 470 | goto finish; 471 | } 472 | 473 | if ((qtype_n = dns_perf_valid_qtype(qtype)) == -1) { 474 | fprintf(stderr, "Error unknown qtype:%s\n", qtype); 475 | goto finish; 476 | } 477 | 478 | d = &g_data_array[g_data_array_len]; 479 | d->len = strlen(domain); 480 | memcpy(d->domain, domain, d->len); 481 | d->qtype = qtype_n; 482 | 483 | g_data_array_len++; 484 | } 485 | 486 | finish: 487 | 488 | if (fclose(file) != 0) { 489 | free(g_data_array); 490 | return -1; 491 | } 492 | 493 | return 0; 494 | } 495 | 496 | /* 497 | * Do I need write description to this file? I don't think so. 498 | */ 499 | int dns_perf_generate_query(query_t *q) 500 | { 501 | static unsigned short query_id = 0; 502 | int len; 503 | unsigned short net_id; 504 | u_char *p, *t; 505 | HEADER *hp; 506 | in_addr_t addr; 507 | 508 | 509 | len = res_mkquery(QUERY, q->data->domain, C_IN, q->data->qtype, NULL, 510 | 0, NULL, q->send_buf, sizeof(q->send_buf)); 511 | if (len == -1) { 512 | fprintf(stderr, "Failed to create query packet: %s %d\n", q->data->domain, 513 | q->data->qtype); 514 | return -1; 515 | } 516 | 517 | hp = (HEADER *) q->send_buf; 518 | hp->rd = 1; /* recursion */ 519 | 520 | query_id++; 521 | q->id = query_id; 522 | 523 | /* set message id */ 524 | net_id = htons(query_id); 525 | p = (u_char *) &net_id; 526 | q->send_buf[0] = p[0]; 527 | q->send_buf[1] = p[1]; 528 | 529 | if (g_real_client) { 530 | q->send_buf[11] = 1; /* set additional count to 1 */ 531 | 532 | p = q->send_buf + len; /* p points to additional section */ 533 | 534 | *p++ = 0; /* root name */ 535 | 536 | *p++ = 0; /* OPT */ 537 | *p++ = 41; 538 | 539 | *p++ = 4; /* UDP payload size: 1024 */ 540 | *p++ = 0; 541 | 542 | *p++ = 0; /* extended RCODE and flags */ 543 | *p++ = 0; 544 | *p++ = 0; 545 | *p++ = 0; 546 | 547 | *p++ = 0; /* edns-client-subnet's length */ 548 | *p++ = 12; 549 | 550 | /* edns-client-subnet */ 551 | *p++ = 0; /* option code: 8 */ 552 | *p++ = 8; 553 | 554 | *p++ = 0; /* option length: 8 */ 555 | *p++ = 8; 556 | 557 | *p++ = 0; /* family: 1 */ 558 | *p++ = 1; 559 | 560 | *p++ = 32; /* source netmask: 32 */ 561 | *p++ = 0; /* scope netmask: 0 */ 562 | 563 | addr = inet_addr(g_real_client); /* client subnet */ 564 | t = (u_char *) &addr; 565 | 566 | *p++ = *t++; 567 | *p++ = *t++; 568 | *p++ = *t++; 569 | *p++ = *t++; 570 | 571 | len += 23; 572 | } 573 | 574 | q->send_len = len; 575 | q->send_pos = 0; 576 | 577 | return 0; 578 | } 579 | 580 | 581 | int dns_perf_query_process_response(query_t *q, unsigned short id, unsigned short flag) 582 | { 583 | /* 做一些统计工作 */ 584 | if (q->id != id) { 585 | return -1; /* TODO: can be happened */ 586 | } 587 | 588 | g_recv_number++; 589 | 590 | switch(flag) { 591 | case 0: 592 | g_success_number++; 593 | break; 594 | 595 | case 1: 596 | g_formerr_number++; 597 | break; 598 | 599 | case 2: 600 | g_serverr_number++; 601 | break; 602 | 603 | case 3: 604 | g_nxdomain_number++; 605 | break; 606 | 607 | case 4: 608 | g_notimp_number++; 609 | break; 610 | 611 | case 5: 612 | g_refuse_number++; 613 | break; 614 | 615 | default: 616 | g_other_number++; 617 | break; 618 | } 619 | 620 | return 0; 621 | } 622 | 623 | 624 | int dns_perf_query_send(void *arg) 625 | { 626 | int ret; 627 | query_t *q = arg; 628 | 629 | 630 | ret = send(q->fd, q->send_buf + q->send_pos, q->send_len - q->send_pos, 0); 631 | if (ret < 0) { 632 | if (errno != EWOULDBLOCK && errno != EAGAIN) { 633 | goto error; 634 | } 635 | 636 | q->state = F_SENDING; 637 | if (dns_perf_eventsys_set_fd(q->fd, MOD_WR, q) == -1) { 638 | fprintf(stderr, "Error set write fd:%d\n", q->fd); 639 | goto error; 640 | } 641 | 642 | } else { /* already send */ 643 | if (ret != q->send_len) { 644 | q->state = F_SENDING; 645 | q->send_pos += ret; 646 | if (dns_perf_eventsys_set_fd(q->fd, MOD_WR, q) == -1) { 647 | fprintf(stderr, "Error set write fd:%d\n", q->fd); 648 | goto error; 649 | } 650 | } 651 | 652 | q->state = F_READING; 653 | if (dns_perf_eventsys_set_fd(q->fd, MOD_RD, q) == -1) { 654 | fprintf(stderr, "Error set read fd:%d\n", q->fd); 655 | goto error; 656 | } 657 | } 658 | 659 | return 0; 660 | 661 | error: 662 | close(q->fd); 663 | q->state = F_UNUSED; 664 | 665 | return 0; 666 | } 667 | 668 | int dns_perf_query_recv(void *arg) 669 | { 670 | //static u_char input[1024]; 671 | int ret; 672 | unsigned short id; 673 | unsigned short flags; 674 | query_t *q = arg; 675 | 676 | 677 | ret = recv(q->fd, q->recv_buf + q->recv_pos, sizeof(q->recv_buf) - q->recv_pos, 0); 678 | 679 | if (ret < 0) { 680 | if (errno != EWOULDBLOCK && errno != EAGAIN) { 681 | close(q->fd); 682 | q->state = F_UNUSED; 683 | return 0; 684 | } 685 | 686 | if (dns_perf_eventsys_set_fd(q->fd, MOD_RD, q) == -1) { 687 | close(q->fd); 688 | q->state = F_UNUSED; 689 | return 0; 690 | } 691 | } else { 692 | close(q->fd); 693 | q->state = F_UNUSED; 694 | 695 | //id = input[0] * 256 + input[1]; 696 | //flags = input[2] * 256 + input[3]; 697 | 698 | id = q->recv_buf[0] << 8 | q->recv_buf[1]; 699 | flags = q->recv_buf[2] << 8 | q->recv_buf[3]; 700 | 701 | dns_perf_query_process_response(q, id, flags & 0xF); 702 | } 703 | 704 | return 0; 705 | } 706 | 707 | 708 | static int dns_perf_cancel_timeout_query() 709 | { 710 | int i; 711 | query_t *query; 712 | timeval_t now, diff; 713 | 714 | /* Deal with timeout */ 715 | gettimeofday(&now, NULL); 716 | for (i = 0; i < g_concurrent_query ; i++) { 717 | 718 | query = &g_query_array[i]; 719 | 720 | if (query->state == F_UNUSED) { 721 | continue; 722 | } 723 | 724 | diff = dns_perf_timer_sub(now, query->sands); 725 | if (diff.tv_sec * 1000000 + diff.tv_usec >= 0) { 726 | /* delete timeouted queries */ 727 | if (query->state == F_SENDING) { 728 | dns_perf_eventsys_clear_fd(query->fd, MOD_WR); 729 | } else if (query->state == F_READING) { 730 | dns_perf_eventsys_clear_fd(query->fd, MOD_RD); 731 | } 732 | 733 | close(query->fd); 734 | query->state = F_UNUSED; 735 | } 736 | } 737 | 738 | return 0; 739 | } 740 | 741 | 742 | 743 | 744 | /* 745 | * dns_perf_prepare: 746 | * Do some preparation before quering. 747 | * 1. allocate query_array 748 | */ 749 | static int dns_perf_prepare() 750 | { 751 | query_t *q; 752 | int i, index; 753 | 754 | g_query_array = calloc(g_concurrent_query, sizeof(query_t)); 755 | if (g_query_array == NULL) { 756 | fprintf(stderr, "Error memory low"); 757 | return -1; 758 | } 759 | 760 | for (i = 0; i < g_concurrent_query; i++) { 761 | 762 | index = random() % g_data_array_len; 763 | 764 | q = &g_query_array[i]; 765 | 766 | q->data = &g_data_array[index]; 767 | q->ops.send = dns_perf_query_send; 768 | q->ops.recv = dns_perf_query_recv; 769 | q->id = q->fd = -1; 770 | q->send_pos = q->recv_pos = 0; 771 | q->state = F_UNUSED; 772 | } 773 | 774 | return 0; 775 | } 776 | 777 | 778 | /* 779 | * Whip query_t to make it as busy as possible. 780 | */ 781 | static int dns_perf_whip_query() 782 | { 783 | int i; 784 | query_t *q; 785 | timeval_t tv; 786 | 787 | for (i = 0; i < g_concurrent_query; i++) { 788 | 789 | q = &g_query_array[i]; 790 | 791 | if (q->state != F_UNUSED) { 792 | continue; 793 | } 794 | 795 | q->fd = dns_perf_open_udp_socket(g_name_server, g_name_server_port, 796 | g_net_family); 797 | if (q->fd == -1) { 798 | fprintf(stderr, "Error create udp socket failed\n"); 799 | return -1; 800 | } 801 | 802 | q->state = F_CONNECTING; 803 | 804 | if (dns_perf_generate_query(q) != 0) { 805 | return -1; 806 | } 807 | 808 | gettimeofday(&tv, NULL); 809 | q->sands = dns_perf_timer_add_long(tv, g_timeout * 1000); 810 | 811 | /* send query to remote name server */ 812 | if (dns_perf_query_send(q) == -1) { 813 | continue; 814 | } 815 | 816 | g_send_number++; 817 | } 818 | 819 | return 0; 820 | } 821 | 822 | 823 | static int dns_perf_clear_query() 824 | { 825 | int i; 826 | query_t *q; 827 | 828 | for (i = 0; i < g_concurrent_query; i++) { 829 | 830 | q = &g_query_array[i]; 831 | 832 | if (q->state != F_UNUSED) { 833 | close(q->fd); 834 | } 835 | 836 | q->state = F_UNUSED; 837 | } 838 | 839 | return 0; 840 | } 841 | 842 | 843 | static void dns_perf_statistic() 844 | { 845 | timeval_t diff; 846 | unsigned int msec; 847 | double elapse, qps; 848 | 849 | 850 | diff = dns_perf_timer_sub(g_query_end, g_query_start); 851 | msec = diff.tv_sec * 1000 + diff.tv_usec / 1000; 852 | 853 | printf("\n[Status]DNS Query Performance Testing Finish\n"); 854 | printf("[Result]Quries sent:\t\t%d\n", g_send_number); 855 | printf("[Result]Quries completed:\t%d\n", g_recv_number); 856 | printf("[Result]Complete percentage:\t%.2f\n\n", g_recv_number * 100.0 / g_send_number); 857 | 858 | if (g_report_rcode) { 859 | printf("[Result]Rcode=Success:\t%d\n\n", g_success_number); 860 | printf("[Result]Rcode=FormatError:\t%d\n\n", g_formerr_number); 861 | printf("[Result]Rcode=ServerError:\t%d\n\n", g_serverr_number); 862 | printf("[Result]Rcode=NXDOMAIN:\t%d\n\n", g_nxdomain_number); 863 | printf("[Result]Rcode=NotImp:\t%d\n\n", g_notimp_number); 864 | printf("[Result]Rcode=Refuse:\t%d\n\n", g_refuse_number); 865 | printf("[Result]Rcode=Others:\t%d\n\n", g_other_number); 866 | } 867 | 868 | elapse = msec * 1.0 / 1000; 869 | printf("[Result]Elapsed time(s):\t%.5f\n\n", elapse); 870 | 871 | 872 | qps = g_send_number / elapse; 873 | printf("[Result]Queries Per Second:\t%.5f\n", qps); 874 | } 875 | 876 | 877 | /* 878 | * dns_perf_setup: 879 | * Init data. 880 | */ 881 | int dns_perf_setup(int argc, char **argv) 882 | { 883 | 884 | if (dns_perf_set_str(&g_name_server, DEFAULT_SERVER) == -1) { 885 | fprintf(stderr, "%s: Unable to set default name_server\n", argv[0]); 886 | return -1; 887 | } 888 | 889 | if (dns_perf_set_uint(&g_name_server_port, DEFAULT_PORT) == -1) { 890 | fprintf(stderr, "%s: Unable to set default name_server's port\n", argv[0]); 891 | return -1; 892 | } 893 | 894 | if (dns_perf_set_uint(&g_timeout, DEFAULT_TIMEOUT) == -1) { 895 | fprintf(stderr, "%s: Unable to set default timeout\n", argv[0]); 896 | return -1; 897 | } 898 | 899 | if (dns_perf_set_uint(&g_query_number, DEFAULT_QUERY_NUM) == -1) { 900 | fprintf(stderr, "%s: Unable to set default query number\n", argv[0]); 901 | return -1; 902 | } 903 | 904 | if (dns_perf_set_uint(&g_concurrent_query, DEFAULT_C_QUERY_NUM) == -1) { 905 | fprintf(stderr, "%s: Unable to set default concurrent query number\n", argv[0]); 906 | return -1; 907 | } 908 | 909 | if (dns_perf_parse_args(argc, argv) == -1) { 910 | dns_perf_show_usage(); 911 | return -1; 912 | } 913 | 914 | if (dns_perf_data_array_init() == -1) { 915 | return -1; 916 | } 917 | 918 | 919 | return 0; 920 | } 921 | 922 | 923 | int main(int argc, char** argv) 924 | { 925 | timeval_t now, age; 926 | 927 | 928 | dns_perf_show_info(); 929 | signal(SIGINT, sig_handler); 930 | signal(SIGTERM, sig_handler); 931 | 932 | if (dns_perf_setup(argc, argv) == -1) { 933 | return -1; 934 | } 935 | 936 | printf("[Status] Processing query data\n"); 937 | if (dns_perf_prepare() == -1) { 938 | return -1; 939 | } 940 | 941 | if (dns_perf_set_event_sys() == -1) { 942 | return -1; 943 | } 944 | 945 | if (dns_perf_eventsys_init() == -1) { 946 | return -1; 947 | } 948 | 949 | printf("[Status] Sending queries to %s:%d\n", g_name_server, g_name_server_port); 950 | gettimeofday(&g_query_start, NULL); 951 | 952 | /* how long can you live */ 953 | age = dns_perf_timer_add_long(g_query_start, g_perf_time * 1000000); 954 | 955 | if (dns_perf_whip_query() == -1) { 956 | return -1; 957 | } 958 | 959 | while (g_stop == 0) { 960 | dns_perf_eventsys_dispatch(g_timeout); 961 | 962 | dns_perf_cancel_timeout_query(); 963 | 964 | /* Is time up? */ 965 | if (g_perf_time != 0) { 966 | gettimeofday(&now, NULL); 967 | if (dns_perf_timer_cmp(now, age) > 0) { 968 | printf("time up"); 969 | break; 970 | } 971 | } 972 | 973 | /* Is query number overflowed? */ 974 | if (g_send_number >= g_query_number) { 975 | break; 976 | } 977 | 978 | dns_perf_whip_query(); 979 | } 980 | 981 | gettimeofday(&g_query_end, NULL); 982 | 983 | dns_perf_statistic(); 984 | 985 | dns_perf_clear_query(); 986 | 987 | free(g_data_array); 988 | free(g_query_array); 989 | free(g_name_server); 990 | free(g_data_file_name); 991 | 992 | dns_perf_eventsys_destroy(); 993 | 994 | return 0; 995 | } 996 | 997 | -------------------------------------------------------------------------------- /dnsperf.conf: -------------------------------------------------------------------------------- 1 | #name_server specifies the DNS server's IP address. 2 | name_server = 127.0.0.1 3 | 4 | #port specifies the DNS server's port 5 | port = 53 6 | 7 | #timeout specifies the timeout for query completion in millisecond. 8 | timeout = 3000 9 | 10 | #max_query specifies the max number of queries to be send. 11 | max_query = 1000 12 | 13 | #concurrent_query specifies the number of concurrent queries. 14 | concurrent_query = 100 15 | 16 | #running_time specifies how long to run tests in seconds. 17 | runningt_time = 1000000 18 | 19 | #protocol specifies the transport layer protocol to send DNS queries. 20 | protocol = udp 21 | 22 | #address_family specifies address family of DNS transport. 23 | address_family = inet 24 | 25 | #verbose report the RCODE of each response on stdout. 26 | verbose = off -------------------------------------------------------------------------------- /events.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file if part of dnsperf. 3 | * 4 | * Copyright (C) 2014 Cobblau 5 | * 6 | * dnsperf is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * dnsperf is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include 21 | 22 | #ifdef HAVE_EPOLL 23 | #include 24 | #endif 25 | 26 | #ifdef HAVE_KQUEUE 27 | #include 28 | #endif 29 | 30 | 31 | dns_perf_eventsys_t *dns_perf_eventsys = NULL; 32 | 33 | #ifdef HAVE_EPOLL 34 | 35 | typedef struct dns_perf_epoll_data_s { 36 | int fd; /* epoll fd */ 37 | struct fddata *fdtab; /* fd data in epoll */ 38 | struct epoll_event *events; 39 | int fd_size; 40 | } dns_perf_epoll_data_t; 41 | 42 | static dns_perf_epoll_data_t *ep = NULL; 43 | 44 | static int dns_perf_epoll_init(void) 45 | { 46 | struct rlimit limit; 47 | 48 | if ((ep = (dns_perf_epoll_data_t *)malloc(sizeof(dns_perf_epoll_data_t))) == NULL) { 49 | return -1; 50 | } 51 | 52 | /* create epoll */ 53 | if ((ep->fd = epoll_create(MAX_EVENT_SOCKS)) < 0) { 54 | fprintf(stderr, "Error call epoll_create"); 55 | goto fail_fd; 56 | } 57 | 58 | /* set fd ulimits */ 59 | limit.rlim_cur = limit.rlim_max = MAX_EVENT_SOCKS; 60 | if (setrlimit(RLIMIT_NOFILE, &limit) == -1){ 61 | fprintf(stderr, "Error: set ulimits fd to %d failure. reason:%s\n", 62 | MAX_EVENT_SOCKS, strerror(errno)); 63 | goto fail_limit; 64 | } 65 | 66 | /* allocate epoll events */ 67 | ep->fd_size = MAX_EVENT_SOCKS; 68 | if ((ep->events = (struct epoll_event *) calloc(ep->fd_size, 69 | sizeof(struct epoll_event))) == NULL) 70 | { 71 | fprintf(stderr, "Error: alloc epoll event failure."); 72 | goto fail_limit; 73 | } 74 | 75 | /* create fdtable */ 76 | if ((ep->fdtab = (struct fddata *) calloc(ep->fd_size, 77 | sizeof(struct fddata))) == NULL) 78 | { 79 | fprintf(stderr, "Error: alloc fddata failure."); 80 | goto fail_fdtab; 81 | } 82 | 83 | return 0; 84 | 85 | fail_fdtab: 86 | free(ep->events); 87 | fail_limit: 88 | close(ep->fd); 89 | fail_fd: 90 | free(ep); 91 | ep = NULL; 92 | return -1; 93 | } 94 | 95 | 96 | static int dns_perf_epoll_destroy(void) 97 | { 98 | if (ep) { 99 | free(ep->fdtab); 100 | free(ep->events); 101 | 102 | if (ep->fd) { 103 | close(ep->fd); 104 | } 105 | 106 | free(ep); 107 | 108 | ep = NULL; 109 | } 110 | 111 | return 0; 112 | } 113 | 114 | 115 | static int dns_perf_epoll_clear_fd(int fd, int mod) 116 | { 117 | int opcode; 118 | struct epoll_event ev; 119 | 120 | 121 | if (mod == MOD_RD) { 122 | ep->fdtab[fd].events &= ~EPOLLIN; 123 | } else if (mod == MOD_WR) { 124 | ep->fdtab[fd].events &= ~EPOLLOUT; 125 | } 126 | ev.events = ep->fdtab[fd].events; 127 | 128 | if (ep->fdtab[fd].events == 0) { 129 | opcode = EPOLL_CTL_DEL; 130 | } else { 131 | opcode = EPOLL_CTL_MOD; 132 | } 133 | 134 | if (epoll_ctl(ep->fd, opcode, fd, &ev) < 0) { 135 | fprintf(stderr, "Error epoll delete fd %d failure.", fd); 136 | return -1; 137 | } 138 | 139 | ep->fdtab[fd].cb[mod].arg = NULL; 140 | 141 | return 0; 142 | } 143 | 144 | static void *dns_perf_epoll_get_obj_by_fd(int fd, int mod) 145 | { 146 | return ep->fdtab[fd].cb[mod].arg; 147 | } 148 | 149 | 150 | static int dns_perf_epoll_do_wait(long timeout) 151 | { 152 | int i, fd; 153 | int nevents; 154 | dns_perf_event_ops_t *op; 155 | 156 | nevents = epoll_wait(ep->fd, ep->events, ep->fd_size, timeout); 157 | 158 | if (nevents < 0) { 159 | fprintf(stderr, "epoll_wait error:%s", strerror(errno)); 160 | return -1; 161 | } 162 | 163 | for (i = 0; i < nevents; i++) { 164 | fd = ep->events[i].data.fd; 165 | 166 | if (ep->events[i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) { 167 | op = (dns_perf_event_ops_t *) ep->fdtab[fd].cb[MOD_WR].arg; 168 | dns_perf_epoll_clear_fd(fd, MOD_WR); 169 | op->send((void *) op); 170 | } 171 | 172 | if (ep->events[i].events & (EPOLLIN | EPOLLERR | EPOLLHUP)) { 173 | op = (dns_perf_event_ops_t *) ep->fdtab[fd].cb[MOD_RD].arg; 174 | dns_perf_epoll_clear_fd(fd, MOD_RD); 175 | op->recv((void *) op); 176 | } 177 | } 178 | 179 | return 0; 180 | } 181 | 182 | static int dns_perf_epoll_set_fd(int fd, int mod, void *arg) 183 | { 184 | int opcode; 185 | struct epoll_event ev; 186 | 187 | 188 | if (fd > ep->fd_size) { 189 | // TODO: expand 190 | } 191 | 192 | if (ep->fdtab[fd].events == 0) { 193 | opcode = EPOLL_CTL_ADD; 194 | } else { 195 | opcode = EPOLL_CTL_MOD; 196 | } 197 | 198 | if (mod == MOD_RD) { 199 | ep->fdtab[fd].events = EPOLLIN; 200 | } else if(mod == MOD_WR) { 201 | ep->fdtab[fd].events = EPOLLOUT; 202 | } else { 203 | return -1; 204 | } 205 | 206 | ev.data.fd = fd; 207 | ev.events = ep->fdtab[fd].events; 208 | 209 | if (epoll_ctl(ep->fd, opcode, fd, &ev) < 0) { 210 | fprintf(stderr, "Error epoll add fd %d error. reason:%s\n", fd, 211 | strerror(errno)); 212 | return -1; 213 | } 214 | 215 | ep->fdtab[fd].cb[mod].arg = arg; 216 | 217 | return 0; 218 | } 219 | 220 | static int dns_perf_epoll_is_fdset(int fd, int mod) 221 | { 222 | if(((mod == MOD_RD) && ((ep->fdtab[fd].events & EPOLLIN) == EPOLLIN)) || 223 | ((mod == MOD_WR) && ((ep->fdtab[fd].events & EPOLLOUT) == EPOLLOUT))) 224 | { 225 | return 1; 226 | } 227 | 228 | return 0; 229 | } 230 | 231 | 232 | static dns_perf_eventsys_t dns_perf_epoll_eventsys = { 233 | "epoll", 234 | dns_perf_epoll_init, 235 | dns_perf_epoll_do_wait, 236 | dns_perf_epoll_set_fd, 237 | dns_perf_epoll_clear_fd, 238 | dns_perf_epoll_destroy, 239 | dns_perf_epoll_get_obj_by_fd, 240 | dns_perf_epoll_is_fdset 241 | }; 242 | 243 | #endif /* HAVE_EPOLL */ 244 | 245 | #ifdef HAVE_KQUEUE 246 | 247 | typedef struct dns_perf_kqueue_data_s { 248 | int fd; /* kqueue fd */ 249 | struct fddata *fdtab; 250 | struct kevent *monlist; /* events we want to monitor */ 251 | struct kevent *evtlist; 252 | int fd_size; 253 | int evtlist_size; 254 | } dns_perf_kqueue_data_t; 255 | 256 | static dns_perf_kqueue_data_t *kq = NULL; 257 | 258 | static int dns_perf_kqueue_init() 259 | { 260 | 261 | if ((kq = (dns_perf_kqueue_data_t *) malloc(sizeof(dns_perf_kqueue_data_t))) 262 | == NULL) 263 | { 264 | fprintf(stderr, "Error memory low"); 265 | goto fail_kd; 266 | } 267 | 268 | if ((kq->fd = kqueue()) == -1) { 269 | fprintf(stderr, "Error create kqueue"); 270 | goto fail_kqueue; 271 | } 272 | 273 | kq->fd_size = MAX_EVENT_SOCKS; 274 | if ((kq->fdtab = (struct fddata*) calloc(kq->fd_size, 275 | sizeof(struct fddata))) == NULL) 276 | { 277 | fprintf(stderr, "Error memory low"); 278 | goto fail_fdlist; 279 | } 280 | 281 | if ((kq->monlist = (struct kevent*) calloc(kq->fd_size, 282 | sizeof(struct kevent))) == NULL) 283 | { 284 | fprintf(stderr, "Error memory low"); 285 | goto fail_monlist; 286 | } 287 | 288 | kq->evtlist_size = kq->fd_size; 289 | if ((kq->evtlist = (struct kevent*) calloc(kq->evtlist_size, 290 | sizeof(struct kevent))) == NULL) 291 | { 292 | fprintf(stderr, "Error memory low"); 293 | goto fail_evtlist; 294 | } 295 | 296 | return 0; 297 | 298 | fail_evtlist: 299 | free(kq->monlist); 300 | fail_monlist: 301 | free(kq->fdtab); 302 | fail_fdlist: 303 | close(kq->fd); 304 | kq->fd = -1; 305 | fail_kqueue: 306 | free(kq); 307 | kq = NULL; 308 | fail_kd: 309 | return -1; 310 | } 311 | 312 | static int dns_perf_kqueue_destroy() 313 | { 314 | free(kq->fdtab); 315 | free(kq->evtlist); 316 | close(kq->fd); 317 | free(kq); 318 | 319 | return 0; 320 | } 321 | 322 | static void *dns_perf_kqueue_get_obj_by_fd(int fd, int mod) 323 | { 324 | return kq->fdtab[fd].cb[mod].arg; 325 | } 326 | 327 | 328 | static int dns_perf_kqueue_clear_fd(int fd, int mod) 329 | { 330 | int filter; 331 | 332 | 333 | if (mod == MOD_RD) { 334 | filter = EVFILT_READ; 335 | } else if (mod == MOD_WR) { 336 | filter = EVFILT_READ; 337 | } else { 338 | return -1; 339 | } 340 | 341 | kq->fdtab[fd].events &= ~filter; 342 | 343 | EV_SET(&kq->monlist[fd], fd, filter, EV_DELETE, 0, 0, 0); 344 | 345 | kq->fdtab[fd].cb[mod].arg = NULL; 346 | 347 | return 0; 348 | } 349 | 350 | static int dns_perf_kqueue_do_kevent(long timeout) 351 | { 352 | int i, nevents, fd, filter; 353 | struct timespec ts, *tsp; 354 | dns_perf_event_ops_t *op; 355 | 356 | if (timeout == 0) { 357 | tsp = NULL; 358 | } else { 359 | ts.tv_sec = (time_t) timeout / 1000; 360 | ts.tv_nsec = (long) (timeout % 1000) * 1000000; 361 | tsp = &ts; 362 | } 363 | 364 | nevents = kevent(kq->fd, kq->monlist, kq->fd_size, kq->evtlist, kq->fd_size, tsp); 365 | 366 | if (nevents < 0) { 367 | fprintf(stderr, "kevent error:%s", strerror(errno)); 368 | return -1; 369 | } 370 | 371 | for (i = 0; i < nevents; i++) { 372 | if (kq->evtlist[i].flags & EV_ERROR) { 373 | fprintf(stderr, "EV_ERROR"); 374 | continue; 375 | } 376 | 377 | fd = kq->evtlist[i].ident; 378 | filter = kq->evtlist[i].filter; 379 | 380 | if (filter == EVFILT_READ) { 381 | op = (dns_perf_event_ops_t *) dns_perf_kqueue_get_obj_by_fd(fd, MOD_RD); 382 | dns_perf_kqueue_clear_fd(fd, MOD_RD); 383 | op->recv((void *) op); 384 | } 385 | 386 | if (filter == EVFILT_WRITE) { 387 | op = (dns_perf_event_ops_t *) dns_perf_kqueue_get_obj_by_fd(fd, MOD_WR); 388 | dns_perf_kqueue_clear_fd(fd, MOD_WR); 389 | op->send((void *) op); 390 | } 391 | 392 | } 393 | 394 | return 0; 395 | } 396 | 397 | static int dns_perf_kqueue_set_fd(int fd, int mod, void *arg) 398 | { 399 | int filter; 400 | 401 | 402 | if (fd > kq->fd_size) { 403 | //TODO: expand lists 404 | } 405 | 406 | if (mod == MOD_RD) { 407 | filter = EVFILT_READ; 408 | } else if(mod == MOD_WR) { 409 | filter = EVFILT_WRITE; 410 | } else { 411 | return -1; 412 | } 413 | 414 | /* Deleted. reason: see comments below 415 | if (kq->fdtab[fd].events != 0) { 416 | EV_SET(&kq->monlist[fd], fd, filter, EV_CLEAR, 0, 0, 0); 417 | kq->fdtab[fd].events = 0; 418 | } 419 | */ 420 | 421 | kq->fdtab[fd].events = filter; 422 | 423 | /* 424 | * Re-adding an existing event will modify the parameters of the original event, 425 | * and not result in a duplicate entry. 426 | * Adding an event automatically enables it, unless overridden by the EV_DISABLE flag. 427 | */ 428 | EV_SET(&kq->monlist[fd], fd, filter, EV_ADD, 0, 0, 0); 429 | 430 | kq->fdtab[fd].cb[mod].arg = arg; 431 | 432 | return 0; 433 | } 434 | 435 | 436 | static int dns_perf_kqueue_is_fdset(int fd, int mod) 437 | { 438 | if(((mod == MOD_RD) && ((kq->fdtab[fd].events & EVFILT_READ) == EVFILT_READ)) || 439 | ((mod == MOD_WR) && ((kq->fdtab[fd].events & EVFILT_WRITE) == EVFILT_WRITE))) 440 | { 441 | return 1; 442 | } 443 | 444 | return 0; 445 | } 446 | 447 | 448 | static dns_perf_eventsys_t dns_perf_kqueue_eventsys = { 449 | "epoll", 450 | dns_perf_kqueue_init, 451 | dns_perf_kqueue_do_kevent, 452 | dns_perf_kqueue_set_fd, 453 | dns_perf_kqueue_clear_fd, 454 | dns_perf_kqueue_destroy, 455 | dns_perf_kqueue_get_obj_by_fd, 456 | dns_perf_kqueue_is_fdset 457 | }; 458 | 459 | #endif /* HAVE_KQUEUE */ 460 | 461 | int dns_perf_set_event_sys() { 462 | int set = -1; 463 | 464 | #ifdef HAVE_EPOLL 465 | dns_perf_eventsys = &dns_perf_epoll_eventsys; 466 | set = 0; 467 | #elif defined HAVE_KQUEUE 468 | dns_perf_eventsys = &dns_perf_kqueue_eventsys; 469 | set = 0; 470 | #endif 471 | 472 | return set; 473 | } 474 | -------------------------------------------------------------------------------- /events.h: -------------------------------------------------------------------------------- 1 | #ifndef _EVENTS_H 2 | #define _EVENTS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | #define MOD_RD 0 14 | #define MOD_WR 1 15 | 16 | #define MAX_EVENT_SOCKS 10000 17 | 18 | /* info about one given fd */ 19 | struct fddata { 20 | int events; 21 | struct { 22 | void *arg; 23 | } cb[2]; 24 | }; 25 | 26 | typedef struct dns_perf_event_ops_s { 27 | int (*send)(void *arg); 28 | int (*recv)(void *arg); 29 | } dns_perf_event_ops_t; 30 | 31 | 32 | typedef struct dns_perf_eventsys_s { 33 | const char *name; 34 | int (*init)(void); 35 | int (*dispatch)(long timeout); 36 | int (*set_fd)(int fd, int mod, void *p); 37 | int (*clear_fd)(int fd, int mod); 38 | int (*destroy)(void); 39 | void *(*get_obj_by_fd)(int fd, int mod); 40 | int (*is_fdset)(int fd, int mod); 41 | } dns_perf_eventsys_t; 42 | 43 | 44 | extern dns_perf_eventsys_t *dns_perf_eventsys; 45 | 46 | int dns_perf_set_event_sys(); 47 | #define dns_perf_eventsys_init() dns_perf_eventsys->init() 48 | #define dns_perf_eventsys_destroy() dns_perf_eventsys->destroy() 49 | #define dns_perf_eventsys_dispatch(t) dns_perf_eventsys->dispatch(t) 50 | #define dns_perf_eventsys_is_fdset(fd) dns_perf_eventsys->is_fdset(fd) 51 | #define dns_perf_eventsys_clear_fd(fd, mod) dns_perf_eventsys->clear_fd(fd, mod) 52 | #define dns_perf_eventsys_set_fd(fd, mod, obj) dns_perf_eventsys->set_fd(fd, mod, obj) 53 | #define dns_perf_eventsys_get_obj_by_fd(fd, mod) dns_perf_eventsys->get_obj_by_fd(fd, mod) 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIST_H 2 | #define _LIST_H 3 | 4 | #define LIST(type) struct { type *head, *tail; } 5 | #define LIST_INIT(list) \ 6 | do { (list).head = NULL; (list).tail = NULL; } while (0) 7 | 8 | #define LINK(type) struct { type *prev, *next; } 9 | #define LINK_INIT_TYPE(elt, link, type) \ 10 | do { \ 11 | (elt)->link.prev = (type *)(-1); \ 12 | (elt)->link.next = (type *)(-1); \ 13 | } while (0) 14 | #define LINK_INIT(elt, link) \ 15 | LINK_INIT_TYPE(elt, link, void) 16 | #define LINK_LINKED(elt, link) ((void *)((elt)->link.prev) != (void *)(-1)) 17 | 18 | #define LIST_HEAD(list) ((list).head) 19 | #define LIST_TAIL(list) ((list).tail) 20 | #define LIST_EMPTY(list) ((list).head == NULL) 21 | 22 | #define __LIST_PREPENDUNSAFE(list, elt, link) \ 23 | do { \ 24 | if ((list).head != NULL) \ 25 | (list).head->link.prev = (elt); \ 26 | else \ 27 | (list).tail = (elt); \ 28 | (elt)->link.prev = NULL; \ 29 | (elt)->link.next = (list).head; \ 30 | (list).head = (elt); \ 31 | } while (0) 32 | 33 | #define LIST_PREPEND(list, elt, link) \ 34 | do { \ 35 | __LIST_PREPENDUNSAFE(list, elt, link); \ 36 | } while (0) 37 | 38 | #define LIST_INITANDPREPEND(list, elt, link) \ 39 | __LIST_PREPENDUNSAFE(list, elt, link) 40 | 41 | #define __LIST_APPENDUNSAFE(list, elt, link) \ 42 | do { \ 43 | if ((list).tail != NULL) \ 44 | (list).tail->link.next = (elt); \ 45 | else \ 46 | (list).head = (elt); \ 47 | (elt)->link.prev = (list).tail; \ 48 | (elt)->link.next = NULL; \ 49 | (list).tail = (elt); \ 50 | } while (0) 51 | 52 | #define LIST_APPEND(list, elt, link) \ 53 | do { \ 54 | __LIST_APPENDUNSAFE(list, elt, link); \ 55 | } while (0) 56 | 57 | #define LIST_INITANDAPPEND(list, elt, link) \ 58 | __LIST_APPENDUNSAFE(list, elt, link) 59 | 60 | #define __LIST_UNLINKUNSAFE_TYPE(list, elt, link, type) \ 61 | do { \ 62 | if ((elt)->link.next != NULL) \ 63 | (elt)->link.next->link.prev = (elt)->link.prev; \ 64 | else { \ 65 | (list).tail = (elt)->link.prev; \ 66 | } \ 67 | if ((elt)->link.prev != NULL) \ 68 | (elt)->link.prev->link.next = (elt)->link.next; \ 69 | else { \ 70 | (list).head = (elt)->link.next; \ 71 | } \ 72 | (elt)->link.prev = (type *)(-1); \ 73 | (elt)->link.next = (type *)(-1); \ 74 | } while (0) 75 | 76 | #define __LIST_UNLINKUNSAFE(list, elt, link) \ 77 | __LIST_UNLINKUNSAFE_TYPE(list, elt, link, void) 78 | 79 | #define LIST_UNLINK_TYPE(list, elt, link, type) \ 80 | do { \ 81 | __LIST_UNLINKUNSAFE_TYPE(list, elt, link, type); \ 82 | } while (0) 83 | #define LIST_UNLINK(list, elt, link) \ 84 | LIST_UNLINK_TYPE(list, elt, link, void) 85 | 86 | #define LIST_PREV(elt, link) ((elt)->link.prev) 87 | #define LIST_NEXT(elt, link) ((elt)->link.next) 88 | 89 | #define __LIST_INSERTBEFOREUNSAFE(list, before, elt, link) \ 90 | do { \ 91 | if ((before)->link.prev == NULL) \ 92 | LIST_PREPEND(list, elt, link); \ 93 | else { \ 94 | (elt)->link.prev = (before)->link.prev; \ 95 | (before)->link.prev = (elt); \ 96 | (elt)->link.prev->link.next = (elt); \ 97 | (elt)->link.next = (before); \ 98 | } \ 99 | } while (0) 100 | 101 | #define LIST_INSERTBEFORE(list, before, elt, link) \ 102 | do { \ 103 | __LIST_INSERTBEFOREUNSAFE(list, before, elt, link); \ 104 | } while (0) 105 | 106 | #define __LIST_INSERTAFTERUNSAFE(list, after, elt, link) \ 107 | do { \ 108 | if ((after)->link.next == NULL) \ 109 | LIST_APPEND(list, elt, link); \ 110 | else { \ 111 | (elt)->link.next = (after)->link.next; \ 112 | (after)->link.next = (elt); \ 113 | (elt)->link.next->link.prev = (elt); \ 114 | (elt)->link.prev = (after); \ 115 | } \ 116 | } while (0) 117 | 118 | #define LIST_INSERTAFTER(list, after, elt, link) \ 119 | do { \ 120 | __LIST_INSERTAFTERUNSAFE(list, after, elt, link); \ 121 | } while (0) 122 | 123 | #define LIST_APPENDLIST(list1, list2, link) \ 124 | do { \ 125 | if (LIST_EMPTY(list1)) \ 126 | (list1) = (list2); \ 127 | else if (!LIST_EMPTY(list2)) { \ 128 | (list1).tail->link.next = (list2).head; \ 129 | (list2).head->link.prev = (list1).tail; \ 130 | (list1).tail = (list2).tail; \ 131 | } \ 132 | (list2).head = NULL; \ 133 | (list2).tail = NULL; \ 134 | } while (0) 135 | 136 | #define LIST_ENQUEUE(list, elt, link) LIST_APPEND(list, elt, link) 137 | #define __LIST_ENQUEUEUNSAFE(list, elt, link) \ 138 | __LIST_APPENDUNSAFE(list, elt, link) 139 | #define LIST_DEQUEUE(list, elt, link) \ 140 | LIST_UNLINK_TYPE(list, elt, link, void) 141 | #define LIST_DEQUEUE_TYPE(list, elt, link, type) \ 142 | LIST_UNLINK_TYPE(list, elt, link, type) 143 | #define __LIST_DEQUEUEUNSAFE(list, elt, link) \ 144 | __LIST_UNLINKUNSAFE_TYPE(list, elt, link, void) 145 | #define __LIST_DEQUEUEUNSAFE_TYPE(list, elt, link, type) \ 146 | __LIST_UNLINKUNSAFE_TYPE(list, elt, link, type) 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /sock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file if part of dnsperf. 3 | * 4 | * Copyright (C) 2014 Cobblau 5 | * 6 | * dnsperf is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * dnsperf is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | 21 | #include 22 | 23 | int dns_perf_open_udp_socket(char *host, unsigned int port, int family) 24 | { 25 | int fd; 26 | struct sockaddr_in addr; 27 | int bufsize, val; 28 | 29 | if ((fd = socket(family, SOCK_DGRAM, 0)) == -1) { 30 | fprintf(stderr, "Error create udp socket\n"); 31 | return -1; 32 | } 33 | 34 | memset((void *)&addr, 0, sizeof(addr)); 35 | addr.sin_family = family; 36 | addr.sin_port = htons(port); 37 | addr.sin_addr.s_addr = inet_addr(host); 38 | 39 | if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0) { 40 | fprintf(stderr, "Error connect udp socket\n"); 41 | return -1; 42 | } 43 | 44 | bufsize = 1024 * DEFAULT_BUF_SIZE; 45 | 46 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &bufsize, 47 | sizeof(bufsize)) < 0 ) 48 | { 49 | fprintf(stderr, "Warning: setsockbuf(SO_RCVBUF) failed\n"); 50 | } 51 | 52 | if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &bufsize, 53 | sizeof(bufsize)) < 0) 54 | { 55 | fprintf(stderr, "Warning: setsockbuf(SO_SNDBUF) failed\n"); 56 | } 57 | 58 | val = fcntl(fd, F_GETFL, 0); 59 | fcntl(fd, F_SETFL, val | O_NONBLOCK); 60 | 61 | return fd; 62 | } 63 | 64 | 65 | int dns_perf_open_tcp_socket(char *host, unsigned int port, int family) 66 | { 67 | int fd; 68 | struct sockaddr_in addr; 69 | int bufsize, val; 70 | 71 | if ((fd = socket(family, SOCK_STREAM, 0)) == -1) { 72 | fprintf(stderr, "Error create tcp socket\n"); 73 | return -1; 74 | } 75 | 76 | memset((void *)&addr, 0, sizeof(addr)); 77 | addr.sin_family = family; 78 | addr.sin_port = htons(port); 79 | addr.sin_addr.s_addr = inet_addr(host); 80 | 81 | bufsize = 1024 * DEFAULT_BUF_SIZE; 82 | 83 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &bufsize, 84 | sizeof(bufsize)) < 0 ) 85 | { 86 | fprintf(stderr, "Warning: setsockbuf(SO_RCVBUF) failed\n"); 87 | } 88 | 89 | if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &bufsize, 90 | sizeof(bufsize)) < 0) 91 | { 92 | fprintf(stderr, "Warning: setsockbuf(SO_SNDBUF) failed\n"); 93 | } 94 | 95 | val = fcntl(fd, F_GETFL, 0); 96 | fcntl(fd, F_SETFL, val | O_NONBLOCK); 97 | 98 | return fd; 99 | } 100 | 101 | int dns_perf_socket_state(int fd) 102 | { 103 | int status = 0; 104 | socklen_t slen; 105 | 106 | /* Check file descriptor */ 107 | slen = sizeof (status); 108 | if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen) == -1) { 109 | return -1; 110 | } 111 | 112 | return 0; 113 | } 114 | -------------------------------------------------------------------------------- /sock.h: -------------------------------------------------------------------------------- 1 | #ifndef _SOCK_H 2 | #define _SOCK_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define DEFAULT_BUF_SIZE 8 14 | 15 | int dns_perf_open_udp_socket(char *host, unsigned int port, int family); 16 | int dns_perf_open_tcp_socket(char *host, unsigned int port, int family); 17 | 18 | int dns_perf_socket_state(int fd); 19 | #endif 20 | --------------------------------------------------------------------------------