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