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