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..b96d40ca7
--- /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 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
19/**
20 * @file src/dns/gnunet-zoneimport.c
21 * @brief import a DNS zone for analysis, brute force
22 * @author Christian Grothoff
23 */
24#include "platform.h"
25#include <gnunet_util_lib.h>
26#include <gnunet_dnsstub_lib.h>
27#include <gnunet_dnsparser_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 case GNUNET_DNSPARSER_TYPE_AAAA:
163 fprintf (stdout,
164 "%s AAAA %s\n",
165 req->hostname,
166 inet_ntop (AF_INET6,
167 rec->data.raw.data,
168 buf,
169 sizeof (buf)));
170 break;
171 case GNUNET_DNSPARSER_TYPE_NS:
172 fprintf (stdout,
173 "%s NS %s\n",
174 req->hostname,
175 rec->data.hostname);
176 break;
177 case GNUNET_DNSPARSER_TYPE_CNAME:
178 fprintf (stdout,
179 "%s CNAME %s\n",
180 req->hostname,
181 rec->data.hostname);
182 break;
183 case GNUNET_DNSPARSER_TYPE_MX:
184 fprintf (stdout,
185 "%s MX %u %s\n",
186 req->hostname,
187 (unsigned int) rec->data.mx->preference,
188 rec->data.mx->mxhost);
189 break;
190 case GNUNET_DNSPARSER_TYPE_SOA:
191 fprintf (stdout,
192 "%s SOA %s %s %u %u %u %u %u\n",
193 req->hostname,
194 rec->data.soa->mname,
195 rec->data.soa->rname,
196 (unsigned int) rec->data.soa->serial,
197 (unsigned int) rec->data.soa->refresh,
198 (unsigned int) rec->data.soa->retry,
199 (unsigned int) rec->data.soa->expire,
200 (unsigned int) rec->data.soa->minimum_ttl);
201 break;
202 case GNUNET_DNSPARSER_TYPE_SRV:
203 fprintf (stdout,
204 "%s SRV %s %u %u %u\n",
205 req->hostname,
206 rec->data.srv->target,
207 rec->data.srv->priority,
208 rec->data.srv->weight,
209 rec->data.srv->port);
210 break;
211 case GNUNET_DNSPARSER_TYPE_PTR:
212 fprintf (stdout,
213 "%s PTR %s\n",
214 req->hostname,
215 rec->data.hostname);
216 break;
217 case GNUNET_DNSPARSER_TYPE_TXT:
218 fprintf (stdout,
219 "%s TXT %.*s\n",
220 req->hostname,
221 (int) rec->data.raw.data_len,
222 (char *) rec->data.raw.data);
223 break;
224 case GNUNET_DNSPARSER_TYPE_DNAME:
225 fprintf (stdout,
226 "%s DNAME %s\n",
227 req->hostname,
228 rec->data.hostname);
229 break;
230
231 /* obscure records */
232 case GNUNET_DNSPARSER_TYPE_AFSDB:
233 case GNUNET_DNSPARSER_TYPE_NAPTR:
234 case GNUNET_DNSPARSER_TYPE_APL:
235 case GNUNET_DNSPARSER_TYPE_DHCID:
236 case GNUNET_DNSPARSER_TYPE_HIP:
237 case GNUNET_DNSPARSER_TYPE_LOC:
238 case GNUNET_DNSPARSER_TYPE_RP:
239 case GNUNET_DNSPARSER_TYPE_TKEY:
240 case GNUNET_DNSPARSER_TYPE_TSIG:
241 case GNUNET_DNSPARSER_TYPE_URI:
242 case GNUNET_DNSPARSER_TYPE_TA:
243
244 /* DNSSEC */
245 case GNUNET_DNSPARSER_TYPE_DS:
246 case GNUNET_DNSPARSER_TYPE_RRSIG:
247 case GNUNET_DNSPARSER_TYPE_NSEC:
248 case GNUNET_DNSPARSER_TYPE_DNSKEY:
249 case GNUNET_DNSPARSER_TYPE_NSEC3:
250 case GNUNET_DNSPARSER_TYPE_NSEC3PARAM:
251 case GNUNET_DNSPARSER_TYPE_CDS:
252 case GNUNET_DNSPARSER_TYPE_CDNSKEY:
253
254 /* DNSSEC payload */
255 case GNUNET_DNSPARSER_TYPE_CERT:
256 case GNUNET_DNSPARSER_TYPE_SSHFP:
257 case GNUNET_DNSPARSER_TYPE_IPSECKEY:
258 case GNUNET_DNSPARSER_TYPE_TLSA:
259 case GNUNET_DNSPARSER_TYPE_OPENPGPKEY:
260
261 /* obsolete records */
262 case GNUNET_DNSPARSER_TYPE_SIG:
263 case GNUNET_DNSPARSER_TYPE_KEY:
264 case GNUNET_DNSPARSER_TYPE_KX:
265 {
266 char *base32;
267
268 base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data,
269 rec->data.raw.data_len);
270 fprintf (stdout,
271 "%s (%u) %s\n",
272 req->hostname,
273 rec->type,
274 base32);
275 GNUNET_free (base32);
276 }
277 break;
278 default:
279 fprintf (stderr,
280 "Unsupported type %u\n",
281 (unsigned int) rec->type);
282 break;
283 }
284}
285
286
287/**
288 * Function called with the result of a DNS resolution.
289 *
290 * @param cls closure with the `struct Request`
291 * @param dns dns response, never NULL
292 * @param dns_len number of bytes in @a dns
293 */
294static void
295process_result (void *cls,
296 const struct GNUNET_TUN_DnsHeader *dns,
297 size_t dns_len)
298{
299 struct Request *req = cls;
300 struct GNUNET_DNSPARSER_Packet *p;
301
302 if (NULL == dns)
303 {
304 /* stub gave up */
305 pending--;
306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
307 "Stub gave up on DNS reply for `%s'\n",
308 req->hostname);
309 GNUNET_CONTAINER_DLL_remove (req_head,
310 req_tail,
311 req);
312 if (req->issue_num > MAX_RETRIES)
313 {
314 failures++;
315 GNUNET_free (req->hostname);
316 GNUNET_free (req->raw);
317 GNUNET_free (req);
318 return;
319 }
320 GNUNET_CONTAINER_DLL_insert_tail (req_head,
321 req_tail,
322 req);
323 req->rs = NULL;
324 return;
325 }
326 if (req->id != dns->id)
327 return;
328 pending--;
329 GNUNET_DNSSTUB_resolve_cancel (req->rs);
330 req->rs = NULL;
331 GNUNET_CONTAINER_DLL_remove (req_head,
332 req_tail,
333 req);
334 p = GNUNET_DNSPARSER_parse ((const char *) dns,
335 dns_len);
336 if (NULL == p)
337 {
338 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
339 "Failed to parse DNS reply for `%s'\n",
340 req->hostname);
341 if (req->issue_num > MAX_RETRIES)
342 {
343 failures++;
344 GNUNET_free (req->hostname);
345 GNUNET_free (req->raw);
346 GNUNET_free (req);
347 return;
348 }
349 GNUNET_CONTAINER_DLL_insert_tail (req_head,
350 req_tail,
351 req);
352 return;
353 }
354 for (unsigned int i=0;i<p->num_answers;i++)
355 {
356 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
357
358 process_record (req,
359 rs);
360 }
361 for (unsigned int i=0;i<p->num_authority_records;i++)
362 {
363 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
364
365 process_record (req,
366 rs);
367 }
368 for (unsigned int i=0;i<p->num_additional_records;i++)
369 {
370 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
371
372 process_record (req,
373 rs);
374 }
375 GNUNET_DNSPARSER_free_packet (p);
376 GNUNET_free (req->hostname);
377 GNUNET_free (req->raw);
378 GNUNET_free (req);
379}
380
381
382/**
383 * Submit a request to DNS unless we need to slow down because
384 * we are at the rate limit.
385 *
386 * @param req request to submit
387 * @return #GNUNET_OK if request was submitted
388 * #GNUNET_NO if request was already submitted
389 * #GNUNET_SYSERR if we are at the rate limit
390 */
391static int
392submit_req (struct Request *req)
393{
394 static struct timeval last_request;
395 struct timeval now;
396
397 if (NULL != req->rs)
398 return GNUNET_NO; /* already submitted */
399 gettimeofday (&now,
400 NULL);
401 if ( ( ( (now.tv_sec - last_request.tv_sec) == 0) &&
402 ( (now.tv_usec - last_request.tv_usec) < TIME_THRESH) ) ||
403 (pending >= THRESH) )
404 return GNUNET_SYSERR;
405 GNUNET_assert (NULL == req->rs);
406 req->rs = GNUNET_DNSSTUB_resolve (ctx,
407 req->raw,
408 req->raw_len,
409 &process_result,
410 req);
411 GNUNET_assert (NULL != req->rs);
412 req->issue_num++;
413 last_request = now;
414 lookups++;
415 pending++;
416 req->time = time (NULL);
417 return GNUNET_OK;
418}
419
420
421/**
422 * Process as many requests as possible from the queue.
423 *
424 * @param cls NULL
425 */
426static void
427process_queue(void *cls)
428{
429 (void) cls;
430 t = NULL;
431 for (struct Request *req = req_head;
432 NULL != req;
433 req = req->next)
434 {
435 if (GNUNET_SYSERR == submit_req (req))
436 break;
437 }
438 if (NULL != req_head)
439 t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
440 &process_queue,
441 NULL);
442 else
443 GNUNET_SCHEDULER_shutdown ();
444}
445
446
447/**
448 * Clean up and terminate the process.
449 *
450 * @param cls NULL
451 */
452static void
453do_shutdown (void *cls)
454{
455 (void) cls;
456 if (NULL != t)
457 {
458 GNUNET_SCHEDULER_cancel (t);
459 t = NULL;
460 }
461 GNUNET_DNSSTUB_stop (ctx);
462 ctx = NULL;
463}
464
465
466/**
467 * Process requests from the queue, then if the queue is
468 * not empty, try again.
469 *
470 * @param cls NULL
471 */
472static void
473run (void *cls)
474{
475 (void) cls;
476
477 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
478 NULL);
479 t = GNUNET_SCHEDULER_add_now (&process_queue,
480 NULL);
481}
482
483
484/**
485 * Add @a hostname to the list of requests to be made.
486 *
487 * @param hostname name to resolve
488 */
489static void
490queue (const char *hostname)
491{
492 struct GNUNET_DNSPARSER_Packet p;
493 struct GNUNET_DNSPARSER_Query q;
494 struct Request *req;
495 char *raw;
496 size_t raw_size;
497 int ret;
498
499 if (GNUNET_OK !=
500 GNUNET_DNSPARSER_check_name (hostname))
501 {
502 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
503 "Refusing invalid hostname `%s'\n",
504 hostname);
505 return;
506 }
507 q.name = (char *) hostname;
508 q.type = GNUNET_DNSPARSER_TYPE_NS;
509 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
510
511 memset (&p,
512 0,
513 sizeof (p));
514 p.num_queries = 1;
515 p.queries = &q;
516 p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
517 UINT16_MAX);
518 ret = GNUNET_DNSPARSER_pack (&p,
519 UINT16_MAX,
520 &raw,
521 &raw_size);
522 if (GNUNET_OK != ret)
523 {
524 if (GNUNET_NO == ret)
525 GNUNET_free (raw);
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}