ea271e9dde67d0eb2ded70569bd70a2d9205c13c
[oweals/gnunet.git] / src / gns / gnunet-service-gns.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011-2013 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 gns/gnunet-service-gns.c
22  * @brief GNU Name System (main service)
23  * @author Martin Schanzenbach
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dns_service.h"
29 #include "gnunet_dnsparser_lib.h"
30 #include "gnunet_dht_service.h"
31 #include "gnunet_namecache_service.h"
32 #include "gnunet_namestore_service.h"
33 #include "gnunet_identity_service.h"
34 #include "gnunet_gns_service.h"
35 #include "gnunet_statistics_service.h"
36 #include "gns.h"
37 #include "gnunet-service-gns_resolver.h"
38 #include "gnunet-service-gns_shorten.h"
39 #include "gnunet-service-gns_interceptor.h"
40 #include "gnunet_protocols.h"
41
42 /**
43  * The initial interval in milliseconds btween puts in
44  * a zone iteration
45  */
46 #define INITIAL_PUT_INTERVAL GNUNET_TIME_UNIT_MILLISECONDS
47
48 /**
49  * The lower bound for the zone iteration interval
50  */
51 #define MINIMUM_ZONE_ITERATION_INTERVAL GNUNET_TIME_UNIT_SECONDS
52
53 /**
54  * The upper bound for the zone iteration interval
55  */
56 #define MAXIMUM_ZONE_ITERATION_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
57
58 /**
59  * The default put interval for the zone iteration. In case
60  * no option is found
61  */
62 #define DEFAULT_ZONE_PUBLISH_TIME_WINDOW GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
63
64 /**
65  * The factor the current zone iteration interval is divided by for each
66  * additional new record
67  */
68 #define LATE_ITERATION_SPEEDUP_FACTOR 2
69
70 /**
71  * How long until a DHT PUT attempt should time out?
72  */
73 #define DHT_OPERATION_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
74
75 /**
76  * What replication level do we use for DHT PUT operations?
77  */
78 #define DHT_GNS_REPLICATION_LEVEL 5
79
80
81 /**
82  * Handle to a lookup operation from api
83  */
84 struct ClientLookupHandle
85 {
86
87   /**
88    * We keep these in a DLL.
89    */
90   struct ClientLookupHandle *next;
91
92   /**
93    * We keep these in a DLL.
94    */
95   struct ClientLookupHandle *prev;
96
97   /**
98    * Handle to the requesting client
99    */
100   struct GNUNET_SERVER_Client *client;
101
102   /**
103    * Active handle for the lookup.
104    */
105   struct GNS_ResolverHandle *lookup;
106
107   /**
108    * request id
109    */
110   uint32_t request_id;
111
112 };
113
114
115 /**
116  * Handle for DHT PUT activity triggered from the namestore monitor.
117  */
118 struct MonitorActivity
119 {
120   /**
121    * Kept in a DLL.
122    */
123   struct MonitorActivity *next;
124
125   /**
126    * Kept in a DLL.
127    */
128   struct MonitorActivity *prev;
129
130   /**
131    * Handle for the DHT PUT operation.
132    */
133   struct GNUNET_DHT_PutHandle *ph;
134 };
135
136
137 /**
138  * Our handle to the DHT
139  */
140 static struct GNUNET_DHT_Handle *dht_handle;
141
142 /**
143  * Active DHT put operation (or NULL)
144  */
145 static struct GNUNET_DHT_PutHandle *active_put;
146
147 /**
148  * Our handle to the namestore service
149  */
150 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
151
152 /**
153  * Our handle to the namecache service
154  */
155 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
156
157 /**
158  * Our handle to the identity service
159  */
160 static struct GNUNET_IDENTITY_Handle *identity_handle;
161
162 /**
163  * Our handle to the identity operation to find the master zone
164  * for intercepted queries.
165  */
166 static struct GNUNET_IDENTITY_Operation *identity_op;
167
168 /**
169  * Handle to iterate over our authoritative zone in namestore
170  */
171 static struct GNUNET_NAMESTORE_ZoneIterator *namestore_iter;
172
173 /**
174  * Handle to monitor namestore changes to instant propagation.
175  */
176 static struct GNUNET_NAMESTORE_ZoneMonitor *zmon;
177
178 /**
179  * Our notification context.
180  */
181 static struct GNUNET_SERVER_NotificationContext *nc;
182
183 /**
184  * Head of the DLL.
185  */
186 static struct ClientLookupHandle *clh_head;
187
188 /**
189  * Tail of the DLL.
190  */
191 static struct ClientLookupHandle *clh_tail;
192
193 /**
194  * Head of monitor activities; kept in a DLL.
195  */
196 static struct MonitorActivity *ma_head;
197
198 /**
199  * Tail of monitor activities; kept in a DLL.
200  */
201 static struct MonitorActivity *ma_tail;
202
203 /**
204  * Useful for zone update for DHT put
205  */
206 static unsigned long long num_public_records;
207
208 /**
209  * Last seen record count
210  */
211 static unsigned long long last_num_public_records;
212
213 /**
214  * Minimum relative expiration time of records seem during the current
215  * zone iteration.
216  */
217 static struct GNUNET_TIME_Relative min_relative_record_time;
218
219 /**
220  * Zone iteration PUT interval.
221  */
222 static struct GNUNET_TIME_Relative put_interval;
223
224 /**
225  * Default time window for zone iteration
226  */
227 static struct GNUNET_TIME_Relative zone_publish_time_window_default;
228
229 /**
230  * Time window for zone iteration, adjusted based on relative record
231  * expiration times in our zone.
232  */
233 static struct GNUNET_TIME_Relative zone_publish_time_window;
234
235 /**
236  * zone publish task
237  */
238 static struct GNUNET_SCHEDULER_Task *zone_publish_task;
239
240 /**
241  * #GNUNET_YES if zone has never been published before
242  */
243 static int first_zone_iteration;
244
245 /**
246  * #GNUNET_YES if ipv6 is supported
247  */
248 static int v6_enabled;
249
250 /**
251  * #GNUNET_YES if ipv4 is supported
252  */
253 static int v4_enabled;
254
255 /**
256  * Handle to the statistics service
257  */
258 static struct GNUNET_STATISTICS_Handle *statistics;
259
260
261 /**
262  * Task run during shutdown.
263  *
264  * @param cls unused
265  * @param tc unused
266  */
267 static void
268 shutdown_task (void *cls)
269 {
270   struct ClientLookupHandle *clh;
271   struct MonitorActivity *ma;
272
273   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
274               "Shutting down!\n");
275   if (NULL != nc)
276   {
277     GNUNET_SERVER_notification_context_destroy (nc);
278     nc = NULL;
279   }
280   while (NULL != (clh = clh_head))
281   {
282     GNUNET_SERVER_client_set_user_context (clh->client,
283                                            NULL);
284     GNS_resolver_lookup_cancel (clh->lookup);
285     GNUNET_CONTAINER_DLL_remove (clh_head,
286                                  clh_tail,
287                                  clh);
288     GNUNET_free (clh);
289   }
290
291   GNS_interceptor_done ();
292   if (NULL != identity_op)
293   {
294     GNUNET_IDENTITY_cancel (identity_op);
295     identity_op = NULL;
296   }
297   if (NULL != identity_handle)
298   {
299     GNUNET_IDENTITY_disconnect (identity_handle);
300     identity_handle = NULL;
301   }
302   GNS_resolver_done ();
303   GNS_shorten_done ();
304   while (NULL != (ma = ma_head))
305   {
306     GNUNET_DHT_put_cancel (ma->ph);
307     GNUNET_CONTAINER_DLL_remove (ma_head,
308                                  ma_tail,
309                                  ma);
310     GNUNET_free (ma);
311   }
312   if (NULL != statistics)
313   {
314     GNUNET_STATISTICS_destroy (statistics,
315                                GNUNET_NO);
316     statistics = NULL;
317   }
318   if (NULL != zone_publish_task)
319   {
320     GNUNET_SCHEDULER_cancel (zone_publish_task);
321     zone_publish_task = NULL;
322   }
323   if (NULL != namestore_iter)
324   {
325     GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
326     namestore_iter = NULL;
327   }
328   if (NULL != zmon)
329   {
330     GNUNET_NAMESTORE_zone_monitor_stop (zmon);
331     zmon = NULL;
332   }
333   if (NULL != namestore_handle)
334   {
335     GNUNET_NAMESTORE_disconnect (namestore_handle);
336     namestore_handle = NULL;
337   }
338   if (NULL != namecache_handle)
339   {
340     GNUNET_NAMECACHE_disconnect (namecache_handle);
341     namecache_handle = NULL;
342   }
343   if (NULL != active_put)
344   {
345     GNUNET_DHT_put_cancel (active_put);
346     active_put = NULL;
347   }
348   if (NULL != dht_handle)
349   {
350     GNUNET_DHT_disconnect (dht_handle);
351     dht_handle = NULL;
352   }
353 }
354
355
356 /**
357  * Method called periodically that triggers iteration over authoritative records
358  *
359  * @param cls closure
360  */
361 static void
362 publish_zone_dht_next (void *cls)
363 {
364   zone_publish_task = NULL;
365   GNUNET_assert (NULL != namestore_iter);
366   GNUNET_NAMESTORE_zone_iterator_next (namestore_iter);
367 }
368
369
370 /**
371  * Periodically iterate over our zone and store everything in dht
372  *
373  * @param cls NULL
374  */
375 static void
376 publish_zone_dht_start (void *cls);
377
378
379 /**
380  * Continuation called from DHT once the PUT operation is done.
381  *
382  * @param cls closure, NULL if called from regular iteration,
383  *        `struct MonitorActivity` if called from #handle_monitor_event.
384  * @param success #GNUNET_OK on success
385  */
386 static void
387 dht_put_continuation (void *cls,
388                       int success)
389 {
390   struct MonitorActivity *ma = cls;
391   struct GNUNET_TIME_Relative next_put_interval;
392
393   num_public_records++;
394   if (NULL == ma)
395   {
396     active_put = NULL;
397     if ( (num_public_records > last_num_public_records) &&
398          (GNUNET_NO == first_zone_iteration) )
399     {
400       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
401                   "Last record count was lower than current record count.  Reducing interval.\n");
402       put_interval = GNUNET_TIME_relative_divide (zone_publish_time_window,
403                                                   num_public_records);
404       next_put_interval = GNUNET_TIME_relative_divide (put_interval,
405                                                        LATE_ITERATION_SPEEDUP_FACTOR);
406     }
407     else
408       next_put_interval = put_interval;
409     next_put_interval = GNUNET_TIME_relative_min (next_put_interval,
410                                                   MAXIMUM_ZONE_ITERATION_INTERVAL);
411     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412                 "PUT complete, next PUT in %s!\n",
413                 GNUNET_STRINGS_relative_time_to_string (next_put_interval,
414                                                         GNUNET_YES));
415
416     GNUNET_STATISTICS_set (statistics,
417                            "Current zone iteration interval (ms)",
418                            next_put_interval.rel_value_us / 1000LL,
419                            GNUNET_NO);
420     GNUNET_assert (NULL == zone_publish_task);
421     zone_publish_task = GNUNET_SCHEDULER_add_delayed (next_put_interval,
422                                                       &publish_zone_dht_next,
423                                                       NULL);
424   }
425   else
426   {
427     GNUNET_CONTAINER_DLL_remove (ma_head,
428                                  ma_tail,
429                                  ma);
430     GNUNET_free (ma);
431   }
432 }
433
434
435 /**
436  * Convert namestore records from the internal format to that
437  * suitable for publication (removes private records, converts
438  * to absolute expiration time).
439  *
440  * @param rd input records
441  * @param rd_count size of the @a rd and @a rd_public arrays
442  * @param rd_public where to write the converted records
443  * @return number of records written to @a rd_public
444  */
445 static unsigned int
446 convert_records_for_export (const struct GNUNET_GNSRECORD_Data *rd,
447                             unsigned int rd_count,
448                             struct GNUNET_GNSRECORD_Data *rd_public)
449 {
450   struct GNUNET_TIME_Absolute now;
451   unsigned int rd_public_count;
452   unsigned int i;
453
454   rd_public_count = 0;
455   now = GNUNET_TIME_absolute_get ();
456   for (i=0;i<rd_count;i++)
457     if (0 == (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE))
458     {
459       rd_public[rd_public_count] = rd[i];
460       if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
461       {
462         /* GNUNET_GNSRECORD_block_create will convert to absolute time;
463            we just need to adjust our iteration frequency */
464         min_relative_record_time.rel_value_us =
465           GNUNET_MIN (rd_public[rd_public_count].expiration_time,
466                       min_relative_record_time.rel_value_us);
467       }
468       else if (rd_public[rd_public_count].expiration_time < now.abs_value_us)
469       {
470         /* record already expired, skip it */
471         continue;
472       }
473       rd_public_count++;
474     }
475   return rd_public_count;
476 }
477
478
479 /**
480  * Store GNS records in the DHT.
481  *
482  * @param key key of the zone
483  * @param label label to store under
484  * @param rd_public public record data
485  * @param rd_public_count number of records in @a rd_public
486  * @param pc_arg closure argument to pass to the #dht_put_continuation
487  * @return DHT PUT handle, NULL on error
488  */
489 static struct GNUNET_DHT_PutHandle *
490 perform_dht_put (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
491                  const char *label,
492                  const struct GNUNET_GNSRECORD_Data *rd_public,
493                  unsigned int rd_public_count,
494                  void *pc_arg)
495 {
496   struct GNUNET_GNSRECORD_Block *block;
497   struct GNUNET_HashCode query;
498   struct GNUNET_TIME_Absolute expire;
499   size_t block_size;
500   struct GNUNET_DHT_PutHandle *ret;
501
502   expire = GNUNET_GNSRECORD_record_get_expiration_time (rd_public_count,
503                                                         rd_public);
504   block = GNUNET_GNSRECORD_block_create (key,
505                                          expire,
506                                          label,
507                                          rd_public,
508                                          rd_public_count);
509   if (NULL == block)
510     return NULL; /* whoops */
511   block_size = ntohl (block->purpose.size)
512     + sizeof (struct GNUNET_CRYPTO_EcdsaSignature)
513     + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
514   GNUNET_GNSRECORD_query_from_private_key (key,
515                                            label,
516                                            &query);
517   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518               "Storing %u record(s) for label `%s' in DHT with expiration `%s' under key %s\n",
519               rd_public_count,
520               label,
521               GNUNET_STRINGS_absolute_time_to_string (expire),
522               GNUNET_h2s (&query));
523   ret = GNUNET_DHT_put (dht_handle,
524                         &query,
525                         DHT_GNS_REPLICATION_LEVEL,
526                         GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
527                         GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
528                         block_size,
529                         block,
530                         expire,
531                         &dht_put_continuation,
532                         pc_arg);
533   GNUNET_free (block);
534   return ret;
535 }
536
537
538 /**
539  * We encountered an error in our zone iteration.
540  */
541 static void
542 zone_iteration_error (void *cls)
543 {
544   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
545               "Got disconnected from namestore database, retrying.\n");
546   namestore_iter = NULL;
547   /* We end up here on error/disconnect/shutdown, so potentially
548      while a zone publish task or a DHT put is still running; hence
549      we need to cancel those. */
550   if (NULL != zone_publish_task)
551   {
552     GNUNET_SCHEDULER_cancel (zone_publish_task);
553     zone_publish_task = NULL;
554   }
555   if (NULL != active_put)
556   {
557     GNUNET_DHT_put_cancel (active_put);
558     active_put = NULL;
559   }
560   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
561                                                 NULL);
562 }
563
564
565 /**
566  * Zone iteration is completed.
567  */
568 static void
569 zone_iteration_finished (void *cls)
570 {
571   /* we're done with one iteration, calculate when to do the next one */
572   namestore_iter = NULL;
573   last_num_public_records = num_public_records;
574   first_zone_iteration = GNUNET_NO;
575   if (0 == num_public_records)
576   {
577     /**
578      * If no records are known (startup) or none present
579      * we can safely set the interval to the value for a single
580      * record
581      */
582     put_interval = zone_publish_time_window;
583     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
584                 "No records in namestore database.\n");
585   }
586   else
587   {
588     /* If records are present, next publication is based on the minimum
589      * relative expiration time of the records published divided by 4
590      */
591     zone_publish_time_window
592       = GNUNET_TIME_relative_min (GNUNET_TIME_relative_divide (min_relative_record_time, 4),
593                                   zone_publish_time_window_default);
594     put_interval = GNUNET_TIME_relative_divide (zone_publish_time_window,
595                                                 num_public_records);
596   }
597   /* reset for next iteration */
598   min_relative_record_time = GNUNET_TIME_UNIT_FOREVER_REL;
599   put_interval = GNUNET_TIME_relative_max (MINIMUM_ZONE_ITERATION_INTERVAL,
600                                            put_interval);
601   put_interval = GNUNET_TIME_relative_min (put_interval,
602                                            MAXIMUM_ZONE_ITERATION_INTERVAL);
603   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
604               "Zone iteration finished. Adjusted zone iteration interval to %s\n",
605               GNUNET_STRINGS_relative_time_to_string (put_interval,
606                                                       GNUNET_YES));
607   GNUNET_STATISTICS_set (statistics,
608                          "Current zone iteration interval (in ms)",
609                          put_interval.rel_value_us / 1000LL,
610                          GNUNET_NO);
611   GNUNET_STATISTICS_update (statistics,
612                             "Number of zone iterations",
613                             1,
614                             GNUNET_NO);
615   GNUNET_STATISTICS_set (statistics,
616                          "Number of public records in DHT",
617                          last_num_public_records,
618                          GNUNET_NO);
619   GNUNET_assert (NULL == zone_publish_task);
620   if (0 == num_public_records)
621     zone_publish_task = GNUNET_SCHEDULER_add_delayed (put_interval,
622                                                       &publish_zone_dht_start,
623                                                       NULL);
624   else
625     zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
626                                                   NULL);
627 }
628
629
630 /**
631  * Function used to put all records successively into the DHT.
632  *
633  * @param cls the closure (NULL)
634  * @param key the private key of the authority (ours)
635  * @param label the name of the records, NULL once the iteration is done
636  * @param rd_count the number of records in @a rd
637  * @param rd the record data
638  */
639 static void
640 put_gns_record (void *cls,
641                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
642                 const char *label,
643                 unsigned int rd_count,
644                 const struct GNUNET_GNSRECORD_Data *rd)
645 {
646   struct GNUNET_GNSRECORD_Data rd_public[rd_count];
647   unsigned int rd_public_count;
648
649   rd_public_count = convert_records_for_export (rd,
650                                                 rd_count,
651                                                 rd_public);
652
653   if (0 == rd_public_count)
654   {
655     GNUNET_assert (NULL == zone_publish_task);
656     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
657                 "Record set empty, moving to next record set\n");
658     zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_next,
659                                                   NULL);
660     return;
661   }
662   /* We got a set of records to publish */
663   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
664               "Starting DHT PUT\n");
665   active_put = perform_dht_put (key,
666                                 label,
667                                 rd_public,
668                                 rd_public_count,
669                                 NULL);
670   if (NULL == active_put)
671   {
672     GNUNET_break (0);
673     dht_put_continuation (NULL, GNUNET_NO);
674   }
675 }
676
677
678 /**
679  * Periodically iterate over all zones and store everything in DHT
680  *
681  * @param cls NULL
682  */
683 static void
684 publish_zone_dht_start (void *cls)
685 {
686   zone_publish_task = NULL;
687
688   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
689               "Starting DHT zone update!\n");
690   /* start counting again */
691   num_public_records = 0;
692   GNUNET_assert (NULL == namestore_iter);
693   namestore_iter
694     = GNUNET_NAMESTORE_zone_iteration_start (namestore_handle,
695                                              NULL, /* All zones */
696                                              &zone_iteration_error,
697                                              NULL,
698                                              &put_gns_record,
699                                              NULL,
700                                              &zone_iteration_finished,
701                                              NULL);
702 }
703
704
705 /**
706  * Process a record that was stored in the namestore
707  * (invoked by the monitor).
708  *
709  * @param cls closure, NULL
710  * @param zone private key of the zone; NULL on disconnect
711  * @param label label of the records; NULL on disconnect
712  * @param rd_count number of entries in @a rd array, 0 if label was deleted
713  * @param rd array of records with data to store
714  */
715 static void
716 handle_monitor_event (void *cls,
717                       const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
718                       const char *label,
719                       unsigned int rd_count,
720                       const struct GNUNET_GNSRECORD_Data *rd)
721 {
722   struct GNUNET_GNSRECORD_Data rd_public[rd_count];
723   unsigned int rd_public_count;
724   struct MonitorActivity *ma;
725
726   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727               "Received %u records for label `%s' via namestore monitor\n",
728               rd_count,
729               label);
730   /* filter out records that are not public, and convert to
731      absolute expiration time. */
732   rd_public_count = convert_records_for_export (rd,
733                                                 rd_count,
734                                                 rd_public);
735   if (0 == rd_public_count)
736     return; /* nothing to do */
737   ma = GNUNET_new (struct MonitorActivity);
738   ma->ph = perform_dht_put (zone,
739                             label,
740                             rd,
741                             rd_count,
742                             ma);
743   if (NULL == ma->ph)
744   {
745     /* PUT failed, do not remember operation */
746     GNUNET_free (ma);
747     return;
748   }
749   GNUNET_CONTAINER_DLL_insert (ma_head,
750                                ma_tail,
751                                ma);
752 }
753
754
755 /* END DHT ZONE PROPAGATION */
756
757
758 /**
759  * Reply to client with the result from our lookup.
760  *
761  * @param cls the closure (our client lookup handle)
762  * @param rd_count the number of records in @a rd
763  * @param rd the record data
764  */
765 static void
766 send_lookup_response (void* cls,
767                       uint32_t rd_count,
768                       const struct GNUNET_GNSRECORD_Data *rd)
769 {
770   struct ClientLookupHandle *clh = cls;
771   struct GNUNET_GNS_ClientLookupResultMessage *rmsg;
772   size_t len;
773
774   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
775               "Sending LOOKUP_RESULT message with %u results\n",
776               (unsigned int) rd_count);
777
778   len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
779   rmsg = GNUNET_malloc (len + sizeof (struct GNUNET_GNS_ClientLookupResultMessage));
780   rmsg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
781   rmsg->header.size = htons (len + sizeof(struct GNUNET_GNS_ClientLookupResultMessage));
782   rmsg->id = clh->request_id;
783   rmsg->rd_count = htonl (rd_count);
784   GNUNET_GNSRECORD_records_serialize (rd_count, rd, len,
785                                       (char*) &rmsg[1]);
786   GNUNET_SERVER_notification_context_unicast (nc,
787                                               clh->client,
788                                               &rmsg->header,
789                                               GNUNET_NO);
790   GNUNET_free (rmsg);
791   GNUNET_CONTAINER_DLL_remove (clh_head, clh_tail, clh);
792   GNUNET_SERVER_client_set_user_context (clh->client, NULL);
793   GNUNET_free (clh);
794   GNUNET_STATISTICS_update (statistics,
795                             "Completed lookups", 1,
796                             GNUNET_NO);
797   GNUNET_STATISTICS_update (statistics,
798                             "Records resolved",
799                             rd_count,
800                             GNUNET_NO);
801 }
802
803
804 /**
805  * Handle lookup requests from client
806  *
807  * @param cls the closure
808  * @param client the client
809  * @param message the message
810  */
811 static void
812 handle_lookup (void *cls,
813                struct GNUNET_SERVER_Client *client,
814                const struct GNUNET_MessageHeader *message)
815 {
816   char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
817   struct ClientLookupHandle *clh;
818   char *nameptr = name;
819   const char *utf_in;
820   const struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
821   uint16_t msg_size;
822   const struct GNUNET_GNS_ClientLookupMessage *sh_msg;
823
824   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
825               "Received LOOKUP message\n");
826   msg_size = ntohs (message->size);
827   if (msg_size < sizeof (struct GNUNET_GNS_ClientLookupMessage))
828   {
829     GNUNET_break (0);
830     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
831     return;
832   }
833   sh_msg = (const struct GNUNET_GNS_ClientLookupMessage *) message;
834   GNUNET_SERVER_notification_context_add (nc, client);
835   if (GNUNET_YES == ntohs (sh_msg->have_key))
836     key = &sh_msg->shorten_key;
837   else
838     key = NULL;
839   utf_in = (const char *) &sh_msg[1];
840   if ( ('\0' != utf_in[msg_size - sizeof (struct GNUNET_GNS_ClientLookupMessage) - 1]) ||
841        (strlen (utf_in) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) )
842   {
843     GNUNET_break (0);
844     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
845     return;
846   }
847   GNUNET_STRINGS_utf8_tolower (utf_in, nameptr);
848   GNUNET_SERVER_receive_done (client, GNUNET_OK);
849
850   clh = GNUNET_new (struct ClientLookupHandle);
851   GNUNET_SERVER_client_set_user_context (client, clh);
852   GNUNET_CONTAINER_DLL_insert (clh_head, clh_tail, clh);
853   clh->client = client;
854   clh->request_id = sh_msg->id;
855   if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
856        (GNUNET_OK != v4_enabled) )
857   {
858     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
859                 "LOOKUP: Query for A record but AF_INET not supported!");
860     send_lookup_response (clh, 0, NULL);
861     return;
862   }
863   if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
864        (GNUNET_OK != v6_enabled) )
865   {
866     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
867                 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
868     send_lookup_response (clh, 0, NULL);
869     return;
870   }
871   clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
872                                      ntohl (sh_msg->type),
873                                      name,
874                                      key,
875                                      (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
876                                      &send_lookup_response, clh);
877   GNUNET_STATISTICS_update (statistics,
878                             "Lookup attempts",
879                             1, GNUNET_NO);
880 }
881
882
883 /**
884  * One of our clients disconnected, clean up after it.
885  *
886  * @param cls NULL
887  * @param client the client that disconnected
888  */
889 static void
890 notify_client_disconnect (void *cls,
891                           struct GNUNET_SERVER_Client *client)
892 {
893   struct ClientLookupHandle *clh;
894
895   if (NULL == client)
896     return;
897   clh = GNUNET_SERVER_client_get_user_context (client, struct ClientLookupHandle);
898   if (NULL == clh)
899     return;
900   GNS_resolver_lookup_cancel (clh->lookup);
901   GNUNET_CONTAINER_DLL_remove (clh_head, clh_tail, clh);
902   GNUNET_free (clh);
903 }
904
905
906 /**
907  * The zone monitor is now in SYNC with the current state of the
908  * name store.  Start to perform periodic iterations.
909  *
910  * @param cls NULL
911  */
912 static void
913 monitor_sync_event (void *cls)
914 {
915   GNUNET_assert (NULL == zone_publish_task);
916   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
917                                                 NULL);
918 }
919
920
921 /**
922  * The zone monitor is now in SYNC with the current state of the
923  * name store.  Start to perform periodic iterations.
924  *
925  * @param cls NULL
926  */
927 static void
928 handle_monitor_error (void *cls)
929 {
930   if (NULL != zone_publish_task)
931   {
932     GNUNET_SCHEDULER_cancel (zone_publish_task);
933     zone_publish_task = NULL;
934   }
935   if (NULL != namestore_iter)
936   {
937     GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
938     namestore_iter = NULL;
939   }
940   if (NULL != active_put)
941   {
942     GNUNET_DHT_put_cancel (active_put);
943     active_put = NULL;
944   }
945   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
946                                                 NULL);
947 }
948
949
950 /**
951  * Method called to inform about the ego to be used for the master zone
952  * for DNS interceptions.
953  *
954  * This function is only called ONCE, and 'NULL' being passed in
955  * @a ego does indicate that interception is not configured.
956  * If @a ego is non-NULL, we should start to intercept DNS queries
957  * and resolve ".gnu" queries using the given ego as the master zone.
958  *
959  * @param cls closure, our `const struct GNUNET_CONFIGURATION_Handle *c`
960  * @param ego ego handle
961  * @param ctx context for application to store data for this ego
962  *                 (during the lifetime of this process, initially NULL)
963  * @param name name assigned by the user for this ego,
964  *                   NULL if the user just deleted the ego and it
965  *                   must thus no longer be used
966  */
967 static void
968 identity_intercept_cb (void *cls,
969                        struct GNUNET_IDENTITY_Ego *ego,
970                        void **ctx,
971                        const char *name)
972 {
973   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
974   struct GNUNET_CRYPTO_EcdsaPublicKey dns_root;
975
976   identity_op = NULL;
977   if (NULL == ego)
978   {
979     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
980                 _("No ego configured for `%s`\n"),
981                 "gns-intercept");
982     return;
983   }
984   GNUNET_IDENTITY_ego_get_public_key (ego,
985                                       &dns_root);
986   if (GNUNET_SYSERR ==
987       GNS_interceptor_init (&dns_root, cfg))
988   {
989     GNUNET_break (0);
990     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
991     return;
992   }
993 }
994
995
996 /**
997  * Process GNS requests.
998  *
999  * @param cls closure
1000  * @param server the initialized server
1001  * @param c configuration to use
1002  */
1003 static void
1004 run (void *cls,
1005      struct GNUNET_SERVER_Handle *server,
1006      const struct GNUNET_CONFIGURATION_Handle *c)
1007 {
1008   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1009     { &handle_lookup, NULL, GNUNET_MESSAGE_TYPE_GNS_LOOKUP, 0},
1010     {NULL, NULL, 0, 0}
1011   };
1012   unsigned long long max_parallel_bg_queries = 0;
1013
1014   v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
1015   v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
1016   min_relative_record_time = GNUNET_TIME_UNIT_FOREVER_REL;
1017   namestore_handle = GNUNET_NAMESTORE_connect (c);
1018   if (NULL == namestore_handle)
1019   {
1020     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1021                 _("Failed to connect to the namestore!\n"));
1022     GNUNET_SCHEDULER_shutdown ();
1023     return;
1024   }
1025   namecache_handle = GNUNET_NAMECACHE_connect (c);
1026   if (NULL == namecache_handle)
1027   {
1028     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1029                 _("Failed to connect to the namecache!\n"));
1030     GNUNET_SCHEDULER_shutdown ();
1031     return;
1032   }
1033
1034   put_interval = INITIAL_PUT_INTERVAL;
1035   zone_publish_time_window_default = DEFAULT_ZONE_PUBLISH_TIME_WINDOW;
1036   if (GNUNET_OK ==
1037       GNUNET_CONFIGURATION_get_value_time (c, "gns",
1038                                            "ZONE_PUBLISH_TIME_WINDOW",
1039                                            &zone_publish_time_window_default))
1040   {
1041     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1042                 "Time window for zone iteration: %s\n",
1043                 GNUNET_STRINGS_relative_time_to_string (zone_publish_time_window,
1044                                                         GNUNET_YES));
1045   }
1046   zone_publish_time_window = zone_publish_time_window_default;
1047   if (GNUNET_OK ==
1048       GNUNET_CONFIGURATION_get_value_number (c, "gns",
1049                                             "MAX_PARALLEL_BACKGROUND_QUERIES",
1050                                             &max_parallel_bg_queries))
1051   {
1052     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1053                 "Number of allowed parallel background queries: %llu\n",
1054                 max_parallel_bg_queries);
1055   }
1056
1057   dht_handle = GNUNET_DHT_connect (c,
1058                                    (unsigned int) max_parallel_bg_queries);
1059   if (NULL == dht_handle)
1060   {
1061     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1062                 _("Could not connect to DHT!\n"));
1063     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1064     return;
1065   }
1066
1067   identity_handle = GNUNET_IDENTITY_connect (c,
1068                                              NULL,
1069                                              NULL);
1070   if (NULL == identity_handle)
1071   {
1072     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1073                 "Could not connect to identity service!\n");
1074   }
1075   else
1076   {
1077     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1078                 "Looking for gns-intercept ego\n");
1079     identity_op = GNUNET_IDENTITY_get (identity_handle,
1080                                        "gns-intercept",
1081                                        &identity_intercept_cb,
1082                                        (void *) c);
1083   }
1084   GNS_resolver_init (namecache_handle,
1085                      dht_handle,
1086                      c,
1087                      max_parallel_bg_queries);
1088   GNS_shorten_init (namestore_handle,
1089                     namecache_handle,
1090                     dht_handle);
1091   GNUNET_SERVER_disconnect_notify (server,
1092                                    &notify_client_disconnect,
1093                                    NULL);
1094   /* Schedule periodic put for our records. */
1095   first_zone_iteration = GNUNET_YES;
1096   GNUNET_SERVER_add_handlers (server, handlers);
1097   statistics = GNUNET_STATISTICS_create ("gns", c);
1098   nc = GNUNET_SERVER_notification_context_create (server, 1);
1099   zmon = GNUNET_NAMESTORE_zone_monitor_start (c,
1100                                               NULL,
1101                                               GNUNET_NO,
1102                                               &handle_monitor_error,
1103                                               NULL,
1104                                               &handle_monitor_event,
1105                                               NULL,
1106                                               &monitor_sync_event,
1107                                               NULL);
1108   GNUNET_break (NULL != zmon);
1109   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1110 }
1111
1112
1113 /**
1114  * The main function for the GNS service.
1115  *
1116  * @param argc number of arguments from the command line
1117  * @param argv command line arguments
1118  * @return 0 ok, 1 on error
1119  */
1120 int
1121 main (int argc, char *const *argv)
1122 {
1123   int ret;
1124
1125   ret =
1126       (GNUNET_OK ==
1127        GNUNET_SERVICE_run (argc, argv, "gns", GNUNET_SERVICE_OPTION_NONE, &run,
1128                            NULL)) ? 0 : 1;
1129   return ret;
1130 }
1131
1132 /* end of gnunet-service-gns.c */