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