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