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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file src/dns/gnunet-zoneimport.c
23 * @brief import a DNS zone for analysis, brute force
24 * @author Christian Grothoff
27 #include <gnunet_util_lib.h>
28 #include <gnunet_dnsstub_lib.h>
29 #include <gnunet_dnsparser_lib.h>
32 * Request we should make.
37 * Requests are kept in a DLL.
42 * Requests are kept in a DLL.
47 * Socket used to make the request, NULL if not active.
49 struct GNUNET_DNSSTUB_RequestSocket *rs;
57 * Number of bytes in @e raw.
62 * Hostname we are resolving.
67 * When did we last issue this request?
72 * How often did we issue this query?
77 * random 16-bit DNS query identifier.
84 * Context for DNS resolution.
86 static struct GNUNET_DNSSTUB_Context *ctx;
89 * The number of queries that are outstanding
91 static unsigned int pending;
94 * Number of lookups we performed overall.
96 static unsigned int lookups;
99 * Number of lookups that failed.
101 static unsigned int failures;
104 * Number of records we found.
106 static unsigned int records;
109 * Head of DLL of all requests to perform.
111 static struct Request *req_head;
114 * Tail of DLL of all requests to perform.
116 static struct Request *req_tail;
121 static struct GNUNET_SCHEDULER_Task *t;
124 * Maximum number of queries pending at the same time.
129 * TIME_THRESH is in usecs. How quickly do we submit fresh queries.
130 * Used as an additional throttle.
132 #define TIME_THRESH 10
135 * How often do we retry a query before giving up for good?
137 #define MAX_RETRIES 5
141 * We received @a rec for @a req. Remember the answer.
144 * @param rec response
147 process_record (struct Request *req,
148 struct GNUNET_DNSPARSER_Record *rec)
150 char buf[INET6_ADDRSTRLEN];
155 case GNUNET_DNSPARSER_TYPE_A:
164 case GNUNET_DNSPARSER_TYPE_AAAA:
173 case GNUNET_DNSPARSER_TYPE_NS:
179 case GNUNET_DNSPARSER_TYPE_CNAME:
185 case GNUNET_DNSPARSER_TYPE_MX:
189 (unsigned int) rec->data.mx->preference,
190 rec->data.mx->mxhost);
192 case GNUNET_DNSPARSER_TYPE_SOA:
194 "%s SOA %s %s %u %u %u %u %u\n",
196 rec->data.soa->mname,
197 rec->data.soa->rname,
198 (unsigned int) rec->data.soa->serial,
199 (unsigned int) rec->data.soa->refresh,
200 (unsigned int) rec->data.soa->retry,
201 (unsigned int) rec->data.soa->expire,
202 (unsigned int) rec->data.soa->minimum_ttl);
204 case GNUNET_DNSPARSER_TYPE_SRV:
206 "%s SRV %s %u %u %u\n",
208 rec->data.srv->target,
209 rec->data.srv->priority,
210 rec->data.srv->weight,
211 rec->data.srv->port);
213 case GNUNET_DNSPARSER_TYPE_PTR:
219 case GNUNET_DNSPARSER_TYPE_TXT:
223 (int) rec->data.raw.data_len,
224 (char *) rec->data.raw.data);
226 case GNUNET_DNSPARSER_TYPE_DNAME:
233 /* obscure records */
234 case GNUNET_DNSPARSER_TYPE_AFSDB:
235 case GNUNET_DNSPARSER_TYPE_NAPTR:
236 case GNUNET_DNSPARSER_TYPE_APL:
237 case GNUNET_DNSPARSER_TYPE_DHCID:
238 case GNUNET_DNSPARSER_TYPE_HIP:
239 case GNUNET_DNSPARSER_TYPE_LOC:
240 case GNUNET_DNSPARSER_TYPE_RP:
241 case GNUNET_DNSPARSER_TYPE_TKEY:
242 case GNUNET_DNSPARSER_TYPE_TSIG:
243 case GNUNET_DNSPARSER_TYPE_URI:
244 case GNUNET_DNSPARSER_TYPE_TA:
247 case GNUNET_DNSPARSER_TYPE_DS:
248 case GNUNET_DNSPARSER_TYPE_RRSIG:
249 case GNUNET_DNSPARSER_TYPE_NSEC:
250 case GNUNET_DNSPARSER_TYPE_DNSKEY:
251 case GNUNET_DNSPARSER_TYPE_NSEC3:
252 case GNUNET_DNSPARSER_TYPE_NSEC3PARAM:
253 case GNUNET_DNSPARSER_TYPE_CDS:
254 case GNUNET_DNSPARSER_TYPE_CDNSKEY:
257 case GNUNET_DNSPARSER_TYPE_CERT:
258 case GNUNET_DNSPARSER_TYPE_SSHFP:
259 case GNUNET_DNSPARSER_TYPE_IPSECKEY:
260 case GNUNET_DNSPARSER_TYPE_TLSA:
261 case GNUNET_DNSPARSER_TYPE_OPENPGPKEY:
263 /* obsolete records */
264 case GNUNET_DNSPARSER_TYPE_SIG:
265 case GNUNET_DNSPARSER_TYPE_KEY:
266 case GNUNET_DNSPARSER_TYPE_KX:
270 base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data,
271 rec->data.raw.data_len);
277 GNUNET_free (base32);
282 "Unsupported type %u\n",
283 (unsigned int) rec->type);
290 * Function called with the result of a DNS resolution.
292 * @param cls closure with the `struct Request`
293 * @param dns dns response, never NULL
294 * @param dns_len number of bytes in @a dns
297 process_result (void *cls,
298 const struct GNUNET_TUN_DnsHeader *dns,
301 struct Request *req = cls;
302 struct GNUNET_DNSPARSER_Packet *p;
308 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
309 "Stub gave up on DNS reply for `%s'\n",
311 GNUNET_CONTAINER_DLL_remove (req_head,
314 if (req->issue_num > MAX_RETRIES)
317 GNUNET_free (req->hostname);
318 GNUNET_free (req->raw);
322 GNUNET_CONTAINER_DLL_insert_tail (req_head,
328 if (req->id != dns->id)
331 GNUNET_DNSSTUB_resolve_cancel (req->rs);
333 GNUNET_CONTAINER_DLL_remove (req_head,
336 p = GNUNET_DNSPARSER_parse ((const char *) dns,
340 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
341 "Failed to parse DNS reply for `%s'\n",
343 if (req->issue_num > MAX_RETRIES)
346 GNUNET_free (req->hostname);
347 GNUNET_free (req->raw);
351 GNUNET_CONTAINER_DLL_insert_tail (req_head,
356 for (unsigned int i=0;i<p->num_answers;i++)
358 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
363 for (unsigned int i=0;i<p->num_authority_records;i++)
365 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
370 for (unsigned int i=0;i<p->num_additional_records;i++)
372 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
377 GNUNET_DNSPARSER_free_packet (p);
378 GNUNET_free (req->hostname);
379 GNUNET_free (req->raw);
385 * Submit a request to DNS unless we need to slow down because
386 * we are at the rate limit.
388 * @param req request to submit
389 * @return #GNUNET_OK if request was submitted
390 * #GNUNET_NO if request was already submitted
391 * #GNUNET_SYSERR if we are at the rate limit
394 submit_req (struct Request *req)
396 static struct timeval last_request;
400 return GNUNET_NO; /* already submitted */
403 if ( ( ( (now.tv_sec - last_request.tv_sec) == 0) &&
404 ( (now.tv_usec - last_request.tv_usec) < TIME_THRESH) ) ||
405 (pending >= THRESH) )
406 return GNUNET_SYSERR;
407 GNUNET_assert (NULL == req->rs);
408 req->rs = GNUNET_DNSSTUB_resolve (ctx,
413 GNUNET_assert (NULL != req->rs);
418 req->time = time (NULL);
424 * Process as many requests as possible from the queue.
429 process_queue(void *cls)
433 for (struct Request *req = req_head;
437 if (GNUNET_SYSERR == submit_req (req))
440 if (NULL != req_head)
441 t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
445 GNUNET_SCHEDULER_shutdown ();
450 * Clean up and terminate the process.
455 do_shutdown (void *cls)
460 GNUNET_SCHEDULER_cancel (t);
463 GNUNET_DNSSTUB_stop (ctx);
469 * Process requests from the queue, then if the queue is
470 * not empty, try again.
479 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
481 t = GNUNET_SCHEDULER_add_now (&process_queue,
487 * Add @a hostname to the list of requests to be made.
489 * @param hostname name to resolve
492 queue (const char *hostname)
494 struct GNUNET_DNSPARSER_Packet p;
495 struct GNUNET_DNSPARSER_Query q;
502 GNUNET_DNSPARSER_check_name (hostname))
504 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
505 "Refusing invalid hostname `%s'\n",
509 q.name = (char *) hostname;
510 q.type = GNUNET_DNSPARSER_TYPE_NS;
511 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
518 p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
520 ret = GNUNET_DNSPARSER_pack (&p,
524 if (GNUNET_OK != ret)
526 if (GNUNET_NO == ret)
528 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
529 "Failed to pack query for hostname `%s'\n",
534 req = GNUNET_new (struct Request);
535 req->hostname = strdup (hostname);
537 req->raw_len = raw_size;
539 GNUNET_CONTAINER_DLL_insert_tail (req_head,
546 * Call with IP address of resolver to query.
548 * @param argc should be 2
549 * @param argv[1] should contain IP address
550 * @return 0 on success
561 "Missing required configuration argument\n");
564 ctx = GNUNET_DNSSTUB_start (256);
568 "Failed to initialize GNUnet DNS STUB\n");
572 GNUNET_DNSSTUB_add_dns_ip (ctx,
576 "Failed to use `%s' for DNS resolver\n",
587 hn[strlen(hn)-1] = '\0'; /* eat newline */
590 GNUNET_SCHEDULER_run (&run,
593 "Did %u lookups, found %u records, %u lookups failed, %u pending on shutdown\n",