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:
165 case GNUNET_DNSPARSER_TYPE_AAAA:
175 case GNUNET_DNSPARSER_TYPE_NS:
182 case GNUNET_DNSPARSER_TYPE_CNAME:
189 case GNUNET_DNSPARSER_TYPE_MX:
193 (unsigned int) rec->data.mx->preference,
194 rec->data.mx->mxhost);
197 case GNUNET_DNSPARSER_TYPE_SOA:
199 "%s SOA %s %s %u %u %u %u %u\n",
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);
210 case GNUNET_DNSPARSER_TYPE_SRV:
212 "%s SRV %s %u %u %u\n",
214 rec->data.srv->target,
215 rec->data.srv->priority,
216 rec->data.srv->weight,
217 rec->data.srv->port);
220 case GNUNET_DNSPARSER_TYPE_PTR:
227 case GNUNET_DNSPARSER_TYPE_TXT:
231 (int) rec->data.raw.data_len,
232 (char *) rec->data.raw.data);
235 case GNUNET_DNSPARSER_TYPE_DNAME:
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:
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:
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:
272 /* obsolete records */
273 case GNUNET_DNSPARSER_TYPE_SIG:
274 case GNUNET_DNSPARSER_TYPE_KEY:
275 case GNUNET_DNSPARSER_TYPE_KX:
279 base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data,
280 rec->data.raw.data_len);
286 GNUNET_free (base32);
292 "Unsupported type %u\n",
293 (unsigned int) rec->type);
300 * Function called with the result of a DNS resolution.
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
307 process_result (void *cls,
308 const struct GNUNET_TUN_DnsHeader *dns,
311 struct Request *req = cls;
312 struct GNUNET_DNSPARSER_Packet *p;
318 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
319 "Stub gave up on DNS reply for `%s'\n",
321 GNUNET_CONTAINER_DLL_remove (req_head,
324 if (req->issue_num > MAX_RETRIES)
327 GNUNET_free (req->hostname);
328 GNUNET_free (req->raw);
332 GNUNET_CONTAINER_DLL_insert_tail (req_head,
338 if (req->id != dns->id)
341 GNUNET_DNSSTUB_resolve_cancel (req->rs);
343 GNUNET_CONTAINER_DLL_remove (req_head,
346 p = GNUNET_DNSPARSER_parse ((const char *) dns,
350 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
351 "Failed to parse DNS reply for `%s'\n",
353 if (req->issue_num > MAX_RETRIES)
356 GNUNET_free (req->hostname);
357 GNUNET_free (req->raw);
361 GNUNET_CONTAINER_DLL_insert_tail (req_head,
366 for (unsigned int i = 0; i < p->num_answers; i++)
368 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
373 for (unsigned int i = 0; i < p->num_authority_records; i++)
375 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
380 for (unsigned int i = 0; i < p->num_additional_records; i++)
382 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
387 GNUNET_DNSPARSER_free_packet (p);
388 GNUNET_free (req->hostname);
389 GNUNET_free (req->raw);
395 * Submit a request to DNS unless we need to slow down because
396 * we are at the rate limit.
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
404 submit_req (struct Request *req)
406 static struct timeval last_request;
410 return GNUNET_NO; /* already submitted */
413 if ((((now.tv_sec - last_request.tv_sec) == 0) &&
414 ((now.tv_usec - last_request.tv_usec) < TIME_THRESH)) ||
416 return GNUNET_SYSERR;
417 GNUNET_assert (NULL == req->rs);
418 req->rs = GNUNET_DNSSTUB_resolve (ctx,
423 GNUNET_assert (NULL != req->rs);
428 req->time = time (NULL);
434 * Process as many requests as possible from the queue.
439 process_queue (void *cls)
443 for (struct Request *req = req_head;
447 if (GNUNET_SYSERR == submit_req (req))
450 if (NULL != req_head)
451 t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
455 GNUNET_SCHEDULER_shutdown ();
460 * Clean up and terminate the process.
465 do_shutdown (void *cls)
470 GNUNET_SCHEDULER_cancel (t);
473 GNUNET_DNSSTUB_stop (ctx);
479 * Process requests from the queue, then if the queue is
480 * not empty, try again.
489 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
491 t = GNUNET_SCHEDULER_add_now (&process_queue,
497 * Add @a hostname to the list of requests to be made.
499 * @param hostname name to resolve
502 queue (const char *hostname)
504 struct GNUNET_DNSPARSER_Packet p;
505 struct GNUNET_DNSPARSER_Query q;
512 GNUNET_DNSPARSER_check_name (hostname))
514 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
515 "Refusing invalid hostname `%s'\n",
519 q.name = (char *) hostname;
520 q.type = GNUNET_DNSPARSER_TYPE_NS;
521 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
528 p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
530 ret = GNUNET_DNSPARSER_pack (&p,
534 if (GNUNET_OK != ret)
536 if (GNUNET_NO == ret)
538 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
539 "Failed to pack query for hostname `%s'\n",
544 req = GNUNET_new (struct Request);
545 req->hostname = strdup (hostname);
547 req->raw_len = raw_size;
549 GNUNET_CONTAINER_DLL_insert_tail (req_head,
556 * Call with IP address of resolver to query.
558 * @param argc should be 2
559 * @param argv[1] should contain IP address
560 * @return 0 on success
571 "Missing required configuration argument\n");
574 ctx = GNUNET_DNSSTUB_start (256);
578 "Failed to initialize GNUnet DNS STUB\n");
582 GNUNET_DNSSTUB_add_dns_ip (ctx,
586 "Failed to use `%s' for DNS resolver\n",
597 hn[strlen (hn) - 1] = '\0'; /* eat newline */
600 GNUNET_SCHEDULER_run (&run,
603 "Did %u lookups, found %u records, %u lookups failed, %u pending on shutdown\n",