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