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