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