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