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