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