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