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 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.
16 * @file src/namestore/gnunet-zoneimport.c
17 * @brief import a DNS zone for publication in GNS, incremental
18 * @author Christian Grothoff
21 #include <gnunet_util_lib.h>
22 #include <gnunet_dnsstub_lib.h>
23 #include <gnunet_dnsparser_lib.h>
24 #include <gnunet_gnsrecord_lib.h>
25 #include <gnunet_namestore_service.h>
26 #include <gnunet_statistics_service.h>
27 #include <gnunet_identity_service.h>
31 * Maximum number of queries pending at the same time.
36 * TIME_THRESH is in usecs. How quickly do we submit fresh queries.
37 * Used as an additional throttle.
39 #define TIME_THRESH 10
42 * How often do we retry a query before giving up for good?
47 * How many DNS requests do we at most issue in rapid series?
52 * How long do we wait at least between series of requests?
54 #define SERIES_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, 10)
57 * How long do DNS records have to last at least after being imported?
59 static struct GNUNET_TIME_Relative minimum_expiration_time;
62 * How many requests do we request from NAMESTORE in one batch
63 * during our initial iteration?
65 #define NS_BATCH_SIZE 1024
68 * Some zones may include authoritative records for other
69 * zones, such as foo.com.uk or bar.com.fr. As for GNS
70 * each dot represents a zone cut, we then need to create a
71 * zone on-the-fly to capture those records properly.
87 * Domain of the zone (i.e. "fr" or "com.fr")
92 * Private key of the zone.
94 struct GNUNET_CRYPTO_EcdsaPrivateKey key;
100 * Record for the request to be stored by GNS.
117 struct GNUNET_GNSRECORD_Data grd;
123 * Request we should make. We keep this struct in memory per request,
124 * thus optimizing it is crucial for the overall memory consumption of
130 * Requests are kept in a heap while waiting to be resolved.
132 struct GNUNET_CONTAINER_HeapNode *hn;
135 * Active requests are kept in a DLL.
137 struct Request *next;
140 * Active requests are kept in a DLL.
142 struct Request *prev;
145 * Head of records that should be published in GNS for
148 struct Record *rec_head;
151 * Tail of records that should be published in GNS for
154 struct Record *rec_tail;
157 * Socket used to make the request, NULL if not active.
159 struct GNUNET_DNSSTUB_RequestSocket *rs;
162 * Hostname we are resolving, allocated at the end of
163 * this struct (optimizing memory consumption by reducing
164 * total number of allocations).
169 * Namestore operation pending for this record.
171 struct GNUNET_NAMESTORE_QueueEntry *qe;
174 * Zone responsible for this request.
176 const struct Zone *zone;
179 * At what time does the (earliest) of the returned records
180 * for this name expire? At this point, we need to re-fetch
183 struct GNUNET_TIME_Absolute expires;
186 * While we are fetching the record, the value is set to the
187 * starting time of the DNS operation. While doing a
188 * NAMESTORE store, again set to the start time of the
189 * NAMESTORE operation.
191 struct GNUNET_TIME_Absolute op_start_time;
194 * How often did we issue this query? (And failed, reset
195 * to zero once we were successful.)
197 unsigned int issue_num;
200 * random 16-bit DNS query identifier.
207 * Command-line argument specifying desired size of the hash map with
208 * all of our pending names. Usually, we use an automatically growing
209 * map, but this is only OK up to about a million entries. Above that
210 * number, the user must explicitly specify the size at startup.
212 static unsigned int map_size = 1024;
215 * Handle to the identity service.
217 static struct GNUNET_IDENTITY_Handle *id;
222 static struct GNUNET_NAMESTORE_Handle *ns;
225 * Handle to the statistics service.
227 static struct GNUNET_STATISTICS_Handle *stats;
230 * Context for DNS resolution.
232 static struct GNUNET_DNSSTUB_Context *ctx;
235 * The number of DNS queries that are outstanding
237 static unsigned int pending;
240 * The number of NAMESTORE record store operations that are outstanding
242 static unsigned int pending_rs;
245 * Number of lookups we performed overall.
247 static unsigned int lookups;
250 * Number of records we had cached.
252 static unsigned int cached;
255 * How many hostnames did we reject (malformed).
257 static unsigned int rejects;
260 * Number of lookups that failed.
262 static unsigned int failures;
265 * Number of records we found.
267 static unsigned int records;
270 * Number of record sets given to namestore.
272 static unsigned int record_sets;
275 * Heap of all requests to perform, sorted by
276 * the time we should next do the request (i.e. by expires).
278 static struct GNUNET_CONTAINER_Heap *req_heap;
281 * Active requests are kept in a DLL.
283 static struct Request *req_head;
286 * Active requests are kept in a DLL.
288 static struct Request *req_tail;
293 static struct GNUNET_SCHEDULER_Task *t;
296 * Hash map of requests for which we may still get a response from
297 * the namestore. Set to NULL once the initial namestore iteration
300 static struct GNUNET_CONTAINER_MultiHashMap *ns_pending;
303 * Current zone iteration handle.
305 static struct GNUNET_NAMESTORE_ZoneIterator *zone_it;
308 * Head of list of zones we are managing.
310 static struct Zone *zone_head;
313 * Tail of list of zones we are managing.
315 static struct Zone *zone_tail;
318 * After how many more results must #ns_lookup_result_cb() ask
319 * the namestore for more?
321 static uint64_t ns_iterator_trigger_next;
324 * Number of DNS requests counted in latency total.
326 static uint64_t total_dns_latency_cnt;
329 * Sum of DNS latencies observed.
331 static struct GNUNET_TIME_Relative total_dns_latency;
334 * Number of records processed (DNS lookup, no NAMESTORE) in total.
336 static uint64_t total_reg_proc_dns;
339 * Number of records processed (DNS lookup, with NAMESTORE) in total.
341 static uint64_t total_reg_proc_dns_ns;
344 * Start time of the regular processing.
346 static struct GNUNET_TIME_Absolute start_time_reg_proc;
349 * Last time we worked before going idle.
351 static struct GNUNET_TIME_Absolute sleep_time_reg_proc;
354 * Time we slept just waiting for work.
356 static struct GNUNET_TIME_Relative idle_time;
360 * Callback for #for_all_records
363 * @param rec a DNS record
366 (*RecordProcessor) (void *cls,
367 const struct GNUNET_DNSPARSER_Record *rec);
371 * Call @a rp for each record in @a p, regardless of
372 * what response section it is in.
374 * @param p packet from DNS
375 * @param rp function to call
376 * @param rp_cls closure for @a rp
379 for_all_records (const struct GNUNET_DNSPARSER_Packet *p,
383 for (unsigned int i=0;i<p->num_answers;i++)
385 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
390 for (unsigned int i=0;i<p->num_authority_records;i++)
392 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
397 for (unsigned int i=0;i<p->num_additional_records;i++)
399 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
408 * Return just the label of the hostname in @a req.
410 * @param req request to process hostname of
411 * @return statically allocated pointer to the label,
412 * overwritten upon the next request!
415 get_label (struct Request *req)
417 static char label[64];
420 dot = strchr (req->hostname,
421 (unsigned char) '.');
427 if (((size_t) (dot - req->hostname)) >= sizeof (label))
432 GNUNET_memcpy (label,
434 dot - req->hostname);
435 label[dot - req->hostname] = '\0';
441 * Build DNS query for @a hostname.
443 * @param hostname host to build query for
444 * @param raw_size[out] number of bytes in the query
445 * @return NULL on error, otherwise pointer to statically (!)
446 * allocated query buffer
449 build_dns_query (struct Request *req,
452 static char raw[512];
454 struct GNUNET_DNSPARSER_Packet p;
455 struct GNUNET_DNSPARSER_Query q;
457 q.name = (char *) req->hostname;
458 q.type = GNUNET_DNSPARSER_TYPE_NS;
459 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
468 GNUNET_DNSPARSER_pack (&p,
473 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
474 "Failed to pack query for hostname `%s'\n",
479 if (*raw_size > sizeof (raw))
481 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
482 "Failed to pack query for hostname `%s'\n",
499 * Free records associated with @a req.
501 * @param req request to free records of
504 free_records (struct Request *req)
509 while (NULL != (rec = req->rec_head))
511 GNUNET_CONTAINER_DLL_remove (req->rec_head,
520 * Free @a req and data structures reachable from it.
522 * @param req request to free
525 free_request (struct Request *req)
533 * Process as many requests as possible from the queue.
538 process_queue (void *cls);
542 * Insert @a req into DLL sorted by next fetch time.
544 * @param req request to insert into #req_heap
547 insert_sorted (struct Request *req)
549 req->hn = GNUNET_CONTAINER_heap_insert (req_heap,
551 req->expires.abs_value_us);
552 if (req == GNUNET_CONTAINER_heap_peek (req_heap))
555 GNUNET_SCHEDULER_cancel (t);
556 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
557 t = GNUNET_SCHEDULER_add_at (req->expires,
565 * Add record to the GNS record set for @a req.
567 * @param req the request to expand GNS record set for
568 * @param type type to use
569 * @param expiration_time when should @a rec expire
570 * @param data raw data to store
571 * @param data_len number of bytes in @a data
574 add_record (struct Request *req,
576 struct GNUNET_TIME_Absolute expiration_time,
582 rec = GNUNET_malloc (sizeof (struct Record) + data_len);
583 rec->grd.data = &rec[1];
584 rec->grd.expiration_time = expiration_time.abs_value_us;
585 rec->grd.data_size = data_len;
586 rec->grd.record_type = type;
587 rec->grd.flags = GNUNET_GNSRECORD_RF_NONE;
588 GNUNET_memcpy (&rec[1],
591 GNUNET_CONTAINER_DLL_insert (req->rec_head,
598 * Closure for #check_for_glue.
603 * Overall request we are processing.
608 * NS name we are looking for glue for.
613 * Set to #GNUNET_YES if glue was found.
620 * Try to find glue records for a given NS record.
622 * @param cls a `struct GlueClosure *`
623 * @param rec record that may contain glue information
626 check_for_glue (void *cls,
627 const struct GNUNET_DNSPARSER_Record *rec)
629 struct GlueClosure *gc = cls;
633 char ip[INET6_ADDRSTRLEN+1];
634 socklen_t ip_size = (socklen_t) sizeof (ip);
635 struct GNUNET_TIME_Absolute expiration_time;
636 struct GNUNET_TIME_Relative left;
638 if (0 != strcasecmp (rec->name,
641 expiration_time = rec->expiration_time;
642 left = GNUNET_TIME_absolute_get_remaining (expiration_time);
643 if (0 == left.rel_value_us)
644 return; /* ignore expired glue records */
645 /* if expiration window is too short, bump it to configured minimum */
646 if (left.rel_value_us < minimum_expiration_time.rel_value_us)
647 expiration_time = GNUNET_TIME_relative_to_absolute (minimum_expiration_time);
648 dst_len = sizeof (dst);
652 case GNUNET_DNSPARSER_TYPE_A:
653 if (sizeof (struct in_addr) != rec->data.raw.data_len)
668 GNUNET_DNSPARSER_builder_add_name (dst,
671 gc->req->hostname)) &&
673 GNUNET_DNSPARSER_builder_add_name (dst,
679 GNUNET_GNSRECORD_TYPE_GNS2DNS,
683 gc->found = GNUNET_YES;
686 case GNUNET_DNSPARSER_TYPE_AAAA:
687 if (sizeof (struct in6_addr) != rec->data.raw.data_len)
702 GNUNET_DNSPARSER_builder_add_name (dst,
705 gc->req->hostname)) &&
707 GNUNET_DNSPARSER_builder_add_name (dst,
713 GNUNET_GNSRECORD_TYPE_GNS2DNS,
717 gc->found = GNUNET_YES;
720 case GNUNET_DNSPARSER_TYPE_CNAME:
722 GNUNET_DNSPARSER_builder_add_name (dst,
725 gc->req->hostname)) &&
727 GNUNET_DNSPARSER_builder_add_name (dst,
730 rec->data.hostname)) )
733 GNUNET_GNSRECORD_TYPE_GNS2DNS,
737 gc->found = GNUNET_YES;
741 /* useless, do nothing */
748 * Closure for #process_record().
750 struct ProcessRecordContext
753 * Answer we got back and are currently parsing, or NULL
756 struct GNUNET_DNSPARSER_Packet *p;
759 * Request we are processing.
766 * We received @a rec for @a req. Remember the answer.
768 * @param cls a `struct ProcessRecordContext`
769 * @param rec response
772 process_record (void *cls,
773 const struct GNUNET_DNSPARSER_Record *rec)
775 struct ProcessRecordContext *prc = cls;
776 struct Request *req = prc->req;
780 struct GNUNET_TIME_Absolute expiration_time;
781 struct GNUNET_TIME_Relative left;
783 dst_len = sizeof (dst);
786 if (0 != strcasecmp (rec->name,
789 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
790 "DNS returned record from zone `%s' of type %u while resolving `%s'\n",
792 (unsigned int) rec->type,
794 return; /* does not match hostname, might be glue, but
795 not useful for this pass! */
797 expiration_time = rec->expiration_time;
798 left = GNUNET_TIME_absolute_get_remaining (expiration_time);
799 if (0 == left.rel_value_us)
801 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
802 "DNS returned expired record for `%s'\n",
804 GNUNET_STATISTICS_update (stats,
805 "# expired records obtained from DNS",
808 return; /* record expired */
811 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
812 "DNS returned record that expires at %s for `%s'\n",
813 GNUNET_STRINGS_absolute_time_to_string (expiration_time),
815 /* if expiration window is too short, bump it to configured minimum */
816 if (left.rel_value_us < minimum_expiration_time.rel_value_us)
817 expiration_time = GNUNET_TIME_relative_to_absolute (minimum_expiration_time);
820 case GNUNET_DNSPARSER_TYPE_NS:
822 struct GlueClosure gc;
826 gc.ns = rec->data.hostname;
827 gc.found = GNUNET_NO;
828 for_all_records (prc->p,
831 if ( (GNUNET_NO == gc.found) &&
833 GNUNET_DNSPARSER_builder_add_name (dst,
838 GNUNET_DNSPARSER_builder_add_name (dst,
841 rec->data.hostname)) )
843 /* FIXME: actually check if this is out-of-bailiwick,
844 and if not request explicit resolution... */
845 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
846 "Converted OOB (`%s') NS record for `%s'\n",
850 GNUNET_GNSRECORD_TYPE_GNS2DNS,
857 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
858 "Converted NS record for `%s' using glue\n",
863 case GNUNET_DNSPARSER_TYPE_CNAME:
865 GNUNET_DNSPARSER_builder_add_name (dst,
870 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
871 "Converting CNAME (`%s') record for `%s'\n",
881 case GNUNET_DNSPARSER_TYPE_DNAME:
882 /* No support for DNAME in GNS yet! FIXME: support later! */
883 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
884 "FIXME: not supported: %s DNAME %s\n",
888 case GNUNET_DNSPARSER_TYPE_MX:
890 GNUNET_DNSPARSER_builder_add_mx (dst,
895 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
896 "Converting MX (`%s') record for `%s'\n",
897 rec->data.mx->mxhost,
906 case GNUNET_DNSPARSER_TYPE_SOA:
908 GNUNET_DNSPARSER_builder_add_soa (dst,
913 /* NOTE: GNS does not really use SOAs */
914 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
915 "Converting SOA record for `%s'\n",
924 case GNUNET_DNSPARSER_TYPE_SRV:
926 GNUNET_DNSPARSER_builder_add_srv (dst,
931 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
932 "Converting SRV record for `%s'\n",
941 case GNUNET_DNSPARSER_TYPE_PTR:
943 GNUNET_DNSPARSER_builder_add_name (dst,
948 /* !?: what does a PTR record do in a regular TLD??? */
949 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
950 "Converting PTR record for `%s' (weird)\n",
959 case GNUNET_DNSPARSER_TYPE_CERT:
961 GNUNET_DNSPARSER_builder_add_cert (dst,
966 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
967 "Converting CERT record for `%s'\n",
976 /* Rest is 'raw' encoded and just needs to be copied IF
977 the hostname matches the requested name; otherwise we
978 simply cannot use it. */
979 case GNUNET_DNSPARSER_TYPE_A:
980 case GNUNET_DNSPARSER_TYPE_AAAA:
981 case GNUNET_DNSPARSER_TYPE_TXT:
983 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
984 "Converting record of type %u for `%s'\n",
985 (unsigned int) rec->type,
991 rec->data.raw.data_len);
998 * Continuation called to notify client about result of the
1001 * @param cls closure with our `struct Request`
1002 * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
1003 * #GNUNET_NO if content was already there or not found
1004 * #GNUNET_YES (or other positive value) on success
1005 * @param emsg NULL on success, otherwise an error message
1008 store_completed_cb (void *cls,
1012 static struct GNUNET_TIME_Absolute last;
1013 struct Request *req = cls;
1016 if (GNUNET_SYSERR == success)
1018 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1019 "Failed to store zone data for `%s': %s\n",
1025 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1026 "Stored records under `%s' (%d)\n",
1030 total_reg_proc_dns_ns++; /* finished regular processing */
1033 /* compute NAMESTORE statistics */
1035 static uint64_t total_ns_latency_cnt;
1036 static struct GNUNET_TIME_Relative total_ns_latency;
1037 struct GNUNET_TIME_Relative ns_latency;
1039 ns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time);
1040 total_ns_latency = GNUNET_TIME_relative_add (total_ns_latency,
1042 if (0 == total_ns_latency_cnt)
1043 last = GNUNET_TIME_absolute_get ();
1044 total_ns_latency_cnt++;
1045 if (0 == (total_ns_latency_cnt % 1000))
1047 struct GNUNET_TIME_Relative delta;
1049 delta = GNUNET_TIME_absolute_get_duration (last);
1050 last = GNUNET_TIME_absolute_get ();
1052 "Processed 1000 records in %s\n",
1053 GNUNET_STRINGS_relative_time_to_string (delta,
1055 GNUNET_STATISTICS_set (stats,
1056 "# average NAMESTORE PUT latency (μs)",
1057 total_ns_latency.rel_value_us / total_ns_latency_cnt,
1061 /* compute and publish overall velocity */
1062 if (0 == (total_reg_proc_dns_ns % 100) )
1064 struct GNUNET_TIME_Relative runtime;
1066 runtime = GNUNET_TIME_absolute_get_duration (start_time_reg_proc);
1067 runtime = GNUNET_TIME_relative_subtract (runtime,
1069 runtime = GNUNET_TIME_relative_divide (runtime,
1070 total_reg_proc_dns + total_reg_proc_dns_ns);
1071 GNUNET_STATISTICS_set (stats,
1072 "# Regular processing completed without NAMESTORE",
1075 GNUNET_STATISTICS_set (stats,
1076 "# Regular processing completed with NAMESTORE PUT",
1077 total_reg_proc_dns_ns,
1079 GNUNET_STATISTICS_set (stats,
1080 "# average request processing latency (μs)",
1081 runtime.rel_value_us,
1083 GNUNET_STATISTICS_set (stats,
1084 "# total time spent idle (μs)",
1085 idle_time.rel_value_us,
1091 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1092 t = GNUNET_SCHEDULER_add_now (&process_queue,
1099 * Function called with the result of a DNS resolution.
1101 * @param cls closure with the `struct Request`
1102 * @param dns dns response, never NULL
1103 * @param dns_len number of bytes in @a dns
1106 process_result (void *cls,
1107 const struct GNUNET_TUN_DnsHeader *dns,
1110 struct Request *req = cls;
1112 struct GNUNET_DNSPARSER_Packet *p;
1113 unsigned int rd_count;
1115 GNUNET_assert (NULL == req->hn);
1119 GNUNET_CONTAINER_DLL_remove (req_head,
1125 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1126 t = GNUNET_SCHEDULER_add_now (&process_queue,
1129 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1130 "Stub gave up on DNS reply for `%s'\n",
1132 GNUNET_STATISTICS_update (stats,
1133 "# DNS lookups timed out",
1136 if (req->issue_num > MAX_RETRIES)
1140 GNUNET_STATISTICS_update (stats,
1141 "# requests given up on",
1146 total_reg_proc_dns++;
1148 insert_sorted (req);
1151 if (req->id != dns->id)
1153 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1154 "DNS ID did not match request, ignoring reply\n");
1155 GNUNET_STATISTICS_update (stats,
1156 "# DNS ID missmatches",
1161 GNUNET_CONTAINER_DLL_remove (req_head,
1164 GNUNET_DNSSTUB_resolve_cancel (req->rs);
1167 p = GNUNET_DNSPARSER_parse ((const char *) dns,
1171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1172 "Failed to parse DNS reply for `%s'\n",
1174 GNUNET_STATISTICS_update (stats,
1175 "# DNS parser errors",
1180 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1181 t = GNUNET_SCHEDULER_add_now (&process_queue,
1184 if (req->issue_num > MAX_RETRIES)
1188 GNUNET_STATISTICS_update (stats,
1189 "# requests given up on",
1194 insert_sorted (req);
1197 /* import new records */
1198 req->issue_num = 0; /* success, reset counter! */
1200 struct ProcessRecordContext prc = {
1209 GNUNET_DNSPARSER_free_packet (p);
1210 /* count records found, determine minimum expiration time */
1211 req->expires = GNUNET_TIME_UNIT_FOREVER_ABS;
1213 struct GNUNET_TIME_Relative dns_latency;
1215 dns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time);
1216 total_dns_latency = GNUNET_TIME_relative_add (total_dns_latency,
1218 total_dns_latency_cnt++;
1219 if (0 == (total_dns_latency_cnt % 1000))
1221 GNUNET_STATISTICS_set (stats,
1222 "# average DNS lookup latency (μs)",
1223 total_dns_latency.rel_value_us / total_dns_latency_cnt,
1228 for (rec = req->rec_head; NULL != rec; rec = rec->next)
1230 struct GNUNET_TIME_Absolute at;
1232 at.abs_value_us = rec->grd.expiration_time;
1233 req->expires = GNUNET_TIME_absolute_min (req->expires,
1237 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1238 "Obtained %u records for `%s'\n",
1241 /* Instead of going for SOA, simplified for now to look each
1242 day in case we got an empty response */
1246 = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
1247 GNUNET_STATISTICS_update (stats,
1248 "# empty DNS replies (usually NXDOMAIN)",
1256 /* convert records to namestore import format */
1258 struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL(rd_count)];
1259 unsigned int off = 0;
1261 /* convert linked list into array */
1262 for (rec = req->rec_head; NULL != rec; rec =rec->next)
1263 rd[off++] = rec->grd;
1265 req->op_start_time = GNUNET_TIME_absolute_get ();
1266 req->qe = GNUNET_NAMESTORE_records_store (ns,
1271 &store_completed_cb,
1273 GNUNET_assert (NULL != req->qe);
1275 insert_sorted (req);
1280 * Process as many requests as possible from the queue.
1285 process_queue (void *cls)
1287 struct Request *req;
1288 unsigned int series;
1291 struct GNUNET_TIME_Relative delay;
1294 delay = GNUNET_TIME_absolute_get_duration (sleep_time_reg_proc);
1295 idle_time = GNUNET_TIME_relative_add (idle_time,
1299 while (pending + pending_rs < THRESH)
1301 req = GNUNET_CONTAINER_heap_peek (req_heap);
1304 if (NULL != req->qe)
1305 return; /* namestore op still pending */
1306 if (NULL != req->rs)
1309 return; /* already submitted */
1311 if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0)
1313 GNUNET_assert (req ==
1314 GNUNET_CONTAINER_heap_remove_root (req_heap));
1316 GNUNET_CONTAINER_DLL_insert (req_head,
1319 GNUNET_assert (NULL == req->rs);
1320 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1321 "Requesting resolution for `%s'\n",
1323 raw = build_dns_query (req,
1331 req->op_start_time = GNUNET_TIME_absolute_get ();
1332 req->rs = GNUNET_DNSSTUB_resolve (ctx,
1337 GNUNET_assert (NULL != req->rs);
1342 if (series > MAX_SERIES)
1345 if (pending + pending_rs >= THRESH)
1347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1348 "Stopped processing queue (%u+%u/%u)]\n",
1352 return; /* wait for replies */
1354 req = GNUNET_CONTAINER_heap_peek (req_heap);
1357 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1358 "Stopped processing queue: empty queue\n");
1361 if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0)
1363 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1364 "Waiting until %s for next record (`%s') to expire\n",
1365 GNUNET_STRINGS_absolute_time_to_string (req->expires),
1368 GNUNET_SCHEDULER_cancel (t);
1369 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1370 t = GNUNET_SCHEDULER_add_at (req->expires,
1375 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1378 GNUNET_SCHEDULER_cancel (t);
1379 sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1380 t = GNUNET_SCHEDULER_add_delayed (SERIES_DELAY,
1387 * Iterator called during #do_shutdown() to free requests in
1388 * the #ns_pending map.
1392 * @param value the `struct Request` to free
1393 * @return #GNUNET_OK
1396 free_request_it (void *cls,
1397 const struct GNUNET_HashCode *key,
1400 struct Request *req = value;
1410 * Clean up and terminate the process.
1415 do_shutdown (void *cls)
1417 struct Request *req;
1423 GNUNET_IDENTITY_disconnect (id);
1428 GNUNET_SCHEDULER_cancel (t);
1431 while (NULL != (req = req_head))
1433 GNUNET_CONTAINER_DLL_remove (req_head,
1436 if (NULL != req->qe)
1437 GNUNET_NAMESTORE_cancel (req->qe);
1440 while (NULL != (req = GNUNET_CONTAINER_heap_remove_root (req_heap)))
1443 if (NULL != req->qe)
1444 GNUNET_NAMESTORE_cancel (req->qe);
1447 if (NULL != zone_it)
1449 GNUNET_NAMESTORE_zone_iteration_stop (zone_it);
1454 GNUNET_NAMESTORE_disconnect (ns);
1459 GNUNET_DNSSTUB_stop (ctx);
1462 if (NULL != req_heap)
1464 GNUNET_CONTAINER_heap_destroy (req_heap);
1467 if (NULL != ns_pending)
1469 GNUNET_CONTAINER_multihashmap_iterate (ns_pending,
1472 GNUNET_CONTAINER_multihashmap_destroy (ns_pending);
1475 while (NULL != (zone = zone_head))
1477 GNUNET_CONTAINER_DLL_remove (zone_head,
1480 GNUNET_free (zone->domain);
1485 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1492 * Iterate over all of the zones we care about and see which records
1493 * we may need to re-fetch when.
1498 iterate_zones (void *cls);
1502 * Function called if #GNUNET_NAMESTORE_records_lookup() failed.
1503 * Just logs an error.
1505 * @param cls a `struct Zone`
1508 ns_lookup_error_cb (void *cls)
1510 struct Zone *zone = cls;
1512 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1513 "Failed to load data from namestore for zone `%s'\n",
1516 ns_iterator_trigger_next = 0;
1517 iterate_zones (NULL);
1522 * Process a record that was stored in the namestore.
1524 * @param cls a `struct Zone *`
1525 * @param key private key of the zone
1526 * @param label label of the records
1527 * @param rd_count number of entries in @a rd array, 0 if label was deleted
1528 * @param rd array of records with data to store
1531 ns_lookup_result_cb (void *cls,
1532 const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
1534 unsigned int rd_count,
1535 const struct GNUNET_GNSRECORD_Data *rd)
1537 struct Zone *zone = cls;
1538 struct Request *req;
1539 struct GNUNET_HashCode hc;
1542 ns_iterator_trigger_next--;
1543 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1544 "Obtained NAMESTORE reply, %llu left in round\n",
1545 (unsigned long long) ns_iterator_trigger_next);
1546 if (0 == ns_iterator_trigger_next)
1548 ns_iterator_trigger_next = NS_BATCH_SIZE;
1549 GNUNET_STATISTICS_update (stats,
1550 "# NAMESTORE records requested from cache",
1551 ns_iterator_trigger_next,
1553 GNUNET_NAMESTORE_zone_iterator_next (zone_it,
1554 ns_iterator_trigger_next);
1556 GNUNET_asprintf (&fqdn,
1560 GNUNET_CRYPTO_hash (fqdn,
1564 req = GNUNET_CONTAINER_multihashmap_get (ns_pending,
1568 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1569 "Ignoring record `%s' in zone `%s': not on my list!\n",
1574 GNUNET_assert (GNUNET_OK ==
1575 GNUNET_CONTAINER_multihashmap_remove (ns_pending,
1578 GNUNET_break (0 == memcmp (key,
1581 GNUNET_break (0 == strcasecmp (label,
1583 for (unsigned int i=0;i<rd_count;i++)
1585 struct GNUNET_TIME_Absolute at;
1587 if (0 != (rd->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
1589 struct GNUNET_TIME_Relative rel;
1591 rel.rel_value_us = rd->expiration_time;
1592 at = GNUNET_TIME_relative_to_absolute (rel);
1596 at.abs_value_us = rd->expiration_time;
1606 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1607 "Empty record set in namestore for `%s'\n",
1612 unsigned int pos = 0;
1615 req->expires = GNUNET_TIME_UNIT_FOREVER_ABS;
1616 for (struct Record *rec = req->rec_head;
1620 struct GNUNET_TIME_Absolute at;
1622 at.abs_value_us = rec->grd.expiration_time;
1623 req->expires = GNUNET_TIME_absolute_min (req->expires,
1628 req->expires = GNUNET_TIME_UNIT_ZERO_ABS;
1629 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1630 "Hot-start with %u existing records for `%s'\n",
1636 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1637 "Adding `%s' to worklist to start at %s\n",
1639 GNUNET_STRINGS_absolute_time_to_string (req->expires));
1640 insert_sorted (req);
1645 * Add @a hostname to the list of requests to be made.
1647 * @param hostname name to resolve
1650 queue (const char *hostname)
1652 struct Request *req;
1656 struct GNUNET_HashCode hc;
1659 GNUNET_DNSPARSER_check_name (hostname))
1661 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1662 "Refusing invalid hostname `%s'\n",
1667 dot = strchr (hostname,
1668 (unsigned char) '.');
1671 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1672 "Refusing invalid hostname `%s' (lacks '.')\n",
1677 for (zone = zone_head; NULL != zone; zone = zone->next)
1678 if (0 == strcmp (zone->domain,
1684 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1685 "Domain name `%s' not in ego list!\n",
1690 hlen = strlen (hostname) + 1;
1691 req = GNUNET_malloc (sizeof (struct Request) + hlen);
1693 req->hostname = (char *) &req[1];
1694 GNUNET_memcpy (req->hostname,
1697 req->id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
1699 GNUNET_CRYPTO_hash (req->hostname,
1703 GNUNET_CONTAINER_multihashmap_put (ns_pending,
1706 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1708 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1709 "Duplicate hostname `%s' ignored\n",
1718 * We have completed the initial iteration over the namestore's database.
1719 * This function is called on each of the remaining records in
1720 * #move_to_queue to #queue() them, as we will simply not find existing
1721 * records for them any longer.
1725 * @param value a `struct Request`
1726 * @return #GNUNET_OK (continue to iterate)
1729 move_to_queue (void *cls,
1730 const struct GNUNET_HashCode *key,
1733 struct Request *req = value;
1737 insert_sorted (req);
1743 * Iterate over all of the zones we care about and see which records
1744 * we may need to re-fetch when.
1749 iterate_zones (void *cls)
1751 static struct Zone *last;
1754 if (NULL != zone_it)
1757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1758 "Finished iteration over zone `%s'!\n",
1760 /* subtract left-overs from previous iteration */
1761 GNUNET_STATISTICS_update (stats,
1762 "# NAMESTORE records requested from cache",
1763 (long long) (- ns_iterator_trigger_next),
1765 ns_iterator_trigger_next = 0;
1767 GNUNET_assert (NULL != zone_tail);
1768 if (zone_tail == last)
1770 /* Done iterating over relevant zones in NAMESTORE, move
1771 rest of hash map to work queue as well. */
1772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1773 "Finished all NAMESTORE iterations!\n");
1774 GNUNET_STATISTICS_set (stats,
1775 "# Domain names without cached reply",
1776 GNUNET_CONTAINER_multihashmap_size (ns_pending),
1778 GNUNET_CONTAINER_multihashmap_iterate (ns_pending,
1781 GNUNET_CONTAINER_multihashmap_destroy (ns_pending);
1783 start_time_reg_proc = GNUNET_TIME_absolute_get ();
1784 total_reg_proc_dns = 0;
1785 total_reg_proc_dns_ns = 0;
1792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1793 "Starting iteration over zone `%s'!\n",
1795 /* subtract left-overs from previous iteration */
1796 GNUNET_STATISTICS_update (stats,
1797 "# NAMESTORE records requested from cache",
1800 ns_iterator_trigger_next = 1;
1801 GNUNET_STATISTICS_update (stats,
1805 zone_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
1807 &ns_lookup_error_cb,
1809 &ns_lookup_result_cb,
1818 * Begin processing hostnames from stdin.
1823 process_stdin (void *cls)
1825 static struct GNUNET_TIME_Absolute last;
1826 static uint64_t idot;
1833 GNUNET_IDENTITY_disconnect (id);
1842 hn[strlen(hn)-1] = '\0'; /* eat newline */
1844 last = GNUNET_TIME_absolute_get ();
1846 if (0 == idot % 100000)
1848 struct GNUNET_TIME_Relative delta;
1850 delta = GNUNET_TIME_absolute_get_duration (last);
1851 last = GNUNET_TIME_absolute_get ();
1853 "Read 100000 domain names in %s\n",
1854 GNUNET_STRINGS_relative_time_to_string (delta,
1856 GNUNET_STATISTICS_set (stats,
1857 "# domain names provided",
1864 "Done reading %llu domain names\n",
1865 (unsigned long long) idot);
1866 GNUNET_STATISTICS_set (stats,
1867 "# domain names provided",
1870 iterate_zones (NULL);
1875 * Method called to inform about the egos of this peer.
1877 * When used with #GNUNET_IDENTITY_connect, this function is
1878 * initially called for all egos and then again whenever a
1879 * ego's name changes or if it is deleted. At the end of
1880 * the initial pass over all egos, the function is once called
1881 * with 'NULL' for @a ego. That does NOT mean that the callback won't
1882 * be invoked in the future or that there was an error.
1884 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
1885 * this function is only called ONCE, and 'NULL' being passed in
1886 * @a ego does indicate an error (i.e. name is taken or no default
1887 * value is known). If @a ego is non-NULL and if '*ctx'
1888 * is set in those callbacks, the value WILL be passed to a subsequent
1889 * call to the identity callback of #GNUNET_IDENTITY_connect (if
1890 * that one was not NULL).
1892 * When an identity is renamed, this function is called with the
1893 * (known) @a ego but the NEW @a name.
1895 * When an identity is deleted, this function is called with the
1896 * (known) ego and "NULL" for the @a name. In this case,
1897 * the @a ego is henceforth invalid (and the @a ctx should also be
1900 * @param cls closure
1901 * @param ego ego handle
1902 * @param ctx context for application to store data for this ego
1903 * (during the lifetime of this process, initially NULL)
1904 * @param name name assigned by the user for this ego,
1905 * NULL if the user just deleted the ego and it
1906 * must thus no longer be used
1909 identity_cb (void *cls,
1910 struct GNUNET_IDENTITY_Ego *ego,
1918 if (NULL != zone_head)
1920 t = GNUNET_SCHEDULER_add_now (&process_stdin,
1925 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1927 GNUNET_SCHEDULER_shutdown ();
1935 zone = GNUNET_new (struct Zone);
1936 zone->key = *GNUNET_IDENTITY_ego_get_private_key (ego);
1937 zone->domain = GNUNET_strdup (name);
1938 GNUNET_CONTAINER_DLL_insert (zone_head,
1946 * Process requests from the queue, then if the queue is
1947 * not empty, try again.
1950 * @param args remaining command-line arguments
1951 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1952 * @param cfg configuration
1957 const char *cfgfile,
1958 const struct GNUNET_CONFIGURATION_Handle *cfg)
1963 stats = GNUNET_STATISTICS_create ("zoneimport",
1965 req_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
1966 ns_pending = GNUNET_CONTAINER_multihashmap_create (map_size,
1968 if (NULL == ns_pending)
1971 "Failed to allocate memory for main hash map\n");
1974 ctx = GNUNET_DNSSTUB_start (256);
1978 "Failed to initialize GNUnet DNS STUB\n");
1981 if (NULL == args[0])
1984 "You must provide a list of DNS resolvers on the command line\n");
1987 for (unsigned int i=0;NULL != args[i];i++)
1990 GNUNET_DNSSTUB_add_dns_ip (ctx,
1994 "Failed to use `%s' for DNS resolver\n",
2001 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
2003 ns = GNUNET_NAMESTORE_connect (cfg);
2006 GNUNET_SCHEDULER_shutdown ();
2009 id = GNUNET_IDENTITY_connect (cfg,
2016 * Call with IP address of resolver to query.
2018 * @param argc should be 2
2019 * @param argv[1] should contain IP address
2020 * @return 0 on success
2026 struct GNUNET_GETOPT_CommandLineOption options[] = {
2027 GNUNET_GETOPT_option_uint ('s',
2030 gettext_noop ("size to use for the main hash map"),
2032 GNUNET_GETOPT_option_relative_time ('m',
2033 "minimum-expiration",
2035 gettext_noop ("minimum expiration time we assume for imported records"),
2036 &minimum_expiration_time),
2037 GNUNET_GETOPT_OPTION_END
2041 GNUNET_STRINGS_get_utf8_args (argc, argv,
2044 GNUNET_PROGRAM_run (argc,
2046 "gnunet-zoneimport",
2047 "import DNS zone into namestore",
2051 GNUNET_free ((void*) argv);
2053 "Rejected %u names, had %u cached, did %u lookups, stored %u record sets\n"
2054 "Found %u records, %u lookups failed, %u/%u pending on shutdown\n",
2066 /* end of gnunet-zoneimport.c */