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