3e718dac851831fced2178b0e9ef4040020319d1
[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_reverse_done ();
305   GNS_shorten_done ();
306   while (NULL != (ma = ma_head))
307   {
308     GNUNET_DHT_put_cancel (ma->ph);
309     GNUNET_CONTAINER_DLL_remove (ma_head,
310                                  ma_tail,
311                                  ma);
312     GNUNET_free (ma);
313   }
314   if (NULL != statistics)
315   {
316     GNUNET_STATISTICS_destroy (statistics,
317                                GNUNET_NO);
318     statistics = NULL;
319   }
320   if (NULL != zone_publish_task)
321   {
322     GNUNET_SCHEDULER_cancel (zone_publish_task);
323     zone_publish_task = NULL;
324   }
325   if (NULL != namestore_iter)
326   {
327     GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
328     namestore_iter = NULL;
329   }
330   if (NULL != zmon)
331   {
332     GNUNET_NAMESTORE_zone_monitor_stop (zmon);
333     zmon = NULL;
334   }
335   if (NULL != namestore_handle)
336   {
337     GNUNET_NAMESTORE_disconnect (namestore_handle);
338     namestore_handle = NULL;
339   }
340   if (NULL != namecache_handle)
341   {
342     GNUNET_NAMECACHE_disconnect (namecache_handle);
343     namecache_handle = NULL;
344   }
345   if (NULL != active_put)
346   {
347     GNUNET_DHT_put_cancel (active_put);
348     active_put = NULL;
349   }
350   if (NULL != dht_handle)
351   {
352     GNUNET_DHT_disconnect (dht_handle);
353     dht_handle = NULL;
354   }
355 }
356
357 /**
358  * Called whenever a client is disconnected.
359  *
360  * @param cls closure
361  * @param client identification of the client
362  * @param app_ctx @a client
363  */
364 static void
365 client_disconnect_cb (void *cls,
366                       struct GNUNET_SERVICE_Client *client,
367                       void *app_ctx)
368 {
369   struct ClientLookupHandle *clh;
370   struct GnsClient *gc = app_ctx;
371
372   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373               "Client %p disconnected\n",
374               client);
375   while (NULL != (clh = gc->clh_head))
376   {
377     if (NULL != clh->lookup)
378       GNS_resolver_lookup_cancel (clh->lookup);
379     if (NULL != clh->rev_lookup)
380       GNS_reverse_lookup_cancel (clh->rev_lookup);
381     GNUNET_CONTAINER_DLL_remove (gc->clh_head,
382                                  gc->clh_tail,
383                                  clh);
384     GNUNET_free (clh);
385   }
386
387   GNUNET_free (gc); 
388 }
389
390
391 /**
392  * Add a client to our list of active clients.
393  *
394  * @param cls NULL
395  * @param client client to add
396  * @param mq message queue for @a client
397  * @return internal namestore client structure for this client
398  */
399 static void *
400 client_connect_cb (void *cls,
401                    struct GNUNET_SERVICE_Client *client,
402                    struct GNUNET_MQ_Handle *mq)
403 {
404   struct GnsClient *gc;
405   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406               "Client %p connected\n",
407               client);
408   gc = GNUNET_new (struct GnsClient);
409   gc->client = client;
410   gc->mq = mq;
411   return gc;
412 }
413
414
415 /**
416  * Method called periodically that triggers iteration over authoritative records
417  *
418  * @param cls closure
419  */
420 static void
421 publish_zone_dht_next (void *cls)
422 {
423   zone_publish_task = NULL;
424   GNUNET_assert (NULL != namestore_iter);
425   GNUNET_NAMESTORE_zone_iterator_next (namestore_iter);
426 }
427
428
429 /**
430  * Periodically iterate over our zone and store everything in dht
431  *
432  * @param cls NULL
433  */
434 static void
435 publish_zone_dht_start (void *cls);
436
437
438 /**
439  * Continuation called from DHT once the PUT operation is done.
440  *
441  * @param cls closure, NULL if called from regular iteration,
442  *        `struct MonitorActivity` if called from #handle_monitor_event.
443  * @param success #GNUNET_OK on success
444  */
445 static void
446 dht_put_continuation (void *cls,
447                       int success)
448 {
449   struct MonitorActivity *ma = cls;
450   struct GNUNET_TIME_Relative next_put_interval;
451
452   num_public_records++;
453   if (NULL == ma)
454   {
455     active_put = NULL;
456     if ( (num_public_records > last_num_public_records) &&
457          (GNUNET_NO == first_zone_iteration) )
458     {
459       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
460                   "Last record count was lower than current record count.  Reducing interval.\n");
461       put_interval = GNUNET_TIME_relative_divide (zone_publish_time_window,
462                                                   num_public_records);
463       next_put_interval = GNUNET_TIME_relative_divide (put_interval,
464                                                        LATE_ITERATION_SPEEDUP_FACTOR);
465     }
466     else
467       next_put_interval = put_interval;
468     next_put_interval = GNUNET_TIME_relative_min (next_put_interval,
469                                                   MAXIMUM_ZONE_ITERATION_INTERVAL);
470     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471                 "PUT complete, next PUT in %s!\n",
472                 GNUNET_STRINGS_relative_time_to_string (next_put_interval,
473                                                         GNUNET_YES));
474
475     GNUNET_STATISTICS_set (statistics,
476                            "Current zone iteration interval (ms)",
477                            next_put_interval.rel_value_us / 1000LL,
478                            GNUNET_NO);
479     GNUNET_assert (NULL == zone_publish_task);
480     zone_publish_task = GNUNET_SCHEDULER_add_delayed (next_put_interval,
481                                                       &publish_zone_dht_next,
482                                                       NULL);
483   }
484   else
485   {
486     GNUNET_CONTAINER_DLL_remove (ma_head,
487                                  ma_tail,
488                                  ma);
489     GNUNET_free (ma);
490   }
491 }
492
493
494 /**
495  * Convert namestore records from the internal format to that
496  * suitable for publication (removes private records, converts
497  * to absolute expiration time).
498  *
499  * @param rd input records
500  * @param rd_count size of the @a rd and @a rd_public arrays
501  * @param rd_public where to write the converted records
502  * @return number of records written to @a rd_public
503  */
504 static unsigned int
505 convert_records_for_export (const struct GNUNET_GNSRECORD_Data *rd,
506                             unsigned int rd_count,
507                             struct GNUNET_GNSRECORD_Data *rd_public)
508 {
509   struct GNUNET_TIME_Absolute now;
510   unsigned int rd_public_count;
511   unsigned int i;
512
513   rd_public_count = 0;
514   now = GNUNET_TIME_absolute_get ();
515   for (i=0;i<rd_count;i++)
516     if (0 == (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE))
517     {
518       rd_public[rd_public_count] = rd[i];
519       if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
520       {
521         /* GNUNET_GNSRECORD_block_create will convert to absolute time;
522            we just need to adjust our iteration frequency */
523         min_relative_record_time.rel_value_us =
524           GNUNET_MIN (rd_public[rd_public_count].expiration_time,
525                       min_relative_record_time.rel_value_us);
526       }
527       else if (rd_public[rd_public_count].expiration_time < now.abs_value_us)
528       {
529         /* record already expired, skip it */
530         continue;
531       }
532       rd_public_count++;
533     }
534   return rd_public_count;
535 }
536
537
538 /**
539  * Store GNS records in the DHT.
540  *
541  * @param key key of the zone
542  * @param label label to store under
543  * @param rd_public public record data
544  * @param rd_public_count number of records in @a rd_public
545  * @param pc_arg closure argument to pass to the #dht_put_continuation
546  * @return DHT PUT handle, NULL on error
547  */
548 static struct GNUNET_DHT_PutHandle *
549 perform_dht_put (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
550                  const char *label,
551                  const struct GNUNET_GNSRECORD_Data *rd_public,
552                  unsigned int rd_public_count,
553                  void *pc_arg)
554 {
555   struct GNUNET_GNSRECORD_Block *block;
556   struct GNUNET_HashCode query;
557   struct GNUNET_TIME_Absolute expire;
558   size_t block_size;
559   struct GNUNET_DHT_PutHandle *ret;
560
561   expire = GNUNET_GNSRECORD_record_get_expiration_time (rd_public_count,
562                                                         rd_public);
563   block = GNUNET_GNSRECORD_block_create (key,
564                                          expire,
565                                          label,
566                                          rd_public,
567                                          rd_public_count);
568   if (NULL == block)
569     return NULL; /* whoops */
570   block_size = ntohl (block->purpose.size)
571     + sizeof (struct GNUNET_CRYPTO_EcdsaSignature)
572     + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
573   GNUNET_GNSRECORD_query_from_private_key (key,
574                                            label,
575                                            &query);
576   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
577               "Storing %u record(s) for label `%s' in DHT with expiration `%s' under key %s\n",
578               rd_public_count,
579               label,
580               GNUNET_STRINGS_absolute_time_to_string (expire),
581               GNUNET_h2s (&query));
582   ret = GNUNET_DHT_put (dht_handle,
583                         &query,
584                         DHT_GNS_REPLICATION_LEVEL,
585                         GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
586                         GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
587                         block_size,
588                         block,
589                         expire,
590                         &dht_put_continuation,
591                         pc_arg);
592   GNUNET_free (block);
593   return ret;
594 }
595
596
597 /**
598  * We encountered an error in our zone iteration.
599  */
600 static void
601 zone_iteration_error (void *cls)
602 {
603   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
604               "Got disconnected from namestore database, retrying.\n");
605   namestore_iter = NULL;
606   /* We end up here on error/disconnect/shutdown, so potentially
607      while a zone publish task or a DHT put is still running; hence
608      we need to cancel those. */
609   if (NULL != zone_publish_task)
610   {
611     GNUNET_SCHEDULER_cancel (zone_publish_task);
612     zone_publish_task = NULL;
613   }
614   if (NULL != active_put)
615   {
616     GNUNET_DHT_put_cancel (active_put);
617     active_put = NULL;
618   }
619   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
620                                                 NULL);
621 }
622
623
624 /**
625  * Zone iteration is completed.
626  */
627 static void
628 zone_iteration_finished (void *cls)
629 {
630   /* we're done with one iteration, calculate when to do the next one */
631   namestore_iter = NULL;
632   last_num_public_records = num_public_records;
633   first_zone_iteration = GNUNET_NO;
634   if (0 == num_public_records)
635   {
636     /**
637      * If no records are known (startup) or none present
638      * we can safely set the interval to the value for a single
639      * record
640      */
641     put_interval = zone_publish_time_window;
642     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
643                 "No records in namestore database.\n");
644   }
645   else
646   {
647     /* If records are present, next publication is based on the minimum
648      * relative expiration time of the records published divided by 4
649      */
650     zone_publish_time_window
651       = GNUNET_TIME_relative_min (GNUNET_TIME_relative_divide (min_relative_record_time, 4),
652                                   zone_publish_time_window_default);
653     put_interval = GNUNET_TIME_relative_divide (zone_publish_time_window,
654                                                 num_public_records);
655   }
656   /* reset for next iteration */
657   min_relative_record_time = GNUNET_TIME_UNIT_FOREVER_REL;
658   put_interval = GNUNET_TIME_relative_max (MINIMUM_ZONE_ITERATION_INTERVAL,
659                                            put_interval);
660   put_interval = GNUNET_TIME_relative_min (put_interval,
661                                            MAXIMUM_ZONE_ITERATION_INTERVAL);
662   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
663               "Zone iteration finished. Adjusted zone iteration interval to %s\n",
664               GNUNET_STRINGS_relative_time_to_string (put_interval,
665                                                       GNUNET_YES));
666   GNUNET_STATISTICS_set (statistics,
667                          "Current zone iteration interval (in ms)",
668                          put_interval.rel_value_us / 1000LL,
669                          GNUNET_NO);
670   GNUNET_STATISTICS_update (statistics,
671                             "Number of zone iterations",
672                             1,
673                             GNUNET_NO);
674   GNUNET_STATISTICS_set (statistics,
675                          "Number of public records in DHT",
676                          last_num_public_records,
677                          GNUNET_NO);
678   GNUNET_assert (NULL == zone_publish_task);
679   if (0 == num_public_records)
680     zone_publish_task = GNUNET_SCHEDULER_add_delayed (put_interval,
681                                                       &publish_zone_dht_start,
682                                                       NULL);
683   else
684     zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
685                                                   NULL);
686 }
687
688
689 /**
690  * Function used to put all records successively into the DHT.
691  *
692  * @param cls the closure (NULL)
693  * @param key the private key of the authority (ours)
694  * @param label the name of the records, NULL once the iteration is done
695  * @param rd_count the number of records in @a rd
696  * @param rd the record data
697  */
698 static void
699 put_gns_record (void *cls,
700                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
701                 const char *label,
702                 unsigned int rd_count,
703                 const struct GNUNET_GNSRECORD_Data *rd)
704 {
705   struct GNUNET_GNSRECORD_Data rd_public[rd_count];
706   unsigned int rd_public_count;
707
708   rd_public_count = convert_records_for_export (rd,
709                                                 rd_count,
710                                                 rd_public);
711
712   if (0 == rd_public_count)
713   {
714     GNUNET_assert (NULL == zone_publish_task);
715     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
716                 "Record set empty, moving to next record set\n");
717     zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_next,
718                                                   NULL);
719     return;
720   }
721   /* We got a set of records to publish */
722   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
723               "Starting DHT PUT\n");
724   active_put = perform_dht_put (key,
725                                 label,
726                                 rd_public,
727                                 rd_public_count,
728                                 NULL);
729   if (NULL == active_put)
730   {
731     GNUNET_break (0);
732     dht_put_continuation (NULL, GNUNET_NO);
733   }
734 }
735
736
737 /**
738  * Periodically iterate over all zones and store everything in DHT
739  *
740  * @param cls NULL
741  */
742 static void
743 publish_zone_dht_start (void *cls)
744 {
745   zone_publish_task = NULL;
746
747   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748               "Starting DHT zone update!\n");
749   /* start counting again */
750   num_public_records = 0;
751   GNUNET_assert (NULL == namestore_iter);
752   namestore_iter
753     = GNUNET_NAMESTORE_zone_iteration_start (namestore_handle,
754                                              NULL, /* All zones */
755                                              &zone_iteration_error,
756                                              NULL,
757                                              &put_gns_record,
758                                              NULL,
759                                              &zone_iteration_finished,
760                                              NULL);
761 }
762
763
764 /**
765  * Process a record that was stored in the namestore
766  * (invoked by the monitor).
767  *
768  * @param cls closure, NULL
769  * @param zone private key of the zone; NULL on disconnect
770  * @param label label of the records; NULL on disconnect
771  * @param rd_count number of entries in @a rd array, 0 if label was deleted
772  * @param rd array of records with data to store
773  */
774 static void
775 handle_monitor_event (void *cls,
776                       const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
777                       const char *label,
778                       unsigned int rd_count,
779                       const struct GNUNET_GNSRECORD_Data *rd)
780 {
781   struct GNUNET_GNSRECORD_Data rd_public[rd_count];
782   unsigned int rd_public_count;
783   struct MonitorActivity *ma;
784
785   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
786               "Received %u records for label `%s' via namestore monitor\n",
787               rd_count,
788               label);
789   /* filter out records that are not public, and convert to
790      absolute expiration time. */
791   rd_public_count = convert_records_for_export (rd,
792                                                 rd_count,
793                                                 rd_public);
794   if (0 == rd_public_count)
795     return; /* nothing to do */
796   ma = GNUNET_new (struct MonitorActivity);
797   ma->ph = perform_dht_put (zone,
798                             label,
799                             rd,
800                             rd_count,
801                             ma);
802   if (NULL == ma->ph)
803   {
804     /* PUT failed, do not remember operation */
805     GNUNET_free (ma);
806     return;
807   }
808   GNUNET_CONTAINER_DLL_insert (ma_head,
809                                ma_tail,
810                                ma);
811 }
812
813
814 /* END DHT ZONE PROPAGATION */
815
816
817 /**
818  * Reply to client with the result from our lookup.
819  *
820  * @param cls the closure (our client lookup handle)
821  * @param rd_count the number of records in @a rd
822  * @param rd the record data
823  */
824 static void
825 send_lookup_response (void* cls,
826                       uint32_t rd_count,
827                       const struct GNUNET_GNSRECORD_Data *rd)
828 {
829   struct ClientLookupHandle *clh = cls;
830   struct GNUNET_MQ_Envelope *env;
831   struct LookupResultMessage *rmsg;
832   size_t len;
833
834   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
835               "Sending LOOKUP_RESULT message with %u results\n",
836               (unsigned int) rd_count);
837
838   len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
839   env = GNUNET_MQ_msg_extra (rmsg,
840                              len,
841                              GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
842   rmsg->id = clh->request_id;
843   rmsg->rd_count = htonl (rd_count);
844   GNUNET_GNSRECORD_records_serialize (rd_count, rd, len,
845                                       (char*) &rmsg[1]);
846   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
847                   env);
848   GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head, clh->gc->clh_tail, clh);
849   GNUNET_free (clh);
850   GNUNET_STATISTICS_update (statistics,
851                             "Completed lookups", 1,
852                             GNUNET_NO);
853   GNUNET_STATISTICS_update (statistics,
854                             "Records resolved",
855                             rd_count,
856                             GNUNET_NO);
857 }
858
859 /**
860  * Reply to client with the result from our reverse lookup.
861  *
862  * @param cls the closure (our client lookup handle)
863  * @param rd_count the number of records in @a rd
864  * @param rd the record data
865  */
866 static void
867 send_reverse_lookup_response (void* cls,
868                               const char *name)
869 {
870   struct ClientLookupHandle *clh = cls;
871   struct GNUNET_MQ_Envelope *env;
872   struct ReverseLookupResultMessage *rmsg;
873   size_t len;
874
875   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
876               "Sending LOOKUP_RESULT message with %s\n",
877               name);
878
879   if (NULL == name)
880     len = 1;
881   else
882     len = strlen (name) + 1;
883   env = GNUNET_MQ_msg_extra (rmsg,
884                              len,
885                              GNUNET_MESSAGE_TYPE_GNS_REVERSE_LOOKUP_RESULT);
886   rmsg->id = clh->request_id;
887   if (1 < len)
888     GNUNET_memcpy ((char*) &rmsg[1],
889                    name,
890                    strlen (name));
891   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
892                   env);
893   GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head, clh->gc->clh_tail, clh);
894   GNUNET_free (clh);
895   GNUNET_STATISTICS_update (statistics,
896                             "Completed reverse lookups", 1,
897                             GNUNET_NO);
898 }
899
900
901 /**
902  * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
903  *
904  * @param cls client sending the message
905  * @param l_msg message of type `struct LookupMessage`
906  * @return #GNUNET_OK if @a l_msg is well-formed
907  */
908 static int
909 check_lookup (void *cls,
910               const struct LookupMessage *l_msg)
911 {
912   size_t msg_size;
913   const char* name;
914
915   msg_size = ntohs (l_msg->header.size);
916   if (msg_size < sizeof (struct LookupMessage))
917   {
918     GNUNET_break (0);
919     return GNUNET_SYSERR;
920   }
921   name = (const char *) &l_msg[1];
922   if ( ('\0' != name[msg_size - sizeof (struct LookupMessage) - 1]) ||
923        (strlen (name) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) )
924   {
925     GNUNET_break (0);
926     return GNUNET_SYSERR;
927   }
928   return GNUNET_OK;
929 }
930
931 /**
932  * Handle lookup requests from client
933  *
934  * @param cls the closure
935  * @param client the client
936  * @param message the message
937  */
938 static void
939 handle_lookup (void *cls,
940                const struct LookupMessage *sh_msg)
941 {
942   struct GnsClient *gc = cls;
943   char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
944   struct ClientLookupHandle *clh;
945   char *nameptr = name;
946   const char *utf_in;
947   const struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
948
949   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
950               "Received LOOKUP message\n");
951   GNUNET_SERVICE_client_continue (gc->client);
952   if (GNUNET_YES == ntohs (sh_msg->have_key))
953     key = &sh_msg->shorten_key;
954   else
955     key = NULL;
956   utf_in = (const char *) &sh_msg[1];
957   GNUNET_STRINGS_utf8_tolower (utf_in, nameptr);
958
959   clh = GNUNET_new (struct ClientLookupHandle);
960   GNUNET_CONTAINER_DLL_insert (gc->clh_head, gc->clh_tail, clh);
961   clh->gc = gc;
962   clh->request_id = sh_msg->id;
963   if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
964        (GNUNET_OK != v4_enabled) )
965   {
966     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
967                 "LOOKUP: Query for A record but AF_INET not supported!");
968     send_lookup_response (clh, 0, NULL);
969     return;
970   }
971   if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
972        (GNUNET_OK != v6_enabled) )
973   {
974     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
975                 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
976     send_lookup_response (clh, 0, NULL);
977     return;
978   }
979   clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
980                                      ntohl (sh_msg->type),
981                                      name,
982                                      key,
983                                      (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
984                                      &send_lookup_response, clh);
985   GNUNET_STATISTICS_update (statistics,
986                             "Lookup attempts",
987                             1, GNUNET_NO);
988 }
989
990 /**
991  * Handle reverse lookup requests from client
992  *
993  * @param cls the closure
994  * @param client the client
995  * @param message the message
996  */
997 static void
998 handle_rev_lookup (void *cls,
999                    const struct ReverseLookupMessage *sh_msg)
1000 {
1001   struct GnsClient *gc = cls;
1002   struct ClientLookupHandle *clh;
1003
1004   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1005               "Received REVERSE_LOOKUP message\n");
1006   GNUNET_SERVICE_client_continue (gc->client);
1007
1008   clh = GNUNET_new (struct ClientLookupHandle);
1009   GNUNET_CONTAINER_DLL_insert (gc->clh_head, gc->clh_tail, clh);
1010   clh->gc = gc;
1011   clh->request_id = sh_msg->id;
1012   clh->rev_lookup = GNS_reverse_lookup (&sh_msg->zone_pkey,
1013                                         &sh_msg->root_pkey,
1014                                         &send_reverse_lookup_response,
1015                                         clh);
1016   GNUNET_STATISTICS_update (statistics,
1017                             "Reverse lookup attempts",
1018                             1, GNUNET_NO);
1019 }
1020
1021
1022 /**
1023  * The zone monitor is now in SYNC with the current state of the
1024  * name store.  Start to perform periodic iterations.
1025  *
1026  * @param cls NULL
1027  */
1028 static void
1029 monitor_sync_event (void *cls)
1030 {
1031   GNUNET_assert (NULL == zone_publish_task);
1032   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
1033                                                 NULL);
1034 }
1035
1036
1037 /**
1038  * The zone monitor is now in SYNC with the current state of the
1039  * name store.  Start to perform periodic iterations.
1040  *
1041  * @param cls NULL
1042  */
1043 static void
1044 handle_monitor_error (void *cls)
1045 {
1046   if (NULL != zone_publish_task)
1047   {
1048     GNUNET_SCHEDULER_cancel (zone_publish_task);
1049     zone_publish_task = NULL;
1050   }
1051   if (NULL != namestore_iter)
1052   {
1053     GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
1054     namestore_iter = NULL;
1055   }
1056   if (NULL != active_put)
1057   {
1058     GNUNET_DHT_put_cancel (active_put);
1059     active_put = NULL;
1060   }
1061   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start,
1062                                                 NULL);
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_reverse_cb (void *cls,
1084                        struct GNUNET_IDENTITY_Ego *ego,
1085                        void **ctx,
1086                        const char *name)
1087 {
1088   identity_op = NULL;
1089
1090   if (NULL == ego)
1091   {
1092     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1093                 _("No ego configured for `%s`\n"),
1094                 "gns-master");
1095
1096     return;
1097   }
1098   if (GNUNET_SYSERR ==
1099       GNS_reverse_init (namestore_handle,
1100                         GNUNET_IDENTITY_ego_get_private_key (ego),
1101                         name))
1102   {
1103     GNUNET_break (0);
1104     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1105     return;
1106   }
1107 }
1108
1109
1110
1111 /**
1112  * Method called to inform about the ego to be used for the master zone
1113  * for DNS interceptions.
1114  *
1115  * This function is only called ONCE, and 'NULL' being passed in
1116  * @a ego does indicate that interception is not configured.
1117  * If @a ego is non-NULL, we should start to intercept DNS queries
1118  * and resolve ".gnu" queries using the given ego as the master zone.
1119  *
1120  * @param cls closure, our `const struct GNUNET_CONFIGURATION_Handle *c`
1121  * @param ego ego handle
1122  * @param ctx context for application to store data for this ego
1123  *                 (during the lifetime of this process, initially NULL)
1124  * @param name name assigned by the user for this ego,
1125  *                   NULL if the user just deleted the ego and it
1126  *                   must thus no longer be used
1127  */
1128 static void
1129 identity_intercept_cb (void *cls,
1130                        struct GNUNET_IDENTITY_Ego *ego,
1131                        void **ctx,
1132                        const char *name)
1133 {
1134   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1135   struct GNUNET_CRYPTO_EcdsaPublicKey dns_root;
1136   identity_op = NULL;
1137
1138   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139               "Looking for gns-intercept ego\n");
1140   identity_op = GNUNET_IDENTITY_get (identity_handle,
1141                                      "gns-reverse",
1142                                      &identity_reverse_cb,
1143                                      (void*)cfg);
1144
1145
1146   if (NULL == ego)
1147   {
1148     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1149                 _("No ego configured for `%s`\n"),
1150                 "gns-intercept");
1151
1152     return;
1153   }
1154   GNUNET_IDENTITY_ego_get_public_key (ego,
1155                                       &dns_root);
1156   if (GNUNET_SYSERR ==
1157       GNS_interceptor_init (&dns_root, cfg))
1158   {
1159     GNUNET_break (0);
1160     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1161     return;
1162   }
1163 }
1164
1165
1166 /**
1167  * Process GNS requests.
1168  *
1169  * @param cls closure
1170  * @param server the initialized server
1171  * @param c configuration to use
1172  */
1173 static void
1174 run (void *cls,
1175      const struct GNUNET_CONFIGURATION_Handle *c,
1176      struct GNUNET_SERVICE_Handle *service)
1177 {
1178   unsigned long long max_parallel_bg_queries = 0;
1179
1180   v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
1181   v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
1182   min_relative_record_time = GNUNET_TIME_UNIT_FOREVER_REL;
1183   namestore_handle = GNUNET_NAMESTORE_connect (c);
1184   if (NULL == namestore_handle)
1185   {
1186     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1187                 _("Failed to connect to the namestore!\n"));
1188     GNUNET_SCHEDULER_shutdown ();
1189     return;
1190   }
1191   namecache_handle = GNUNET_NAMECACHE_connect (c);
1192   if (NULL == namecache_handle)
1193   {
1194     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1195                 _("Failed to connect to the namecache!\n"));
1196     GNUNET_SCHEDULER_shutdown ();
1197     return;
1198   }
1199
1200   put_interval = INITIAL_PUT_INTERVAL;
1201   zone_publish_time_window_default = DEFAULT_ZONE_PUBLISH_TIME_WINDOW;
1202   if (GNUNET_OK ==
1203       GNUNET_CONFIGURATION_get_value_time (c, "gns",
1204                                            "ZONE_PUBLISH_TIME_WINDOW",
1205                                            &zone_publish_time_window_default))
1206   {
1207     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1208                 "Time window for zone iteration: %s\n",
1209                 GNUNET_STRINGS_relative_time_to_string (zone_publish_time_window,
1210                                                         GNUNET_YES));
1211   }
1212   zone_publish_time_window = zone_publish_time_window_default;
1213   if (GNUNET_OK ==
1214       GNUNET_CONFIGURATION_get_value_number (c, "gns",
1215                                              "MAX_PARALLEL_BACKGROUND_QUERIES",
1216                                              &max_parallel_bg_queries))
1217   {
1218     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1219                 "Number of allowed parallel background queries: %llu\n",
1220                 max_parallel_bg_queries);
1221   }
1222
1223   dht_handle = GNUNET_DHT_connect (c,
1224                                    (unsigned int) max_parallel_bg_queries);
1225   if (NULL == dht_handle)
1226   {
1227     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1228                 _("Could not connect to DHT!\n"));
1229     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1230     return;
1231   }
1232
1233   identity_handle = GNUNET_IDENTITY_connect (c,
1234                                              NULL,
1235                                              NULL);
1236   if (NULL == identity_handle)
1237   {
1238     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1239                 "Could not connect to identity service!\n");
1240   }
1241   else
1242   {
1243     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1244                 "Looking for gns-intercept ego\n");
1245     identity_op = GNUNET_IDENTITY_get (identity_handle,
1246                                        "gns-intercept",
1247                                        &identity_intercept_cb,
1248                                        (void *) c);
1249   }
1250   GNS_resolver_init (namecache_handle,
1251                      dht_handle,
1252                      c,
1253                      max_parallel_bg_queries);
1254   GNS_shorten_init (namestore_handle,
1255                     namecache_handle,
1256                     dht_handle);
1257   /* Schedule periodic put for our records. */
1258   first_zone_iteration = GNUNET_YES;
1259   statistics = GNUNET_STATISTICS_create ("gns", c);
1260   zmon = GNUNET_NAMESTORE_zone_monitor_start (c,
1261                                               NULL,
1262                                               GNUNET_NO,
1263                                               &handle_monitor_error,
1264                                               NULL,
1265                                               &handle_monitor_event,
1266                                               NULL,
1267                                               &monitor_sync_event,
1268                                               NULL);
1269   GNUNET_break (NULL != zmon);
1270   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
1271 }
1272
1273
1274 /**
1275  * Define "main" method using service macro.
1276  */
1277 GNUNET_SERVICE_MAIN
1278 ("gns",
1279  GNUNET_SERVICE_OPTION_NONE,
1280  &run,
1281  &client_connect_cb,
1282  &client_disconnect_cb,
1283  NULL,
1284  GNUNET_MQ_hd_var_size (lookup,
1285                         GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
1286                         struct LookupMessage,
1287                         NULL),
1288  GNUNET_MQ_hd_fixed_size (rev_lookup,
1289                           GNUNET_MESSAGE_TYPE_GNS_REVERSE_LOOKUP,
1290                           struct ReverseLookupMessage,
1291                           NULL),
1292  GNUNET_MQ_handler_end());
1293
1294
1295 /* end of gnunet-service-gns.c */