From: Christian Grothoff Date: Sun, 13 May 2018 14:20:47 +0000 (+0200) Subject: rename gnunet-zoneimport to gnunet-zonewalk to avoid sharing the name X-Git-Tag: v0.11.0pre66~59^2~12 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=334b50de9fc2e492e5801c43b42074d8e2e65259;p=oweals%2Fgnunet.git rename gnunet-zoneimport to gnunet-zonewalk to avoid sharing the name --- diff --git a/src/dns/Makefile.am b/src/dns/Makefile.am index 8e5b06043..9a4ecdcfd 100644 --- a/src/dns/Makefile.am +++ b/src/dns/Makefile.am @@ -37,7 +37,7 @@ libexec_PROGRAMS = \ noinst_PROGRAMS = \ gnunet-dns-monitor \ gnunet-dns-redirector \ - gnunet-zoneimport + gnunet-zonewalk plugin_LTLIBRARIES = \ libgnunet_plugin_block_dns.la @@ -62,9 +62,9 @@ gnunet_dns_monitor_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(GN_LIBINTL) -gnunet_zoneimport_SOURCES = \ - gnunet-zoneimport.c -gnunet_zoneimport_LDADD = \ +gnunet_zonewalk_SOURCES = \ + gnunet-zonewalk.c +gnunet_zonewalk_LDADD = \ libgnunetdnsparser.la \ libgnunetdnsstub.la \ $(top_builddir)/src/util/libgnunetutil.la \ diff --git a/src/dns/gnunet-zoneimport.c b/src/dns/gnunet-zoneimport.c deleted file mode 100644 index 860672e7a..000000000 --- a/src/dns/gnunet-zoneimport.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2018 GNUnet e.V. - - GNUnet is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file src/dns/gnunet-zoneimport.c - * @brief import a DNS zone for analysis, brute force - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include - -/** - * Request we should make. - */ -struct Request -{ - /** - * Requests are kept in a DLL. - */ - struct Request *next; - - /** - * Requests are kept in a DLL. - */ - struct Request *prev; - - /** - * Socket used to make the request, NULL if not active. - */ - struct GNUNET_DNSSTUB_RequestSocket *rs; - - /** - * Raw DNS query. - */ - void *raw; - - /** - * Number of bytes in @e raw. - */ - size_t raw_len; - - /** - * Hostname we are resolving. - */ - char *hostname; - - /** - * When did we last issue this request? - */ - time_t time; - - /** - * How often did we issue this query? - */ - int issue_num; - - /** - * random 16-bit DNS query identifier. - */ - uint16_t id; -}; - - -/** - * Context for DNS resolution. - */ -static struct GNUNET_DNSSTUB_Context *ctx; - -/** - * The number of queries that are outstanding - */ -static unsigned int pending; - -/** - * Number of lookups we performed overall. - */ -static unsigned int lookups; - -/** - * Number of lookups that failed. - */ -static unsigned int failures; - -/** - * Number of records we found. - */ -static unsigned int records; - -/** - * Head of DLL of all requests to perform. - */ -static struct Request *req_head; - -/** - * Tail of DLL of all requests to perform. - */ -static struct Request *req_tail; - -/** - * Main task. - */ -static struct GNUNET_SCHEDULER_Task *t; - -/** - * Maximum number of queries pending at the same time. - */ -#define THRESH 20 - -/** - * TIME_THRESH is in usecs. How quickly do we submit fresh queries. - * Used as an additional throttle. - */ -#define TIME_THRESH 10 - -/** - * How often do we retry a query before giving up for good? - */ -#define MAX_RETRIES 5 - - -/** - * We received @a rec for @a req. Remember the answer. - * - * @param req request - * @param rec response - */ -static void -process_record (struct Request *req, - struct GNUNET_DNSPARSER_Record *rec) -{ - char buf[INET6_ADDRSTRLEN]; - - records++; - switch (rec->type) - { - case GNUNET_DNSPARSER_TYPE_A: - fprintf (stdout, - "%s A %s\n", - req->hostname, - inet_ntop (AF_INET, - rec->data.raw.data, - buf, - sizeof (buf))); - break; - case GNUNET_DNSPARSER_TYPE_AAAA: - fprintf (stdout, - "%s AAAA %s\n", - req->hostname, - inet_ntop (AF_INET6, - rec->data.raw.data, - buf, - sizeof (buf))); - break; - case GNUNET_DNSPARSER_TYPE_NS: - fprintf (stdout, - "%s NS %s\n", - req->hostname, - rec->data.hostname); - break; - case GNUNET_DNSPARSER_TYPE_CNAME: - fprintf (stdout, - "%s CNAME %s\n", - req->hostname, - rec->data.hostname); - break; - case GNUNET_DNSPARSER_TYPE_MX: - fprintf (stdout, - "%s MX %u %s\n", - req->hostname, - (unsigned int) rec->data.mx->preference, - rec->data.mx->mxhost); - break; - case GNUNET_DNSPARSER_TYPE_SOA: - fprintf (stdout, - "%s SOA %s %s %u %u %u %u %u\n", - req->hostname, - rec->data.soa->mname, - rec->data.soa->rname, - (unsigned int) rec->data.soa->serial, - (unsigned int) rec->data.soa->refresh, - (unsigned int) rec->data.soa->retry, - (unsigned int) rec->data.soa->expire, - (unsigned int) rec->data.soa->minimum_ttl); - break; - case GNUNET_DNSPARSER_TYPE_SRV: - fprintf (stdout, - "%s SRV %s %u %u %u\n", - req->hostname, - rec->data.srv->target, - rec->data.srv->priority, - rec->data.srv->weight, - rec->data.srv->port); - break; - case GNUNET_DNSPARSER_TYPE_PTR: - fprintf (stdout, - "%s PTR %s\n", - req->hostname, - rec->data.hostname); - break; - case GNUNET_DNSPARSER_TYPE_TXT: - fprintf (stdout, - "%s TXT %.*s\n", - req->hostname, - (int) rec->data.raw.data_len, - (char *) rec->data.raw.data); - break; - case GNUNET_DNSPARSER_TYPE_DNAME: - fprintf (stdout, - "%s DNAME %s\n", - req->hostname, - rec->data.hostname); - break; - - /* obscure records */ - case GNUNET_DNSPARSER_TYPE_AFSDB: - case GNUNET_DNSPARSER_TYPE_NAPTR: - case GNUNET_DNSPARSER_TYPE_APL: - case GNUNET_DNSPARSER_TYPE_DHCID: - case GNUNET_DNSPARSER_TYPE_HIP: - case GNUNET_DNSPARSER_TYPE_LOC: - case GNUNET_DNSPARSER_TYPE_RP: - case GNUNET_DNSPARSER_TYPE_TKEY: - case GNUNET_DNSPARSER_TYPE_TSIG: - case GNUNET_DNSPARSER_TYPE_URI: - case GNUNET_DNSPARSER_TYPE_TA: - - /* DNSSEC */ - case GNUNET_DNSPARSER_TYPE_DS: - case GNUNET_DNSPARSER_TYPE_RRSIG: - case GNUNET_DNSPARSER_TYPE_NSEC: - case GNUNET_DNSPARSER_TYPE_DNSKEY: - case GNUNET_DNSPARSER_TYPE_NSEC3: - case GNUNET_DNSPARSER_TYPE_NSEC3PARAM: - case GNUNET_DNSPARSER_TYPE_CDS: - case GNUNET_DNSPARSER_TYPE_CDNSKEY: - - /* DNSSEC payload */ - case GNUNET_DNSPARSER_TYPE_CERT: - case GNUNET_DNSPARSER_TYPE_SSHFP: - case GNUNET_DNSPARSER_TYPE_IPSECKEY: - case GNUNET_DNSPARSER_TYPE_TLSA: - case GNUNET_DNSPARSER_TYPE_OPENPGPKEY: - - /* obsolete records */ - case GNUNET_DNSPARSER_TYPE_SIG: - case GNUNET_DNSPARSER_TYPE_KEY: - case GNUNET_DNSPARSER_TYPE_KX: - { - char *base32; - - base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data, - rec->data.raw.data_len); - fprintf (stdout, - "%s (%u) %s\n", - req->hostname, - rec->type, - base32); - GNUNET_free (base32); - } - break; - default: - fprintf (stderr, - "Unsupported type %u\n", - (unsigned int) rec->type); - break; - } -} - - -/** - * Function called with the result of a DNS resolution. - * - * @param cls closure with the `struct Request` - * @param dns dns response, never NULL - * @param dns_len number of bytes in @a dns - */ -static void -process_result (void *cls, - const struct GNUNET_TUN_DnsHeader *dns, - size_t dns_len) -{ - struct Request *req = cls; - struct GNUNET_DNSPARSER_Packet *p; - - if (NULL == dns) - { - /* stub gave up */ - pending--; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Stub gave up on DNS reply for `%s'\n", - req->hostname); - GNUNET_CONTAINER_DLL_remove (req_head, - req_tail, - req); - if (req->issue_num > MAX_RETRIES) - { - failures++; - GNUNET_free (req->hostname); - GNUNET_free (req->raw); - GNUNET_free (req); - return; - } - GNUNET_CONTAINER_DLL_insert_tail (req_head, - req_tail, - req); - req->rs = NULL; - return; - } - if (req->id != dns->id) - return; - pending--; - GNUNET_DNSSTUB_resolve_cancel (req->rs); - req->rs = NULL; - GNUNET_CONTAINER_DLL_remove (req_head, - req_tail, - req); - p = GNUNET_DNSPARSER_parse ((const char *) dns, - dns_len); - if (NULL == p) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse DNS reply for `%s'\n", - req->hostname); - if (req->issue_num > MAX_RETRIES) - { - failures++; - GNUNET_free (req->hostname); - GNUNET_free (req->raw); - GNUNET_free (req); - return; - } - GNUNET_CONTAINER_DLL_insert_tail (req_head, - req_tail, - req); - return; - } - for (unsigned int i=0;inum_answers;i++) - { - struct GNUNET_DNSPARSER_Record *rs = &p->answers[i]; - - process_record (req, - rs); - } - for (unsigned int i=0;inum_authority_records;i++) - { - struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i]; - - process_record (req, - rs); - } - for (unsigned int i=0;inum_additional_records;i++) - { - struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i]; - - process_record (req, - rs); - } - GNUNET_DNSPARSER_free_packet (p); - GNUNET_free (req->hostname); - GNUNET_free (req->raw); - GNUNET_free (req); -} - - -/** - * Submit a request to DNS unless we need to slow down because - * we are at the rate limit. - * - * @param req request to submit - * @return #GNUNET_OK if request was submitted - * #GNUNET_NO if request was already submitted - * #GNUNET_SYSERR if we are at the rate limit - */ -static int -submit_req (struct Request *req) -{ - static struct timeval last_request; - struct timeval now; - - if (NULL != req->rs) - return GNUNET_NO; /* already submitted */ - gettimeofday (&now, - NULL); - if ( ( ( (now.tv_sec - last_request.tv_sec) == 0) && - ( (now.tv_usec - last_request.tv_usec) < TIME_THRESH) ) || - (pending >= THRESH) ) - return GNUNET_SYSERR; - GNUNET_assert (NULL == req->rs); - req->rs = GNUNET_DNSSTUB_resolve (ctx, - req->raw, - req->raw_len, - &process_result, - req); - GNUNET_assert (NULL != req->rs); - req->issue_num++; - last_request = now; - lookups++; - pending++; - req->time = time (NULL); - return GNUNET_OK; -} - - -/** - * Process as many requests as possible from the queue. - * - * @param cls NULL - */ -static void -process_queue(void *cls) -{ - (void) cls; - t = NULL; - for (struct Request *req = req_head; - NULL != req; - req = req->next) - { - if (GNUNET_SYSERR == submit_req (req)) - break; - } - if (NULL != req_head) - t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, - &process_queue, - NULL); - else - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Clean up and terminate the process. - * - * @param cls NULL - */ -static void -do_shutdown (void *cls) -{ - (void) cls; - if (NULL != t) - { - GNUNET_SCHEDULER_cancel (t); - t = NULL; - } - GNUNET_DNSSTUB_stop (ctx); - ctx = NULL; -} - - -/** - * Process requests from the queue, then if the queue is - * not empty, try again. - * - * @param cls NULL - */ -static void -run (void *cls) -{ - (void) cls; - - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - NULL); - t = GNUNET_SCHEDULER_add_now (&process_queue, - NULL); -} - - -/** - * Add @a hostname to the list of requests to be made. - * - * @param hostname name to resolve - */ -static void -queue (const char *hostname) -{ - struct GNUNET_DNSPARSER_Packet p; - struct GNUNET_DNSPARSER_Query q; - struct Request *req; - char *raw; - size_t raw_size; - - if (GNUNET_OK != - GNUNET_DNSPARSER_check_name (hostname)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Refusing invalid hostname `%s'\n", - hostname); - return; - } - q.name = (char *) hostname; - q.type = GNUNET_DNSPARSER_TYPE_NS; - q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET; - - memset (&p, - 0, - sizeof (p)); - p.num_queries = 1; - p.queries = &q; - p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, - UINT16_MAX); - - if (GNUNET_OK != - GNUNET_DNSPARSER_pack (&p, - UINT16_MAX, - &raw, - &raw_size)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to pack query for hostname `%s'\n", - hostname); - return; - } - - req = GNUNET_new (struct Request); - req->hostname = strdup (hostname); - req->raw = raw; - req->raw_len = raw_size; - req->id = p.id; - GNUNET_CONTAINER_DLL_insert_tail (req_head, - req_tail, - req); -} - - -/** - * Call with IP address of resolver to query. - * - * @param argc should be 2 - * @param argv[1] should contain IP address - * @return 0 on success - */ -int -main (int argc, - char **argv) -{ - char hn[256]; - - if (2 != argc) - { - fprintf (stderr, - "Missing required configuration argument\n"); - return -1; - } - ctx = GNUNET_DNSSTUB_start (256); - if (NULL == ctx) - { - fprintf (stderr, - "Failed to initialize GNUnet DNS STUB\n"); - return 1; - } - if (GNUNET_OK != - GNUNET_DNSSTUB_add_dns_ip (ctx, - argv[1])) - { - fprintf (stderr, - "Failed to use `%s' for DNS resolver\n", - argv[1]); - return 1; - } - - while (NULL != - fgets (hn, - sizeof (hn), - stdin)) - { - if (strlen(hn) > 0) - hn[strlen(hn)-1] = '\0'; /* eat newline */ - queue (hn); - } - GNUNET_SCHEDULER_run (&run, - NULL); - fprintf (stderr, - "Did %u lookups, found %u records, %u lookups failed, %u pending on shutdown\n", - lookups, - records, - failures, - pending); - return 0; -} diff --git a/src/dns/gnunet-zonewalk.c b/src/dns/gnunet-zonewalk.c new file mode 100644 index 000000000..860672e7a --- /dev/null +++ b/src/dns/gnunet-zonewalk.c @@ -0,0 +1,597 @@ +/* + This file is part of GNUnet + Copyright (C) 2018 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file src/dns/gnunet-zoneimport.c + * @brief import a DNS zone for analysis, brute force + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include + +/** + * Request we should make. + */ +struct Request +{ + /** + * Requests are kept in a DLL. + */ + struct Request *next; + + /** + * Requests are kept in a DLL. + */ + struct Request *prev; + + /** + * Socket used to make the request, NULL if not active. + */ + struct GNUNET_DNSSTUB_RequestSocket *rs; + + /** + * Raw DNS query. + */ + void *raw; + + /** + * Number of bytes in @e raw. + */ + size_t raw_len; + + /** + * Hostname we are resolving. + */ + char *hostname; + + /** + * When did we last issue this request? + */ + time_t time; + + /** + * How often did we issue this query? + */ + int issue_num; + + /** + * random 16-bit DNS query identifier. + */ + uint16_t id; +}; + + +/** + * Context for DNS resolution. + */ +static struct GNUNET_DNSSTUB_Context *ctx; + +/** + * The number of queries that are outstanding + */ +static unsigned int pending; + +/** + * Number of lookups we performed overall. + */ +static unsigned int lookups; + +/** + * Number of lookups that failed. + */ +static unsigned int failures; + +/** + * Number of records we found. + */ +static unsigned int records; + +/** + * Head of DLL of all requests to perform. + */ +static struct Request *req_head; + +/** + * Tail of DLL of all requests to perform. + */ +static struct Request *req_tail; + +/** + * Main task. + */ +static struct GNUNET_SCHEDULER_Task *t; + +/** + * Maximum number of queries pending at the same time. + */ +#define THRESH 20 + +/** + * TIME_THRESH is in usecs. How quickly do we submit fresh queries. + * Used as an additional throttle. + */ +#define TIME_THRESH 10 + +/** + * How often do we retry a query before giving up for good? + */ +#define MAX_RETRIES 5 + + +/** + * We received @a rec for @a req. Remember the answer. + * + * @param req request + * @param rec response + */ +static void +process_record (struct Request *req, + struct GNUNET_DNSPARSER_Record *rec) +{ + char buf[INET6_ADDRSTRLEN]; + + records++; + switch (rec->type) + { + case GNUNET_DNSPARSER_TYPE_A: + fprintf (stdout, + "%s A %s\n", + req->hostname, + inet_ntop (AF_INET, + rec->data.raw.data, + buf, + sizeof (buf))); + break; + case GNUNET_DNSPARSER_TYPE_AAAA: + fprintf (stdout, + "%s AAAA %s\n", + req->hostname, + inet_ntop (AF_INET6, + rec->data.raw.data, + buf, + sizeof (buf))); + break; + case GNUNET_DNSPARSER_TYPE_NS: + fprintf (stdout, + "%s NS %s\n", + req->hostname, + rec->data.hostname); + break; + case GNUNET_DNSPARSER_TYPE_CNAME: + fprintf (stdout, + "%s CNAME %s\n", + req->hostname, + rec->data.hostname); + break; + case GNUNET_DNSPARSER_TYPE_MX: + fprintf (stdout, + "%s MX %u %s\n", + req->hostname, + (unsigned int) rec->data.mx->preference, + rec->data.mx->mxhost); + break; + case GNUNET_DNSPARSER_TYPE_SOA: + fprintf (stdout, + "%s SOA %s %s %u %u %u %u %u\n", + req->hostname, + rec->data.soa->mname, + rec->data.soa->rname, + (unsigned int) rec->data.soa->serial, + (unsigned int) rec->data.soa->refresh, + (unsigned int) rec->data.soa->retry, + (unsigned int) rec->data.soa->expire, + (unsigned int) rec->data.soa->minimum_ttl); + break; + case GNUNET_DNSPARSER_TYPE_SRV: + fprintf (stdout, + "%s SRV %s %u %u %u\n", + req->hostname, + rec->data.srv->target, + rec->data.srv->priority, + rec->data.srv->weight, + rec->data.srv->port); + break; + case GNUNET_DNSPARSER_TYPE_PTR: + fprintf (stdout, + "%s PTR %s\n", + req->hostname, + rec->data.hostname); + break; + case GNUNET_DNSPARSER_TYPE_TXT: + fprintf (stdout, + "%s TXT %.*s\n", + req->hostname, + (int) rec->data.raw.data_len, + (char *) rec->data.raw.data); + break; + case GNUNET_DNSPARSER_TYPE_DNAME: + fprintf (stdout, + "%s DNAME %s\n", + req->hostname, + rec->data.hostname); + break; + + /* obscure records */ + case GNUNET_DNSPARSER_TYPE_AFSDB: + case GNUNET_DNSPARSER_TYPE_NAPTR: + case GNUNET_DNSPARSER_TYPE_APL: + case GNUNET_DNSPARSER_TYPE_DHCID: + case GNUNET_DNSPARSER_TYPE_HIP: + case GNUNET_DNSPARSER_TYPE_LOC: + case GNUNET_DNSPARSER_TYPE_RP: + case GNUNET_DNSPARSER_TYPE_TKEY: + case GNUNET_DNSPARSER_TYPE_TSIG: + case GNUNET_DNSPARSER_TYPE_URI: + case GNUNET_DNSPARSER_TYPE_TA: + + /* DNSSEC */ + case GNUNET_DNSPARSER_TYPE_DS: + case GNUNET_DNSPARSER_TYPE_RRSIG: + case GNUNET_DNSPARSER_TYPE_NSEC: + case GNUNET_DNSPARSER_TYPE_DNSKEY: + case GNUNET_DNSPARSER_TYPE_NSEC3: + case GNUNET_DNSPARSER_TYPE_NSEC3PARAM: + case GNUNET_DNSPARSER_TYPE_CDS: + case GNUNET_DNSPARSER_TYPE_CDNSKEY: + + /* DNSSEC payload */ + case GNUNET_DNSPARSER_TYPE_CERT: + case GNUNET_DNSPARSER_TYPE_SSHFP: + case GNUNET_DNSPARSER_TYPE_IPSECKEY: + case GNUNET_DNSPARSER_TYPE_TLSA: + case GNUNET_DNSPARSER_TYPE_OPENPGPKEY: + + /* obsolete records */ + case GNUNET_DNSPARSER_TYPE_SIG: + case GNUNET_DNSPARSER_TYPE_KEY: + case GNUNET_DNSPARSER_TYPE_KX: + { + char *base32; + + base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data, + rec->data.raw.data_len); + fprintf (stdout, + "%s (%u) %s\n", + req->hostname, + rec->type, + base32); + GNUNET_free (base32); + } + break; + default: + fprintf (stderr, + "Unsupported type %u\n", + (unsigned int) rec->type); + break; + } +} + + +/** + * Function called with the result of a DNS resolution. + * + * @param cls closure with the `struct Request` + * @param dns dns response, never NULL + * @param dns_len number of bytes in @a dns + */ +static void +process_result (void *cls, + const struct GNUNET_TUN_DnsHeader *dns, + size_t dns_len) +{ + struct Request *req = cls; + struct GNUNET_DNSPARSER_Packet *p; + + if (NULL == dns) + { + /* stub gave up */ + pending--; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Stub gave up on DNS reply for `%s'\n", + req->hostname); + GNUNET_CONTAINER_DLL_remove (req_head, + req_tail, + req); + if (req->issue_num > MAX_RETRIES) + { + failures++; + GNUNET_free (req->hostname); + GNUNET_free (req->raw); + GNUNET_free (req); + return; + } + GNUNET_CONTAINER_DLL_insert_tail (req_head, + req_tail, + req); + req->rs = NULL; + return; + } + if (req->id != dns->id) + return; + pending--; + GNUNET_DNSSTUB_resolve_cancel (req->rs); + req->rs = NULL; + GNUNET_CONTAINER_DLL_remove (req_head, + req_tail, + req); + p = GNUNET_DNSPARSER_parse ((const char *) dns, + dns_len); + if (NULL == p) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse DNS reply for `%s'\n", + req->hostname); + if (req->issue_num > MAX_RETRIES) + { + failures++; + GNUNET_free (req->hostname); + GNUNET_free (req->raw); + GNUNET_free (req); + return; + } + GNUNET_CONTAINER_DLL_insert_tail (req_head, + req_tail, + req); + return; + } + for (unsigned int i=0;inum_answers;i++) + { + struct GNUNET_DNSPARSER_Record *rs = &p->answers[i]; + + process_record (req, + rs); + } + for (unsigned int i=0;inum_authority_records;i++) + { + struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i]; + + process_record (req, + rs); + } + for (unsigned int i=0;inum_additional_records;i++) + { + struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i]; + + process_record (req, + rs); + } + GNUNET_DNSPARSER_free_packet (p); + GNUNET_free (req->hostname); + GNUNET_free (req->raw); + GNUNET_free (req); +} + + +/** + * Submit a request to DNS unless we need to slow down because + * we are at the rate limit. + * + * @param req request to submit + * @return #GNUNET_OK if request was submitted + * #GNUNET_NO if request was already submitted + * #GNUNET_SYSERR if we are at the rate limit + */ +static int +submit_req (struct Request *req) +{ + static struct timeval last_request; + struct timeval now; + + if (NULL != req->rs) + return GNUNET_NO; /* already submitted */ + gettimeofday (&now, + NULL); + if ( ( ( (now.tv_sec - last_request.tv_sec) == 0) && + ( (now.tv_usec - last_request.tv_usec) < TIME_THRESH) ) || + (pending >= THRESH) ) + return GNUNET_SYSERR; + GNUNET_assert (NULL == req->rs); + req->rs = GNUNET_DNSSTUB_resolve (ctx, + req->raw, + req->raw_len, + &process_result, + req); + GNUNET_assert (NULL != req->rs); + req->issue_num++; + last_request = now; + lookups++; + pending++; + req->time = time (NULL); + return GNUNET_OK; +} + + +/** + * Process as many requests as possible from the queue. + * + * @param cls NULL + */ +static void +process_queue(void *cls) +{ + (void) cls; + t = NULL; + for (struct Request *req = req_head; + NULL != req; + req = req->next) + { + if (GNUNET_SYSERR == submit_req (req)) + break; + } + if (NULL != req_head) + t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, + &process_queue, + NULL); + else + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Clean up and terminate the process. + * + * @param cls NULL + */ +static void +do_shutdown (void *cls) +{ + (void) cls; + if (NULL != t) + { + GNUNET_SCHEDULER_cancel (t); + t = NULL; + } + GNUNET_DNSSTUB_stop (ctx); + ctx = NULL; +} + + +/** + * Process requests from the queue, then if the queue is + * not empty, try again. + * + * @param cls NULL + */ +static void +run (void *cls) +{ + (void) cls; + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + t = GNUNET_SCHEDULER_add_now (&process_queue, + NULL); +} + + +/** + * Add @a hostname to the list of requests to be made. + * + * @param hostname name to resolve + */ +static void +queue (const char *hostname) +{ + struct GNUNET_DNSPARSER_Packet p; + struct GNUNET_DNSPARSER_Query q; + struct Request *req; + char *raw; + size_t raw_size; + + if (GNUNET_OK != + GNUNET_DNSPARSER_check_name (hostname)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Refusing invalid hostname `%s'\n", + hostname); + return; + } + q.name = (char *) hostname; + q.type = GNUNET_DNSPARSER_TYPE_NS; + q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET; + + memset (&p, + 0, + sizeof (p)); + p.num_queries = 1; + p.queries = &q; + p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT16_MAX); + + if (GNUNET_OK != + GNUNET_DNSPARSER_pack (&p, + UINT16_MAX, + &raw, + &raw_size)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to pack query for hostname `%s'\n", + hostname); + return; + } + + req = GNUNET_new (struct Request); + req->hostname = strdup (hostname); + req->raw = raw; + req->raw_len = raw_size; + req->id = p.id; + GNUNET_CONTAINER_DLL_insert_tail (req_head, + req_tail, + req); +} + + +/** + * Call with IP address of resolver to query. + * + * @param argc should be 2 + * @param argv[1] should contain IP address + * @return 0 on success + */ +int +main (int argc, + char **argv) +{ + char hn[256]; + + if (2 != argc) + { + fprintf (stderr, + "Missing required configuration argument\n"); + return -1; + } + ctx = GNUNET_DNSSTUB_start (256); + if (NULL == ctx) + { + fprintf (stderr, + "Failed to initialize GNUnet DNS STUB\n"); + return 1; + } + if (GNUNET_OK != + GNUNET_DNSSTUB_add_dns_ip (ctx, + argv[1])) + { + fprintf (stderr, + "Failed to use `%s' for DNS resolver\n", + argv[1]); + return 1; + } + + while (NULL != + fgets (hn, + sizeof (hn), + stdin)) + { + if (strlen(hn) > 0) + hn[strlen(hn)-1] = '\0'; /* eat newline */ + queue (hn); + } + GNUNET_SCHEDULER_run (&run, + NULL); + fprintf (stderr, + "Did %u lookups, found %u records, %u lookups failed, %u pending on shutdown\n", + lookups, + records, + failures, + pending); + return 0; +}