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