-doxy
[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
809   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "SHORTEN");
810   msg_size = ntohs (message->size);
811   if (msg_size < sizeof (struct GNUNET_GNS_ClientShortenMessage))
812   {
813     GNUNET_break (0);
814     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
815     return;
816   }
817
818
819   const struct GNUNET_GNS_ClientShortenMessage *sh_msg =
820     (const struct GNUNET_GNS_ClientShortenMessage *) message;
821   
822   csh = GNUNET_malloc(sizeof (struct ClientShortenHandle));
823   csh->client = client;
824   csh->request_id = sh_msg->id;
825   GNUNET_CONTAINER_DLL_insert (csh_head, csh_tail, csh); 
826   GNUNET_STRINGS_utf8_tolower (utf_in, &nameptr);
827   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
828                "SHORTEN: Converted `%s' to `%s'\n", 
829              utf_in, 
830              nameptr);
831   GNUNET_SERVER_notification_context_add (nc, client);  
832   if (strlen (name) < strlen (GNUNET_GNS_TLD)) 
833   {
834     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
835                "SHORTEN: %s is too short\n", name);
836     GNUNET_CONTAINER_DLL_remove (csh_head, csh_tail, csh);
837     send_shorten_response(csh, name);
838     GNUNET_SERVER_receive_done (client, GNUNET_OK);
839     return;
840   }
841   if (strlen (name) > MAX_DNS_NAME_LENGTH) 
842   {
843     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
844                "SHORTEN: %s is too long\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   if ( (! is_gnunet_tld (name)) && 
851        (! is_zkey_tld (name)) )
852   {
853     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
854                 "%s is not our domain. Returning\n", name);
855     GNUNET_CONTAINER_DLL_remove (csh_head, csh_tail, csh);
856     send_shorten_response (csh, name);
857     GNUNET_SERVER_receive_done (client, GNUNET_OK);
858     return;
859   }
860   csh->shorten_zone = sh_msg->shorten_zone;
861   csh->private_zone = sh_msg->private_zone;
862   strcpy (csh->name, name);  
863   if (1 == ntohl(sh_msg->use_default_zone))
864     csh->root_zone = zone_hash; //Default zone
865   else
866     csh->root_zone = sh_msg->zone;
867   start_shorten_name (csh);
868   GNUNET_STATISTICS_update (statistics,
869                             "Name shorten attempts", 1, GNUNET_NO);
870   GNUNET_SERVER_receive_done (client, GNUNET_OK);
871 }
872
873
874 /**
875  * Send get authority response back to client
876  * 
877  * @param cls the closure containing a client get auth handle
878  * @param name the shortened name result or NULL if cannot be shortened
879  */
880 static void 
881 send_get_auth_response(void *cls, const char* name)
882 {
883   struct ClientGetAuthHandle *cah = cls;
884   struct GNUNET_GNS_ClientGetAuthResultMessage *rmsg;
885   
886   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message with %s\n",
887               "GET_AUTH_RESULT", name);
888   if (name != NULL)
889   {
890     GNUNET_STATISTICS_update (statistics,
891                               "Authorities resolved", 1, GNUNET_NO);
892   }  
893   if (name == NULL)  
894     name = "";  
895
896   rmsg = GNUNET_malloc(sizeof(struct GNUNET_GNS_ClientGetAuthResultMessage)
897                        + strlen(name) + 1);
898   
899   rmsg->id = cah->request_id;
900   rmsg->header.type = htons(GNUNET_MESSAGE_TYPE_GNS_GET_AUTH_RESULT);
901   rmsg->header.size = 
902     htons(sizeof(struct GNUNET_GNS_ClientGetAuthResultMessage) +
903           strlen(name) + 1);
904   strcpy ((char*)&rmsg[1], name);
905
906   GNUNET_SERVER_notification_context_unicast (nc, cah->client,
907                                               &rmsg->header,
908                                               GNUNET_NO);
909   GNUNET_SERVER_receive_done (cah->client, GNUNET_OK);
910   
911   GNUNET_free(rmsg);
912   GNUNET_free_non_null(cah->name);
913   GNUNET_free(cah);  
914 }
915
916
917 /**
918  * Handle a get authority message from the api
919  *
920  * @param cls the closure
921  * @param client the client
922  * @param message the message
923  */
924 static void 
925 handle_get_authority (void *cls,
926                       struct GNUNET_SERVER_Client * client,
927                       const struct GNUNET_MessageHeader * message)
928 {
929   struct ClientGetAuthHandle *cah;
930   const char *utf_in;
931   char name[MAX_DNS_NAME_LENGTH];
932   char* nameptr = name;
933
934   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "GET_AUTH");
935   msg_size = ntohs(message->size);
936   if (msg_size < sizeof (struct GNUNET_GNS_ClientGetAuthMessage))
937   {
938     GNUNET_break (0);
939     GNUNET_SERVER_receive_done (client, GNUNET_OK);
940     return;
941   }
942   GNUNET_SERVER_notification_context_add (nc, client);
943
944   struct GNUNET_GNS_ClientGetAuthMessage *sh_msg =
945     (struct GNUNET_GNS_ClientGetAuthMessage *) message;
946   
947   GNUNET_STRINGS_utf8_tolower((const char*)&sh_msg[1], &nameptr);
948
949
950   cah = GNUNET_malloc(sizeof(struct ClientGetAuthHandle));
951   cah->client = client;
952   cah->request_id = sh_msg->id;
953   if (strlen (name) < strlen(GNUNET_GNS_TLD))
954   {
955     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
956                 "GET_AUTH: `%s' is too short. Returning\n", name);
957     cah->name = NULL;
958     send_get_auth_response(cah, name);
959     return;
960   }  
961   if (strlen (name) > MAX_DNS_NAME_LENGTH) 
962   {
963     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
964                "GET_AUTH: `%s' is too long", name);
965     cah->name = NULL;
966     send_get_auth_response(cah, name);
967     return;
968   }  
969   if (0 != strcmp (name + strlen (name) - strlen(GNUNET_GNS_TLD),
970                    GNUNET_GNS_TLD))
971   {
972     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
973                 "GET_AUTH: %s is not our domain. Returning\n", name);
974     cah->name = NULL;
975     send_get_auth_response(cah, name);
976     return;
977   }
978
979   if (0 == strcmp(name, GNUNET_GNS_TLD))
980   {
981     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
982                 "GET_AUTH: %s is us. Returning\n", name);
983     cah->name = NULL;
984     send_get_auth_response(cah, name);
985     return;
986   }
987   
988   cah->name = GNUNET_malloc (strlen (name)
989                             - strlen (GNUNET_GNS_TLD) + 1);
990   memcpy(cah->name, name,
991          strlen (name) - strlen (GNUNET_GNS_TLD));
992
993   /* Start delegation resolution in our namestore */
994   gns_resolver_get_authority (zone_hash, zone_hash, name,
995                               &send_get_auth_response, cah);
996   GNUNET_STATISTICS_update (statistics,
997                             "Authority lookup attempts", 1, GNUNET_NO);
998 }
999
1000
1001
1002 /**
1003  * Reply to client with the result from our lookup.
1004  *
1005  * @param cls the closure (our client lookup handle)
1006  * @param rd_count the number of records
1007  * @param rd the record data
1008  */
1009 static void
1010 send_lookup_response(void* cls,
1011                      uint32_t rd_count,
1012                      const struct GNUNET_NAMESTORE_RecordData *rd)
1013 {
1014   struct ClientLookupHandle* clh = cls;
1015   struct GNUNET_GNS_ClientLookupResultMessage *rmsg;
1016   size_t len;
1017   
1018   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message with %d results\n",
1019               "LOOKUP_RESULT", rd_count);
1020   
1021   len = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
1022   rmsg = GNUNET_malloc (
1023                       len + sizeof (struct GNUNET_GNS_ClientLookupResultMessage));
1024   
1025   rmsg->id = clh->request_id;
1026   rmsg->rd_count = htonl(rd_count);
1027   rmsg->header.type = htons(GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
1028   rmsg->header.size = 
1029     htons(len+sizeof(struct GNUNET_GNS_ClientLookupResultMessage));
1030   
1031   GNUNET_NAMESTORE_records_serialize (rd_count, rd, len, (char*)&rmsg[1]);
1032   
1033   GNUNET_SERVER_notification_context_unicast (nc, clh->client,
1034                                 (const struct GNUNET_MessageHeader *) rmsg,
1035                                 GNUNET_NO);
1036   GNUNET_SERVER_receive_done (clh->client, GNUNET_OK);
1037   
1038   GNUNET_free(rmsg);
1039   GNUNET_free(clh->name);
1040   
1041   if (NULL != clh->shorten_key)
1042     GNUNET_CRYPTO_rsa_key_free (clh->shorten_key);
1043   GNUNET_free(clh);
1044
1045   GNUNET_STATISTICS_update (statistics,
1046                             "Completed lookups", 1, GNUNET_NO);
1047   if (rd != NULL)
1048     GNUNET_STATISTICS_update (statistics,
1049                               "Records resolved", rd_count, GNUNET_NO);
1050 }
1051
1052
1053 /**
1054  * Handle lookup requests from client
1055  *
1056  * @param cls the closure
1057  * @param client the client
1058  * @param message the message
1059  */
1060 static void
1061 handle_lookup(void *cls,
1062               struct GNUNET_SERVER_Client * client,
1063               const struct GNUNET_MessageHeader * message)
1064 {
1065   size_t namelen;
1066   char name[MAX_DNS_NAME_LENGTH];
1067   struct ClientLookupHandle *clh;
1068   char* nameptr = name;
1069   const char *utf_in;
1070   int only_cached;
1071   struct GNUNET_CRYPTO_RsaPrivateKey *key;
1072   struct GNUNET_CRYPTO_RsaPrivateKeyBinaryEncoded *pkey;
1073   char* tmp_pkey;
1074   
1075   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "LOOKUP");
1076   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "LOOKUP");
1077   msg_size = ntohs(message->size);
1078   if (msg_size < sizeof (struct GNUNET_GNS_ClientLookupMessage))
1079   {
1080     GNUNET_break (0);
1081     GNUNET_SERVER_receive_done (client, GNUNET_OK);
1082     return;
1083   }
1084   sh_msg = (const struct GNUNET_GNS_ClientLookupMessage *) message;
1085   GNUNET_SERVER_notification_context_add (nc, client);
1086
1087   struct GNUNET_GNS_ClientLookupMessage *sh_msg =
1088     (struct GNUNET_GNS_ClientLookupMessage *) message;
1089   
1090   if (GNUNET_YES == ntohl (sh_msg->have_key))
1091   {
1092     pkey = (struct GNUNET_CRYPTO_RsaPrivateKeyBinaryEncoded *) &sh_msg[1];
1093     tmp_pkey = (char*) &sh_msg[1];
1094     key = GNUNET_CRYPTO_rsa_decode_key (tmp_pkey, ntohs (pkey->len));
1095     GNUNET_STRINGS_utf8_tolower (&tmp_pkey[ntohs (pkey->len)], &nameptr);
1096   }
1097   else
1098   {
1099     key = NULL;
1100     GNUNET_STRINGS_utf8_tolower ((char*) &sh_msg[1], &nameptr);
1101   }
1102   
1103   namelen = strlen(name)+1;
1104   clh = GNUNET_malloc (sizeof (struct ClientLookupHandle));
1105   memset (clh, 0, sizeof (struct ClientLookupHandle));
1106   clh->client = client;
1107   clh->name = GNUNET_malloc (namelen);
1108   strcpy (clh->name, name);
1109   clh->request_id = sh_msg->id;
1110   clh->type = ntohl (sh_msg->type);
1111   clh->shorten_key = key;
1112
1113   only_cached = ntohl (sh_msg->only_cached);
1114   
1115   if (strlen (name) > MAX_DNS_NAME_LENGTH) {
1116     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1117                 "LOOKUP: %s is too long", name);
1118     clh->name = NULL;
1119     send_lookup_response (clh, 0, NULL);
1120     return;
1121   }
1122
1123   if ((clh->type == GNUNET_GNS_RECORD_A) &&
1124       (GNUNET_OK != v4_enabled))
1125   {
1126     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1127                "LOOKUP: Query for A record but AF_INET not supported!");
1128     clh->name = NULL;
1129     send_lookup_response (clh, 0, NULL);
1130     return;
1131   }
1132   
1133   if ((clh->type == GNUNET_GNS_RECORD_AAAA) &&
1134       (GNUNET_OK != v6_enabled))
1135   {
1136     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1137                 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
1138     clh->name = NULL;
1139     send_lookup_response (clh, 0, NULL);
1140     return;
1141   }
1142   
1143   if (1 == ntohl (sh_msg->use_default_zone))
1144     clh->zone = zone_hash; //Default zone
1145   else
1146     clh->zone = sh_msg->zone;
1147   
1148   if (GNUNET_YES == auto_import_pkey)
1149   {
1150     gns_resolver_lookup_record (clh->zone, clh->zone, clh->type, clh->name,
1151                                 clh->shorten_key,
1152                                 default_lookup_timeout,
1153                                 clh->only_cached,
1154                                 &send_lookup_response, clh);  
1155   }
1156   else
1157   {
1158     gns_resolver_lookup_record (clh->zone, clh->zone, clh->type, name,
1159                                 NULL,
1160                                 default_lookup_timeout,
1161                                 only_cached,
1162                                 &send_lookup_response, clh);
1163   }
1164
1165   GNUNET_STATISTICS_update (statistics,
1166                             "Record lookup attempts", 1, GNUNET_NO);
1167 }
1168
1169
1170 /**
1171  * Test if the given AF is supported by this system.
1172  *
1173  * @param af to test
1174  * @return GNUNET_OK if the AF is supported
1175  */
1176 static int
1177 test_af (int af)
1178 {
1179   int s;
1180
1181   s = socket (af, SOCK_STREAM, 0);
1182   if (-1 == s)
1183   {
1184     if (EAFNOSUPPORT == errno)
1185       return GNUNET_NO;
1186     fprintf (stderr, "Failed to create test socket: %s\n", STRERROR (errno));
1187     return GNUNET_SYSERR;
1188   }
1189 #if WINDOWS
1190   closesocket (s);
1191 #else
1192   close (s);
1193 #endif
1194   return GNUNET_OK;
1195 }
1196
1197
1198 /**
1199  * Process GNS requests.
1200  *
1201  * @param cls closure)
1202  * @param server the initialized server
1203  * @param c configuration to use
1204  */
1205 static void
1206 run (void *cls, struct GNUNET_SERVER_Handle *server,
1207      const struct GNUNET_CONFIGURATION_Handle *c)
1208 {
1209   char* keyfile;
1210   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
1211   unsigned long long max_parallel_bg_queries = 0;
1212   unsigned long long default_lookup_timeout_secs = 0;
1213   int ignore_pending = GNUNET_NO;
1214
1215   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1216     {&handle_shorten, NULL, GNUNET_MESSAGE_TYPE_GNS_SHORTEN, 0},
1217     {&handle_lookup, NULL, GNUNET_MESSAGE_TYPE_GNS_LOOKUP, 0},
1218     {&handle_get_authority, NULL, GNUNET_MESSAGE_TYPE_GNS_GET_AUTH, 0}
1219   };
1220
1221   v6_enabled = test_af (AF_INET6);
1222   v4_enabled = test_af (AF_INET);
1223
1224   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c, "gns",
1225                                              "ZONEKEY", &keyfile))
1226   {
1227     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1228                 "No private key for root zone specified!\n");
1229     GNUNET_SCHEDULER_shutdown ();
1230     return;
1231   }
1232
1233   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1234              "Using keyfile %s for root zone.\n", keyfile);
1235
1236   zone_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
1237   GNUNET_CRYPTO_rsa_key_get_public (zone_key, &pkey);
1238
1239   GNUNET_CRYPTO_short_hash(&pkey,
1240                      sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1241                      &zone_hash);
1242   GNUNET_free(keyfile);
1243   namestore_handle = GNUNET_NAMESTORE_connect (c);
1244   if (NULL == namestore_handle)
1245   {
1246     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1247                 _("Failed to connect to the namestore!\n"));
1248     GNUNET_SCHEDULER_shutdown ();
1249     return;
1250   }
1251   
1252   auto_import_pkey = GNUNET_NO;
1253   if (GNUNET_YES ==
1254       GNUNET_CONFIGURATION_get_value_yesno (c, "gns",
1255                                             "AUTO_IMPORT_PKEY"))
1256   {
1257     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1258                "Automatic PKEY import is enabled.\n");
1259     auto_import_pkey = GNUNET_YES;
1260
1261   }
1262
1263   put_interval = INITIAL_PUT_INTERVAL;
1264
1265   zone_publish_time_window = DEFAULT_ZONE_PUBLISH_TIME_WINDOW;
1266
1267   if (GNUNET_OK ==
1268       GNUNET_CONFIGURATION_get_value_time (c, "gns",
1269                                              "ZONE_PUBLISH_TIME_WINDOW",
1270                                              &zone_publish_time_window))
1271   {
1272     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1273                 "Time window for zone iteration: %s\n",
1274                 GNUNET_STRINGS_relative_time_to_string (zone_publish_time_window, GNUNET_YES));
1275   }
1276
1277   if (GNUNET_OK ==
1278       GNUNET_CONFIGURATION_get_value_number (c, "gns",
1279                                             "MAX_PARALLEL_BACKGROUND_QUERIES",
1280                                             &max_parallel_bg_queries))
1281   {
1282     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1283                "Number of allowed parallel background queries: %llu\n",
1284                max_parallel_bg_queries);
1285   }
1286
1287   if (GNUNET_YES ==
1288       GNUNET_CONFIGURATION_get_value_yesno (c, "gns",
1289                                             "AUTO_IMPORT_CONFIRMATION_REQ"))
1290   {
1291     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1292                "Auto import requires user confirmation\n");
1293     ignore_pending = GNUNET_YES;
1294   }
1295
1296   if (GNUNET_OK ==
1297       GNUNET_CONFIGURATION_get_value_number(c, "gns",
1298                                             "DEFAULT_LOOKUP_TIMEOUT",
1299                                             &default_lookup_timeout_secs))
1300   {
1301     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1302                "Default lookup timeout: %llus\n", default_lookup_timeout_secs);
1303     default_lookup_timeout = GNUNET_TIME_relative_multiply(
1304                                             GNUNET_TIME_UNIT_SECONDS,
1305                                             default_lookup_timeout_secs);
1306   }
1307   
1308   /**
1309    * handle to the dht
1310    */
1311   dht_handle = GNUNET_DHT_connect(c,
1312                        //max_parallel_bg_queries); //FIXME get ht_len from cfg
1313                        1024);
1314
1315   if (NULL == dht_handle)
1316   {
1317     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not connect to DHT!\n");
1318   }
1319   
1320   if (gns_resolver_init(namestore_handle, dht_handle, zone_hash, c,
1321                         max_parallel_bg_queries,
1322                         ignore_pending)
1323       == GNUNET_SYSERR)
1324   {
1325     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1326                "Unable to initialize resolver!\n");
1327     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
1328     return;
1329   }
1330
1331   if (GNUNET_YES ==
1332       GNUNET_CONFIGURATION_get_value_yesno (c, "gns", "HIJACK_DNS"))
1333   {
1334     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1335                "DNS hijacking enabled... connecting to service.\n");
1336
1337     if (gns_interceptor_init(zone_hash, zone_key, c) == GNUNET_SYSERR)
1338     {
1339       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1340                "Failed to enable the dns interceptor!\n");
1341     }
1342   }
1343   
1344   /**
1345    * Schedule periodic put
1346    * for our records
1347    * We have roughly an hour for all records;
1348    */
1349   first_zone_iteration = GNUNET_YES;
1350   zone_publish_task = GNUNET_SCHEDULER_add_now (&publish_zone_dht_start, NULL);
1351
1352   GNUNET_SERVER_add_handlers (server, handlers);
1353   
1354   //FIXME
1355   //GNUNET_SERVER_disconnect_notify (server,
1356   //                                 &client_disconnect_notification,
1357   //                                 NULL);
1358
1359   statistics = GNUNET_STATISTICS_create ("gns", c);
1360
1361   nc = GNUNET_SERVER_notification_context_create (server, 1);
1362
1363   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1364                                 NULL);
1365 }
1366
1367
1368 /**
1369  * The main function for the GNS service.
1370  *
1371  * @param argc number of arguments from the command line
1372  * @param argv command line arguments
1373  * @return 0 ok, 1 on error
1374  */
1375 int
1376 main (int argc, char *const *argv)
1377 {
1378   int ret;
1379
1380   ret =
1381       (GNUNET_OK ==
1382        GNUNET_SERVICE_run (argc, argv, "gns", GNUNET_SERVICE_OPTION_NONE, &run,
1383                            NULL)) ? 0 : 1;
1384   return ret;
1385 }
1386
1387 /* end of gnunet-service-gns.c */