aboutsummaryrefslogtreecommitdiff
path: root/src/dns/gnunet-zonewalk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns/gnunet-zonewalk.c')
-rw-r--r--src/dns/gnunet-zonewalk.c597
1 files changed, 597 insertions, 0 deletions
diff --git a/src/dns/gnunet-zonewalk.c b/src/dns/gnunet-zonewalk.c
new file mode 100644
index 000000000..860672e7a
--- /dev/null
+++ b/src/dns/gnunet-zonewalk.c
@@ -0,0 +1,597 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2018 GNUnet e.V.
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21/**
22 * @file src/dns/gnunet-zoneimport.c
23 * @brief import a DNS zone for analysis, brute force
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <gnunet_util_lib.h>
28#include <gnunet_dnsstub_lib.h>
29#include <gnunet_dnsparser_lib.h>
30
31/**
32 * Request we should make.
33 */
34struct Request
35{
36 /**
37 * Requests are kept in a DLL.
38 */
39 struct Request *next;
40
41 /**
42 * Requests are kept in a DLL.
43 */
44 struct Request *prev;
45
46 /**
47 * Socket used to make the request, NULL if not active.
48 */
49 struct GNUNET_DNSSTUB_RequestSocket *rs;
50
51 /**
52 * Raw DNS query.
53 */
54 void *raw;
55
56 /**
57 * Number of bytes in @e raw.
58 */
59 size_t raw_len;
60
61 /**
62 * Hostname we are resolving.
63 */
64 char *hostname;
65
66 /**
67 * When did we last issue this request?
68 */
69 time_t time;
70
71 /**
72 * How often did we issue this query?
73 */
74 int issue_num;
75
76 /**
77 * random 16-bit DNS query identifier.
78 */
79 uint16_t id;
80};
81
82
83/**
84 * Context for DNS resolution.
85 */
86static struct GNUNET_DNSSTUB_Context *ctx;
87
88/**
89 * The number of queries that are outstanding
90 */
91static unsigned int pending;
92
93/**
94 * Number of lookups we performed overall.
95 */
96static unsigned int lookups;
97
98/**
99 * Number of lookups that failed.
100 */
101static unsigned int failures;
102
103/**
104 * Number of records we found.
105 */
106static unsigned int records;
107
108/**
109 * Head of DLL of all requests to perform.
110 */
111static struct Request *req_head;
112
113/**
114 * Tail of DLL of all requests to perform.
115 */
116static struct Request *req_tail;
117
118/**
119 * Main task.
120 */
121static struct GNUNET_SCHEDULER_Task *t;
122
123/**
124 * Maximum number of queries pending at the same time.
125 */
126#define THRESH 20
127
128/**
129 * TIME_THRESH is in usecs. How quickly do we submit fresh queries.
130 * Used as an additional throttle.
131 */
132#define TIME_THRESH 10
133
134/**
135 * How often do we retry a query before giving up for good?
136 */
137#define MAX_RETRIES 5
138
139
140/**
141 * We received @a rec for @a req. Remember the answer.
142 *
143 * @param req request
144 * @param rec response
145 */
146static void
147process_record (struct Request *req,
148 struct GNUNET_DNSPARSER_Record *rec)
149{
150 char buf[INET6_ADDRSTRLEN];
151
152 records++;
153 switch (rec->type)
154 {
155 case GNUNET_DNSPARSER_TYPE_A:
156 fprintf (stdout,
157 "%s A %s\n",
158 req->hostname,
159 inet_ntop (AF_INET,
160 rec->data.raw.data,
161 buf,
162 sizeof (buf)));
163 break;
164 case GNUNET_DNSPARSER_TYPE_AAAA:
165 fprintf (stdout,
166 "%s AAAA %s\n",
167 req->hostname,
168 inet_ntop (AF_INET6,
169 rec->data.raw.data,
170 buf,
171 sizeof (buf)));
172 break;
173 case GNUNET_DNSPARSER_TYPE_NS:
174 fprintf (stdout,
175 "%s NS %s\n",
176 req->hostname,
177 rec->data.hostname);
178 break;
179 case GNUNET_DNSPARSER_TYPE_CNAME:
180 fprintf (stdout,
181 "%s CNAME %s\n",
182 req->hostname,
183 rec->data.hostname);
184 break;
185 case GNUNET_DNSPARSER_TYPE_MX:
186 fprintf (stdout,
187 "%s MX %u %s\n",
188 req->hostname,
189 (unsigned int) rec->data.mx->preference,
190 rec->data.mx->mxhost);
191 break;
192 case GNUNET_DNSPARSER_TYPE_SOA:
193 fprintf (stdout,
194 "%s SOA %s %s %u %u %u %u %u\n",
195 req->hostname,
196 rec->data.soa->mname,
197 rec->data.soa->rname,
198 (unsigned int) rec->data.soa->serial,
199 (unsigned int) rec->data.soa->refresh,
200 (unsigned int) rec->data.soa->retry,
201 (unsigned int) rec->data.soa->expire,
202 (unsigned int) rec->data.soa->minimum_ttl);
203 break;
204 case GNUNET_DNSPARSER_TYPE_SRV:
205 fprintf (stdout,
206 "%s SRV %s %u %u %u\n",
207 req->hostname,
208 rec->data.srv->target,
209 rec->data.srv->priority,
210 rec->data.srv->weight,
211 rec->data.srv->port);
212 break;
213 case GNUNET_DNSPARSER_TYPE_PTR:
214 fprintf (stdout,
215 "%s PTR %s\n",
216 req->hostname,
217 rec->data.hostname);
218 break;
219 case GNUNET_DNSPARSER_TYPE_TXT:
220 fprintf (stdout,
221 "%s TXT %.*s\n",
222 req->hostname,
223 (int) rec->data.raw.data_len,
224 (char *) rec->data.raw.data);
225 break;
226 case GNUNET_DNSPARSER_TYPE_DNAME:
227 fprintf (stdout,
228 "%s DNAME %s\n",
229 req->hostname,
230 rec->data.hostname);
231 break;
232
233 /* obscure records */
234 case GNUNET_DNSPARSER_TYPE_AFSDB:
235 case GNUNET_DNSPARSER_TYPE_NAPTR:
236 case GNUNET_DNSPARSER_TYPE_APL:
237 case GNUNET_DNSPARSER_TYPE_DHCID:
238 case GNUNET_DNSPARSER_TYPE_HIP:
239 case GNUNET_DNSPARSER_TYPE_LOC:
240 case GNUNET_DNSPARSER_TYPE_RP:
241 case GNUNET_DNSPARSER_TYPE_TKEY:
242 case GNUNET_DNSPARSER_TYPE_TSIG:
243 case GNUNET_DNSPARSER_TYPE_URI:
244 case GNUNET_DNSPARSER_TYPE_TA:
245
246 /* DNSSEC */
247 case GNUNET_DNSPARSER_TYPE_DS:
248 case GNUNET_DNSPARSER_TYPE_RRSIG:
249 case GNUNET_DNSPARSER_TYPE_NSEC:
250 case GNUNET_DNSPARSER_TYPE_DNSKEY:
251 case GNUNET_DNSPARSER_TYPE_NSEC3:
252 case GNUNET_DNSPARSER_TYPE_NSEC3PARAM:
253 case GNUNET_DNSPARSER_TYPE_CDS:
254 case GNUNET_DNSPARSER_TYPE_CDNSKEY:
255
256 /* DNSSEC payload */
257 case GNUNET_DNSPARSER_TYPE_CERT:
258 case GNUNET_DNSPARSER_TYPE_SSHFP:
259 case GNUNET_DNSPARSER_TYPE_IPSECKEY:
260 case GNUNET_DNSPARSER_TYPE_TLSA:
261 case GNUNET_DNSPARSER_TYPE_OPENPGPKEY:
262
263 /* obsolete records */
264 case GNUNET_DNSPARSER_TYPE_SIG:
265 case GNUNET_DNSPARSER_TYPE_KEY:
266 case GNUNET_DNSPARSER_TYPE_KX:
267 {
268 char *base32;
269
270 base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data,
271 rec->data.raw.data_len);
272 fprintf (stdout,
273 "%s (%u) %s\n",
274 req->hostname,
275 rec->type,
276 base32);
277 GNUNET_free (base32);
278 }
279 break;
280 default:
281 fprintf (stderr,
282 "Unsupported type %u\n",
283 (unsigned int) rec->type);
284 break;
285 }
286}
287
288
289/**
290 * Function called with the result of a DNS resolution.
291 *
292 * @param cls closure with the `struct Request`
293 * @param dns dns response, never NULL
294 * @param dns_len number of bytes in @a dns
295 */
296static void
297process_result (void *cls,
298 const struct GNUNET_TUN_DnsHeader *dns,
299 size_t dns_len)
300{
301 struct Request *req = cls;
302 struct GNUNET_DNSPARSER_Packet *p;
303
304 if (NULL == dns)
305 {
306 /* stub gave up */
307 pending--;
308 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
309 "Stub gave up on DNS reply for `%s'\n",
310 req->hostname);
311 GNUNET_CONTAINER_DLL_remove (req_head,
312 req_tail,
313 req);
314 if (req->issue_num > MAX_RETRIES)
315 {
316 failures++;
317 GNUNET_free (req->hostname);
318 GNUNET_free (req->raw);
319 GNUNET_free (req);
320 return;
321 }
322 GNUNET_CONTAINER_DLL_insert_tail (req_head,
323 req_tail,
324 req);
325 req->rs = NULL;
326 return;
327 }
328 if (req->id != dns->id)
329 return;
330 pending--;
331 GNUNET_DNSSTUB_resolve_cancel (req->rs);
332 req->rs = NULL;
333 GNUNET_CONTAINER_DLL_remove (req_head,
334 req_tail,
335 req);
336 p = GNUNET_DNSPARSER_parse ((const char *) dns,
337 dns_len);
338 if (NULL == p)
339 {
340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
341 "Failed to parse DNS reply for `%s'\n",
342 req->hostname);
343 if (req->issue_num > MAX_RETRIES)
344 {
345 failures++;
346 GNUNET_free (req->hostname);
347 GNUNET_free (req->raw);
348 GNUNET_free (req);
349 return;
350 }
351 GNUNET_CONTAINER_DLL_insert_tail (req_head,
352 req_tail,
353 req);
354 return;
355 }
356 for (unsigned int i=0;i<p->num_answers;i++)
357 {
358 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
359
360 process_record (req,
361 rs);
362 }
363 for (unsigned int i=0;i<p->num_authority_records;i++)
364 {
365 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
366
367 process_record (req,
368 rs);
369 }
370 for (unsigned int i=0;i<p->num_additional_records;i++)
371 {
372 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
373
374 process_record (req,
375 rs);
376 }
377 GNUNET_DNSPARSER_free_packet (p);
378 GNUNET_free (req->hostname);
379 GNUNET_free (req->raw);
380 GNUNET_free (req);
381}
382
383
384/**
385 * Submit a request to DNS unless we need to slow down because
386 * we are at the rate limit.
387 *
388 * @param req request to submit
389 * @return #GNUNET_OK if request was submitted
390 * #GNUNET_NO if request was already submitted
391 * #GNUNET_SYSERR if we are at the rate limit
392 */
393static int
394submit_req (struct Request *req)
395{
396 static struct timeval last_request;
397 struct timeval now;
398
399 if (NULL != req->rs)
400 return GNUNET_NO; /* already submitted */
401 gettimeofday (&now,
402 NULL);
403 if ( ( ( (now.tv_sec - last_request.tv_sec) == 0) &&
404 ( (now.tv_usec - last_request.tv_usec) < TIME_THRESH) ) ||
405 (pending >= THRESH) )
406 return GNUNET_SYSERR;
407 GNUNET_assert (NULL == req->rs);
408 req->rs = GNUNET_DNSSTUB_resolve (ctx,
409 req->raw,
410 req->raw_len,
411 &process_result,
412 req);
413 GNUNET_assert (NULL != req->rs);
414 req->issue_num++;
415 last_request = now;
416 lookups++;
417 pending++;
418 req->time = time (NULL);
419 return GNUNET_OK;
420}
421
422
423/**
424 * Process as many requests as possible from the queue.
425 *
426 * @param cls NULL
427 */
428static void
429process_queue(void *cls)
430{
431 (void) cls;
432 t = NULL;
433 for (struct Request *req = req_head;
434 NULL != req;
435 req = req->next)
436 {
437 if (GNUNET_SYSERR == submit_req (req))
438 break;
439 }
440 if (NULL != req_head)
441 t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
442 &process_queue,
443 NULL);
444 else
445 GNUNET_SCHEDULER_shutdown ();
446}
447
448
449/**
450 * Clean up and terminate the process.
451 *
452 * @param cls NULL
453 */
454static void
455do_shutdown (void *cls)
456{
457 (void) cls;
458 if (NULL != t)
459 {
460 GNUNET_SCHEDULER_cancel (t);
461 t = NULL;
462 }
463 GNUNET_DNSSTUB_stop (ctx);
464 ctx = NULL;
465}
466
467
468/**
469 * Process requests from the queue, then if the queue is
470 * not empty, try again.
471 *
472 * @param cls NULL
473 */
474static void
475run (void *cls)
476{
477 (void) cls;
478
479 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
480 NULL);
481 t = GNUNET_SCHEDULER_add_now (&process_queue,
482 NULL);
483}
484
485
486/**
487 * Add @a hostname to the list of requests to be made.
488 *
489 * @param hostname name to resolve
490 */
491static void
492queue (const char *hostname)
493{
494 struct GNUNET_DNSPARSER_Packet p;
495 struct GNUNET_DNSPARSER_Query q;
496 struct Request *req;
497 char *raw;
498 size_t raw_size;
499
500 if (GNUNET_OK !=
501 GNUNET_DNSPARSER_check_name (hostname))
502 {
503 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
504 "Refusing invalid hostname `%s'\n",
505 hostname);
506 return;
507 }
508 q.name = (char *) hostname;
509 q.type = GNUNET_DNSPARSER_TYPE_NS;
510 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
511
512 memset (&p,
513 0,
514 sizeof (p));
515 p.num_queries = 1;
516 p.queries = &q;
517 p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
518 UINT16_MAX);
519
520 if (GNUNET_OK !=
521 GNUNET_DNSPARSER_pack (&p,
522 UINT16_MAX,
523 &raw,
524 &raw_size))
525 {
526 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
527 "Failed to pack query for hostname `%s'\n",
528 hostname);
529 return;
530 }
531
532 req = GNUNET_new (struct Request);
533 req->hostname = strdup (hostname);
534 req->raw = raw;
535 req->raw_len = raw_size;
536 req->id = p.id;
537 GNUNET_CONTAINER_DLL_insert_tail (req_head,
538 req_tail,
539 req);
540}
541
542
543/**
544 * Call with IP address of resolver to query.
545 *
546 * @param argc should be 2
547 * @param argv[1] should contain IP address
548 * @return 0 on success
549 */
550int
551main (int argc,
552 char **argv)
553{
554 char hn[256];
555
556 if (2 != argc)
557 {
558 fprintf (stderr,
559 "Missing required configuration argument\n");
560 return -1;
561 }
562 ctx = GNUNET_DNSSTUB_start (256);
563 if (NULL == ctx)
564 {
565 fprintf (stderr,
566 "Failed to initialize GNUnet DNS STUB\n");
567 return 1;
568 }
569 if (GNUNET_OK !=
570 GNUNET_DNSSTUB_add_dns_ip (ctx,
571 argv[1]))
572 {
573 fprintf (stderr,
574 "Failed to use `%s' for DNS resolver\n",
575 argv[1]);
576 return 1;
577 }
578
579 while (NULL !=
580 fgets (hn,
581 sizeof (hn),
582 stdin))
583 {
584 if (strlen(hn) > 0)
585 hn[strlen(hn)-1] = '\0'; /* eat newline */
586 queue (hn);
587 }
588 GNUNET_SCHEDULER_run (&run,
589 NULL);
590 fprintf (stderr,
591 "Did %u lookups, found %u records, %u lookups failed, %u pending on shutdown\n",
592 lookups,
593 records,
594 failures,
595 pending);
596 return 0;
597}