add option to create identity from private key
[oweals/gnunet.git] / src / namestore / gnunet-zoneimport.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2018 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
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.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @file src/namestore/gnunet-zoneimport.c
22  * @brief import a DNS zone for publication in GNS, incremental
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
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>
33
34
35 /**
36  * Maximum number of queries pending at the same time.
37  */
38 #define THRESH 100
39
40 /**
41  * TIME_THRESH is in usecs.  How quickly do we submit fresh queries.
42  * Used as an additional throttle.
43  */
44 #define TIME_THRESH 10
45
46 /**
47  * How often do we retry a query before giving up for good?
48  */
49 #define MAX_RETRIES 5
50
51 /**
52  * How many DNS requests do we at most issue in rapid series?
53  */
54 #define MAX_SERIES 10
55
56 /**
57  * How long do we wait at least between series of requests?
58  */
59 #define SERIES_DELAY \
60   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, 10)
61
62 /**
63  * How long do DNS records have to last at least after being imported?
64  */
65 static struct GNUNET_TIME_Relative minimum_expiration_time;
66
67 /**
68  * How many requests do we request from NAMESTORE in one batch
69  * during our initial iteration?
70  */
71 #define NS_BATCH_SIZE 1024
72
73 /**
74  * Some zones may include authoritative records for other
75  * zones, such as foo.com.uk or bar.com.fr.  As for GNS
76  * each dot represents a zone cut, we then need to create a
77  * zone on-the-fly to capture those records properly.
78  */
79 struct Zone
80 {
81   /**
82    * Kept in a DLL.
83    */
84   struct Zone *next;
85
86   /**
87    * Kept in a DLL.
88    */
89   struct Zone *prev;
90
91   /**
92    * Domain of the zone (i.e. "fr" or "com.fr")
93    */
94   char *domain;
95
96   /**
97    * Private key of the zone.
98    */
99   struct GNUNET_CRYPTO_EcdsaPrivateKey key;
100 };
101
102
103 /**
104  * Record for the request to be stored by GNS.
105  */
106 struct Record
107 {
108   /**
109    * Kept in a DLL.
110    */
111   struct Record *next;
112
113   /**
114    * Kept in a DLL.
115    */
116   struct Record *prev;
117
118   /**
119    * GNS record.
120    */
121   struct GNUNET_GNSRECORD_Data grd;
122 };
123
124
125 /**
126  * Request we should make.  We keep this struct in memory per request,
127  * thus optimizing it is crucial for the overall memory consumption of
128  * the zone importer.
129  */
130 struct Request
131 {
132   /**
133    * Requests are kept in a heap while waiting to be resolved.
134    */
135   struct GNUNET_CONTAINER_HeapNode *hn;
136
137   /**
138    * Active requests are kept in a DLL.
139    */
140   struct Request *next;
141
142   /**
143    * Active requests are kept in a DLL.
144    */
145   struct Request *prev;
146
147   /**
148    * Head of records that should be published in GNS for
149    * this hostname.
150    */
151   struct Record *rec_head;
152
153   /**
154    * Tail of records that should be published in GNS for
155    * this hostname.
156    */
157   struct Record *rec_tail;
158
159   /**
160    * Socket used to make the request, NULL if not active.
161    */
162   struct GNUNET_DNSSTUB_RequestSocket *rs;
163
164   /**
165    * Hostname we are resolving, allocated at the end of
166    * this struct (optimizing memory consumption by reducing
167    * total number of allocations).
168    */
169   char *hostname;
170
171   /**
172    * Namestore operation pending for this record.
173    */
174   struct GNUNET_NAMESTORE_QueueEntry *qe;
175
176   /**
177    * Zone responsible for this request.
178    */
179   const struct Zone *zone;
180
181   /**
182    * At what time does the (earliest) of the returned records
183    * for this name expire? At this point, we need to re-fetch
184    * the record.
185    */
186   struct GNUNET_TIME_Absolute expires;
187
188   /**
189    * While we are fetching the record, the value is set to the
190    * starting time of the DNS operation.  While doing a
191    * NAMESTORE store, again set to the start time of the
192    * NAMESTORE operation.
193    */
194   struct GNUNET_TIME_Absolute op_start_time;
195
196   /**
197    * How often did we issue this query? (And failed, reset
198    * to zero once we were successful.)
199    */
200   unsigned int issue_num;
201
202   /**
203    * random 16-bit DNS query identifier.
204    */
205   uint16_t id;
206 };
207
208
209 /**
210  * Command-line argument specifying desired size of the hash map with
211  * all of our pending names.  Usually, we use an automatically growing
212  * map, but this is only OK up to about a million entries.  Above that
213  * number, the user must explicitly specify the size at startup.
214  */
215 static unsigned int map_size = 1024;
216
217 /**
218  * Handle to the identity service.
219  */
220 static struct GNUNET_IDENTITY_Handle *id;
221
222 /**
223  * Namestore handle.
224  */
225 static struct GNUNET_NAMESTORE_Handle *ns;
226
227 /**
228  * Handle to the statistics service.
229  */
230 static struct GNUNET_STATISTICS_Handle *stats;
231
232 /**
233  * Context for DNS resolution.
234  */
235 static struct GNUNET_DNSSTUB_Context *ctx;
236
237 /**
238  * The number of DNS queries that are outstanding
239  */
240 static unsigned int pending;
241
242 /**
243  * The number of NAMESTORE record store operations that are outstanding
244  */
245 static unsigned int pending_rs;
246
247 /**
248  * Number of lookups we performed overall.
249  */
250 static unsigned int lookups;
251
252 /**
253  * Number of records we had cached.
254  */
255 static unsigned int cached;
256
257 /**
258  * How many hostnames did we reject (malformed).
259  */
260 static unsigned int rejects;
261
262 /**
263  * Number of lookups that failed.
264  */
265 static unsigned int failures;
266
267 /**
268  * Number of records we found.
269  */
270 static unsigned int records;
271
272 /**
273  * Number of record sets given to namestore.
274  */
275 static unsigned int record_sets;
276
277 /**
278  * Heap of all requests to perform, sorted by
279  * the time we should next do the request (i.e. by expires).
280  */
281 static struct GNUNET_CONTAINER_Heap *req_heap;
282
283 /**
284  * Active requests are kept in a DLL.
285  */
286 static struct Request *req_head;
287
288 /**
289  * Active requests are kept in a DLL.
290  */
291 static struct Request *req_tail;
292
293 /**
294  * Main task.
295  */
296 static struct GNUNET_SCHEDULER_Task *t;
297
298 /**
299  * Hash map of requests for which we may still get a response from
300  * the namestore.  Set to NULL once the initial namestore iteration
301  * is done.
302  */
303 static struct GNUNET_CONTAINER_MultiHashMap *ns_pending;
304
305 /**
306  * Current zone iteration handle.
307  */
308 static struct GNUNET_NAMESTORE_ZoneIterator *zone_it;
309
310 /**
311  * Head of list of zones we are managing.
312  */
313 static struct Zone *zone_head;
314
315 /**
316  * Tail of list of zones we are managing.
317  */
318 static struct Zone *zone_tail;
319
320 /**
321  * After how many more results must #ns_lookup_result_cb() ask
322  * the namestore for more?
323  */
324 static uint64_t ns_iterator_trigger_next;
325
326 /**
327  * Number of DNS requests counted in latency total.
328  */
329 static uint64_t total_dns_latency_cnt;
330
331 /**
332  * Sum of DNS latencies observed.
333  */
334 static struct GNUNET_TIME_Relative total_dns_latency;
335
336 /**
337  * Number of records processed (DNS lookup, no NAMESTORE) in total.
338  */
339 static uint64_t total_reg_proc_dns;
340
341 /**
342  * Number of records processed (DNS lookup, with NAMESTORE) in total.
343  */
344 static uint64_t total_reg_proc_dns_ns;
345
346 /**
347  * Start time of the regular processing.
348  */
349 static struct GNUNET_TIME_Absolute start_time_reg_proc;
350
351 /**
352  * Last time we worked before going idle.
353  */
354 static struct GNUNET_TIME_Absolute sleep_time_reg_proc;
355
356 /**
357  * Time we slept just waiting for work.
358  */
359 static struct GNUNET_TIME_Relative idle_time;
360
361
362 /**
363  * Callback for #for_all_records
364  *
365  * @param cls closure
366  * @param rec a DNS record
367  */
368 typedef void (*RecordProcessor) (void *cls,
369                                  const struct GNUNET_DNSPARSER_Record *rec);
370
371
372 /**
373  * Call @a rp for each record in @a p, regardless of
374  * what response section it is in.
375  *
376  * @param p packet from DNS
377  * @param rp function to call
378  * @param rp_cls closure for @a rp
379  */
380 static void
381 for_all_records (const struct GNUNET_DNSPARSER_Packet *p,
382                  RecordProcessor rp,
383                  void *rp_cls)
384 {
385   for (unsigned int i = 0; i < p->num_answers; i++)
386   {
387     struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
388
389     rp (rp_cls, rs);
390   }
391   for (unsigned int i = 0; i < p->num_authority_records; i++)
392   {
393     struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
394
395     rp (rp_cls, rs);
396   }
397   for (unsigned int i = 0; i < p->num_additional_records; i++)
398   {
399     struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
400
401     rp (rp_cls, rs);
402   }
403 }
404
405
406 /**
407  * Return just the label of the hostname in @a req.
408  *
409  * @param req request to process hostname of
410  * @return statically allocated pointer to the label,
411  *         overwritten upon the next request!
412  */
413 static const char *
414 get_label (struct Request *req)
415 {
416   static char label[64];
417   const char *dot;
418
419   dot = strchr (req->hostname, (unsigned char) '.');
420   if (NULL == dot)
421   {
422     GNUNET_break (0);
423     return NULL;
424   }
425   if (((size_t) (dot - req->hostname)) >= sizeof(label))
426   {
427     GNUNET_break (0);
428     return NULL;
429   }
430   GNUNET_memcpy (label, req->hostname, dot - req->hostname);
431   label[dot - req->hostname] = '\0';
432   return label;
433 }
434
435
436 /**
437  * Build DNS query for @a hostname.
438  *
439  * @param hostname host to build query for
440  * @param raw_size[out] number of bytes in the query
441  * @return NULL on error, otherwise pointer to statically (!)
442  *         allocated query buffer
443  */
444 static void *
445 build_dns_query (struct Request *req, size_t *raw_size)
446 {
447   static char raw[512];
448   char *rawp;
449   struct GNUNET_DNSPARSER_Packet p;
450   struct GNUNET_DNSPARSER_Query q;
451   int ret;
452
453   q.name = (char *) req->hostname;
454   q.type = GNUNET_DNSPARSER_TYPE_NS;
455   q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
456
457   memset (&p, 0, sizeof(p));
458   p.num_queries = 1;
459   p.queries = &q;
460   p.id = req->id;
461   ret = GNUNET_DNSPARSER_pack (&p, UINT16_MAX, &rawp, raw_size);
462   if (GNUNET_OK != ret)
463   {
464     if (GNUNET_NO == ret)
465       GNUNET_free (rawp);
466     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
467                 "Failed to pack query for hostname `%s'\n",
468                 req->hostname);
469     rejects++;
470     return NULL;
471   }
472   if (*raw_size > sizeof(raw))
473   {
474     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
475                 "Failed to pack query for hostname `%s'\n",
476                 req->hostname);
477     rejects++;
478     GNUNET_break (0);
479     GNUNET_free (rawp);
480     return NULL;
481   }
482   GNUNET_memcpy (raw, rawp, *raw_size);
483   GNUNET_free (rawp);
484   return raw;
485 }
486
487
488 /**
489  * Free records associated with @a req.
490  *
491  * @param req request to free records of
492  */
493 static void
494 free_records (struct Request *req)
495 {
496   struct Record *rec;
497
498   /* Free records */
499   while (NULL != (rec = req->rec_head))
500   {
501     GNUNET_CONTAINER_DLL_remove (req->rec_head, req->rec_tail, rec);
502     GNUNET_free (rec);
503   }
504 }
505
506
507 /**
508  * Free @a req and data structures reachable from it.
509  *
510  * @param req request to free
511  */
512 static void
513 free_request (struct Request *req)
514 {
515   free_records (req);
516   GNUNET_free (req);
517 }
518
519
520 /**
521  * Process as many requests as possible from the queue.
522  *
523  * @param cls NULL
524  */
525 static void
526 process_queue (void *cls);
527
528
529 /**
530  * Insert @a req into DLL sorted by next fetch time.
531  *
532  * @param req request to insert into #req_heap
533  */
534 static void
535 insert_sorted (struct Request *req)
536 {
537   req->hn =
538     GNUNET_CONTAINER_heap_insert (req_heap, req, req->expires.abs_value_us);
539   if (req == GNUNET_CONTAINER_heap_peek (req_heap))
540   {
541     if (NULL != t)
542       GNUNET_SCHEDULER_cancel (t);
543     sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
544     t = GNUNET_SCHEDULER_add_at (req->expires, &process_queue, NULL);
545   }
546 }
547
548
549 /**
550  * Add record to the GNS record set for @a req.
551  *
552  * @param req the request to expand GNS record set for
553  * @param type type to use
554  * @param expiration_time when should @a rec expire
555  * @param data raw data to store
556  * @param data_len number of bytes in @a data
557  */
558 static void
559 add_record (struct Request *req,
560             uint32_t type,
561             struct GNUNET_TIME_Absolute expiration_time,
562             const void *data,
563             size_t data_len)
564 {
565   struct Record *rec;
566
567   rec = GNUNET_malloc (sizeof(struct Record) + data_len);
568   rec->grd.data = &rec[1];
569   rec->grd.expiration_time = expiration_time.abs_value_us;
570   rec->grd.data_size = data_len;
571   rec->grd.record_type = type;
572   rec->grd.flags = GNUNET_GNSRECORD_RF_NONE;
573   GNUNET_memcpy (&rec[1], data, data_len);
574   GNUNET_CONTAINER_DLL_insert (req->rec_head, req->rec_tail, rec);
575 }
576
577
578 /**
579  * Closure for #check_for_glue.
580  */
581 struct GlueClosure
582 {
583   /**
584    * Overall request we are processing.
585    */
586   struct Request *req;
587
588   /**
589    * NS name we are looking for glue for.
590    */
591   const char *ns;
592
593   /**
594    * Set to #GNUNET_YES if glue was found.
595    */
596   int found;
597 };
598
599
600 /**
601  * Try to find glue records for a given NS record.
602  *
603  * @param cls a `struct GlueClosure *`
604  * @param rec record that may contain glue information
605  */
606 static void
607 check_for_glue (void *cls, const struct GNUNET_DNSPARSER_Record *rec)
608 {
609   struct GlueClosure *gc = cls;
610   char dst[65536];
611   size_t dst_len;
612   size_t off;
613   char ip[INET6_ADDRSTRLEN + 1];
614   socklen_t ip_size = (socklen_t) sizeof(ip);
615   struct GNUNET_TIME_Absolute expiration_time;
616   struct GNUNET_TIME_Relative left;
617
618   if (0 != strcasecmp (rec->name, gc->ns))
619     return;
620   expiration_time = rec->expiration_time;
621   left = GNUNET_TIME_absolute_get_remaining (expiration_time);
622   if (0 == left.rel_value_us)
623     return; /* ignore expired glue records */
624   /* if expiration window is too short, bump it to configured minimum */
625   if (left.rel_value_us < minimum_expiration_time.rel_value_us)
626     expiration_time =
627       GNUNET_TIME_relative_to_absolute (minimum_expiration_time);
628   dst_len = sizeof(dst);
629   off = 0;
630   switch (rec->type)
631   {
632   case GNUNET_DNSPARSER_TYPE_A:
633     if (sizeof(struct in_addr) != rec->data.raw.data_len)
634     {
635       GNUNET_break (0);
636       return;
637     }
638     if (NULL == inet_ntop (AF_INET, rec->data.raw.data, ip, ip_size))
639     {
640       GNUNET_break (0);
641       return;
642     }
643     if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
644                                                          dst_len,
645                                                          &off,
646                                                          gc->req->hostname)) &&
647         (GNUNET_OK ==
648          GNUNET_DNSPARSER_builder_add_name (dst, dst_len, &off, ip)))
649     {
650       add_record (gc->req,
651                   GNUNET_GNSRECORD_TYPE_GNS2DNS,
652                   expiration_time,
653                   dst,
654                   off);
655       gc->found = GNUNET_YES;
656     }
657     break;
658
659   case GNUNET_DNSPARSER_TYPE_AAAA:
660     if (sizeof(struct in6_addr) != rec->data.raw.data_len)
661     {
662       GNUNET_break (0);
663       return;
664     }
665     if (NULL == inet_ntop (AF_INET6, rec->data.raw.data, ip, ip_size))
666     {
667       GNUNET_break (0);
668       return;
669     }
670     if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
671                                                          dst_len,
672                                                          &off,
673                                                          gc->req->hostname)) &&
674         (GNUNET_OK ==
675          GNUNET_DNSPARSER_builder_add_name (dst, dst_len, &off, ip)))
676     {
677       add_record (gc->req,
678                   GNUNET_GNSRECORD_TYPE_GNS2DNS,
679                   expiration_time,
680                   dst,
681                   off);
682       gc->found = GNUNET_YES;
683     }
684     break;
685
686   case GNUNET_DNSPARSER_TYPE_CNAME:
687     if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
688                                                          dst_len,
689                                                          &off,
690                                                          gc->req->hostname)) &&
691         (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
692                                                          dst_len,
693                                                          &off,
694                                                          rec->data.hostname)))
695     {
696       add_record (gc->req,
697                   GNUNET_GNSRECORD_TYPE_GNS2DNS,
698                   expiration_time,
699                   dst,
700                   off);
701       gc->found = GNUNET_YES;
702     }
703     break;
704
705   default:
706     /* useless, do nothing */
707     break;
708   }
709 }
710
711
712 /**
713  * Closure for #process_record().
714  */
715 struct ProcessRecordContext
716 {
717   /**
718    * Answer we got back and are currently parsing, or NULL
719    * if not active.
720    */
721   struct GNUNET_DNSPARSER_Packet *p;
722
723   /**
724    * Request we are processing.
725    */
726   struct Request *req;
727 };
728
729
730 /**
731  * We received @a rec for @a req. Remember the answer.
732  *
733  * @param cls a `struct ProcessRecordContext`
734  * @param rec response
735  */
736 static void
737 process_record (void *cls, const struct GNUNET_DNSPARSER_Record *rec)
738 {
739   struct ProcessRecordContext *prc = cls;
740   struct Request *req = prc->req;
741   char dst[65536];
742   size_t dst_len;
743   size_t off;
744   struct GNUNET_TIME_Absolute expiration_time;
745   struct GNUNET_TIME_Relative left;
746
747   dst_len = sizeof(dst);
748   off = 0;
749   records++;
750   if (0 != strcasecmp (rec->name, req->hostname))
751   {
752     GNUNET_log (
753       GNUNET_ERROR_TYPE_DEBUG,
754       "DNS returned record from zone `%s' of type %u while resolving `%s'\n",
755       rec->name,
756       (unsigned int) rec->type,
757       req->hostname);
758     return;   /* does not match hostname, might be glue, but
759                  not useful for this pass! */
760   }
761   expiration_time = rec->expiration_time;
762   left = GNUNET_TIME_absolute_get_remaining (expiration_time);
763   if (0 == left.rel_value_us)
764   {
765     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
766                 "DNS returned expired record for `%s'\n",
767                 req->hostname);
768     GNUNET_STATISTICS_update (stats,
769                               "# expired records obtained from DNS",
770                               1,
771                               GNUNET_NO);
772     return;   /* record expired */
773   }
774
775   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
776               "DNS returned record that expires at %s for `%s'\n",
777               GNUNET_STRINGS_absolute_time_to_string (expiration_time),
778               req->hostname);
779   /* if expiration window is too short, bump it to configured minimum */
780   if (left.rel_value_us < minimum_expiration_time.rel_value_us)
781     expiration_time =
782       GNUNET_TIME_relative_to_absolute (minimum_expiration_time);
783   switch (rec->type)
784   {
785   case GNUNET_DNSPARSER_TYPE_NS: {
786       struct GlueClosure gc;
787
788       /* check for glue */
789       gc.req = req;
790       gc.ns = rec->data.hostname;
791       gc.found = GNUNET_NO;
792       for_all_records (prc->p, &check_for_glue, &gc);
793       if ((GNUNET_NO == gc.found) &&
794           (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
795                                                            dst_len,
796                                                            &off,
797                                                            req->hostname)) &&
798           (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
799                                                            dst_len,
800                                                            &off,
801                                                            rec->data.hostname)))
802       {
803         /* FIXME: actually check if this is out-of-bailiwick,
804              and if not request explicit resolution... */
805         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
806                     "Converted OOB (`%s') NS record for `%s'\n",
807                     rec->data.hostname,
808                     rec->name);
809         add_record (req,
810                     GNUNET_GNSRECORD_TYPE_GNS2DNS,
811                     expiration_time,
812                     dst,
813                     off);
814       }
815       else
816       {
817         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
818                     "Converted NS record for `%s' using glue\n",
819                     rec->name);
820       }
821       break;
822     }
823
824   case GNUNET_DNSPARSER_TYPE_CNAME:
825     if (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
826                                                         dst_len,
827                                                         &off,
828                                                         rec->data.hostname))
829     {
830       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
831                   "Converting CNAME (`%s') record for `%s'\n",
832                   rec->data.hostname,
833                   rec->name);
834       add_record (req, rec->type, expiration_time, dst, off);
835     }
836     break;
837
838   case GNUNET_DNSPARSER_TYPE_DNAME:
839     /* No support for DNAME in GNS yet! FIXME: support later! */
840     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
841                 "FIXME: not supported: %s DNAME %s\n",
842                 rec->name,
843                 rec->data.hostname);
844     break;
845
846   case GNUNET_DNSPARSER_TYPE_MX:
847     if (GNUNET_OK ==
848         GNUNET_DNSPARSER_builder_add_mx (dst, dst_len, &off, rec->data.mx))
849     {
850       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
851                   "Converting MX (`%s') record for `%s'\n",
852                   rec->data.mx->mxhost,
853                   rec->name);
854       add_record (req, rec->type, expiration_time, dst, off);
855     }
856     break;
857
858   case GNUNET_DNSPARSER_TYPE_SOA:
859     if (GNUNET_OK ==
860         GNUNET_DNSPARSER_builder_add_soa (dst, dst_len, &off, rec->data.soa))
861     {
862       /* NOTE: GNS does not really use SOAs */
863       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864                   "Converting SOA record for `%s'\n",
865                   rec->name);
866       add_record (req, rec->type, expiration_time, dst, off);
867     }
868     break;
869
870   case GNUNET_DNSPARSER_TYPE_SRV:
871     if (GNUNET_OK ==
872         GNUNET_DNSPARSER_builder_add_srv (dst, dst_len, &off, rec->data.srv))
873     {
874       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
875                   "Converting SRV record for `%s'\n",
876                   rec->name);
877       add_record (req, rec->type, expiration_time, dst, off);
878     }
879     break;
880
881   case GNUNET_DNSPARSER_TYPE_PTR:
882     if (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst,
883                                                         dst_len,
884                                                         &off,
885                                                         rec->data.hostname))
886     {
887       /* !?: what does a PTR record do in a regular TLD??? */
888       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889                   "Converting PTR record for `%s' (weird)\n",
890                   rec->name);
891       add_record (req, rec->type, expiration_time, dst, off);
892     }
893     break;
894
895   case GNUNET_DNSPARSER_TYPE_CERT:
896     if (GNUNET_OK ==
897         GNUNET_DNSPARSER_builder_add_cert (dst, dst_len, &off, rec->data.cert))
898     {
899       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
900                   "Converting CERT record for `%s'\n",
901                   rec->name);
902       add_record (req, rec->type, expiration_time, dst, off);
903     }
904     break;
905
906   /* Rest is 'raw' encoded and just needs to be copied IF
907      the hostname matches the requested name; otherwise we
908      simply cannot use it. */
909   case GNUNET_DNSPARSER_TYPE_A:
910   case GNUNET_DNSPARSER_TYPE_AAAA:
911   case GNUNET_DNSPARSER_TYPE_TXT:
912   default:
913     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
914                 "Converting record of type %u for `%s'\n",
915                 (unsigned int) rec->type,
916                 rec->name);
917     add_record (req,
918                 rec->type,
919                 expiration_time,
920                 rec->data.raw.data,
921                 rec->data.raw.data_len);
922     break;
923   }
924 }
925
926
927 /**
928  * Continuation called to notify client about result of the
929  * operation.
930  *
931  * @param cls closure with our `struct Request`
932  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
933  *                #GNUNET_NO if content was already there or not found
934  *                #GNUNET_YES (or other positive value) on success
935  * @param emsg NULL on success, otherwise an error message
936  */
937 static void
938 store_completed_cb (void *cls, int32_t success, const char *emsg)
939 {
940   static struct GNUNET_TIME_Absolute last;
941   struct Request *req = cls;
942
943   req->qe = NULL;
944   if (GNUNET_SYSERR == success)
945   {
946     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
947                 "Failed to store zone data for `%s': %s\n",
948                 req->hostname,
949                 emsg);
950   }
951   else
952   {
953     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
954                 "Stored records under `%s' (%d)\n",
955                 req->hostname,
956                 success);
957   }
958   total_reg_proc_dns_ns++; /* finished regular processing */
959   pending_rs--;
960   free_records (req);
961   /* compute NAMESTORE statistics */
962   {
963     static uint64_t total_ns_latency_cnt;
964     static struct GNUNET_TIME_Relative total_ns_latency;
965     struct GNUNET_TIME_Relative ns_latency;
966
967     ns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time);
968     total_ns_latency = GNUNET_TIME_relative_add (total_ns_latency, ns_latency);
969     if (0 == total_ns_latency_cnt)
970       last = GNUNET_TIME_absolute_get ();
971     total_ns_latency_cnt++;
972     if (0 == (total_ns_latency_cnt % 1000))
973     {
974       struct GNUNET_TIME_Relative delta;
975
976       delta = GNUNET_TIME_absolute_get_duration (last);
977       last = GNUNET_TIME_absolute_get ();
978       fprintf (stderr,
979                "Processed 1000 records in %s\n",
980                GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES));
981       GNUNET_STATISTICS_set (stats,
982                              "# average NAMESTORE PUT latency (μs)",
983                              total_ns_latency.rel_value_us
984                              / total_ns_latency_cnt,
985                              GNUNET_NO);
986     }
987   }
988   /* compute and publish overall velocity */
989   if (0 == (total_reg_proc_dns_ns % 100))
990   {
991     struct GNUNET_TIME_Relative runtime;
992
993     runtime = GNUNET_TIME_absolute_get_duration (start_time_reg_proc);
994     runtime = GNUNET_TIME_relative_subtract (runtime, idle_time);
995     runtime =
996       GNUNET_TIME_relative_divide (runtime,
997                                    total_reg_proc_dns + total_reg_proc_dns_ns);
998     GNUNET_STATISTICS_set (stats,
999                            "# Regular processing completed without NAMESTORE",
1000                            total_reg_proc_dns,
1001                            GNUNET_NO);
1002     GNUNET_STATISTICS_set (stats,
1003                            "# Regular processing completed with NAMESTORE PUT",
1004                            total_reg_proc_dns_ns,
1005                            GNUNET_NO);
1006     GNUNET_STATISTICS_set (stats,
1007                            "# average request processing latency (μs)",
1008                            runtime.rel_value_us,
1009                            GNUNET_NO);
1010     GNUNET_STATISTICS_set (stats,
1011                            "# total time spent idle (μs)",
1012                            idle_time.rel_value_us,
1013                            GNUNET_NO);
1014   }
1015
1016   if (NULL == t)
1017   {
1018     sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1019     t = GNUNET_SCHEDULER_add_now (&process_queue, NULL);
1020   }
1021 }
1022
1023
1024 /**
1025  * Function called with the result of a DNS resolution.
1026  *
1027  * @param cls closure with the `struct Request`
1028  * @param dns dns response, never NULL
1029  * @param dns_len number of bytes in @a dns
1030  */
1031 static void
1032 process_result (void *cls,
1033                 const struct GNUNET_TUN_DnsHeader *dns,
1034                 size_t dns_len)
1035 {
1036   struct Request *req = cls;
1037   struct Record *rec;
1038   struct GNUNET_DNSPARSER_Packet *p;
1039   unsigned int rd_count;
1040
1041   GNUNET_assert (NULL == req->hn);
1042   if (NULL == dns)
1043   {
1044     /* stub gave up */
1045     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req);
1046     pending--;
1047     if (NULL == t)
1048     {
1049       sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1050       t = GNUNET_SCHEDULER_add_now (&process_queue, NULL);
1051     }
1052     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1053                 "Stub gave up on DNS reply for `%s'\n",
1054                 req->hostname);
1055     GNUNET_STATISTICS_update (stats, "# DNS lookups timed out", 1, GNUNET_NO);
1056     if (req->issue_num > MAX_RETRIES)
1057     {
1058       failures++;
1059       free_request (req);
1060       GNUNET_STATISTICS_update (stats, "# requests given up on", 1, GNUNET_NO);
1061       return;
1062     }
1063     total_reg_proc_dns++;
1064     req->rs = NULL;
1065     insert_sorted (req);
1066     return;
1067   }
1068   if (req->id != dns->id)
1069   {
1070     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1071                 "DNS ID did not match request, ignoring reply\n");
1072     GNUNET_STATISTICS_update (stats, "# DNS ID mismatches", 1, GNUNET_NO);
1073     return;
1074   }
1075   GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req);
1076   GNUNET_DNSSTUB_resolve_cancel (req->rs);
1077   req->rs = NULL;
1078   pending--;
1079   p = GNUNET_DNSPARSER_parse ((const char *) dns, dns_len);
1080   if (NULL == p)
1081   {
1082     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1083                 "Failed to parse DNS reply for `%s'\n",
1084                 req->hostname);
1085     GNUNET_STATISTICS_update (stats, "# DNS parser errors", 1, GNUNET_NO);
1086     if (NULL == t)
1087     {
1088       sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1089       t = GNUNET_SCHEDULER_add_now (&process_queue, NULL);
1090     }
1091     if (req->issue_num > MAX_RETRIES)
1092     {
1093       failures++;
1094       free_request (req);
1095       GNUNET_STATISTICS_update (stats, "# requests given up on", 1, GNUNET_NO);
1096       return;
1097     }
1098     insert_sorted (req);
1099     return;
1100   }
1101   /* import new records */
1102   req->issue_num = 0; /* success, reset counter! */
1103   {
1104     struct ProcessRecordContext prc = { .req = req, .p = p };
1105
1106     for_all_records (p, &process_record, &prc);
1107   }
1108   GNUNET_DNSPARSER_free_packet (p);
1109   /* count records found, determine minimum expiration time */
1110   req->expires = GNUNET_TIME_UNIT_FOREVER_ABS;
1111   {
1112     struct GNUNET_TIME_Relative dns_latency;
1113
1114     dns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time);
1115     total_dns_latency =
1116       GNUNET_TIME_relative_add (total_dns_latency, dns_latency);
1117     total_dns_latency_cnt++;
1118     if (0 == (total_dns_latency_cnt % 1000))
1119     {
1120       GNUNET_STATISTICS_set (stats,
1121                              "# average DNS lookup latency (μs)",
1122                              total_dns_latency.rel_value_us
1123                              / total_dns_latency_cnt,
1124                              GNUNET_NO);
1125     }
1126   }
1127   rd_count = 0;
1128   for (rec = req->rec_head; NULL != rec; rec = rec->next)
1129   {
1130     struct GNUNET_TIME_Absolute at;
1131
1132     at.abs_value_us = rec->grd.expiration_time;
1133     req->expires = GNUNET_TIME_absolute_min (req->expires, at);
1134     rd_count++;
1135   }
1136   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1137               "Obtained %u records for `%s'\n",
1138               rd_count,
1139               req->hostname);
1140   /* Instead of going for SOA, simplified for now to look each
1141      day in case we got an empty response */
1142   if (0 == rd_count)
1143   {
1144     req->expires = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
1145     GNUNET_STATISTICS_update (stats,
1146                               "# empty DNS replies (usually NXDOMAIN)",
1147                               1,
1148                               GNUNET_NO);
1149   }
1150   else
1151   {
1152     record_sets++;
1153   }
1154   /* convert records to namestore import format */
1155   {
1156     struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)];
1157     unsigned int off = 0;
1158
1159     /* convert linked list into array */
1160     for (rec = req->rec_head; NULL != rec; rec = rec->next)
1161       rd[off++] = rec->grd;
1162     pending_rs++;
1163     req->op_start_time = GNUNET_TIME_absolute_get ();
1164     req->qe = GNUNET_NAMESTORE_records_store (ns,
1165                                               &req->zone->key,
1166                                               get_label (req),
1167                                               rd_count,
1168                                               rd,
1169                                               &store_completed_cb,
1170                                               req);
1171     GNUNET_assert (NULL != req->qe);
1172   }
1173   insert_sorted (req);
1174 }
1175
1176
1177 /**
1178  * Process as many requests as possible from the queue.
1179  *
1180  * @param cls NULL
1181  */
1182 static void
1183 process_queue (void *cls)
1184 {
1185   struct Request *req;
1186   unsigned int series;
1187   void *raw;
1188   size_t raw_size;
1189   struct GNUNET_TIME_Relative delay;
1190
1191   (void) cls;
1192   delay = GNUNET_TIME_absolute_get_duration (sleep_time_reg_proc);
1193   idle_time = GNUNET_TIME_relative_add (idle_time, delay);
1194   series = 0;
1195   t = NULL;
1196   while (pending + pending_rs < THRESH)
1197   {
1198     req = GNUNET_CONTAINER_heap_peek (req_heap);
1199     if (NULL == req)
1200       break;
1201     if (NULL != req->qe)
1202       return;   /* namestore op still pending */
1203     if (NULL != req->rs)
1204     {
1205       GNUNET_break (0);
1206       return;     /* already submitted */
1207     }
1208     if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0)
1209       break;
1210     GNUNET_assert (req == GNUNET_CONTAINER_heap_remove_root (req_heap));
1211     req->hn = NULL;
1212     GNUNET_CONTAINER_DLL_insert (req_head, req_tail, req);
1213     GNUNET_assert (NULL == req->rs);
1214     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1215                 "Requesting resolution for `%s'\n",
1216                 req->hostname);
1217     raw = build_dns_query (req, &raw_size);
1218     if (NULL == raw)
1219     {
1220       GNUNET_break (0);
1221       free_request (req);
1222       continue;
1223     }
1224     req->op_start_time = GNUNET_TIME_absolute_get ();
1225     req->rs = GNUNET_DNSSTUB_resolve (ctx, raw, raw_size, &process_result, req);
1226     GNUNET_assert (NULL != req->rs);
1227     req->issue_num++;
1228     lookups++;
1229     pending++;
1230     series++;
1231     if (series > MAX_SERIES)
1232       break;
1233   }
1234   if (pending + pending_rs >= THRESH)
1235   {
1236     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1237                 "Stopped processing queue (%u+%u/%u)]\n",
1238                 pending,
1239                 pending_rs,
1240                 THRESH);
1241     return;   /* wait for replies */
1242   }
1243   req = GNUNET_CONTAINER_heap_peek (req_heap);
1244   if (NULL == req)
1245   {
1246     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1247                 "Stopped processing queue: empty queue\n");
1248     return;
1249   }
1250   if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0)
1251   {
1252     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1253                 "Waiting until %s for next record (`%s') to expire\n",
1254                 GNUNET_STRINGS_absolute_time_to_string (req->expires),
1255                 req->hostname);
1256     if (NULL != t)
1257       GNUNET_SCHEDULER_cancel (t);
1258     sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1259     t = GNUNET_SCHEDULER_add_at (req->expires, &process_queue, NULL);
1260     return;
1261   }
1262   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Throttling\n");
1263   if (NULL != t)
1264     GNUNET_SCHEDULER_cancel (t);
1265   sleep_time_reg_proc = GNUNET_TIME_absolute_get ();
1266   t = GNUNET_SCHEDULER_add_delayed (SERIES_DELAY, &process_queue, NULL);
1267 }
1268
1269
1270 /**
1271  * Iterator called during #do_shutdown() to free requests in
1272  * the #ns_pending map.
1273  *
1274  * @param cls NULL
1275  * @param key unused
1276  * @param value the `struct Request` to free
1277  * @return #GNUNET_OK
1278  */
1279 static int
1280 free_request_it (void *cls, const struct GNUNET_HashCode *key, void *value)
1281 {
1282   struct Request *req = value;
1283
1284   (void) cls;
1285   (void) key;
1286   free_request (req);
1287   return GNUNET_OK;
1288 }
1289
1290
1291 /**
1292  * Clean up and terminate the process.
1293  *
1294  * @param cls NULL
1295  */
1296 static void
1297 do_shutdown (void *cls)
1298 {
1299   struct Request *req;
1300   struct Zone *zone;
1301
1302   (void) cls;
1303   if (NULL != id)
1304   {
1305     GNUNET_IDENTITY_disconnect (id);
1306     id = NULL;
1307   }
1308   if (NULL != t)
1309   {
1310     GNUNET_SCHEDULER_cancel (t);
1311     t = NULL;
1312   }
1313   while (NULL != (req = req_head))
1314   {
1315     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req);
1316     if (NULL != req->qe)
1317       GNUNET_NAMESTORE_cancel (req->qe);
1318     free_request (req);
1319   }
1320   while (NULL != (req = GNUNET_CONTAINER_heap_remove_root (req_heap)))
1321   {
1322     req->hn = NULL;
1323     if (NULL != req->qe)
1324       GNUNET_NAMESTORE_cancel (req->qe);
1325     free_request (req);
1326   }
1327   if (NULL != zone_it)
1328   {
1329     GNUNET_NAMESTORE_zone_iteration_stop (zone_it);
1330     zone_it = NULL;
1331   }
1332   if (NULL != ns)
1333   {
1334     GNUNET_NAMESTORE_disconnect (ns);
1335     ns = NULL;
1336   }
1337   if (NULL != ctx)
1338   {
1339     GNUNET_DNSSTUB_stop (ctx);
1340     ctx = NULL;
1341   }
1342   if (NULL != req_heap)
1343   {
1344     GNUNET_CONTAINER_heap_destroy (req_heap);
1345     req_heap = NULL;
1346   }
1347   if (NULL != ns_pending)
1348   {
1349     GNUNET_CONTAINER_multihashmap_iterate (ns_pending, &free_request_it, NULL);
1350     GNUNET_CONTAINER_multihashmap_destroy (ns_pending);
1351     ns_pending = NULL;
1352   }
1353   while (NULL != (zone = zone_head))
1354   {
1355     GNUNET_CONTAINER_DLL_remove (zone_head, zone_tail, zone);
1356     GNUNET_free (zone->domain);
1357     GNUNET_free (zone);
1358   }
1359   if (NULL != stats)
1360   {
1361     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1362     stats = NULL;
1363   }
1364 }
1365
1366
1367 /**
1368  * Iterate over all of the zones we care about and see which records
1369  * we may need to re-fetch when.
1370  *
1371  * @param cls NULL
1372  */
1373 static void
1374 iterate_zones (void *cls);
1375
1376
1377 /**
1378  * Function called if #GNUNET_NAMESTORE_records_lookup() failed.
1379  * Just logs an error.
1380  *
1381  * @param cls a `struct Zone`
1382  */
1383 static void
1384 ns_lookup_error_cb (void *cls)
1385 {
1386   struct Zone *zone = cls;
1387
1388   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1389               "Failed to load data from namestore for zone `%s'\n",
1390               zone->domain);
1391   zone_it = NULL;
1392   ns_iterator_trigger_next = 0;
1393   iterate_zones (NULL);
1394 }
1395
1396
1397 /**
1398  * Process a record that was stored in the namestore.
1399  *
1400  * @param cls a `struct Zone *`
1401  * @param key private key of the zone
1402  * @param label label of the records
1403  * @param rd_count number of entries in @a rd array, 0 if label was deleted
1404  * @param rd array of records with data to store
1405  */
1406 static void
1407 ns_lookup_result_cb (void *cls,
1408                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
1409                      const char *label,
1410                      unsigned int rd_count,
1411                      const struct GNUNET_GNSRECORD_Data *rd)
1412 {
1413   struct Zone *zone = cls;
1414   struct Request *req;
1415   struct GNUNET_HashCode hc;
1416   char *fqdn;
1417
1418   ns_iterator_trigger_next--;
1419   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1420               "Obtained NAMESTORE reply, %llu left in round\n",
1421               (unsigned long long) ns_iterator_trigger_next);
1422   if (0 == ns_iterator_trigger_next)
1423   {
1424     ns_iterator_trigger_next = NS_BATCH_SIZE;
1425     GNUNET_STATISTICS_update (stats,
1426                               "# NAMESTORE records requested from cache",
1427                               ns_iterator_trigger_next,
1428                               GNUNET_NO);
1429     GNUNET_NAMESTORE_zone_iterator_next (zone_it, ns_iterator_trigger_next);
1430   }
1431   GNUNET_asprintf (&fqdn, "%s.%s", label, zone->domain);
1432   GNUNET_CRYPTO_hash (fqdn, strlen (fqdn) + 1, &hc);
1433   GNUNET_free (fqdn);
1434   req = GNUNET_CONTAINER_multihashmap_get (ns_pending, &hc);
1435   if (NULL == req)
1436   {
1437     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1438                 "Ignoring record `%s' in zone `%s': not on my list!\n",
1439                 label,
1440                 zone->domain);
1441     return;
1442   }
1443   GNUNET_assert (GNUNET_OK ==
1444                  GNUNET_CONTAINER_multihashmap_remove (ns_pending, &hc, req));
1445   GNUNET_break (0 == GNUNET_memcmp (key, &req->zone->key));
1446   GNUNET_break (0 == strcasecmp (label, get_label (req)));
1447   for (unsigned int i = 0; i < rd_count; i++)
1448   {
1449     struct GNUNET_TIME_Absolute at;
1450
1451     if (0 != (rd->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
1452     {
1453       struct GNUNET_TIME_Relative rel;
1454
1455       rel.rel_value_us = rd->expiration_time;
1456       at = GNUNET_TIME_relative_to_absolute (rel);
1457     }
1458     else
1459     {
1460       at.abs_value_us = rd->expiration_time;
1461     }
1462     add_record (req, rd->record_type, at, rd->data, rd->data_size);
1463   }
1464   if (0 == rd_count)
1465   {
1466     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1467                 "Empty record set in namestore for `%s'\n",
1468                 req->hostname);
1469   }
1470   else
1471   {
1472     unsigned int pos = 0;
1473
1474     cached++;
1475     req->expires = GNUNET_TIME_UNIT_FOREVER_ABS;
1476     for (struct Record *rec = req->rec_head; NULL != rec; rec = rec->next)
1477     {
1478       struct GNUNET_TIME_Absolute at;
1479
1480       at.abs_value_us = rec->grd.expiration_time;
1481       req->expires = GNUNET_TIME_absolute_min (req->expires, at);
1482       pos++;
1483     }
1484     if (0 == pos)
1485       req->expires = GNUNET_TIME_UNIT_ZERO_ABS;
1486     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1487                 "Hot-start with %u existing records for `%s'\n",
1488                 pos,
1489                 req->hostname);
1490   }
1491   free_records (req);
1492
1493   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1494               "Adding `%s' to worklist to start at %s\n",
1495               req->hostname,
1496               GNUNET_STRINGS_absolute_time_to_string (req->expires));
1497   insert_sorted (req);
1498 }
1499
1500
1501 /**
1502  * Add @a hostname to the list of requests to be made.
1503  *
1504  * @param hostname name to resolve
1505  */
1506 static void
1507 queue (const char *hostname)
1508 {
1509   struct Request *req;
1510   const char *dot;
1511   struct Zone *zone;
1512   size_t hlen;
1513   struct GNUNET_HashCode hc;
1514
1515   if (GNUNET_OK != GNUNET_DNSPARSER_check_name (hostname))
1516   {
1517     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1518                 "Refusing invalid hostname `%s'\n",
1519                 hostname);
1520     rejects++;
1521     return;
1522   }
1523   dot = strchr (hostname, (unsigned char) '.');
1524   if (NULL == dot)
1525   {
1526     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1527                 "Refusing invalid hostname `%s' (lacks '.')\n",
1528                 hostname);
1529     rejects++;
1530     return;
1531   }
1532   for (zone = zone_head; NULL != zone; zone = zone->next)
1533     if (0 == strcmp (zone->domain, dot + 1))
1534       break;
1535   if (NULL == zone)
1536   {
1537     rejects++;
1538     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1539                 "Domain name `%s' not in ego list!\n",
1540                 dot + 1);
1541     return;
1542   }
1543
1544   hlen = strlen (hostname) + 1;
1545   req = GNUNET_malloc (sizeof(struct Request) + hlen);
1546   req->zone = zone;
1547   req->hostname = (char *) &req[1];
1548   GNUNET_memcpy (req->hostname, hostname, hlen);
1549   req->id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
1550                                                  UINT16_MAX);
1551   GNUNET_CRYPTO_hash (req->hostname, hlen, &hc);
1552   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (
1553         ns_pending,
1554         &hc,
1555         req,
1556         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1557   {
1558     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1559                 "Duplicate hostname `%s' ignored\n",
1560                 hostname);
1561     GNUNET_free (req);
1562     return;
1563   }
1564 }
1565
1566
1567 /**
1568  * We have completed the initial iteration over the namestore's database.
1569  * This function is called on each of the remaining records in
1570  * #move_to_queue to #queue() them, as we will simply not find existing
1571  * records for them any longer.
1572  *
1573  * @param cls NULL
1574  * @param key unused
1575  * @param value a `struct Request`
1576  * @return #GNUNET_OK (continue to iterate)
1577  */
1578 static int
1579 move_to_queue (void *cls, const struct GNUNET_HashCode *key, void *value)
1580 {
1581   struct Request *req = value;
1582
1583   (void) cls;
1584   (void) key;
1585   insert_sorted (req);
1586   return GNUNET_OK;
1587 }
1588
1589
1590 /**
1591  * Iterate over all of the zones we care about and see which records
1592  * we may need to re-fetch when.
1593  *
1594  * @param cls NULL
1595  */
1596 static void
1597 iterate_zones (void *cls)
1598 {
1599   static struct Zone *last;
1600
1601   (void) cls;
1602   if (NULL != zone_it)
1603   {
1604     zone_it = NULL;
1605     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1606                 "Finished iteration over zone `%s'!\n",
1607                 last->domain);
1608     /* subtract left-overs from previous iteration */
1609     GNUNET_STATISTICS_update (stats,
1610                               "# NAMESTORE records requested from cache",
1611                               (long long) (-ns_iterator_trigger_next),
1612                               GNUNET_NO);
1613     ns_iterator_trigger_next = 0;
1614   }
1615   GNUNET_assert (NULL != zone_tail);
1616   if (zone_tail == last)
1617   {
1618     /* Done iterating over relevant zones in NAMESTORE, move
1619        rest of hash map to work queue as well. */
1620     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1621                 "Finished all NAMESTORE iterations!\n");
1622     GNUNET_STATISTICS_set (stats,
1623                            "# Domain names without cached reply",
1624                            GNUNET_CONTAINER_multihashmap_size (ns_pending),
1625                            GNUNET_NO);
1626     GNUNET_CONTAINER_multihashmap_iterate (ns_pending, &move_to_queue, NULL);
1627     GNUNET_CONTAINER_multihashmap_destroy (ns_pending);
1628     ns_pending = NULL;
1629     start_time_reg_proc = GNUNET_TIME_absolute_get ();
1630     total_reg_proc_dns = 0;
1631     total_reg_proc_dns_ns = 0;
1632     return;
1633   }
1634   if (NULL == last)
1635     last = zone_head;
1636   else
1637     last = last->next;
1638   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1639               "Starting iteration over zone `%s'!\n",
1640               last->domain);
1641   /* subtract left-overs from previous iteration */
1642   GNUNET_STATISTICS_update (stats,
1643                             "# NAMESTORE records requested from cache",
1644                             1,
1645                             GNUNET_NO);
1646   ns_iterator_trigger_next = 1;
1647   GNUNET_STATISTICS_update (stats, "# zones iterated", 1, GNUNET_NO);
1648   zone_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
1649                                                    &last->key,
1650                                                    &ns_lookup_error_cb,
1651                                                    NULL,
1652                                                    &ns_lookup_result_cb,
1653                                                    last,
1654                                                    &iterate_zones,
1655                                                    NULL);
1656 }
1657
1658
1659 /**
1660  * Begin processing hostnames from stdin.
1661  *
1662  * @param cls NULL
1663  */
1664 static void
1665 process_stdin (void *cls)
1666 {
1667   static struct GNUNET_TIME_Absolute last;
1668   static uint64_t idot;
1669   char hn[256];
1670
1671   (void) cls;
1672   t = NULL;
1673   if (NULL != id)
1674   {
1675     GNUNET_IDENTITY_disconnect (id);
1676     id = NULL;
1677   }
1678   while (NULL != fgets (hn, sizeof(hn), stdin))
1679   {
1680     if (strlen (hn) > 0)
1681       hn[strlen (hn) - 1] = '\0';  /* eat newline */
1682     if (0 == idot)
1683       last = GNUNET_TIME_absolute_get ();
1684     idot++;
1685     if (0 == idot % 100000)
1686     {
1687       struct GNUNET_TIME_Relative delta;
1688
1689       delta = GNUNET_TIME_absolute_get_duration (last);
1690       last = GNUNET_TIME_absolute_get ();
1691       fprintf (stderr,
1692                "Read 100000 domain names in %s\n",
1693                GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES));
1694       GNUNET_STATISTICS_set (stats, "# domain names provided", idot, GNUNET_NO);
1695     }
1696     queue (hn);
1697   }
1698   fprintf (stderr,
1699            "Done reading %llu domain names\n",
1700            (unsigned long long) idot);
1701   GNUNET_STATISTICS_set (stats, "# domain names provided", idot, GNUNET_NO);
1702   iterate_zones (NULL);
1703 }
1704
1705
1706 /**
1707  * Method called to inform about the egos of this peer.
1708  *
1709  * When used with #GNUNET_IDENTITY_connect, this function is
1710  * initially called for all egos and then again whenever a
1711  * ego's name changes or if it is deleted.  At the end of
1712  * the initial pass over all egos, the function is once called
1713  * with 'NULL' for @a ego. That does NOT mean that the callback won't
1714  * be invoked in the future or that there was an error.
1715  *
1716  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
1717  * this function is only called ONCE, and 'NULL' being passed in
1718  * @a ego does indicate an error (i.e. name is taken or no default
1719  * value is known).  If @a ego is non-NULL and if '*ctx'
1720  * is set in those callbacks, the value WILL be passed to a subsequent
1721  * call to the identity callback of #GNUNET_IDENTITY_connect (if
1722  * that one was not NULL).
1723  *
1724  * When an identity is renamed, this function is called with the
1725  * (known) @a ego but the NEW @a name.
1726  *
1727  * When an identity is deleted, this function is called with the
1728  * (known) ego and "NULL" for the @a name.  In this case,
1729  * the @a ego is henceforth invalid (and the @a ctx should also be
1730  * cleaned up).
1731  *
1732  * @param cls closure
1733  * @param ego ego handle, NULL for end of list
1734  * @param ctx context for application to store data for this ego
1735  *                 (during the lifetime of this process, initially NULL)
1736  * @param name name assigned by the user for this ego,
1737  *                   NULL if the user just deleted the ego and it
1738  *                   must thus no longer be used
1739  */
1740 static void
1741 identity_cb (void *cls,
1742              struct GNUNET_IDENTITY_Ego *ego,
1743              void **ctx,
1744              const char *name)
1745 {
1746   (void) cls;
1747   (void) ctx;
1748
1749   if (NULL == ego)
1750   {
1751     /* end of iteration */
1752     if (NULL == zone_head)
1753     {
1754       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No zone found\n");
1755       GNUNET_SCHEDULER_shutdown ();
1756       return;
1757     }
1758     /* zone_head non-null, process hostnames from stdin */
1759     t = GNUNET_SCHEDULER_add_now (&process_stdin, NULL);
1760     return;
1761   }
1762   if (NULL != name)
1763   {
1764     struct Zone *zone;
1765
1766     zone = GNUNET_new (struct Zone);
1767     zone->key = *GNUNET_IDENTITY_ego_get_private_key (ego);
1768     zone->domain = GNUNET_strdup (name);
1769     GNUNET_CONTAINER_DLL_insert (zone_head, zone_tail, zone);
1770   }
1771 }
1772
1773
1774 /**
1775  * Process requests from the queue, then if the queue is
1776  * not empty, try again.
1777  *
1778  * @param cls NULL
1779  * @param args remaining command-line arguments
1780  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1781  * @param cfg configuration
1782  */
1783 static void
1784 run (void *cls,
1785      char *const *args,
1786      const char *cfgfile,
1787      const struct GNUNET_CONFIGURATION_Handle *cfg)
1788 {
1789   (void) cls;
1790   (void) args;
1791   (void) cfgfile;
1792   stats = GNUNET_STATISTICS_create ("zoneimport", cfg);
1793   req_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
1794   ns_pending = GNUNET_CONTAINER_multihashmap_create (map_size, GNUNET_NO);
1795   if (NULL == ns_pending)
1796   {
1797     fprintf (stderr, "Failed to allocate memory for main hash map\n");
1798     return;
1799   }
1800   ctx = GNUNET_DNSSTUB_start (256);
1801   if (NULL == ctx)
1802   {
1803     fprintf (stderr, "Failed to initialize GNUnet DNS STUB\n");
1804     return;
1805   }
1806   if (NULL == args[0])
1807   {
1808     fprintf (stderr,
1809              "You must provide a list of DNS resolvers on the command line\n");
1810     return;
1811   }
1812   for (unsigned int i = 0; NULL != args[i]; i++)
1813   {
1814     if (GNUNET_OK != GNUNET_DNSSTUB_add_dns_ip (ctx, args[i]))
1815     {
1816       fprintf (stderr, "Failed to use `%s' for DNS resolver\n", args[i]);
1817       return;
1818     }
1819   }
1820
1821
1822   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1823   ns = GNUNET_NAMESTORE_connect (cfg);
1824   if (NULL == ns)
1825   {
1826     GNUNET_SCHEDULER_shutdown ();
1827     return;
1828   }
1829   id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
1830 }
1831
1832
1833 /**
1834  * Call with IP address of resolver to query.
1835  *
1836  * @param argc should be 2
1837  * @param argv[1] should contain IP address
1838  * @return 0 on success
1839  */
1840 int
1841 main (int argc, char *const *argv)
1842 {
1843   struct GNUNET_GETOPT_CommandLineOption options[] =
1844   { GNUNET_GETOPT_option_uint ('s',
1845                                "size",
1846                                "MAPSIZE",
1847                                gettext_noop (
1848                                  "size to use for the main hash map"),
1849                                &map_size),
1850     GNUNET_GETOPT_option_relative_time (
1851       'm',
1852       "minimum-expiration",
1853       "RELATIVETIME",
1854       gettext_noop ("minimum expiration time we assume for imported records"),
1855       &minimum_expiration_time),
1856     GNUNET_GETOPT_OPTION_END };
1857   int ret;
1858
1859   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1860     return 2;
1861   if (GNUNET_OK != (ret = GNUNET_PROGRAM_run (argc,
1862                                               argv,
1863                                               "gnunet-zoneimport",
1864                                               "import DNS zone into namestore",
1865                                               options,
1866                                               &run,
1867                                               NULL)))
1868     return ret;
1869   GNUNET_free_nz ((void *) argv);
1870   fprintf (stderr,
1871            "Rejected %u names, had %u cached, did %u lookups, stored %u record sets\n"
1872            "Found %u records, %u lookups failed, %u/%u pending on shutdown\n",
1873            rejects,
1874            cached,
1875            lookups,
1876            record_sets,
1877            records,
1878            failures,
1879            pending,
1880            pending_rs);
1881   return 0;
1882 }
1883
1884
1885 /* end of gnunet-zoneimport.c */