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
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 * @file src/namestore/gnunet-zoneimport.c
22 * @brief import a DNS zone for publication in GNS, incremental
23 * @author Christian Grothoff
26 #include <gnunet_util_lib.h>
27 #include <gnunet_dnsstub_lib.h>
28 #include <gnunet_dnsparser_lib.h>
29 #include <gnunet_gnsrecord_lib.h>
30 #include <gnunet_namestore_service.h>
31 #include <gnunet_statistics_service.h>
32 #include <gnunet_identity_service.h>
36 * Maximum number of queries pending at the same time.
41 * TIME_THRESH is in usecs. How quickly do we submit fresh queries.
42 * Used as an additional throttle.
44 #define TIME_THRESH 10
47 * How often do we retry a query before giving up for good?
52 * How many DNS requests do we at most issue in rapid series?
57 * How long do we wait at least between series of requests?
59 #define SERIES_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, 10)
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 * Handle to the identity service.
209 static struct GNUNET_IDENTITY_Handle *id;
214 static struct GNUNET_NAMESTORE_Handle *ns;
217 * Handle to the statistics service.
219 static struct GNUNET_STATISTICS_Handle *stats;
222 * Context for DNS resolution.
224 static struct GNUNET_DNSSTUB_Context *ctx;
227 * The number of DNS queries that are outstanding
229 static unsigned int pending;
232 * The number of NAMESTORE record store operations that are outstanding
234 static unsigned int pending_rs;
237 * Number of lookups we performed overall.
239 static unsigned int lookups;
242 * Number of records we had cached.
244 static unsigned int cached;
247 * How many hostnames did we reject (malformed).
249 static unsigned int rejects;
252 * Number of lookups that failed.
254 static unsigned int failures;
257 * Number of records we found.
259 static unsigned int records;
262 * Number of record sets given to namestore.
264 static unsigned int record_sets;
267 * Heap of all requests to perform, sorted by
268 * the time we should next do the request (i.e. by expires).
270 static struct GNUNET_CONTAINER_Heap *req_heap;
273 * Active requests are kept in a DLL.
275 static struct Request *req_head;
278 * Active requests are kept in a DLL.
280 static struct Request *req_tail;
285 static struct GNUNET_SCHEDULER_Task *t;
288 * Hash map of requests for which we may still get a response from
289 * the namestore. Set to NULL once the initial namestore iteration
292 static struct GNUNET_CONTAINER_MultiHashMap *ns_pending;
295 * Current zone iteration handle.
297 static struct GNUNET_NAMESTORE_ZoneIterator *zone_it;
300 * Head of list of zones we are managing.
302 static struct Zone *zone_head;
305 * Tail of list of zones we are managing.
307 static struct Zone *zone_tail;
310 * After how many more results must #ns_lookup_result_cb() ask
311 * the namestore for more?
313 static uint64_t ns_iterator_trigger_next;
316 * Number of DNS requests counted in latency total.
318 static uint64_t total_dns_latency_cnt;
321 * Sum of DNS latencies observed.
323 static struct GNUNET_TIME_Relative total_dns_latency;
326 * Number of NAMESTORE requests counted in latency total.
328 static uint64_t total_ns_latency_cnt;
331 * Sum of NAMESTORE latencies observed.
333 static struct GNUNET_TIME_Relative total_ns_latency;
337 * Callback for #for_all_records
340 * @param rec a DNS record
343 (*RecordProcessor) (void *cls,
344 const struct GNUNET_DNSPARSER_Record *rec);
348 * Call @a rp for each record in @a p, regardless of
349 * what response section it is in.
351 * @param p packet from DNS
352 * @param rp function to call
353 * @param rp_cls closure for @a rp
356 for_all_records (const struct GNUNET_DNSPARSER_Packet *p,
360 for (unsigned int i=0;i<p->num_answers;i++)
362 struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
367 for (unsigned int i=0;i<p->num_authority_records;i++)
369 struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
374 for (unsigned int i=0;i<p->num_additional_records;i++)
376 struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
385 * Return just the label of the hostname in @a req.
387 * @param req request to process hostname of
388 * @return statically allocated pointer to the label,
389 * overwritten upon the next request!
392 get_label (struct Request *req)
394 static char label[64];
397 dot = strchr (req->hostname,
398 (unsigned char) '.');
404 if (((size_t) (dot - req->hostname)) >= sizeof (label))
411 dot - req->hostname);
412 label[dot - req->hostname] = '\0';
418 * Build DNS query for @a hostname.
420 * @param hostname host to build query for
421 * @param raw_size[out] number of bytes in the query
422 * @return NULL on error, otherwise pointer to statically (!)
423 * allocated query buffer
426 build_dns_query (struct Request *req,
429 static char raw[512];
431 struct GNUNET_DNSPARSER_Packet p;
432 struct GNUNET_DNSPARSER_Query q;
434 q.name = (char *) req->hostname;
435 q.type = GNUNET_DNSPARSER_TYPE_NS;
436 q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
445 GNUNET_DNSPARSER_pack (&p,
450 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
451 "Failed to pack query for hostname `%s'\n",
456 if (*raw_size > sizeof (raw))
458 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
459 "Failed to pack query for hostname `%s'\n",
476 * Free records associated with @a req.
478 * @param req request to free records of
481 free_records (struct Request *req)
486 while (NULL != (rec = req->rec_head))
488 GNUNET_CONTAINER_DLL_remove (req->rec_head,
497 * Free @a req and data structures reachable from it.
499 * @param req request to free
502 free_request (struct Request *req)
510 * Process as many requests as possible from the queue.
515 process_queue (void *cls);
519 * Insert @a req into DLL sorted by next fetch time.
521 * @param req request to insert into #req_heap
524 insert_sorted (struct Request *req)
526 req->hn = GNUNET_CONTAINER_heap_insert (req_heap,
528 req->expires.abs_value_us);
529 if (req == GNUNET_CONTAINER_heap_peek (req_heap))
532 GNUNET_SCHEDULER_cancel (t);
533 t = GNUNET_SCHEDULER_add_at (req->expires,
541 * Add record to the GNS record set for @a req.
543 * @param req the request to expand GNS record set for
544 * @param type type to use
545 * @param expiration_time when should @a rec expire
546 * @param data raw data to store
547 * @param data_len number of bytes in @a data
550 add_record (struct Request *req,
552 struct GNUNET_TIME_Absolute expiration_time,
558 rec = GNUNET_malloc (sizeof (struct Record) + data_len);
559 rec->grd.data = &rec[1];
560 rec->grd.expiration_time = expiration_time.abs_value_us;
561 rec->grd.data_size = data_len;
562 rec->grd.record_type = type;
563 rec->grd.flags = GNUNET_GNSRECORD_RF_NONE;
564 GNUNET_memcpy (&rec[1],
567 GNUNET_CONTAINER_DLL_insert (req->rec_head,
574 * Closure for #check_for_glue.
579 * Overall request we are processing.
584 * NS name we are looking for glue for.
589 * Set to #GNUNET_YES if glue was found.
596 * Try to find glue records for a given NS record.
598 * @param cls a `struct GlueClosure *`
599 * @param rec record that may contain glue information
602 check_for_glue (void *cls,
603 const struct GNUNET_DNSPARSER_Record *rec)
605 struct GlueClosure *gc = cls;
609 char ip[INET6_ADDRSTRLEN+1];
610 socklen_t ip_size = (socklen_t) sizeof (ip);
612 if (0 != strcasecmp (rec->name,
615 dst_len = sizeof (dst);
619 case GNUNET_DNSPARSER_TYPE_A:
620 if (sizeof (struct in_addr) != rec->data.raw.data_len)
635 GNUNET_DNSPARSER_builder_add_name (dst,
638 gc->req->hostname)) &&
640 GNUNET_DNSPARSER_builder_add_name (dst,
646 GNUNET_GNSRECORD_TYPE_GNS2DNS,
647 rec->expiration_time,
650 gc->found = GNUNET_YES;
653 case GNUNET_DNSPARSER_TYPE_AAAA:
654 if (sizeof (struct in6_addr) != rec->data.raw.data_len)
669 GNUNET_DNSPARSER_builder_add_name (dst,
672 gc->req->hostname)) &&
674 GNUNET_DNSPARSER_builder_add_name (dst,
680 GNUNET_GNSRECORD_TYPE_GNS2DNS,
681 rec->expiration_time,
684 gc->found = GNUNET_YES;
687 case GNUNET_DNSPARSER_TYPE_CNAME:
689 GNUNET_DNSPARSER_builder_add_name (dst,
692 gc->req->hostname)) &&
694 GNUNET_DNSPARSER_builder_add_name (dst,
697 rec->data.hostname)) )
700 GNUNET_GNSRECORD_TYPE_GNS2DNS,
701 rec->expiration_time,
704 gc->found = GNUNET_YES;
708 /* useless, do nothing */
715 * Closure for #process_record().
717 struct ProcessRecordContext
720 * Answer we got back and are currently parsing, or NULL
723 struct GNUNET_DNSPARSER_Packet *p;
726 * Request we are processing.
733 * We received @a rec for @a req. Remember the answer.
735 * @param cls a `struct ProcessRecordContext`
736 * @param rec response
739 process_record (void *cls,
740 const struct GNUNET_DNSPARSER_Record *rec)
742 struct ProcessRecordContext *prc = cls;
743 struct Request *req = prc->req;
748 dst_len = sizeof (dst);
751 if (0 != strcasecmp (rec->name,
754 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
755 "DNS returned record from zone `%s' of type %u while resolving `%s'\n",
757 (unsigned int) rec->type,
759 return; /* does not match hostname, might be glue, but
760 not useful for this pass! */
763 GNUNET_TIME_absolute_get_remaining (rec->expiration_time).rel_value_us)
765 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
766 "DNS returned expired record for `%s'\n",
768 return; /* record expired */
770 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
771 "DNS returned record that expires at %s for `%s'\n",
772 GNUNET_STRINGS_absolute_time_to_string (rec->expiration_time),
776 case GNUNET_DNSPARSER_TYPE_NS:
778 struct GlueClosure gc;
782 gc.ns = rec->data.hostname;
783 gc.found = GNUNET_NO;
784 for_all_records (prc->p,
787 if ( (GNUNET_NO == gc.found) &&
789 GNUNET_DNSPARSER_builder_add_name (dst,
794 GNUNET_DNSPARSER_builder_add_name (dst,
797 rec->data.hostname)) )
799 /* FIXME: actually check if this is out-of-bailiwick,
800 and if not request explicit resolution... */
801 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
802 "Converted OOB (`%s') NS record for `%s'\n",
806 GNUNET_GNSRECORD_TYPE_GNS2DNS,
807 rec->expiration_time,
813 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
814 "Converted NS record for `%s' using glue\n",
819 case GNUNET_DNSPARSER_TYPE_CNAME:
821 GNUNET_DNSPARSER_builder_add_name (dst,
826 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
827 "Converting CNAME (`%s') record for `%s'\n",
832 rec->expiration_time,
837 case GNUNET_DNSPARSER_TYPE_DNAME:
838 /* No support for DNAME in GNS yet! FIXME: support later! */
839 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
840 "FIXME: not supported: %s DNAME %s\n",
844 case GNUNET_DNSPARSER_TYPE_MX:
846 GNUNET_DNSPARSER_builder_add_mx (dst,
851 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
852 "Converting MX (`%s') record for `%s'\n",
853 rec->data.mx->mxhost,
857 rec->expiration_time,
862 case GNUNET_DNSPARSER_TYPE_SOA:
864 GNUNET_DNSPARSER_builder_add_soa (dst,
869 /* NOTE: GNS does not really use SOAs */
870 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
871 "Converting SOA record for `%s'\n",
875 rec->expiration_time,
880 case GNUNET_DNSPARSER_TYPE_SRV:
882 GNUNET_DNSPARSER_builder_add_srv (dst,
887 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
888 "Converting SRV record for `%s'\n",
892 rec->expiration_time,
897 case GNUNET_DNSPARSER_TYPE_PTR:
899 GNUNET_DNSPARSER_builder_add_name (dst,
904 /* !?: what does a PTR record do in a regular TLD??? */
905 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
906 "Converting PTR record for `%s' (weird)\n",
910 rec->expiration_time,
915 case GNUNET_DNSPARSER_TYPE_CERT:
917 GNUNET_DNSPARSER_builder_add_cert (dst,
922 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
923 "Converting CERT record for `%s'\n",
927 rec->expiration_time,
932 /* Rest is 'raw' encoded and just needs to be copied IF
933 the hostname matches the requested name; otherwise we
934 simply cannot use it. */
935 case GNUNET_DNSPARSER_TYPE_A:
936 case GNUNET_DNSPARSER_TYPE_AAAA:
937 case GNUNET_DNSPARSER_TYPE_TXT:
939 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
940 "Converting record of type %u for `%s'\n",
941 (unsigned int) rec->type,
945 rec->expiration_time,
947 rec->data.raw.data_len);
954 * Continuation called to notify client about result of the
957 * @param cls closure with our `struct Request`
958 * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
959 * #GNUNET_NO if content was already there or not found
960 * #GNUNET_YES (or other positive value) on success
961 * @param emsg NULL on success, otherwise an error message
964 store_completed_cb (void *cls,
968 static struct GNUNET_TIME_Absolute last;
969 static unsigned int pdot;
970 struct Request *req = cls;
973 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
974 "Stored record set in database (%d)\n",
978 struct GNUNET_TIME_Relative ns_latency;
980 ns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time);
981 total_ns_latency = GNUNET_TIME_relative_add (total_ns_latency,
983 total_ns_latency_cnt++;
984 if (0 == (total_ns_latency_cnt % 1000))
986 GNUNET_STATISTICS_update (stats,
987 "# average NAMESTORE PUT latency (μs)",
988 total_ns_latency.rel_value_us / total_ns_latency_cnt,
990 GNUNET_STATISTICS_update (stats,
992 total_ns_latency_cnt,
998 t = GNUNET_SCHEDULER_add_now (&process_queue,
1000 if (GNUNET_SYSERR == success)
1002 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1003 "Failed to store zone data for `%s': %s\n",
1009 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1010 "Stored records under `%s'\n",
1013 last = GNUNET_TIME_absolute_get ();
1015 if (0 == pdot % 1000)
1017 struct GNUNET_TIME_Relative delta;
1019 delta = GNUNET_TIME_absolute_get_duration (last);
1020 last = GNUNET_TIME_absolute_get ();
1022 "Processed 1000 records in %s\n",
1023 GNUNET_STRINGS_relative_time_to_string (delta,
1032 * Function called with the result of a DNS resolution.
1034 * @param cls closure with the `struct Request`
1035 * @param dns dns response, never NULL
1036 * @param dns_len number of bytes in @a dns
1039 process_result (void *cls,
1040 const struct GNUNET_TUN_DnsHeader *dns,
1043 struct Request *req = cls;
1045 struct GNUNET_DNSPARSER_Packet *p;
1046 unsigned int rd_count;
1048 GNUNET_assert (NULL == req->hn);
1052 GNUNET_CONTAINER_DLL_remove (req_head,
1057 t = GNUNET_SCHEDULER_add_now (&process_queue,
1059 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1060 "Stub gave up on DNS reply for `%s'\n",
1062 GNUNET_STATISTICS_update (stats,
1063 "# DNS lookups timed out",
1066 if (req->issue_num > MAX_RETRIES)
1070 GNUNET_STATISTICS_update (stats,
1071 "# requests given up on",
1077 insert_sorted (req);
1080 if (req->id != dns->id)
1082 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1083 "DNS ID did not match request, ignoring reply\n");
1084 GNUNET_STATISTICS_update (stats,
1085 "# DNS ID missmatches",
1090 GNUNET_CONTAINER_DLL_remove (req_head,
1093 GNUNET_DNSSTUB_resolve_cancel (req->rs);
1096 p = GNUNET_DNSPARSER_parse ((const char *) dns,
1100 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1101 "Failed to parse DNS reply for `%s'\n",
1103 GNUNET_STATISTICS_update (stats,
1104 "# DNS parser errors",
1108 t = GNUNET_SCHEDULER_add_now (&process_queue,
1110 if (req->issue_num > MAX_RETRIES)
1114 GNUNET_STATISTICS_update (stats,
1115 "# requests given up on",
1120 insert_sorted (req);
1123 /* import new records */
1124 req->issue_num = 0; /* success, reset counter! */
1126 struct ProcessRecordContext prc = {
1135 GNUNET_DNSPARSER_free_packet (p);
1136 /* count records found, determine minimum expiration time */
1137 req->expires = GNUNET_TIME_UNIT_FOREVER_ABS;
1139 struct GNUNET_TIME_Relative dns_latency;
1141 dns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time);
1142 total_dns_latency = GNUNET_TIME_relative_add (total_dns_latency,
1144 total_dns_latency_cnt++;
1145 if (0 == (total_dns_latency_cnt % 1000))
1147 GNUNET_STATISTICS_update (stats,
1148 "# average DNS latency (μs)",
1149 total_dns_latency.rel_value_us / total_dns_latency_cnt,
1151 GNUNET_STATISTICS_update (stats,
1153 total_dns_latency_cnt,
1158 for (rec = req->rec_head; NULL != rec; rec = rec->next)
1160 struct GNUNET_TIME_Absolute at;
1162 at.abs_value_us = rec->grd.expiration_time;
1163 req->expires = GNUNET_TIME_absolute_min (req->expires,
1167 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1168 "Obtained %u records for `%s'\n",
1171 /* Instead of going for SOA, simplified for now to look each
1172 day in case we got an empty response */
1175 = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
1178 /* convert records to namestore import format */
1180 struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL(rd_count)];
1181 unsigned int off = 0;
1183 /* convert linked list into array */
1184 for (rec = req->rec_head; NULL != rec; rec =rec->next)
1185 rd[off++] = rec->grd;
1187 req->op_start_time = GNUNET_TIME_absolute_get ();
1188 req->qe = GNUNET_NAMESTORE_records_store (ns,
1193 &store_completed_cb,
1195 GNUNET_assert (NULL != req->qe);
1197 insert_sorted (req);
1202 * Process as many requests as possible from the queue.
1207 process_queue (void *cls)
1209 struct Request *req;
1210 unsigned int series;
1217 while (pending + pending_rs < THRESH)
1219 req = GNUNET_CONTAINER_heap_peek (req_heap);
1222 if (NULL != req->qe)
1223 return; /* namestore op still pending */
1224 if (NULL != req->rs)
1227 return; /* already submitted */
1229 if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0)
1231 GNUNET_assert (req ==
1232 GNUNET_CONTAINER_heap_remove_root (req_heap));
1234 GNUNET_CONTAINER_DLL_insert (req_head,
1237 GNUNET_assert (NULL == req->rs);
1238 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1239 "Requesting resolution for `%s'\n",
1241 raw = build_dns_query (req,
1249 req->op_start_time = GNUNET_TIME_absolute_get ();
1250 req->rs = GNUNET_DNSSTUB_resolve (ctx,
1255 GNUNET_assert (NULL != req->rs);
1260 if (series > MAX_SERIES)
1263 if (pending + pending_rs >= THRESH)
1265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1266 "Stopped processing queue (%u+%u/%u)]\n",
1270 return; /* wait for replies */
1272 req = GNUNET_CONTAINER_heap_peek (req_heap);
1275 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1276 "Stopped processing queue: empty queue\n");
1279 if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0)
1281 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1282 "Waiting until %s for next record (`%s') to expire\n",
1283 GNUNET_STRINGS_absolute_time_to_string (req->expires),
1286 GNUNET_SCHEDULER_cancel (t);
1287 t = GNUNET_SCHEDULER_add_at (req->expires,
1292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1295 GNUNET_SCHEDULER_cancel (t);
1296 t = GNUNET_SCHEDULER_add_delayed (SERIES_DELAY,
1303 * Iterator called during #do_shutdown() to free requests in
1304 * the #ns_pending map.
1308 * @param value the `struct Request` to free
1309 * @return #GNUNET_OK
1312 free_request_it (void *cls,
1313 const struct GNUNET_HashCode *key,
1316 struct Request *req = value;
1326 * Clean up and terminate the process.
1331 do_shutdown (void *cls)
1333 struct Request *req;
1339 GNUNET_IDENTITY_disconnect (id);
1344 GNUNET_SCHEDULER_cancel (t);
1347 while (NULL != (req = req_head))
1349 GNUNET_CONTAINER_DLL_remove (req_head,
1352 if (NULL != req->qe)
1353 GNUNET_NAMESTORE_cancel (req->qe);
1356 while (NULL != (req = GNUNET_CONTAINER_heap_remove_root (req_heap)))
1359 if (NULL != req->qe)
1360 GNUNET_NAMESTORE_cancel (req->qe);
1363 if (NULL != zone_it)
1365 GNUNET_NAMESTORE_zone_iteration_stop (zone_it);
1370 GNUNET_NAMESTORE_disconnect (ns);
1375 GNUNET_DNSSTUB_stop (ctx);
1378 if (NULL != req_heap)
1380 GNUNET_CONTAINER_heap_destroy (req_heap);
1383 if (NULL != ns_pending)
1385 GNUNET_CONTAINER_multihashmap_iterate (ns_pending,
1388 GNUNET_CONTAINER_multihashmap_destroy (ns_pending);
1391 while (NULL != (zone = zone_head))
1393 GNUNET_CONTAINER_DLL_remove (zone_head,
1396 GNUNET_free (zone->domain);
1401 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1408 * Function called if #GNUNET_NAMESTORE_records_lookup() failed.
1409 * Just logs an error.
1411 * @param cls a `struct Zone`
1414 ns_lookup_error_cb (void *cls)
1416 struct Zone *zone = cls;
1418 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1419 "Failed to load data from namestore for zone `%s'\n",
1425 * Process a record that was stored in the namestore.
1427 * @param cls a `struct Zone *`
1428 * @param key private key of the zone
1429 * @param label label of the records
1430 * @param rd_count number of entries in @a rd array, 0 if label was deleted
1431 * @param rd array of records with data to store
1434 ns_lookup_result_cb (void *cls,
1435 const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
1437 unsigned int rd_count,
1438 const struct GNUNET_GNSRECORD_Data *rd)
1440 struct Zone *zone = cls;
1441 struct Request *req;
1442 struct GNUNET_HashCode hc;
1445 ns_iterator_trigger_next--;
1446 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1447 "Obtained NAMESTORE reply, %llu left in round\n",
1448 (unsigned long long) ns_iterator_trigger_next);
1449 if (0 == ns_iterator_trigger_next)
1451 ns_iterator_trigger_next = NS_BATCH_SIZE;
1452 GNUNET_STATISTICS_update (stats,
1453 "# NAMESTORE records requested",
1454 ns_iterator_trigger_next,
1456 GNUNET_NAMESTORE_zone_iterator_next (zone_it,
1457 ns_iterator_trigger_next);
1459 GNUNET_asprintf (&fqdn,
1463 GNUNET_CRYPTO_hash (fqdn,
1467 req = GNUNET_CONTAINER_multihashmap_get (ns_pending,
1471 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1472 "Ignoring record `%s' in zone `%s': not on my list!\n",
1477 GNUNET_assert (GNUNET_OK ==
1478 GNUNET_CONTAINER_multihashmap_remove (ns_pending,
1481 GNUNET_break (0 == memcmp (key,
1484 GNUNET_break (0 == strcasecmp (label,
1486 for (unsigned int i=0;i<rd_count;i++)
1488 struct GNUNET_TIME_Absolute at;
1490 at.abs_value_us = rd->expiration_time;
1499 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1500 "Empty record set in namestore for `%s'\n",
1505 unsigned int pos = 0;
1508 req->expires = GNUNET_TIME_UNIT_FOREVER_ABS;
1509 for (struct Record *rec = req->rec_head;
1513 struct GNUNET_TIME_Absolute at;
1515 at.abs_value_us = rec->grd.expiration_time;
1516 req->expires = GNUNET_TIME_absolute_min (req->expires,
1521 req->expires = GNUNET_TIME_UNIT_ZERO_ABS;
1522 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1523 "Hot-start with %u existing records for `%s'\n",
1529 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1530 "Adding `%s' to worklist to start at %s\n",
1532 GNUNET_STRINGS_absolute_time_to_string (req->expires));
1533 insert_sorted (req);
1538 * Add @a hostname to the list of requests to be made.
1540 * @param hostname name to resolve
1543 queue (const char *hostname)
1545 struct Request *req;
1549 struct GNUNET_HashCode hc;
1552 GNUNET_DNSPARSER_check_name (hostname))
1554 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1555 "Refusing invalid hostname `%s'\n",
1560 dot = strchr (hostname,
1561 (unsigned char) '.');
1564 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1565 "Refusing invalid hostname `%s' (lacks '.')\n",
1570 for (zone = zone_head; NULL != zone; zone = zone->next)
1571 if (0 == strcmp (zone->domain,
1577 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1578 "Domain name `%s' not in ego list!\n",
1583 hlen = strlen (hostname) + 1;
1584 req = GNUNET_malloc (sizeof (struct Request) + hlen);
1586 req->hostname = (char *) &req[1];
1587 memcpy (req->hostname,
1590 req->id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
1592 GNUNET_CRYPTO_hash (req->hostname,
1596 GNUNET_CONTAINER_multihashmap_put (ns_pending,
1599 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1601 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1602 "Duplicate hostname `%s' ignored\n",
1611 * We have completed the initial iteration over the namestore's database.
1612 * This function is called on each of the remaining records in
1613 * #move_to_queue to #queue() them, as we will simply not find existing
1614 * records for them any longer.
1618 * @param value a `struct Request`
1619 * @return #GNUNET_OK (continue to iterate)
1622 move_to_queue (void *cls,
1623 const struct GNUNET_HashCode *key,
1626 struct Request *req = value;
1630 insert_sorted (req);
1636 * Iterate over all of the zones we care about and see which records
1637 * we may need to re-fetch when.
1642 iterate_zones (void *cls)
1644 static struct Zone *last;
1647 if (NULL != zone_it)
1650 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1651 "Finished iteration over zone `%s'!\n",
1653 /* subtract left-overs from previous iteration */
1654 GNUNET_STATISTICS_update (stats,
1655 "# NAMESTORE records requested",
1656 (long long) (- ns_iterator_trigger_next),
1658 ns_iterator_trigger_next = 0;
1660 GNUNET_assert (NULL != zone_tail);
1661 if (zone_tail == last)
1663 /* Done iterating over relevant zones in NAMESTORE, move
1664 rest of hash map to work queue as well. */
1665 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1666 "Finished all NAMESTORE iterations!\n");
1667 GNUNET_STATISTICS_set (stats,
1668 "# NAMESTORE lookups without reply",
1669 GNUNET_CONTAINER_multihashmap_size (ns_pending),
1671 GNUNET_CONTAINER_multihashmap_iterate (ns_pending,
1674 GNUNET_CONTAINER_multihashmap_destroy (ns_pending);
1682 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1683 "Starting iteration over zone `%s'!\n",
1685 /* subtract left-overs from previous iteration */
1686 GNUNET_STATISTICS_update (stats,
1687 "# NAMESTORE records requested",
1690 ns_iterator_trigger_next = 1;
1691 GNUNET_STATISTICS_update (stats,
1695 zone_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
1697 &ns_lookup_error_cb,
1699 &ns_lookup_result_cb,
1708 * Begin processing hostnames from stdin.
1713 process_stdin (void *cls)
1715 static struct GNUNET_TIME_Absolute last;
1716 static unsigned int pdot;
1723 GNUNET_IDENTITY_disconnect (id);
1732 hn[strlen(hn)-1] = '\0'; /* eat newline */
1734 last = GNUNET_TIME_absolute_get ();
1736 if (0 == pdot % 1000)
1738 struct GNUNET_TIME_Relative delta;
1740 delta = GNUNET_TIME_absolute_get_duration (last);
1741 last = GNUNET_TIME_absolute_get ();
1743 "Imported 1000 records in %s\n",
1744 GNUNET_STRINGS_relative_time_to_string (delta,
1746 GNUNET_STATISTICS_set (stats,
1747 "# domain names provided",
1753 fprintf (stderr, "\n");
1754 GNUNET_STATISTICS_set (stats,
1755 "# domain names provided",
1758 iterate_zones (NULL);
1763 * Method called to inform about the egos of this peer.
1765 * When used with #GNUNET_IDENTITY_connect, this function is
1766 * initially called for all egos and then again whenever a
1767 * ego's name changes or if it is deleted. At the end of
1768 * the initial pass over all egos, the function is once called
1769 * with 'NULL' for @a ego. That does NOT mean that the callback won't
1770 * be invoked in the future or that there was an error.
1772 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
1773 * this function is only called ONCE, and 'NULL' being passed in
1774 * @a ego does indicate an error (i.e. name is taken or no default
1775 * value is known). If @a ego is non-NULL and if '*ctx'
1776 * is set in those callbacks, the value WILL be passed to a subsequent
1777 * call to the identity callback of #GNUNET_IDENTITY_connect (if
1778 * that one was not NULL).
1780 * When an identity is renamed, this function is called with the
1781 * (known) @a ego but the NEW @a name.
1783 * When an identity is deleted, this function is called with the
1784 * (known) ego and "NULL" for the @a name. In this case,
1785 * the @a ego is henceforth invalid (and the @a ctx should also be
1788 * @param cls closure
1789 * @param ego ego handle
1790 * @param ctx context for application to store data for this ego
1791 * (during the lifetime of this process, initially NULL)
1792 * @param name name assigned by the user for this ego,
1793 * NULL if the user just deleted the ego and it
1794 * must thus no longer be used
1797 identity_cb (void *cls,
1798 struct GNUNET_IDENTITY_Ego *ego,
1806 if (NULL != zone_head)
1808 t = GNUNET_SCHEDULER_add_now (&process_stdin,
1813 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1815 GNUNET_SCHEDULER_shutdown ();
1823 zone = GNUNET_new (struct Zone);
1824 zone->key = *GNUNET_IDENTITY_ego_get_private_key (ego);
1825 zone->domain = GNUNET_strdup (name);
1826 GNUNET_CONTAINER_DLL_insert (zone_head,
1834 * Process requests from the queue, then if the queue is
1835 * not empty, try again.
1838 * @param args remaining command-line arguments
1839 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1840 * @param cfg configuration
1845 const char *cfgfile,
1846 const struct GNUNET_CONFIGURATION_Handle *cfg)
1851 stats = GNUNET_STATISTICS_create ("zoneimport",
1853 req_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
1854 ns_pending = GNUNET_CONTAINER_multihashmap_create (1024,
1856 ctx = GNUNET_DNSSTUB_start (256);
1860 "Failed to initialize GNUnet DNS STUB\n");
1863 if (NULL == args[0])
1866 "You must provide a list of DNS resolvers on the command line\n");
1869 for (unsigned int i=0;NULL != args[i];i++)
1872 GNUNET_DNSSTUB_add_dns_ip (ctx,
1876 "Failed to use `%s' for DNS resolver\n",
1883 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1885 ns = GNUNET_NAMESTORE_connect (cfg);
1888 GNUNET_SCHEDULER_shutdown ();
1891 id = GNUNET_IDENTITY_connect (cfg,
1898 * Call with IP address of resolver to query.
1900 * @param argc should be 2
1901 * @param argv[1] should contain IP address
1902 * @return 0 on success
1908 struct GNUNET_GETOPT_CommandLineOption options[] = {
1909 GNUNET_GETOPT_OPTION_END
1913 GNUNET_STRINGS_get_utf8_args (argc, argv,
1916 GNUNET_PROGRAM_run (argc,
1918 "gnunet-zoneimport",
1919 "import DNS zone into namestore",
1923 GNUNET_free ((void*) argv);
1925 "Rejected %u names, had %u cached, did %u lookups, stored %u record sets\n"
1926 "Found %u records, %u lookups failed, %u/%u pending on shutdown\n",
1938 /* end of gnunet-zoneimport.c */