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