2 This file is part of GNUnet
3 Copyright (C) 2018 GNUnet e.V.
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.
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.
17 * @file src/dns/gnunet-zoneimport.c
18 * @brief import a DNS zone for analysis, brute force
19 * @author Christian Grothoff
22 #include <gnunet_util_lib.h>
23 #include <gnunet_dnsstub_lib.h>
24 #include <gnunet_dnsparser_lib.h>
27 * Request we should make.
32 * Requests are kept in a DLL.
37 * Requests are kept in a DLL.
42 * Socket used to make the request, NULL if not active.
44 struct GNUNET_DNSSTUB_RequestSocket *rs;
52 * Number of bytes in @e raw.
57 * Hostname we are resolving.
62 * When did we last issue this request?
67 * How often did we issue this query?
72 * random 16-bit DNS query identifier.
79 * Context for DNS resolution.
81 static struct GNUNET_DNSSTUB_Context *ctx;
84 * The number of queries that are outstanding
86 static unsigned int pending;
89 * Number of lookups we performed overall.
91 static unsigned int lookups;
94 * Number of lookups that failed.
96 static unsigned int failures;
99 * Number of records we found.
101 static unsigned int records;
104 * Head of DLL of all requests to perform.
106 static struct Request *req_head;
109 * Tail of DLL of all requests to perform.
111 static struct Request *req_tail;
116 static struct GNUNET_SCHEDULER_Task *t;
119 * Maximum number of queries pending at the same time.
124 * TIME_THRESH is in usecs. How quickly do we submit fresh queries.
125 * Used as an additional throttle.
127 #define TIME_THRESH 10
130 * How often do we retry a query before giving up for good?
132 #define MAX_RETRIES 5
136 * We received @a rec for @a req. Remember the answer.
139 * @param rec response
142 process_record (struct Request *req,
143 struct GNUNET_DNSPARSER_Record *rec)
145 char buf[INET6_ADDRSTRLEN];
150 case GNUNET_DNSPARSER_TYPE_A:
159 case GNUNET_DNSPARSER_TYPE_AAAA:
168 case GNUNET_DNSPARSER_TYPE_NS:
174 case GNUNET_DNSPARSER_TYPE_CNAME:
180 case GNUNET_DNSPARSER_TYPE_MX:
184 (unsigned int) rec->data.mx->preference,
185 rec->data.mx->mxhost);
187 case GNUNET_DNSPARSER_TYPE_SOA:
189 "%s SOA %s %s %u %u %u %u %u\n",
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);
199 case GNUNET_DNSPARSER_TYPE_SRV:
201 "%s SRV %s %u %u %u\n",
203 rec->data.srv->target,
204 rec->data.srv->priority,
205 rec->data.srv->weight,
206 rec->data.srv->port);
208 case GNUNET_DNSPARSER_TYPE_PTR:
214 case GNUNET_DNSPARSER_TYPE_TXT:
218 (int) rec->data.raw.data_len,
219 (char *) rec->data.raw.data);
221 case GNUNET_DNSPARSER_TYPE_DNAME:
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:
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:
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:
258 /* obsolete records */
259 case GNUNET_DNSPARSER_TYPE_SIG:
260 case GNUNET_DNSPARSER_TYPE_KEY:
261 case GNUNET_DNSPARSER_TYPE_KX:
265 base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data,
266 rec->data.raw.data_len);
272 GNUNET_free (base32);
277 "Unsupported type %u\n",
278 (unsigned int) rec->type);
285 * Function called with the result of a DNS resolution.
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
292 process_result (void *cls,
293 const struct GNUNET_TUN_DnsHeader *dns,
296 struct Request *req = cls;
297 struct GNUNET_DNSPARSER_Packet *p;
303 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
304 "Stub gave up on DNS reply for `%s'\n",
306 GNUNET_CONTAINER_DLL_remove (req_head,
309 if (req->issue_num > MAX_RETRIES)
312 GNUNET_free (req->hostname);
313 GNUNET_free (req->raw);
317 GNUNET_CONTAINER_DLL_insert_tail (req_head,
323 if (req->id != dns->id)
326 GNUNET_DNSSTUB_resolve_cancel (req->rs);
328 GNUNET_CONTAINER_DLL_remove (req_head,
331 p = GNUNET_DNSPARSER_parse ((const char *) dns,
335 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
336 "Failed to parse DNS reply for `%s'\n",
338 if (req->issue_num > MAX_RETRIES)
341 GNUNET_free (req->hostname);
342 GNUNET_free (req->raw);
346 GNUNET_CONTAINER_DLL_insert_tail (req_head,
351 for (unsigned int i=0;i<p->num_answers;i++)
353 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
358 for (unsigned int i=0;i<p->num_authority_records;i++)
360 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
365 for (unsigned int i=0;i<p->num_additional_records;i++)
367 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
372 GNUNET_DNSPARSER_free_packet (p);
373 GNUNET_free (req->hostname);
374 GNUNET_free (req->raw);
380 * Submit a request to DNS unless we need to slow down because
381 * we are at the rate limit.
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
389 submit_req (struct Request *req)
391 static struct timeval last_request;
395 return GNUNET_NO; /* already submitted */
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,
408 GNUNET_assert (NULL != req->rs);
413 req->time = time (NULL);
419 * Process as many requests as possible from the queue.
424 process_queue(void *cls)
428 for (struct Request *req = req_head;
432 if (GNUNET_SYSERR == submit_req (req))
435 if (NULL != req_head)
436 t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
440 GNUNET_SCHEDULER_shutdown ();
445 * Clean up and terminate the process.
450 do_shutdown (void *cls)
455 GNUNET_SCHEDULER_cancel (t);
458 GNUNET_DNSSTUB_stop (ctx);
464 * Process requests from the queue, then if the queue is
465 * not empty, try again.
474 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
476 t = GNUNET_SCHEDULER_add_now (&process_queue,
482 * Add @a hostname to the list of requests to be made.
484 * @param hostname name to resolve
487 queue (const char *hostname)
489 struct GNUNET_DNSPARSER_Packet p;
490 struct GNUNET_DNSPARSER_Query q;
496 GNUNET_DNSPARSER_check_name (hostname))
498 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
499 "Refusing invalid hostname `%s'\n",
503 q.name = (char *) hostname;
504 q.type = GNUNET_DNSPARSER_TYPE_NS;
505 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
512 p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
516 GNUNET_DNSPARSER_pack (&p,
521 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
522 "Failed to pack query for hostname `%s'\n",
527 req = GNUNET_new (struct Request);
528 req->hostname = strdup (hostname);
530 req->raw_len = raw_size;
532 GNUNET_CONTAINER_DLL_insert_tail (req_head,
539 * Call with IP address of resolver to query.
541 * @param argc should be 2
542 * @param argv[1] should contain IP address
543 * @return 0 on success
554 "Missing required configuration argument\n");
557 ctx = GNUNET_DNSSTUB_start (256);
561 "Failed to initialize GNUnet DNS STUB\n");
565 GNUNET_DNSSTUB_add_dns_ip (ctx,
569 "Failed to use `%s' for DNS resolver\n",
580 hn[strlen(hn)-1] = '\0'; /* eat newline */
583 GNUNET_SCHEDULER_run (&run,
586 "Did %u lookups, found %u records, %u lookups failed, %u pending on shutdown\n",