0b617b0cd8652346cd4fe57c6f1b2421f320b2ed
[oweals/gnunet.git] / src / gns / gnunet-service-gns.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011, 2012 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 /**
22  *
23  * @file gns/gnunet-service-gns.c
24  * @brief GNUnet GNS service
25  * @author Martin Schanzenbach
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_transport_service.h"
30 #include "gnunet_dns_service.h"
31 #include "gnunet_dnsparser_lib.h"
32 #include "gnunet_dht_service.h"
33 #include "gnunet_namestore_service.h"
34 #include "gnunet_gns_service.h"
35 #include "gnunet_statistics_service.h"
36 #include "block_gns.h"
37 #include "gns.h"
38 #include "gns_common.h"
39 #include "gnunet-service-gns_resolver.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 upper bound for the zone iteration interval in milliseconds
51  */
52 #define MINIMUM_ZONE_ITERATION_INTERVAL GNUNET_TIME_UNIT_SECONDS
53
54 /**
55  * The default put interval for the zone iteration. In case
56  * No option is found
57  */
58 #define DEFAULT_ZONE_PUBLISH_TIME_WINDOW GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
59
60 /**
61  * The factor the current zone iteration interval is divided by for each
62  * additional new record
63  */
64 #define LATE_ITERATION_SPEEDUP_FACTOR 2
65
66
67 /**
68  * Handle to a shorten operation from api
69  */
70 struct ClientShortenHandle
71 {
72
73   /**
74    * List for all shorten requests
75    */
76   struct ClientShortenHandle *next;
77   /**
78    * List for all shorten requests
79    */
80   struct ClientShortenHandle *prev;
81
82   /**
83    * Handle to the requesting client
84    */
85   struct GNUNET_SERVER_Client *client;
86
87   /**
88    * The request id
89    */
90   uint64_t request_id;
91
92   /**
93    * request type
94    */
95   enum GNUNET_GNS_RecordType type;
96
97   /** 
98    * name to shorten
99    */
100   char name[MAX_DNS_NAME_LENGTH];
101
102   /**
103    * name of private zone (relative to root)
104    */
105   char private_zone_id[MAX_DNS_NAME_LENGTH];
106   
107   /**
108    * name of shorten zone (relative to root)
109    */
110   char shorten_zone_id[MAX_DNS_NAME_LENGTH];
111   
112   /**
113    * master zone
114    */
115   struct GNUNET_CRYPTO_ShortHashCode root_zone;
116
117   /**
118    * private zone
119    */
120   struct GNUNET_CRYPTO_ShortHashCode private_zone;
121   
122   /**
123    * shorten zone
124    */
125   struct GNUNET_CRYPTO_ShortHashCode shorten_zone;
126
127   /**
128    * Namestore lookup task
129    */
130   struct GNUNET_NAMESTORE_QueueEntry *namestore_task;
131 };
132
133
134 /**
135  * Handle to a get authority operation from api
136  */
137 struct ClientGetAuthHandle
138 {
139   /**
140    * Handle to the requesting client */
141   struct GNUNET_SERVER_Client *client;
142
143   /**
144    * request id
145    */
146   uint64_t request_id;
147
148   /**
149    * name to lookup authority
150    */
151   char *name;
152 };
153
154
155 /**
156  * Handle to a lookup operation from api
157  */
158 struct ClientLookupHandle
159 {
160
161   /**
162    * Handle to the requesting client
163    */
164   struct GNUNET_SERVER_Client *client;
165
166   /**
167    * The zone we look up in
168    */
169   struct GNUNET_CRYPTO_ShortHashCode zone;
170
171   /**
172    * GNUNET_YES if we only want to lookup from local cache
173    */
174   int only_cached;
175
176   /**
177    * request id 
178    */
179   uint64_t request_id;
180
181   /**
182    * request type
183    */
184   enum GNUNET_GNS_RecordType type;
185
186   /**
187    * optional zone private key used for shorten
188    */
189   struct GNUNET_CRYPTO_RsaPrivateKey *shorten_key;
190
191   /**
192    * the name to look up
193    */
194   char *name; 
195 };
196
197
198 /**
199  * Our handle to the DHT
200  */
201 static struct GNUNET_DHT_Handle *dht_handle;
202
203 /**
204  * Our zone's private key
205  */
206 static struct GNUNET_CRYPTO_RsaPrivateKey *zone_key;
207
208 /**
209  * Our handle to the namestore service
210  */
211 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
212
213 /**
214  * Handle to iterate over our authoritative zone in namestore
215  */
216 static struct GNUNET_NAMESTORE_ZoneIterator *namestore_iter;
217
218 /**
219  * Our notification context.
220  */
221 static struct GNUNET_SERVER_NotificationContext *nc;
222
223 /**
224  * Our zone hash
225  */
226 static struct GNUNET_CRYPTO_ShortHashCode zone_hash;
227
228 /**
229  * Useful for zone update for DHT put
230  */
231 static unsigned long long num_public_records;
232
233 /**
234  * Last seen record count
235  */
236 static unsigned long long last_num_public_records;
237
238 /**
239  * Zone iteration PUT interval.
240  */
241 static struct GNUNET_TIME_Relative put_interval;
242
243 /**
244  * Time window for zone iteration
245  */
246 static struct GNUNET_TIME_Relative zone_publish_time_window;
247
248 /**
249  * zone publish task
250  */
251 static GNUNET_SCHEDULER_TaskIdentifier zone_publish_task;
252
253 /**
254  * GNUNET_YES if automatic pkey import for name shortening
255  * is enabled
256  */
257 static int auto_import_pkey;
258
259 /**
260  * GNUNET_YES if zone has never been published before
261  */
262 static int first_zone_iteration;
263
264 /**
265  * The lookup timeout
266  */
267 static struct GNUNET_TIME_Relative default_lookup_timeout;
268
269 /**
270  * GNUNET_YES if ipv6 is supported
271  */
272 static int v6_enabled;
273
274 /**
275  * GNUNET_YES if ipv4 is supported
276  */
277 static int v4_enabled;
278
279 /**
280  * List for shorten requests
281  */
282 static struct ClientShortenHandle *csh_head;
283
284 /**
285  * List for shorten requests
286  */
287 static struct ClientShortenHandle *csh_tail;
288
289 /**
290  * Handle to the statistics service
291  */
292 static struct GNUNET_STATISTICS_Handle *statistics;
293
294
295 /**
296  * Task run during shutdown.
297  *
298  * @param cls unused
299  * @param tc unused
300  */
301 static void
302 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
303 {
304   struct ClientShortenHandle *csh_tmp;
305
306   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
307              "Shutting down!");
308   while (NULL != (csh_tmp = csh_head))
309   {
310     GNUNET_CONTAINER_DLL_remove (csh_head, csh_tail, csh_tmp);
311     GNUNET_free(csh_tmp);
312   }
313   GNUNET_SERVER_notification_context_destroy (nc);  
314   gns_interceptor_stop ();
315   gns_resolver_cleanup ();
316   if (NULL != statistics)
317     GNUNET_STATISTICS_destroy (statistics, GNUNET_NO);
318   if (zone_publish_task != GNUNET_SCHEDULER_NO_TASK)
319     GNUNET_SCHEDULER_cancel (zone_publish_task);
320   if (NULL != namestore_iter)
321     GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
322   GNUNET_NAMESTORE_disconnect(namestore_handle);
323   GNUNET_DHT_disconnect(dht_handle);
324 }
325
326
327 /**
328  * Method called periodically that triggers iteration over authoritative records
329  *
330  * @param cls closure
331  * @param tc task context
332  */
333 static void
334 publish_zone_dht_next (void *cls,
335                        const struct GNUNET_SCHEDULER_TaskContext *tc)
336 {
337   zone_publish_task = GNUNET_SCHEDULER_NO_TASK;
338   GNUNET_NAMESTORE_zone_iterator_next (namestore_iter);
339 }
340
341
342 /**
343  * Periodically iterate over our zone and store everything in dht
344  *
345  * @param cls NULL
346  * @param tc task context
347  */
348 static void
349 publish_zone_dht_start (void *cls, 
350                        const struct GNUNET_SCHEDULER_TaskContext *tc);
351
352
353 /**
354  * Function used to put all records successively into the DHT.
355  *
356  * @param cls the closure (NULL)
357  * @param key the public key of the authority (ours)
358  * @param expiration lifetime of the namestore entry
359  * @param name the name of the records
360  * @param rd_count the number of records in data
361  * @param rd the record data
362  * @param signature the signature for the record data
363  */
364 static void
365 put_gns_record(void *cls,
366                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
367                 struct GNUNET_TIME_Absolute expiration,
368                 const char *name,
369                 unsigned int rd_count,
370                 const struct GNUNET_NAMESTORE_RecordData *rd,
371                 const struct GNUNET_CRYPTO_RsaSignature *signature)
372 {  
373   struct GNSNameRecordBlock *nrb;
374   struct GNUNET_CRYPTO_ShortHashCode zhash;
375   struct GNUNET_HashCode dht_key;
376   uint32_t rd_payload_length;
377   char* nrb_data = NULL;
378   size_t namelen;
379   struct GNUNET_TIME_Relative next_put_interval; 
380
381   if (NULL == name)
382   {
383     /* we're done */
384     namestore_iter = NULL;
385     last_num_public_records = num_public_records;
386     first_zone_iteration = GNUNET_NO;
387     if (0 == num_public_records)
388     {
389       /**
390        * If no records are known (startup) or none present
391        * we can safely set the interval to the value for a single
392        * record
393        */
394       put_interval = GNUNET_TIME_relative_divide (zone_publish_time_window,
395                                                              1);
396
397       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
398                   "No records in db.\n");
399     }
400     else
401     {
402       put_interval = GNUNET_TIME_relative_divide (zone_publish_time_window,
403                                                              num_public_records);
404     }
405     put_interval = GNUNET_TIME_relative_max (MINIMUM_ZONE_ITERATION_INTERVAL,
406                                                         put_interval);
407
408     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
409                 "Zone iteration finished. Adjusted zone iteration interval to %s\n",
410                 GNUNET_STRINGS_relative_time_to_string (put_interval, GNUNET_YES));
411     GNUNET_STATISTICS_set (statistics,
412                            "Current zone iteration interval (in ms)",
413                            put_interval.rel_value,
414                            GNUNET_NO);
415     GNUNET_STATISTICS_update (statistics,
416                               "Number of zone iterations", 1, GNUNET_NO);
417     GNUNET_STATISTICS_set (statistics,
418                            "Number of public records in DHT",
419                            last_num_public_records,
420                            GNUNET_NO);
421     if (0 == num_public_records)
422       zone_publish_task = GNUNET_SCHEDULER_add_delayed (put_interval,
423                                                          &publish_zone_dht_start,
424                                                          NULL);
425     else
426       zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start, NULL);
427     return;
428   }
429   
430   namelen = strlen (name) + 1;
431   if (0 == rd_count)
432   {
433     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434                 "No records for name `%s'! Skipping.\n",
435                 name);
436     zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_next,
437                                                    NULL);
438     return;
439   }
440   if (NULL == signature)
441   {
442     GNUNET_break (0);
443     zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_next,
444                                                    NULL);
445     return;
446   }
447   
448   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
449               "Putting records for `%s' into the DHT\n", name); 
450   rd_payload_length = GNUNET_NAMESTORE_records_get_size (rd_count, rd); 
451   nrb = GNUNET_malloc (rd_payload_length + namelen
452                        + sizeof (struct GNSNameRecordBlock));
453   nrb->signature = *signature;
454   nrb->public_key = *key;
455   nrb->rd_count = htonl (rd_count);
456   memcpy (&nrb[1], name, namelen);
457   nrb_data = (char *) &nrb[1];
458   nrb_data += namelen;
459   rd_payload_length += sizeof(struct GNSNameRecordBlock) + namelen;
460   GNUNET_CRYPTO_short_hash (key,
461                             sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
462                             &zhash);
463   if (-1 == GNUNET_NAMESTORE_records_serialize (rd_count,
464                                                 rd,
465                                                 rd_payload_length,
466                                                 nrb_data))
467   {
468     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
469                 _("Records for name `%s' in zone %s too large to fit into DHT"),
470                 name,
471                 GNUNET_short_h2s (&zhash));
472     GNUNET_free (nrb);
473     zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_next,
474                                                    NULL);
475     return;
476   }
477
478   GNUNET_GNS_get_key_for_record (name, &zhash, &dht_key);
479   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
480               "putting %u records from zone %s for `%s' under key: %s with size %u and timeout %s\n",
481               rd_count,
482               GNUNET_short_h2s (&zhash),
483               name, 
484               GNUNET_h2s (&dht_key), 
485               (unsigned int) rd_payload_length,
486               GNUNET_STRINGS_relative_time_to_string (DHT_OPERATION_TIMEOUT, GNUNET_YES));
487   
488   GNUNET_STATISTICS_update (statistics,
489                             "Record bytes put into DHT", rd_payload_length, GNUNET_NO);
490
491   (void) GNUNET_DHT_put (dht_handle, &dht_key,
492                          DHT_GNS_REPLICATION_LEVEL,
493                          GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
494                          GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
495                          rd_payload_length,
496                          (char*)nrb,
497                          expiration,
498                          DHT_OPERATION_TIMEOUT,
499                          NULL,
500                          NULL); 
501   GNUNET_free (nrb);
502
503   num_public_records++;  
504   if ( (num_public_records > last_num_public_records)
505        && (GNUNET_NO == first_zone_iteration) )
506   {
507     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
508                 "Last record count was lower than current record count.  Reducing interval.\n");
509     put_interval = GNUNET_TIME_relative_divide (zone_publish_time_window,
510                                                            num_public_records);
511     next_put_interval = GNUNET_TIME_relative_divide (put_interval,
512                                                      LATE_ITERATION_SPEEDUP_FACTOR);
513   }
514   else
515     next_put_interval = put_interval;
516
517   GNUNET_STATISTICS_set (statistics,
518                          "Current zone iteration interval (ms)",
519                          next_put_interval.rel_value,
520                          GNUNET_NO); 
521   zone_publish_task = GNUNET_SCHEDULER_add_delayed (next_put_interval,
522                                                      &publish_zone_dht_next,
523                                                      NULL);
524 }
525
526
527 /**
528  * Periodically iterate over our zone and store everything in dht
529  *
530  * @param cls NULL
531  * @param tc task context
532  */
533 static void
534 publish_zone_dht_start(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
535 {
536   zone_publish_task = GNUNET_SCHEDULER_NO_TASK;
537
538   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Scheduling DHT zone update!\n");  
539   /* start counting again */
540   num_public_records = 0;
541   namestore_iter = GNUNET_NAMESTORE_zone_iteration_start (namestore_handle,
542                                                           NULL, /* All zones */
543                                                           GNUNET_NAMESTORE_RF_AUTHORITY,
544                                                           GNUNET_NAMESTORE_RF_PRIVATE,
545                                                           &put_gns_record,
546                                                           NULL);
547 }
548
549
550 /* END DHT ZONE PROPAGATION */
551
552
553 /**
554  * Send shorten response back to client
555  * 
556  * @param cls the closure containing a client shorten handle
557  * @param name the shortened name result or NULL if cannot be shortened
558  */
559 static void
560 send_shorten_response (void* cls, const char* name)
561 {
562   struct ClientShortenHandle *csh = cls;
563   struct GNUNET_GNS_ClientShortenResultMessage *rmsg;
564   size_t name_len;
565   
566   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message with %s\n",
567               "SHORTEN_RESULT", name);
568   if (NULL == name)
569     name_len = 0;
570   else
571     name_len = strlen (name) + 1;
572   GNUNET_STATISTICS_update (statistics,
573                             "Name shorten results", 1, GNUNET_NO);
574
575   rmsg = GNUNET_malloc (sizeof (struct GNUNET_GNS_ClientShortenResultMessage) +
576                         name_len);
577   
578   rmsg->id = csh->request_id;
579   rmsg->header.type = htons(GNUNET_MESSAGE_TYPE_GNS_SHORTEN_RESULT);
580   rmsg->header.size = 
581     htons(sizeof(struct GNUNET_GNS_ClientShortenResultMessage) +
582           name_len);
583   memcpy (&rmsg[1], name, name_len);
584   GNUNET_SERVER_notification_context_unicast (nc, csh->client,
585                                               &rmsg->header,
586                                               GNUNET_NO);
587   if (NULL != csh->namestore_task)
588     GNUNET_NAMESTORE_cancel (csh->namestore_task); 
589   GNUNET_free (rmsg);
590   GNUNET_free (csh);
591 }
592
593
594 /**
595  * Lookup the zone infos and shorten name
596  *
597  * @param cls the client shorten handle
598  * @param key key of the zone
599  * @param expiration expiration of record
600  * @param name name found or null if no result
601  * @param rd_count number of records found
602  * @param rd record data
603  * @param signature
604  *
605  */
606 static void
607 process_shorten_in_private_zone_lookup (void *cls,
608                                         const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
609                                         struct GNUNET_TIME_Absolute expiration,
610                                         const char *name,
611                                         unsigned int rd_count,
612                                         const struct GNUNET_NAMESTORE_RecordData *rd,
613                                         const struct GNUNET_CRYPTO_RsaSignature *signature)
614 {
615   struct ClientShortenHandle *csh = cls;
616   struct GNUNET_CRYPTO_ShortHashCode *szone = &csh->shorten_zone;
617   struct GNUNET_CRYPTO_ShortHashCode *pzone = &csh->private_zone;
618
619   csh->namestore_task = NULL;
620   if (0 == strcmp (csh->private_zone_id, ""))
621     pzone = NULL;
622   
623   if (rd_count == 0)
624   {
625     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626                 "No shorten zone in private zone!\n");
627     strcpy (csh->shorten_zone_id, "");
628     szone = NULL;
629   }
630   else
631   {
632     GNUNET_assert (rd_count == 1);
633
634     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
635                 "Shorten zone %s found in private zone %s\n",
636                 name, csh->private_zone_id);
637
638     sprintf (csh->shorten_zone_id, "%s.%s", name, csh->private_zone_id);
639   }
640   
641   GNUNET_CONTAINER_DLL_remove (csh_head, csh_tail, csh);
642
643   gns_resolver_shorten_name (&csh->root_zone,
644                              pzone,
645                              szone,
646                              csh->name,
647                              csh->private_zone_id,
648                              csh->shorten_zone_id,
649                              &send_shorten_response, csh);
650
651 }
652
653
654 /**
655  * Lookup the zone infos and shorten name
656  *
657  * @param cls the shorten handle
658  * @param key key of the zone
659  * @param expiration expiration of record
660  * @param name name found or null if no result
661  * @param rd_count number of records found
662  * @param rd record data
663  * @param signature
664  *
665  */
666 static void
667 process_shorten_in_root_zone_lookup (void *cls,
668                       const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
669                       struct GNUNET_TIME_Absolute expiration,
670                       const char *name,
671                       unsigned int rd_count,
672                       const struct GNUNET_NAMESTORE_RecordData *rd,
673                       const struct GNUNET_CRYPTO_RsaSignature *signature)
674 {
675   struct ClientShortenHandle *csh = cls;
676   struct GNUNET_CRYPTO_ShortHashCode *szone = &csh->shorten_zone;
677   struct GNUNET_CRYPTO_ShortHashCode *pzone = &csh->private_zone;
678   
679   csh->namestore_task = NULL;
680   if (0 == strcmp (csh->private_zone_id, ""))
681     pzone = NULL;
682
683   if (rd_count == 0)
684   {
685     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
686                 "No shorten zone in zone and no private zone!\n");
687
688     strcpy (csh->shorten_zone_id, "");
689
690     GNUNET_CONTAINER_DLL_remove (csh_head, csh_tail, csh);
691     szone = NULL;
692
693     gns_resolver_shorten_name (&csh->root_zone,
694                                pzone,
695                                szone,
696                                csh->name,
697                                csh->private_zone_id,
698                                csh->shorten_zone_id,
699                                &send_shorten_response, csh);
700     return;
701   }
702
703   GNUNET_assert (rd_count == 1);
704
705   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
706               "Private zone %s found in root zone\n", name);
707
708   strcpy (csh->private_zone_id, name);
709
710   csh->namestore_task = GNUNET_NAMESTORE_zone_to_name (namestore_handle,
711                                   pzone,
712                                   szone,
713                                   &process_shorten_in_private_zone_lookup,
714                                   csh);
715 }
716
717
718 /**
719  * Lookup the zone infos and shorten name
720  *
721  * @param cls the shorten handle
722  * @param key key of the zone
723  * @param expiration expiration of record
724  * @param name name found or null if no result
725  * @param rd_count number of records found
726  * @param rd record data
727  * @param signature
728  *
729  */
730 static void
731 process_private_in_root_zone_lookup (void *cls,
732                       const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
733                       struct GNUNET_TIME_Absolute expiration,
734                       const char *name,
735                       unsigned int rd_count,
736                       const struct GNUNET_NAMESTORE_RecordData *rd,
737                       const struct GNUNET_CRYPTO_RsaSignature *signature)
738 {
739   struct ClientShortenHandle *csh = cls;
740   csh->namestore_task = NULL;
741
742   if (rd_count == 0)
743   {
744     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745                 "No private zone in root zone\n");
746
747     strcpy (csh->private_zone_id, "");
748   
749     csh->namestore_task = GNUNET_NAMESTORE_zone_to_name (namestore_handle,
750                                   &csh->root_zone,
751                                   &csh->shorten_zone,
752                                   &process_shorten_in_root_zone_lookup,
753                                   csh);
754     return;
755   }
756
757   GNUNET_assert (rd_count == 1);
758
759   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
760               "Private zone %s found in root zone\n", name);
761
762   strcpy (csh->private_zone_id, name);
763
764   csh->namestore_task = GNUNET_NAMESTORE_zone_to_name (namestore_handle,
765                                   &csh->private_zone,
766                                   &csh->shorten_zone,
767                                   &process_shorten_in_private_zone_lookup,
768                                   csh);
769 }
770
771
772 /**
773  * Lookup the zone infos and shorten name
774  *
775  * @param csh the shorten handle
776  *
777  */
778 static void
779 start_shorten_name (struct ClientShortenHandle *csh)
780 {
781   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
782               "Looking for private zone name in root zone\n");
783
784   csh->namestore_task = GNUNET_NAMESTORE_zone_to_name (namestore_handle,
785                                   &csh->root_zone,
786                                   &csh->private_zone,
787                                   &process_private_in_root_zone_lookup,
788                                   csh);
789 }
790
791
792 /**
793  * Handle a shorten message from the api
794  *
795  * @param cls the closure (unused)
796  * @param client the client
797  * @param message the message
798  */
799 static void 
800 handle_shorten (void *cls,
801                 struct GNUNET_SERVER_Client * client,
802                 const struct GNUNET_MessageHeader * message)
803 {
804   struct ClientShortenHandle *csh;
805   const char *utf_in;
806   char name[MAX_DNS_NAME_LENGTH];
807   char* nameptr = name;
808   uint16_t msg_size;
809
810   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "SHORTEN");
811   msg_size = ntohs (message->size);
812   if (msg_size < sizeof (struct GNUNET_GNS_ClientShortenMessage))
813   {
814     GNUNET_break (0);
815     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
816     return;
817   }
818
819
820   const struct GNUNET_GNS_ClientShortenMessage *sh_msg =
821     (const struct GNUNET_GNS_ClientShortenMessage *) message;
822   
823   csh = GNUNET_malloc(sizeof (struct ClientShortenHandle));
824   csh->client = client;
825   csh->request_id = sh_msg->id;
826   GNUNET_CONTAINER_DLL_insert (csh_head, csh_tail, csh); 
827   GNUNET_STRINGS_utf8_tolower (utf_in, &nameptr);
828   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
829                "SHORTEN: Converted `%s' to `%s'\n", 
830              utf_in, 
831              nameptr);
832   GNUNET_SERVER_notification_context_add (nc, client);  
833   if (strlen (name) < strlen (GNUNET_GNS_TLD)) 
834   {
835     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
836                "SHORTEN: %s is too short\n", name);
837     GNUNET_CONTAINER_DLL_remove (csh_head, csh_tail, csh);
838     send_shorten_response(csh, name);
839     GNUNET_SERVER_receive_done (client, GNUNET_OK);
840     return;
841   }
842   if (strlen (name) > MAX_DNS_NAME_LENGTH) 
843   {
844     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
845                "SHORTEN: %s is too long\n", name);
846     GNUNET_CONTAINER_DLL_remove (csh_head, csh_tail, csh);
847     send_shorten_response(csh, name);
848     GNUNET_SERVER_receive_done (client, GNUNET_OK);
849     return;
850   }  
851   if ( (! is_gnunet_tld (name)) && 
852        (! is_zkey_tld (name)) )
853   {
854     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
855                 "%s is not our domain. Returning\n", name);
856     GNUNET_CONTAINER_DLL_remove (csh_head, csh_tail, csh);
857     send_shorten_response (csh, name);
858     GNUNET_SERVER_receive_done (client, GNUNET_OK);
859     return;
860   }
861   csh->shorten_zone = sh_msg->shorten_zone;
862   csh->private_zone = sh_msg->private_zone;
863   strcpy (csh->name, name);  
864   if (1 == ntohl(sh_msg->use_default_zone))
865     csh->root_zone = zone_hash; //Default zone
866   else
867     csh->root_zone = sh_msg->zone;
868   start_shorten_name (csh);
869   GNUNET_STATISTICS_update (statistics,
870                             "Name shorten attempts", 1, GNUNET_NO);
871   GNUNET_SERVER_receive_done (client, GNUNET_OK);
872 }
873
874
875 /**
876  * Send get authority response back to client
877  * 
878  * @param cls the closure containing a client get auth handle
879  * @param name the shortened name result or NULL if cannot be shortened
880  */
881 static void 
882 send_get_auth_response(void *cls, const char* name)
883 {
884   struct ClientGetAuthHandle *cah = cls;
885   struct GNUNET_GNS_ClientGetAuthResultMessage *rmsg;
886   
887   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message with %s\n",
888               "GET_AUTH_RESULT", name);
889   if (name != NULL)
890   {
891     GNUNET_STATISTICS_update (statistics,
892                               "Authorities resolved", 1, GNUNET_NO);
893   }  
894   if (name == NULL)  
895     name = "";  
896
897   rmsg = GNUNET_malloc(sizeof(struct GNUNET_GNS_ClientGetAuthResultMessage)
898                        + strlen(name) + 1);
899   
900   rmsg->id = cah->request_id;
901   rmsg->header.type = htons(GNUNET_MESSAGE_TYPE_GNS_GET_AUTH_RESULT);
902   rmsg->header.size = 
903     htons(sizeof(struct GNUNET_GNS_ClientGetAuthResultMessage) +
904           strlen(name) + 1);
905   strcpy ((char*)&rmsg[1], name);
906
907   GNUNET_SERVER_notification_context_unicast (nc, cah->client,
908                                               &rmsg->header,
909                                               GNUNET_NO);
910   GNUNET_SERVER_receive_done (cah->client, GNUNET_OK);
911   
912   GNUNET_free(rmsg);
913   GNUNET_free_non_null(cah->name);
914   GNUNET_free(cah);  
915 }
916
917
918 /**
919  * Handle a get authority message from the api
920  *
921  * @param cls the closure
922  * @param client the client
923  * @param message the message
924  */
925 static void 
926 handle_get_authority (void *cls,
927                       struct GNUNET_SERVER_Client * client,
928                       const struct GNUNET_MessageHeader * message)
929 {
930   struct ClientGetAuthHandle *cah;
931   const char *utf_in;
932   char name[MAX_DNS_NAME_LENGTH];
933   char* nameptr = name;
934   uint16_t msg_size;
935
936   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "GET_AUTH");
937   msg_size = ntohs(message->size);
938   if (msg_size < sizeof (struct GNUNET_GNS_ClientGetAuthMessage))
939   {
940     GNUNET_break (0);
941     GNUNET_SERVER_receive_done (client, GNUNET_OK);
942     return;
943   }
944   GNUNET_SERVER_notification_context_add (nc, client);
945
946   struct GNUNET_GNS_ClientGetAuthMessage *sh_msg =
947     (struct GNUNET_GNS_ClientGetAuthMessage *) message;
948   
949   GNUNET_STRINGS_utf8_tolower((const char*)&sh_msg[1], &nameptr);
950
951
952   cah = GNUNET_malloc(sizeof(struct ClientGetAuthHandle));
953   cah->client = client;
954   cah->request_id = sh_msg->id;
955   if (strlen (name) < strlen(GNUNET_GNS_TLD))
956   {
957     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
958                 "GET_AUTH: `%s' is too short. Returning\n", name);
959     cah->name = NULL;
960     send_get_auth_response(cah, name);
961     return;
962   }  
963   if (strlen (name) > MAX_DNS_NAME_LENGTH) 
964   {
965     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
966                "GET_AUTH: `%s' is too long", name);
967     cah->name = NULL;
968     send_get_auth_response(cah, name);
969     return;
970   }  
971   if (0 != strcmp (name + strlen (name) - strlen(GNUNET_GNS_TLD),
972                    GNUNET_GNS_TLD))
973   {
974     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
975                 "GET_AUTH: %s is not our domain. Returning\n", name);
976     cah->name = NULL;
977     send_get_auth_response(cah, name);
978     return;
979   }
980
981   if (0 == strcmp(name, GNUNET_GNS_TLD))
982   {
983     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
984                 "GET_AUTH: %s is us. Returning\n", name);
985     cah->name = NULL;
986     send_get_auth_response(cah, name);
987     return;
988   }
989   
990   cah->name = GNUNET_malloc (strlen (name)
991                             - strlen (GNUNET_GNS_TLD) + 1);
992   memcpy(cah->name, name,
993          strlen (name) - strlen (GNUNET_GNS_TLD));
994
995   /* Start delegation resolution in our namestore */
996   gns_resolver_get_authority (zone_hash, zone_hash, name,
997                               &send_get_auth_response, cah);
998   GNUNET_STATISTICS_update (statistics,
999                             "Authority lookup attempts", 1, GNUNET_NO);
1000 }
1001
1002
1003
1004 /**
1005  * Reply to client with the result from our lookup.
1006  *
1007  * @param cls the closure (our client lookup handle)
1008  * @param rd_count the number of records
1009  * @param rd the record data
1010  */
1011 static void
1012 send_lookup_response(void* cls,
1013                      uint32_t rd_count,
1014                      const struct GNUNET_NAMESTORE_RecordData *rd)
1015 {
1016   struct ClientLookupHandle* clh = cls;
1017   struct GNUNET_GNS_ClientLookupResultMessage *rmsg;
1018   size_t len;
1019   
1020   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message with %d results\n",
1021               "LOOKUP_RESULT", rd_count);
1022   
1023   len = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
1024   rmsg = GNUNET_malloc (
1025                       len + sizeof (struct GNUNET_GNS_ClientLookupResultMessage));
1026   
1027   rmsg->id = clh->request_id;
1028   rmsg->rd_count = htonl(rd_count);
1029   rmsg->header.type = htons(GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
1030   rmsg->header.size = 
1031     htons(len+sizeof(struct GNUNET_GNS_ClientLookupResultMessage));
1032   
1033   GNUNET_NAMESTORE_records_serialize (rd_count, rd, len, (char*)&rmsg[1]);
1034   
1035   GNUNET_SERVER_notification_context_unicast (nc, clh->client,
1036                                 (const struct GNUNET_MessageHeader *) rmsg,
1037                                 GNUNET_NO);
1038   GNUNET_SERVER_receive_done (clh->client, GNUNET_OK);
1039   
1040   GNUNET_free(rmsg);
1041   GNUNET_free(clh->name);
1042   
1043   if (NULL != clh->shorten_key)
1044     GNUNET_CRYPTO_rsa_key_free (clh->shorten_key);
1045   GNUNET_free(clh);
1046
1047   GNUNET_STATISTICS_update (statistics,
1048                             "Completed lookups", 1, GNUNET_NO);
1049   if (rd != NULL)
1050     GNUNET_STATISTICS_update (statistics,
1051                               "Records resolved", rd_count, GNUNET_NO);
1052 }
1053
1054
1055 /**
1056  * Handle lookup requests from client
1057  *
1058  * @param cls the closure
1059  * @param client the client
1060  * @param message the message
1061  */
1062 static void
1063 handle_lookup(void *cls,
1064               struct GNUNET_SERVER_Client * client,
1065               const struct GNUNET_MessageHeader * message)
1066 {
1067   size_t namelen;
1068   char name[MAX_DNS_NAME_LENGTH];
1069   struct ClientLookupHandle *clh;
1070   char* nameptr = name;
1071   const char *utf_in;
1072   int only_cached;
1073   struct GNUNET_CRYPTO_RsaPrivateKey *key;
1074   struct GNUNET_CRYPTO_RsaPrivateKeyBinaryEncoded *pkey;
1075   char* tmp_pkey;
1076   uint16_t msg_size;
1077   const struct GNUNET_GNS_ClientLookupMessage *sh_msg;
1078   
1079   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "LOOKUP");
1080   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "LOOKUP");
1081   msg_size = ntohs(message->size);
1082   if (msg_size < sizeof (struct GNUNET_GNS_ClientLookupMessage))
1083   {
1084     GNUNET_break (0);
1085     GNUNET_SERVER_receive_done (client, GNUNET_OK);
1086     return;
1087   }
1088   sh_msg = (const struct GNUNET_GNS_ClientLookupMessage *) message;
1089   GNUNET_SERVER_notification_context_add (nc, client);
1090
1091   if (GNUNET_YES == ntohl (sh_msg->have_key))
1092   {
1093     pkey = (struct GNUNET_CRYPTO_RsaPrivateKeyBinaryEncoded *) &sh_msg[1];
1094     tmp_pkey = (char*) &sh_msg[1];
1095     key = GNUNET_CRYPTO_rsa_decode_key (tmp_pkey, ntohs (pkey->len));
1096     GNUNET_STRINGS_utf8_tolower (&tmp_pkey[ntohs (pkey->len)], &nameptr);
1097   }
1098   else
1099   {
1100     key = NULL;
1101     GNUNET_STRINGS_utf8_tolower ((char*) &sh_msg[1], &nameptr);
1102   }
1103   
1104   namelen = strlen(name)+1;
1105   clh = GNUNET_malloc (sizeof (struct ClientLookupHandle));
1106   memset (clh, 0, sizeof (struct ClientLookupHandle));
1107   clh->client = client;
1108   clh->name = GNUNET_malloc (namelen);
1109   strcpy (clh->name, name);
1110   clh->request_id = sh_msg->id;
1111   clh->type = ntohl (sh_msg->type);
1112   clh->shorten_key = key;
1113
1114   only_cached = ntohl (sh_msg->only_cached);
1115   
1116   if (strlen (name) > MAX_DNS_NAME_LENGTH) {
1117     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1118                 "LOOKUP: %s is too long", name);
1119     clh->name = NULL;
1120     send_lookup_response (clh, 0, NULL);
1121     return;
1122   }
1123
1124   if ((clh->type == GNUNET_GNS_RECORD_A) &&
1125       (GNUNET_OK != v4_enabled))
1126   {
1127     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1128                "LOOKUP: Query for A record but AF_INET not supported!");
1129     clh->name = NULL;
1130     send_lookup_response (clh, 0, NULL);
1131     return;
1132   }
1133   
1134   if ((clh->type == GNUNET_GNS_RECORD_AAAA) &&
1135       (GNUNET_OK != v6_enabled))
1136   {
1137     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1138                 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
1139     clh->name = NULL;
1140     send_lookup_response (clh, 0, NULL);
1141     return;
1142   }
1143   
1144   if (1 == ntohl (sh_msg->use_default_zone))
1145     clh->zone = zone_hash; //Default zone
1146   else
1147     clh->zone = sh_msg->zone;
1148   
1149   if (GNUNET_YES == auto_import_pkey)
1150   {
1151     gns_resolver_lookup_record (clh->zone, clh->zone, clh->type, clh->name,
1152                                 clh->shorten_key,
1153                                 default_lookup_timeout,
1154                                 clh->only_cached,
1155                                 &send_lookup_response, clh);  
1156   }
1157   else
1158   {
1159     gns_resolver_lookup_record (clh->zone, clh->zone, clh->type, name,
1160                                 NULL,
1161                                 default_lookup_timeout,
1162                                 only_cached,
1163                                 &send_lookup_response, clh);
1164   }
1165
1166   GNUNET_STATISTICS_update (statistics,
1167                             "Record lookup attempts", 1, GNUNET_NO);
1168 }
1169
1170
1171 /**
1172  * Test if the given AF is supported by this system.
1173  *
1174  * @param af to test
1175  * @return GNUNET_OK if the AF is supported
1176  */
1177 static int
1178 test_af (int af)
1179 {
1180   int s;
1181
1182   s = socket (af, SOCK_STREAM, 0);
1183   if (-1 == s)
1184   {
1185     if (EAFNOSUPPORT == errno)
1186       return GNUNET_NO;
1187     fprintf (stderr, "Failed to create test socket: %s\n", STRERROR (errno));
1188     return GNUNET_SYSERR;
1189   }
1190 #if WINDOWS
1191   closesocket (s);
1192 #else
1193   close (s);
1194 #endif
1195   return GNUNET_OK;
1196 }
1197
1198
1199 /**
1200  * Process GNS requests.
1201  *
1202  * @param cls closure)
1203  * @param server the initialized server
1204  * @param c configuration to use
1205  */
1206 static void
1207 run (void *cls, struct GNUNET_SERVER_Handle *server,
1208      const struct GNUNET_CONFIGURATION_Handle *c)
1209 {
1210   char* keyfile;
1211   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
1212   unsigned long long max_parallel_bg_queries = 0;
1213   unsigned long long default_lookup_timeout_secs = 0;
1214   int ignore_pending = GNUNET_NO;
1215
1216   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1217     {&handle_shorten, NULL, GNUNET_MESSAGE_TYPE_GNS_SHORTEN, 0},
1218     {&handle_lookup, NULL, GNUNET_MESSAGE_TYPE_GNS_LOOKUP, 0},
1219     {&handle_get_authority, NULL, GNUNET_MESSAGE_TYPE_GNS_GET_AUTH, 0}
1220   };
1221
1222   v6_enabled = test_af (AF_INET6);
1223   v4_enabled = test_af (AF_INET);
1224
1225   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c, "gns",
1226                                              "ZONEKEY", &keyfile))
1227   {
1228     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1229                 "No private key for root zone specified!\n");
1230     GNUNET_SCHEDULER_shutdown ();
1231     return;
1232   }
1233
1234   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1235              "Using keyfile %s for root zone.\n", keyfile);
1236
1237   zone_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
1238   GNUNET_CRYPTO_rsa_key_get_public (zone_key, &pkey);
1239
1240   GNUNET_CRYPTO_short_hash(&pkey,
1241                      sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1242                      &zone_hash);
1243   GNUNET_free(keyfile);
1244   namestore_handle = GNUNET_NAMESTORE_connect (c);
1245   if (NULL == namestore_handle)
1246   {
1247     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1248                 _("Failed to connect to the namestore!\n"));
1249     GNUNET_SCHEDULER_shutdown ();
1250     return;
1251   }
1252   
1253   auto_import_pkey = GNUNET_NO;
1254   if (GNUNET_YES ==
1255       GNUNET_CONFIGURATION_get_value_yesno (c, "gns",
1256                                             "AUTO_IMPORT_PKEY"))
1257   {
1258     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1259                "Automatic PKEY import is enabled.\n");
1260     auto_import_pkey = GNUNET_YES;
1261
1262   }
1263
1264   put_interval = INITIAL_PUT_INTERVAL;
1265
1266   zone_publish_time_window = DEFAULT_ZONE_PUBLISH_TIME_WINDOW;
1267
1268   if (GNUNET_OK ==
1269       GNUNET_CONFIGURATION_get_value_time (c, "gns",
1270                                              "ZONE_PUBLISH_TIME_WINDOW",
1271                                              &zone_publish_time_window))
1272   {
1273     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1274                 "Time window for zone iteration: %s\n",
1275                 GNUNET_STRINGS_relative_time_to_string (zone_publish_time_window, GNUNET_YES));
1276   }
1277
1278   if (GNUNET_OK ==
1279       GNUNET_CONFIGURATION_get_value_number (c, "gns",
1280                                             "MAX_PARALLEL_BACKGROUND_QUERIES",
1281                                             &max_parallel_bg_queries))
1282   {
1283     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1284                "Number of allowed parallel background queries: %llu\n",
1285                max_parallel_bg_queries);
1286   }
1287
1288   if (GNUNET_YES ==
1289       GNUNET_CONFIGURATION_get_value_yesno (c, "gns",
1290                                             "AUTO_IMPORT_CONFIRMATION_REQ"))
1291   {
1292     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1293                "Auto import requires user confirmation\n");
1294     ignore_pending = GNUNET_YES;
1295   }
1296
1297   if (GNUNET_OK ==
1298       GNUNET_CONFIGURATION_get_value_number(c, "gns",
1299                                             "DEFAULT_LOOKUP_TIMEOUT",
1300                                             &default_lookup_timeout_secs))
1301   {
1302     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1303                "Default lookup timeout: %llus\n", default_lookup_timeout_secs);
1304     default_lookup_timeout = GNUNET_TIME_relative_multiply(
1305                                             GNUNET_TIME_UNIT_SECONDS,
1306                                             default_lookup_timeout_secs);
1307   }
1308   
1309   /**
1310    * handle to the dht
1311    */
1312   dht_handle = GNUNET_DHT_connect(c,
1313                        //max_parallel_bg_queries); //FIXME get ht_len from cfg
1314                        1024);
1315
1316   if (NULL == dht_handle)
1317   {
1318     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not connect to DHT!\n");
1319   }
1320   
1321   if (gns_resolver_init(namestore_handle, dht_handle, zone_hash, c,
1322                         max_parallel_bg_queries,
1323                         ignore_pending)
1324       == GNUNET_SYSERR)
1325   {
1326     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1327                "Unable to initialize resolver!\n");
1328     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1329     return;
1330   }
1331
1332   if (GNUNET_YES ==
1333       GNUNET_CONFIGURATION_get_value_yesno (c, "gns", "HIJACK_DNS"))
1334   {
1335     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1336                "DNS hijacking enabled... connecting to service.\n");
1337
1338     if (gns_interceptor_init(zone_hash, zone_key, c) == GNUNET_SYSERR)
1339     {
1340       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1341                "Failed to enable the dns interceptor!\n");
1342     }
1343   }
1344   
1345   /**
1346    * Schedule periodic put
1347    * for our records
1348    * We have roughly an hour for all records;
1349    */
1350   first_zone_iteration = GNUNET_YES;
1351   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start, NULL);
1352
1353   GNUNET_SERVER_add_handlers (server, handlers);
1354   
1355   //FIXME
1356   //GNUNET_SERVER_disconnect_notify (server,
1357   //                                 &client_disconnect_notification,
1358   //                                 NULL);
1359
1360   statistics = GNUNET_STATISTICS_create ("gns", c);
1361
1362   nc = GNUNET_SERVER_notification_context_create (server, 1);
1363
1364   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1365                                 NULL);
1366 }
1367
1368
1369 /**
1370  * The main function for the GNS service.
1371  *
1372  * @param argc number of arguments from the command line
1373  * @param argv command line arguments
1374  * @return 0 ok, 1 on error
1375  */
1376 int
1377 main (int argc, char *const *argv)
1378 {
1379   int ret;
1380
1381   ret =
1382       (GNUNET_OK ==
1383        GNUNET_SERVICE_run (argc, argv, "gns", GNUNET_SERVICE_OPTION_NONE, &run,
1384                            NULL)) ? 0 : 1;
1385   return ret;
1386 }
1387
1388 /* end of gnunet-service-gns.c */