1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup test
36 * @ingroup test
37 * @brief test
38 *
39 * Hammers queries on a zone.
40 * It knows the records doing an AXFR or using an .axfr file as a source.
41 * Can also be used to specify an fqdn + type.
42 *
43 */
44
45 #include <dnscore/dnscore.h>
46 #include <dnscore/dnsname.h>
47 #include <dnscore/format.h>
48 #include <dnscore/base64.h>
49 #include <dnscore/tsig.h>
50 #include <dnscore/tcp_io_stream.h>
51 #include <dnscore/message.h>
52 #include <dnscore/config_settings.h>
53 #include <dnscore/random.h>
54 #include <dnscore/packet_reader.h>
55 #include <dnscore/nsec3-hash.h>
56 #include <dnscore/base32hex.h>
57 #include <dnscore/xfr_input_stream.h>
58 #include <dnscore/timems.h>
59 #include <dnscore/bytearray_output_stream.h>
60 #include <dnscore/bytearray_input_stream.h>
61 #include <dnscore/dns_resource_record.h>
62 #include <dnscore/file_input_stream.h>
63 #include <dnscore/buffer_input_stream.h>
64 #include <dnscore/dnskey-keyring.h>
65 #include <dnscore/message_verify_rrsig.h>
66
67 #include <dnscore/logger.h>
68 #include <dnscore/logger_channel_stream.h>
69
70 #include <dnscore/dns-udp.h>
71
72 #define VERBOSE 1
73 #define TIMEOUT 3 // seconds
74 #define LOADFIRST 0
75
76 #define LOG_ENABLE 0 // enable dnscore system log to get
77 static int verbose = VERBOSE;
78
79 #if LOG_ENABLE
80
81
logger_setup()82 static void logger_setup()
83 {
84 logger_init();
85 logger_start();
86
87 logger_handle_create_to_stdout("system", MSG_ALL_MASK);
88 }
89
90 #endif
91
92 static smp_int queries_count = SMP_INT_INITIALIZER;
93
94 static void
query_hammer_test_async_done_cb(struct async_message_s * amsg)95 query_hammer_test_async_done_cb(struct async_message_s *amsg)
96 {
97 dns_simple_message_s* msg = (dns_simple_message_s*)amsg->args;
98 if(verbose == VERBOSE)
99 {
100 s64 *startp = (s64*)amsg->handler_args;
101 s64 stop = timeus();
102 double dt = (stop - *startp);
103 ZFREE_OBJECT(startp);
104 dt /= 1000.0;
105 if(ISOK(amsg->error_code))
106 {
107 u8 rcode = message_get_rcode(msg->answer);
108 osformatln(termout,"answer: @%{hostaddr} %{dnsname} %{dnstype} %{dnsclass} %.3fms: %r (%s)", msg->name_server, msg->fqdn, &msg->qtype, &msg->qclass, dt, amsg->error_code, dns_message_rcode_get_name(rcode));
109 }
110 else
111 {
112 osformatln(termout,"answer: @%{hostaddr} %{dnsname} %{dnstype} %{dnsclass} %.3fms: %r", msg->name_server, msg->fqdn, &msg->qtype, &msg->qclass, dt, amsg->error_code);
113 s64 *startp;
114 ZALLOC_OBJECT_OR_DIE(startp, s64, GENERIC_TAG);
115 *startp = timeus();
116 smp_int_inc(&queries_count);
117 dns_udp_send_simple_message(msg->name_server, msg->fqdn, msg->qtype, msg->qclass, 0, query_hammer_test_async_done_cb, startp);
118 }
119 }
120 smp_int_dec(&queries_count);
121 dns_udp_simple_message_release(msg);
122 }
123
124 static ya_result
query_hammer_test(const host_address * ip,const u8 * zone_fqdn,const u8 * fqdn,u16 qtype)125 query_hammer_test(const host_address *ip, const u8 *zone_fqdn, const u8 *fqdn, u16 qtype)
126 {
127 (void)zone_fqdn;
128
129 s64 *startp;
130 ZALLOC_OBJECT_OR_DIE(startp, s64, GENERIC_TAG);
131 *startp = timeus();
132 message_edns0_setmaxsize(4096);
133 smp_int_inc(&queries_count);
134 dns_udp_send_simple_message(ip, fqdn, qtype, CLASS_IN, 0, query_hammer_test_async_done_cb, startp);
135
136 return SUCCESS;
137 }
138
139 #if 0
140 static ya_result
141 query_hammer_test(const host_address *ip, const u8 *zone_fqdn, const u8 *fqdn, u16 qtype)
142 {
143 random_ctx rndctx = random_init_auto();
144 message_data* mesg = message_new_instance();
145 ya_result ret;
146
147 u64 start = timeus();
148 u64 stop;
149 double dt;
150
151 message_edns0_setmaxsize(4096);
152
153 u16 id = (u16)random_next(rndctx);
154 message_make_query_ex(mesg, id, zone_fqdn, qtype, CLASS_IN, MESSAGE_EDNS0_DNSSEC);
155
156 if(ISOK(ret = message_query(mesg, ip)))
157 {
158 stop = timeus();
159
160 dt = stop - start;
161 dt /= 1000.0;
162
163 formatln("%{dnsname} %{dnstype}: query too %fms", zone_fqdn, &qtype, dt);
164
165 if(verbose)
166 {
167 message_print_format_dig(termout, message_get_buffer_const(mesg), message_get_size(mesg), 15, 0);
168 }
169
170 if((message_get_rcode(mesg) == RCODE_NOERROR) || (message_get_rcode(mesg) == RCODE_NXDOMAIN))
171 {
172 formatln("%{dnsname} %{dnstype}: query done", zone_fqdn, &qtype, dt);
173 }
174 else
175 {
176 formatln("error: %{dnsname} %{dnstype}: query failed with: RCODE=%s", zone_fqdn, &qtype, dns_message_rcode_get_name(message_get_rcode(mesg)));
177 }
178 }
179 else
180 {
181 formatln("error: %{dnsname} %{dnstype}: network failed with: %r (%i)", zone_fqdn, &qtype, ret, ret);
182 }
183
184 message_free(mesg);
185 random_finalize(rndctx);
186
187 return ret;
188 }
189 #endif
190
191 static ya_result
zone_forall(const host_address * ip,const u8 * zone_fqdn)192 zone_forall(const host_address *ip, const u8 *zone_fqdn)
193 {
194 random_ctx rndctx = random_init_auto();
195 message_data *query = message_new_instance();
196 message_make_query(query, (u16)random_next(rndctx), zone_fqdn, TYPE_AXFR, CLASS_IN);
197
198 /*
199 * connect & send
200 */
201
202 input_stream xfris;
203 input_stream is;
204 output_stream os;
205 #if LOADFIRST
206 output_stream baos;
207 input_stream bais;
208 #endif
209 u8 *fqdn;
210 ya_result ret;
211 int fd;
212 int soa_count = 0;
213
214 // connect
215 u16 rtype;
216 u16 rclass;
217 s32 rttl;
218 u16 rdata_size;
219 u8 brol_fqdn[256];
220 #if LOADFIRST
221 u8 prev_fqdn[256];
222 #endif
223
224 brol_fqdn[0] = 4;
225 brol_fqdn[1] = 'b';
226 brol_fqdn[2] = 'r';
227 brol_fqdn[3] = 'o';
228 brol_fqdn[4] = 'l';
229
230 fqdn = &brol_fqdn[5];
231
232 input_stream_set_sink(&is);
233 input_stream_set_sink(&xfris);
234 output_stream_set_sink(&os);
235
236 #if LOADFIRST
237 output_stream_set_sink(&baos);
238 input_stream_set_sink(&bais);
239 #endif
240
241 if(FAIL(ret = tcp_input_output_stream_connect_host_address(ip, &is, &os, 3)))
242 {
243 goto zone_forall_cleanup;
244 }
245
246 if(FAIL(ret = message_write_tcp(query, &os)))
247 {
248 goto zone_forall_cleanup;
249 }
250
251 output_stream_flush(&os);
252
253 fd = fd_input_stream_get_filedescriptor(&is);
254
255 tcp_set_sendtimeout(fd, TIMEOUT, 0);
256 tcp_set_recvtimeout(fd, TIMEOUT, 0);
257
258 if(FAIL(ret = xfr_input_stream_init(&xfris, zone_fqdn, &is, query, 0, XFR_ALLOW_AXFR)))
259 {
260 goto zone_forall_cleanup;
261 }
262
263 #if LOADFIRST
264 bytearray_output_stream_init_ex(&baos, NULL, 0x10000000, BYTEARRAY_DYNAMIC);
265 #endif
266
267 int record_count = 0;
268
269 while(soa_count < 2)
270 {
271 if(FAIL(ret = input_stream_read_dnsname(&xfris, fqdn)))
272 {
273 break;
274 }
275
276 if(FAIL(ret = input_stream_read_u16(&xfris, &rtype)))
277 {
278 break;
279 }
280
281 if(rtype == TYPE_SOA)
282 {
283 ++soa_count;
284 }
285
286 if(FAIL(ret = input_stream_read_u16(&xfris, &rclass)))
287 {
288 break;
289 }
290
291 if(FAIL(ret = input_stream_read_u32(&xfris, (u32*)&rttl)))
292 {
293 break;
294 }
295
296 if(FAIL(ret = input_stream_read_u16(&xfris, &rdata_size)))
297 {
298 break;
299 }
300
301 rdata_size = ntohs(rdata_size);
302
303 if(FAIL(ret = input_stream_skip(&xfris, rdata_size)))
304 {
305 break;
306 }
307
308 #if LOADFIRST
309 if(!dnsname_equals(fqdn, prev_fqdn))
310 {
311 output_stream_write_u8(&baos, 0xff);
312 output_stream_write_dnsname(&baos, fqdn);
313 }
314 else
315 {
316 dnsname_copy(prev_fqdn, fqdn);
317 }
318 output_stream_write_u8(&baos, 0xfe);
319 output_stream_write_u16(&baos, rtype);
320 #else
321 // got a name from the zone
322
323 if(FAIL(ret = query_hammer_test(ip, zone_fqdn, fqdn, rtype)))
324 {
325 formatln("ERROR: %{hostaddr}: %{dnsname}: %{dnsname}: %{dnstype}: %r", ip, zone_fqdn, fqdn, &rtype, ret);
326 }
327
328 if(FAIL(ret = query_hammer_test(ip, zone_fqdn, brol_fqdn, rtype)))
329 {
330 formatln("ERROR: %{hostaddr}: %{dnsname}: %{dnsname}: %{dnstype}: %r", ip, zone_fqdn, brol_fqdn, &rtype, ret);
331 }
332
333 if((record_count < 64) || ((record_count % 97) == 0))
334 {
335 if(FAIL(ret = query_hammer_test(ip, zone_fqdn, fqdn, 65535 - record_count)))
336 {
337 formatln("ERROR: %{hostaddr}: %{dnsname}: %{dnsname}: %{dnstype}: %r", ip, zone_fqdn, fqdn, &rtype, ret);
338 }
339
340 if(FAIL(ret = query_hammer_test(ip, zone_fqdn, brol_fqdn, 65535 - record_count)))
341 {
342 formatln("ERROR: %{hostaddr}: %{dnsname}: %{dnsname}: %{dnstype}: %r", ip, zone_fqdn, brol_fqdn, &rtype, ret);
343 }
344 }
345
346 ++record_count;
347 #endif
348 }
349
350 #if LOADFIRST
351 // now the stream can be replayed
352
353 bytearray_input_stream_init(&bais, bytearray_output_stream_buffer(&baos), bytearray_output_stream_size(&baos), FALSE);
354 while(bytearray_input_stream_remaining(&bais) > 0)
355 {
356 u16 rcode;
357 u8 code;
358 input_stream_read_u8(&bais, &code);
359 switch(code)
360 {
361 case 0xff:
362 {
363 input_stream_read_dnsname(&bais, fqdn);
364 break;
365 }
366 case 0xfe:
367 {
368 input_stream_read_u16(&bais, &rcode);
369
370 // got a name from the zone
371
372 if(FAIL(ret = query_hammer_test(ip, zone_fqdn, fqdn, rtype)))
373 {
374 formatln("ERROR: %{hostaddr}: %{dnsname}: %{dnsname}: %{dnstype}: %r", ip, zone_fqdn, fqdn, &rtype, ret);
375 }
376
377 if(FAIL(ret = query_hammer_test(ip, zone_fqdn, brol_fqdn, rtype)))
378 {
379 formatln("ERROR: %{hostaddr}: %{dnsname}: %{dnsname}: %{dnstype}: %r", ip, zone_fqdn, brol_fqdn, &rtype, ret);
380 }
381
382 if((record_count < 64) || ((record_count % 97) == 0))
383 {
384 if(FAIL(ret = query_hammer_test(ip, zone_fqdn, fqdn, 65535 - record_count)))
385 {
386 formatln("ERROR: %{hostaddr}: %{dnsname}: %{dnsname}: %{dnstype}: %r", ip, zone_fqdn, fqdn, &rtype, ret);
387 }
388
389 if(FAIL(ret = query_hammer_test(ip, zone_fqdn, brol_fqdn, 65535 - record_count)))
390 {
391 formatln("ERROR: %{hostaddr}: %{dnsname}: %{dnsname}: %{dnstype}: %r", ip, zone_fqdn, brol_fqdn, &rtype, ret);
392 }
393 }
394
395 ++record_count;
396
397 break;
398 }
399 default:
400 {
401 formatln("ERROR: unexpected code %x", code);
402 goto zone_forall_cleanup;
403 }
404 }
405 }
406 #endif
407
408 zone_forall_cleanup:
409
410 #if LOADFIRST
411 input_stream_close(&bais);
412 output_stream_close(&baos);
413 #endif
414
415 input_stream_close(&xfris);
416 input_stream_close(&is);
417 output_stream_close(&os);
418
419 message_free(query);
420 return ret;
421 }
422
423 static ya_result
zone_for_all_in_axfr(const host_address * ip,const u8 * zone_fqdn,const char * filename)424 zone_for_all_in_axfr(const host_address *ip, const u8 *zone_fqdn, const char *filename)
425 {
426 input_stream is;
427 dns_resource_record rr;
428 ya_result ret;
429
430 if(FAIL(ret = file_input_stream_open(&is, filename)))
431 {
432 return ret;
433 }
434
435 buffer_input_stream_init(&is, &is, 4096);
436
437 dns_resource_record_init(&rr);
438
439 int record_count = 0;
440
441 for(int soa_count = 0; soa_count < 2;)
442 {
443 if(FAIL(ret = dns_resource_record_read(&rr, &is)))
444 {
445 break;
446 }
447
448 if(rr.tctr.qtype == TYPE_SOA)
449 {
450 ++soa_count;
451 }
452
453 if(FAIL(ret = query_hammer_test(ip, zone_fqdn, rr.name, rr.tctr.qtype)))
454 {
455 formatln("ERROR: %{hostaddr}: %{dnsname}: %{dnsname}: %{dnstype}: %r", ip, zone_fqdn, rr.name, &rr.tctr.qtype, ret);
456 }
457
458 ++record_count;
459 }
460
461 dns_resource_record_clear(&rr);
462 input_stream_close(&is);
463 return ret;
464 }
465
help()466 static void help()
467 {
468 println("parameters: server-ip zone [axfr-image-path]|[fqdn type]*");
469 flushout();
470 }
471
472 int
main(int argc,char * argv[])473 main(int argc, char *argv[])
474 {
475 host_address *ip = NULL;
476 ya_result ret;
477 u16 query_type;
478 u8 zone_fqdn[256];
479 u8 fqdn[256];
480
481 /* initialises the core library */
482 dnscore_init();
483
484 if(argc < 3)
485 {
486 help();
487 return EXIT_FAILURE;
488 }
489
490 anytype defaults = {._8u8={CONFIG_HOST_LIST_FLAGS_DEFAULT,128,0,0,0,0,0,0}};
491 if(FAIL(ret = config_set_host_list(argv[1], &ip, defaults)))
492 {
493 formatln("%s is an invalid ip: %r", argv[1], ret);
494 help();
495 return EXIT_FAILURE;
496 }
497
498 if(ip->port == 0)
499 {
500 ip->port = NU16(53);
501 }
502
503 if(FAIL(ret = cstr_to_dnsname_with_check(zone_fqdn, argv[2])))
504 {
505 formatln("%s is an invalid zone: %r", argv[2], ret);
506 help();
507 return EXIT_FAILURE;
508 }
509
510 #if LOG_ENABLE
511 logger_setup();
512 #endif
513
514 async_message_pool_init();
515
516 static dns_udp_settings_s dns_udp_settings =
517 {
518 6000000,//DNS_UDP_TIMEOUT_US,
519 1000000,//DNS_UDP_SEND_RATE,
520 1000000000,//DNS_UDP_SEND_BANDWIDTH,
521 1000000000,// RECV BANDWIDTH,
522 1000000,//DNS_UDP_SEND_QUEUE,
523 8,//DNS_UDP_PORT_COUNT_OVERRIDE, // parallel tasks
524 64,//DNS_UDP_RETRY_COUNT,
525 1000000, // per server rate
526 1000000000, // per server bandwidth
527 0, // per server frequency (0 = no limit)
528
529 4096, // unused
530 DNS_UDP_CALLBACK_QUEUE_SIZE,
531 DNS_UDP_CALLBACK_THREAD_COUNT,
532 1, // TCP thread pool size
533 FALSE // fallback on timeout
534 };
535 dns_udp_handler_configure(&dns_udp_settings);
536 dns_udp_handler_init();
537
538 //dns_udp_handler_host_limit_set(ip, 1000000, 1000000000, 0);
539
540 dns_udp_handler_start();
541
542 if(argc >= 5)
543 {
544 for(int i = 3; i < argc; i += 2)
545 {
546 if(FAIL(ret = cstr_to_dnsname_with_check(fqdn, argv[i])))
547 {
548 formatln("%s is an invalid fqdn: %r", argv[i], ret);
549 help();
550 break;
551 }
552
553 if(FAIL(ret = dns_type_from_case_name(argv[i + 1], &query_type)))
554 {
555 formatln("%s is an invalid type: %r", argv[i + 1], ret);
556 help();
557 break;
558 }
559
560 if(ISOK(ret = query_hammer_test(ip, zone_fqdn, fqdn, query_type)))
561 {
562 println("SUCCESS");
563 }
564 else
565 {
566 formatln("FAILURE % r", ret);
567 }
568 }
569 }
570 else if(argc == 4)
571 {
572 // expects a path
573 zone_for_all_in_axfr(ip, zone_fqdn, argv[3]);
574 }
575 else // argc == 3
576 {
577 ret = zone_forall(ip, zone_fqdn);
578
579 if(FAIL(ret))
580 {
581 formatln("zone_forall(%{hostaddr}, %{dnsname}) failed with %r", ip, zone_fqdn, ret);
582 }
583 }
584
585 while(smp_int_get(&queries_count) > 0)
586 {
587 sleep(1);
588 }
589
590 dns_udp_handler_stop();
591 dns_udp_handler_finalize();
592
593 flushout();
594 flusherr();
595 fflush(NULL);
596
597 dnscore_finalize();
598
599 return ISOK(ret)?EXIT_SUCCESS:EXIT_FAILURE;
600 }
601