-fix
[oweals/gnunet.git] / src / gns / gnunet-service-gns.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011 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  * TODO:
24  *    - Write xquery and block plugin
25  *    - The smaller FIXME issues all around
26  *
27  * @file gns/gnunet-service-gns.c
28  * @brief GNUnet GNS service
29  * @author Martin Schanzenbach
30  */
31 #include "platform.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_dns_service.h"
35 #include "gnunet_dnsparser_lib.h"
36 #include "gnunet_dht_service.h"
37 #include "gnunet_namestore_service.h"
38 #include "gnunet_gns_service.h"
39 #include "block_gns.h"
40 #include "gns.h"
41
42 #define DHT_OPERATION_TIMEOUT  GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
43 #define DHT_LOOKUP_TIMEOUT DHT_OPERATION_TIMEOUT
44 #define DHT_GNS_REPLICATION_LEVEL 5
45 #define MAX_DNS_LABEL_LENGTH 63
46
47 /* Ignore for now not used anyway and probably never will */
48 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP 23
49 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT 24
50
51 /**
52  * Handle to a currenty pending resolution
53  */
54 struct GNUNET_GNS_ResolverHandle
55 {
56   /* The name to resolve */
57   char *name;
58
59   /* the request handle to reply to */
60   struct GNUNET_DNS_RequestHandle *request_handle;
61   
62   /* the dns parser packet received */
63   struct GNUNET_DNSPARSER_Packet *packet;
64   
65   /* the query parsed from the packet */
66
67   struct GNUNET_DNSPARSER_Query *query;
68   
69   /* has this query been answered? how many matches */
70   int answered;
71
72   /* the authoritative zone to query */
73   GNUNET_HashCode authority;
74
75   /* the name of the authoritative zone to query */
76   char *authority_name;
77
78   /**
79    * we have an authority in namestore that
80    * may be able to resolve
81    */
82   int authority_found;
83
84   /* a handle for dht lookups. should be NULL if no lookups are in progress */
85   struct GNUNET_DHT_GetHandle *get_handle;
86
87   /* timeout task for dht lookups */
88   GNUNET_SCHEDULER_TaskIdentifier dht_timeout_task;
89
90 };
91
92
93 /**
94  * Our handle to the DNS handler library
95  */
96 struct GNUNET_DNS_Handle *dns_handle;
97
98 /**
99  * Our handle to the DHT
100  */
101 struct GNUNET_DHT_Handle *dht_handle;
102
103 /**
104  * Our zone's private key
105  */
106 struct GNUNET_CRYPTO_RsaPrivateKey *zone_key;
107
108 /**
109  * Our handle to the namestore service
110  * FIXME maybe need a second handle for iteration
111  */
112 struct GNUNET_NAMESTORE_Handle *namestore_handle;
113
114 /**
115  * Handle to iterate over our authoritative zone in namestore
116  */
117 struct GNUNET_NAMESTORE_ZoneIterator *namestore_iter;
118
119 /**
120  * The configuration the GNS service is running with
121  */
122 const struct GNUNET_CONFIGURATION_Handle *GNS_cfg;
123
124 /**
125  * Our notification context.
126  */
127 static struct GNUNET_SERVER_NotificationContext *nc;
128
129 /**
130  * Our zone hash
131  */
132 GNUNET_HashCode zone_hash;
133
134 /**
135  * Our tld. Maybe get from config file
136  */
137 const char* gnunet_tld = ".gnunet";
138
139 /**
140  * Useful for zone update for DHT put
141  */
142 static int num_public_records =  3600;
143 struct GNUNET_TIME_Relative dht_update_interval;
144 GNUNET_SCHEDULER_TaskIdentifier zone_update_taskid = GNUNET_SCHEDULER_NO_TASK;
145
146 static void resolve_name(struct GNUNET_GNS_ResolverHandle *rh);
147
148 /**
149  * Reply to client with the result from our lookup.
150  *
151  * @param rh the request handle of the lookup
152  * @param rd_count the number of records to return
153  * @param rd the record data
154  */
155 static void
156 reply_to_dns(struct GNUNET_GNS_ResolverHandle *rh, uint32_t rd_count,
157              const struct GNUNET_NAMESTORE_RecordData *rd)
158 {
159   int i;
160   size_t len;
161   int ret;
162   char *buf;
163   struct GNUNET_DNSPARSER_Packet *packet = rh->packet;
164   struct GNUNET_DNSPARSER_Record answer_records[rh->answered];
165   struct GNUNET_DNSPARSER_Record additional_records[rd_count-(rh->answered)];
166   packet->answers = answer_records;
167   packet->additional_records = additional_records;
168   
169   /**
170    * Put records in the DNS packet and modify it
171    * to a response
172    */
173   len = sizeof(struct GNUNET_DNSPARSER_Record*);
174   for (i=0; i < rd_count; i++)
175   {
176     
177     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
178                "Adding type %d to DNS response\n", rd[i].record_type);
179     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Name: %s\n", rh->name);
180     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "QName: %s\n", rh->query->name);
181     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Record %d/%d\n", i+1, rd_count);
182     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Record len %d\n", rd[i].data_size);
183     
184     if (rd[i].record_type == rh->query->type)
185     {
186       answer_records[i].name = rh->query->name;
187       answer_records[i].type = rd[i].record_type;
188       answer_records[i].data.raw.data_len = rd[i].data_size;
189       answer_records[i].data.raw.data = (char*)rd[i].data;
190       answer_records[i].expiration_time = rd[i].expiration;
191       answer_records[i].class = GNUNET_DNSPARSER_CLASS_INTERNET;//hmmn
192     }
193     else
194     {
195       additional_records[i].name = rh->query->name;
196       additional_records[i].type = rd[i].record_type;
197       additional_records[i].data.raw.data_len = rd[i].data_size;
198       additional_records[i].data.raw.data = (char*)rd[i].data;
199       additional_records[i].expiration_time = rd[i].expiration;
200       additional_records[i].class = GNUNET_DNSPARSER_CLASS_INTERNET;//hmmn
201     }
202   }
203   
204   packet->num_answers = rh->answered;
205   packet->num_additional_records = rd_count-(rh->answered);
206   
207   if (0 == GNUNET_CRYPTO_hash_cmp(&rh->authority, &zone_hash))
208     packet->flags.authoritative_answer = 1;
209   else
210     packet->flags.authoritative_answer = 0;
211
212   if (rd == NULL)
213     packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NAME_ERROR;
214   else
215     packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NO_ERROR;
216   
217   packet->flags.query_or_response = 1;
218
219   
220   /**
221    * Reply to DNS
222    */
223   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
224              "Building DNS response\n");
225   ret = GNUNET_DNSPARSER_pack (packet,
226                                1024, /* FIXME magic from dns redirector */
227                                &buf,
228                                &len);
229   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
230              "Built DNS response! (ret=%d,len=%d)\n", ret, len);
231   if (ret == GNUNET_OK)
232   {
233     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
234                "Answering DNS request\n");
235     GNUNET_DNS_request_answer(rh->request_handle,
236                               len,
237                               buf);
238     //GNUNET_free(answer);
239     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Answered DNS request\n");
240   }
241   else
242   {
243     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
244                "Error building DNS response! (ret=%d)", ret);
245   }
246
247   GNUNET_free(rh->name);
248   GNUNET_free(rh);
249 }
250
251
252 /**
253  * Task run during shutdown.
254  *
255  * @param cls unused
256  * @param tc unused
257  */
258 static void
259 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
260 {
261   /* Kill zone task for it may make the scheduler hang */
262   if (zone_update_taskid)
263     GNUNET_SCHEDULER_cancel(zone_update_taskid);
264
265   GNUNET_DNS_disconnect(dns_handle);
266   GNUNET_NAMESTORE_disconnect(namestore_handle, 1);
267   GNUNET_DHT_disconnect(dht_handle);
268 }
269
270 /**
271  * Callback when record data is put into namestore
272  *
273  * @param cls the closure
274  * @param success GNUNET_OK on success
275  * @param emsg the error message. NULL if SUCCESS==GNUNET_OK
276  */
277 void
278 on_namestore_record_put_result(void *cls,
279                                int32_t success,
280                                const char *emsg)
281 {
282   if (GNUNET_NO == success)
283   {
284     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "records already in namestore\n");
285     return;
286   }
287   else if (GNUNET_YES == success)
288   {
289     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
290                "records successfully put in namestore\n");
291     return;
292   }
293
294   GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
295              "Error putting records into namestore: %s\n", emsg);
296 }
297
298 /**
299  * Handle timeout for DHT requests
300  *
301  * @param cls the request handle as closure
302  * @param tc the task context
303  */
304 static void
305 dht_lookup_timeout(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
306 {
307   struct GNUNET_GNS_ResolverHandle *rh = cls;
308
309   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
310              "dht lookup for query %s (type=%d) timed out.\n",
311              rh->name, rh->query->type);
312
313   GNUNET_DHT_get_stop (rh->get_handle);
314   reply_to_dns(rh, 0, NULL);
315 }
316
317
318
319 /**
320  * Function called when we get a result from the dht
321  * for our query
322  *
323  * @param cls the request handle
324  * @param exp lifetime
325  * @param key the key the record was stored under
326  * @param get_path get path
327  * @param get_path_length get path length
328  * @param put_path put path
329  * @param put_path_length put path length
330  * @param type the block type
331  * @param size the size of the record
332  * @param data the record data
333  */
334 static void
335 process_record_dht_result(void* cls,
336                  struct GNUNET_TIME_Absolute exp,
337                  const GNUNET_HashCode * key,
338                  const struct GNUNET_PeerIdentity *get_path,
339                  unsigned int get_path_length,
340                  const struct GNUNET_PeerIdentity *put_path,
341                  unsigned int put_path_length,
342                  enum GNUNET_BLOCK_Type type,
343                  size_t size, const void *data)
344 {
345   struct GNUNET_GNS_ResolverHandle *rh;
346   struct GNSNameRecordBlock *nrb;
347   uint32_t num_records;
348   char* name = NULL;
349   char* rd_data = (char*)data;
350   int i;
351   int rd_size;
352   
353   GNUNET_HashCode zone, name_hash;
354   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "got dht result (size=%d)\n", size);
355   
356   if (data == NULL)
357     return;
358
359   //FIXME maybe check expiration here, check block type
360   
361   rh = (struct GNUNET_GNS_ResolverHandle *)cls;
362   nrb = (struct GNSNameRecordBlock*)data;
363   
364   /* stop lookup and timeout task */
365   GNUNET_DHT_get_stop (rh->get_handle);
366   GNUNET_SCHEDULER_cancel(rh->dht_timeout_task);
367   
368   rh->get_handle = NULL;
369   name = (char*)&nrb[1];
370   num_records = ntohl(nrb->rd_count);
371   {
372     struct GNUNET_NAMESTORE_RecordData rd[num_records];
373
374     rd_data += strlen(name) + 1 + sizeof(struct GNSNameRecordBlock);
375     rd_size = size - strlen(name) - 1 - sizeof(struct GNSNameRecordBlock);
376   
377     if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_size,
378                                                                rd_data,
379                                                                num_records,
380                                                                rd))
381     {
382       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Error deserializing data!\n");
383       return;
384     }
385
386     for (i=0; i<num_records; i++)
387     {
388       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
389                "Got name: %s (wanted %s)\n", name, rh->name);
390       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
391                "Got type: %d (wanted %d)\n",
392                rd[i].record_type, rh->query->type);
393       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
394                "Got data length: %d\n", rd[i].data_size);
395       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
396                "Got flag %d\n", rd[i].flags);
397     
398      if ((strcmp(name, rh->name) == 0) &&
399          (rd[i].record_type == rh->query->type))
400       {
401         rh->answered++;
402       }
403
404     }
405
406     GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
407     GNUNET_CRYPTO_hash_xor(key, &name_hash, &zone);
408   
409     /**
410      * FIXME check pubkey against existing key in namestore?
411      * https://gnunet.org/bugs/view.php?id=2179
412      */
413
414     /* Save to namestore */
415     GNUNET_NAMESTORE_record_put (namestore_handle,
416                                  &nrb->public_key,
417                                  name,
418                                  exp,
419                                  num_records,
420                                  rd,
421                                  &nrb->signature,
422                                  &on_namestore_record_put_result, //cont
423                                  NULL); //cls
424   
425     if (rh->answered)
426       reply_to_dns(rh, num_records, rd);
427     else
428       reply_to_dns(rh, 0, NULL);
429   }
430
431 }
432
433
434 /**
435  * Start DHT lookup for a (name -> query->record_type) record in
436  * query->authority's zone
437  *
438  * @param rh the pending gns query context
439  * @param name the name to query record
440  */
441 static void
442 resolve_record_dht(struct GNUNET_GNS_ResolverHandle *rh)
443 {
444   uint32_t xquery;
445   GNUNET_HashCode name_hash;
446   GNUNET_HashCode lookup_key;
447   struct GNUNET_CRYPTO_HashAsciiEncoded lookup_key_string;
448
449   GNUNET_CRYPTO_hash(rh->name, strlen(rh->name), &name_hash);
450   GNUNET_CRYPTO_hash_xor(&name_hash, &rh->authority, &lookup_key);
451   GNUNET_CRYPTO_hash_to_enc (&lookup_key, &lookup_key_string);
452   
453   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
454              "starting dht lookup for %s with key: %s\n",
455              rh->name, (char*)&lookup_key_string);
456
457   rh->dht_timeout_task = GNUNET_SCHEDULER_add_delayed(DHT_LOOKUP_TIMEOUT,
458                                                       &dht_lookup_timeout, rh);
459
460   xquery = htonl(rh->query->type);
461   rh->get_handle = GNUNET_DHT_get_start(dht_handle, 
462                        DHT_OPERATION_TIMEOUT,
463                        GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
464                        &lookup_key,
465                        DHT_GNS_REPLICATION_LEVEL,
466                        GNUNET_DHT_RO_NONE,
467                        &xquery, 
468                        sizeof(xquery),
469                        &process_record_dht_result,
470                        rh);
471
472 }
473
474
475 /**
476  * Namestore calls this function if we have record for this name.
477  * (or with rd_count=0 to indicate no matches)
478  *
479  * @param cls the pending query
480  * @param key the key of the zone we did the lookup
481  * @param expiration expiration date of the namestore entry
482  * @param name the name for which we need an authority
483  * @param rd_count the number of records with 'name'
484  * @param rd the record data
485  * @param signature the signature of the authority for the record data
486  */
487 static void
488 process_record_lookup(void* cls,
489                   const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
490                   struct GNUNET_TIME_Absolute expiration,
491                   const char *name, unsigned int rd_count,
492                   const struct GNUNET_NAMESTORE_RecordData *rd,
493                   const struct GNUNET_CRYPTO_RsaSignature *signature)
494 {
495   struct GNUNET_GNS_ResolverHandle *rh;
496   struct GNUNET_TIME_Relative remaining_time;
497   GNUNET_HashCode zone;
498
499   rh = (struct GNUNET_GNS_ResolverHandle *) cls;
500   GNUNET_CRYPTO_hash(key,
501                      sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
502                      &zone);
503   remaining_time = GNUNET_TIME_absolute_get_remaining (expiration);
504
505   if (rd_count == 0)
506   {
507     /**
508      * Lookup terminated and no results
509      * -> DHT Phase unless data is recent
510      */
511     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
512                "Namestore lookup for %s terminated without results\n", name);
513     
514     /**
515      * Not our root and no record found. Try dht if expired
516      */
517     if ((0 != GNUNET_CRYPTO_hash_cmp(&rh->authority, &zone_hash)) &&
518         (remaining_time.rel_value != 0))
519     {
520       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
521                  "Record %s unknown in namestore, trying dht\n",
522                  rh->name);
523       resolve_record_dht(rh);
524       return;
525     }
526     
527     /**
528      * Our zone and no result? Cannot resolve TT
529      */
530     GNUNET_assert(rh->answered == 0);
531     reply_to_dns(rh, 0, NULL);
532     return;
533
534   }
535   else
536   {
537     
538     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
539                "Processing additional result %s from namestore\n", name);
540     int i;
541     for (i=0; i<rd_count;i++)
542     {
543       
544       if (rd[i].record_type != rh->query->type)
545         continue;
546       
547       if ((GNUNET_TIME_absolute_get_remaining (rd[i].expiration)).rel_value
548           == 0)
549       {
550         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "This record is expired. Skipping\n");
551         continue;
552       }
553       
554       rh->answered++;
555       
556     }
557     
558     /**
559      * no answers found
560      */
561     if (rh->answered == 0)
562     {
563       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, 
564                  "No answers found. This is odd!\n");
565       reply_to_dns(rh, 0, NULL);
566       return;
567     }
568     
569     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Found %d answer(s) to query!\n",
570                rh->answered);
571
572     reply_to_dns(rh, rd_count, rd);
573   }
574 }
575
576
577 /**
578  * The final phase of resolution.
579  * This is a name that is canonical and we do not have a delegation.
580  *
581  * @param rh the pending lookup
582  */
583 static void
584 resolve_record(struct GNUNET_GNS_ResolverHandle *rh)
585 {
586   
587   /**
588    * Try to resolve this record in our namestore.
589    * The name to resolve is now in rh->authority_name
590    * since we tried to resolve it to an authority
591    * and failed.
592    **/
593   GNUNET_NAMESTORE_lookup_record(namestore_handle,
594                                  &rh->authority,
595                                  rh->name,
596                                  rh->query->type,
597                                  &process_record_lookup,
598                                  rh);
599
600 }
601
602
603 /**
604  * Handle timeout for DHT requests
605  *
606  * @param cls the request handle as closure
607  * @param tc the task context
608  */
609 static void
610 dht_authority_lookup_timeout(void *cls,
611                              const struct GNUNET_SCHEDULER_TaskContext *tc)
612 {
613   struct GNUNET_GNS_ResolverHandle *rh = cls;
614
615   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
616              "dht lookup for query %s (type=%d) timed out.\n",
617              rh->name, rh->query->type);
618   
619   if (strcmp(rh->name, "") == 0)
620   {
621     /*
622      * promote authority back to name and try to resolve record
623      */
624     strcpy(rh->name, rh->authority_name);
625     resolve_record(rh);
626   }
627   else
628   {
629     GNUNET_DHT_get_stop (rh->get_handle);
630     reply_to_dns(rh, 0, NULL);
631   }
632 }
633
634
635 /**
636  * Function called when we get a result from the dht
637  * for our query
638  *
639  * @param cls the request handle
640  * @param exp lifetime
641  * @param key the key the record was stored under
642  * @param get_path get path
643  * @param get_path_length get path length
644  * @param put_path put path
645  * @param put_path_length put path length
646  * @param type the block type
647  * @param size the size of the record
648  * @param data the record data
649  */
650 static void
651 process_authority_dht_result(void* cls,
652                  struct GNUNET_TIME_Absolute exp,
653                  const GNUNET_HashCode * key,
654                  const struct GNUNET_PeerIdentity *get_path,
655                  unsigned int get_path_length,
656                  const struct GNUNET_PeerIdentity *put_path,
657                  unsigned int put_path_length,
658                  enum GNUNET_BLOCK_Type type,
659                  size_t size, const void *data)
660 {
661   struct GNUNET_GNS_ResolverHandle *rh;
662   struct GNSNameRecordBlock *nrb;
663   uint32_t num_records;
664   char* name = NULL;
665   char* rd_data = (char*) data;
666   int i;
667   int rd_size;
668   GNUNET_HashCode zone, name_hash;
669   
670   if (data == NULL)
671     return;
672   
673   //FIXME check expiration?
674   
675   rh = (struct GNUNET_GNS_ResolverHandle *)cls;
676   nrb = (struct GNSNameRecordBlock*)data;
677   
678   /* stop dht lookup and timeout task */
679   GNUNET_DHT_get_stop (rh->get_handle);
680   GNUNET_SCHEDULER_cancel(rh->dht_timeout_task);
681
682   rh->get_handle = NULL;
683   num_records = ntohl(nrb->rd_count);
684   name = (char*)&nrb[1];
685   {
686     struct GNUNET_NAMESTORE_RecordData rd[num_records];
687     
688     rd_data += strlen(name) + 1 + sizeof(struct GNSNameRecordBlock);
689     rd_size = size - strlen(name) - 1 - sizeof(struct GNSNameRecordBlock);
690   
691     if (GNUNET_SYSERR == GNUNET_NAMESTORE_records_deserialize (rd_size,
692                                                                rd_data,
693                                                                num_records,
694                                                                rd))
695     {
696       GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Error deserializing data!\n");
697       return;
698     }
699
700     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
701                "Got name: %s (wanted %s)\n", name, rh->authority_name);
702     for (i=0; i<num_records; i++)
703     {
704     
705       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
706                 "Got name: %s (wanted %s)\n", name, rh->authority_name);
707       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
708                  "Got type: %d (wanted %d)\n",
709                  rd[i].record_type, GNUNET_GNS_RECORD_PKEY);
710       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
711                  "Got data length: %d\n", rd[i].data_size);
712       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
713                  "Got flag %d\n", rd[i].flags);
714
715       if ((strcmp(name, rh->authority_name) == 0) &&
716           (rd[i].record_type == GNUNET_GNS_RECORD_PKEY))
717       {
718         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Authority found in DHT\n");
719         rh->answered = 1;
720         GNUNET_CRYPTO_hash(
721                    (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *)rd[i].data,
722                    rd[i].data_size,
723                    &rh->authority);
724       }
725
726     }
727
728
729     GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
730     GNUNET_CRYPTO_hash_xor(key, &name_hash, &zone);
731
732     /* Save to namestore */
733     if (0 != GNUNET_CRYPTO_hash_cmp(&zone_hash, &zone))
734     {
735       GNUNET_NAMESTORE_record_put (namestore_handle,
736                                  &nrb->public_key,
737                                  name,
738                                  exp,
739                                  num_records,
740                                  rd,
741                                  &nrb->signature,
742                                  &on_namestore_record_put_result, //cont
743                                  NULL); //cls
744     }
745   }
746   
747   if (rh->answered)
748   {
749     rh->answered = 0;
750     /* delegate */
751     if (strcmp(rh->name, "") == 0)
752       resolve_record(rh);
753     else
754       resolve_name(rh);
755     return;
756   }
757
758   /* resolve */
759   if (strcmp(rh->name, "") == 0)
760   {
761     /* promote authority back to name */
762     strcpy(rh->name, rh->authority_name);
763     resolve_record(rh);
764     return;
765   }
766   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "No authority in records\n");
767   reply_to_dns(rh, 0, NULL);
768 }
769
770 /**
771  * Start DHT lookup for a name -> PKEY (compare NS) record in
772  * query->authority's zone
773  *
774  * @param rh the pending gns query
775  * @param name the name of the PKEY record
776  */
777 static void
778 resolve_authority_dht(struct GNUNET_GNS_ResolverHandle *rh)
779 {
780   uint32_t xquery;
781   GNUNET_HashCode name_hash;
782   GNUNET_HashCode lookup_key;
783
784   GNUNET_CRYPTO_hash(rh->authority_name,
785                      strlen(rh->authority_name),
786                      &name_hash);
787   GNUNET_CRYPTO_hash_xor(&name_hash, &rh->authority, &lookup_key);
788
789   rh->dht_timeout_task = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT,
790                                                   &dht_authority_lookup_timeout,
791                                                        rh);
792
793   xquery = htonl(GNUNET_GNS_RECORD_PKEY);
794   //FIXME how long to wait for results?
795   rh->get_handle = GNUNET_DHT_get_start(dht_handle,
796                        DHT_OPERATION_TIMEOUT,
797                        GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
798                        &lookup_key,
799                        DHT_GNS_REPLICATION_LEVEL,
800                        GNUNET_DHT_RO_NONE,
801                        NULL,
802                        0,//sizeof(xquery),
803                        &process_authority_dht_result,
804                        rh);
805
806 }
807
808 /**
809  * This is a callback function that should give us only PKEY
810  * records. Used to query the namestore for the authority (PKEY)
811  * for 'name'
812  *
813  * @param cls the pending query
814  * @param key the key of the zone we did the lookup
815  * @param expiration expiration date of the record data set in the namestore
816  * @param name the name for which we need an authority
817  * @param rd_count the number of records with 'name'
818  * @param rd the record data
819  * @param signature the signature of the authority for the record data
820  */
821 static void
822 process_authority_lookup(void* cls,
823                    const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
824                    struct GNUNET_TIME_Absolute expiration,
825                    const char *name,
826                    unsigned int rd_count,
827                    const struct GNUNET_NAMESTORE_RecordData *rd,
828                    const struct GNUNET_CRYPTO_RsaSignature *signature)
829 {
830   struct GNUNET_GNS_ResolverHandle *rh;
831   struct GNUNET_TIME_Relative remaining_time;
832   GNUNET_HashCode zone;
833   
834   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Got %d records from authority lookup\n",
835              rd_count);
836
837   rh = (struct GNUNET_GNS_ResolverHandle *)cls;
838   GNUNET_CRYPTO_hash(key,
839                      sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
840                      &zone);
841   remaining_time = GNUNET_TIME_absolute_get_remaining (expiration);
842   
843   /**
844    * No authority found in namestore.
845    */
846   if (rd_count == 0)
847   {
848     /**
849      * We did not find an authority in the namestore
850      * _IF_ the current authoritative zone is us we cannot resolve
851      * _ELSE_ we can still check the _expired_ dht
852      */
853     
854     /**
855      * No PKEY in our root. Try to resolve actual type in our zone
856      * if name is canonical. Else we cannot resolve.
857      */
858     if (0 == GNUNET_CRYPTO_hash_cmp(&rh->authority, &zone_hash))
859     {
860       if (strcmp(rh->name, "") == 0)
861       {
862         /**
863          * Promote this authority back to a name
864          */
865         strcpy(rh->name, rh->authority_name);
866         resolve_record(rh);
867         return;
868       }
869       else
870       {
871         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
872                    "Authority %s unknown in namestore, cannot resolve\n",
873                    rh->authority_name);
874       }
875       reply_to_dns(rh, 0, NULL);
876       return;
877     }
878     
879     /**
880      * Not our root and no PKEY found. Try dht if expired
881      * FIXME only do when expired?
882      */
883     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "AAAA %d\n", remaining_time.rel_value);
884     if ((0 != GNUNET_CRYPTO_hash_cmp(&rh->authority, &zone_hash)))
885     {
886       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
887                  "Authority %s unknown in namestore, trying dht\n",
888                  rh->authority_name);
889       resolve_authority_dht(rh);
890       return;
891     }
892     
893     /**
894      * Not our root and not expired or no records. Cannot resolve
895      */
896     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Authority %s unknown\n",
897                rh->authority_name);
898     reply_to_dns(rh, 0, NULL);
899     return;
900   }
901
902   //Note only 1 pkey should have been returned.. anything else would be strange
903   /**
904    * We found an authority that may be able to help us
905    * move on with query
906    */
907   int i;
908   for (i=0; i<rd_count;i++)
909   {
910   
911     if (rd[i].record_type != GNUNET_GNS_RECORD_PKEY)
912       continue;
913     
914     if ((GNUNET_TIME_absolute_get_remaining (rd[i].expiration)).rel_value
915          == 0)
916     {
917       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "This pkey is expired.\n");
918       if (remaining_time.rel_value == 0)
919       {
920         GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
921                    "This dht entry is expired. Refreshing\n");
922         resolve_authority_dht(rh);
923         return;
924       }
925
926       continue;
927     }
928
929     /**
930      * Resolve rest of query with new authority
931      */
932     GNUNET_assert(rd[i].record_type == GNUNET_GNS_RECORD_PKEY);
933     GNUNET_CRYPTO_hash(rd[i].data,
934                        sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
935                        &rh->authority);
936     if (strcmp(rh->name, "") == 0)
937       resolve_record(rh);
938     else
939       resolve_name(rh);
940     return;
941   }
942     
943   /**
944    * no answers found
945    */
946   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
947              "Authority lookup successful but no PKEY... never get here\n");
948   reply_to_dns(rh, 0, NULL);
949 }
950
951
952 /**
953  * Determine if this name is canonical.
954  * i.e.
955  * a.b.gnunet  = not canonical
956  * a           = canonical
957  *
958  * @param name the name to test
959  * @return 1 if canonical
960  */
961 static int
962 is_canonical(char* name)
963 {
964   uint32_t len = strlen(name);
965   int i;
966
967   for (i=0; i<len; i++)
968   {
969     if (*(name+i) == '.')
970       return 0;
971   }
972   return 1;
973 }
974
975 /**
976  * Move one level up in the domain hierarchy and return the
977  * passed top level domain.
978  *
979  * @param name the domain
980  * @param dest the destination where the tld will be put
981  */
982 void
983 pop_tld(char* name, char* dest)
984 {
985   uint32_t len;
986
987   if (is_canonical(name))
988   {
989     strcpy(dest, name);
990     strcpy(name, "");
991     return;
992   }
993
994   for (len = strlen(name); len > 0; len--)
995   {
996     if (*(name+len) == '.')
997       break;
998   }
999   
1000   //Was canonical?
1001   if (len == 0)
1002     return;
1003
1004   name[len] = '\0';
1005
1006   strcpy(dest, (name+len+1));
1007 }
1008
1009
1010 /**
1011  * The first phase of resolution.
1012  * First check if the name is canonical.
1013  * If it is then try to resolve directly.
1014  * If not then we first have to resolve the authoritative entities.
1015  *
1016  * @param rh the pending lookup
1017  */
1018 static void
1019 resolve_name(struct GNUNET_GNS_ResolverHandle *rh)
1020 {
1021   
1022   /**
1023    * Try to resolve this name to a delegation.
1024    **/
1025   pop_tld(rh->name, rh->authority_name);
1026   GNUNET_NAMESTORE_lookup_record(namestore_handle,
1027                                  &rh->authority,
1028                                  rh->authority_name,
1029                                  GNUNET_GNS_RECORD_PKEY,
1030                                  &process_authority_lookup,
1031                                  rh);
1032
1033 }
1034
1035 /**
1036  * Entry point for name resolution
1037  * Setup a new query and try to resolve
1038  *
1039  * @param request the request handle of the DNS request from a client
1040  * @param p the DNS query packet we received
1041  * @param q the DNS query we received parsed from p
1042  */
1043 static void
1044 start_resolution(struct GNUNET_DNS_RequestHandle *request,
1045                  struct GNUNET_DNSPARSER_Packet *p,
1046                  struct GNUNET_DNSPARSER_Query *q)
1047 {
1048   struct GNUNET_GNS_ResolverHandle *rh;
1049   
1050   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1051               "Starting resolution for %s (type=%d)!\n",
1052               q->name, q->type);
1053   
1054   rh = GNUNET_malloc(sizeof (struct GNUNET_GNS_ResolverHandle));
1055   rh->packet = p;
1056   rh->query = q;
1057   rh->authority = zone_hash;
1058   
1059   rh->name = GNUNET_malloc(strlen(q->name)
1060                               - strlen(gnunet_tld) + 1);
1061   memset(rh->name, 0,
1062          strlen(q->name)-strlen(gnunet_tld) + 1);
1063   memcpy(rh->name, q->name,
1064          strlen(q->name)-strlen(gnunet_tld));
1065
1066   rh->authority_name = GNUNET_malloc(sizeof(char)*MAX_DNS_LABEL_LENGTH);
1067
1068   rh->request_handle = request;
1069
1070   /* Start resolution in our zone */
1071   resolve_name(rh);
1072 }
1073
1074 /**
1075  * The DNS request handler
1076  * Called for every incoming DNS request.
1077  *
1078  * @param cls closure
1079  * @param rh request handle to user for reply
1080  * @param request_length number of bytes in request
1081  * @param request udp payload of the DNS request
1082  */
1083 static void
1084 handle_dns_request(void *cls,
1085                    struct GNUNET_DNS_RequestHandle *rh,
1086                    size_t request_length,
1087                    const char *request)
1088 {
1089   struct GNUNET_DNSPARSER_Packet *p;
1090   char *tldoffset;
1091
1092   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hijacked a DNS request...processing\n");
1093   p = GNUNET_DNSPARSER_parse (request, request_length);
1094   
1095   if (NULL == p)
1096   {
1097     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1098                 "Received malformed DNS packet, leaving it untouched\n");
1099     GNUNET_DNS_request_forward (rh);
1100     return;
1101   }
1102   
1103   /**
1104    * Check tld and decide if we or
1105    * legacy dns is responsible
1106    *
1107    * FIXME now in theory there could be more than 1 query in the request
1108    * but if this is case we get into trouble:
1109    * either we query the GNS or the DNS. We cannot do both!
1110    * So I suggest to either only allow a single query per request or
1111    * only allow GNS or DNS requests.
1112    * The way it is implemented here now is buggy and will lead to erratic
1113    * behaviour (if multiple queries are present).
1114    */
1115   if (p->num_queries == 0)
1116   {
1117     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1118                 "No Queries in DNS packet... forwarding\n");
1119     GNUNET_DNS_request_forward (rh);
1120   }
1121
1122   if (p->num_queries > 1)
1123   {
1124     /* Note: We could also look for .gnunet */
1125     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1126                 ">1 queriy in DNS packet... odd. We only process #1\n");
1127   }
1128
1129   
1130   /**
1131    * Check for .gnunet
1132    */
1133   tldoffset = p->queries[0].name + strlen(p->queries[0].name);
1134
1135   while ((*tldoffset) != '.')
1136     tldoffset--;
1137   
1138   if (0 == strcmp(tldoffset, gnunet_tld))
1139   {
1140     start_resolution(rh, p, p->queries);
1141   }
1142   else
1143   {
1144     /**
1145      * This request does not concern us. Forward to real DNS.
1146      */
1147     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1148                "Request for %s is forwarded to DNS\n", p->queries[0].name);
1149     GNUNET_DNS_request_forward (rh);
1150   }
1151
1152 }
1153
1154 /**
1155  * Method called periodicattluy that triggers
1156  * iteration over root zone
1157  *
1158  * @param cls closure
1159  * @param tc task context
1160  */
1161 static void
1162 update_zone_dht_next(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1163 {
1164   GNUNET_NAMESTORE_zone_iterator_next(namestore_iter);
1165 }
1166
1167 /**
1168  * Continuation for DHT put
1169  *
1170  * @param cls closure
1171  * @param tc task context
1172  */
1173 static void
1174 record_dht_put(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1175 {
1176   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "put request transmitted\n");
1177 }
1178
1179 /* prototype */
1180 static void
1181 update_zone_dht_start(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1182
1183 /**
1184  * Function used to put all records successively into the DHT.
1185  *
1186  * @param cls the closure (NULL)
1187  * @param key the public key of the authority (ours)
1188  * @param expiration lifetime of the namestore entry
1189  * @param name the name of the records
1190  * @param rd_count the number of records in data
1191  * @param rd the record data
1192  * @param signature the signature for the record data
1193  */
1194 static void
1195 put_gns_record(void *cls,
1196                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
1197                 struct GNUNET_TIME_Absolute expiration,
1198                 const char *name,
1199                 unsigned int rd_count,
1200                 const struct GNUNET_NAMESTORE_RecordData *rd,
1201                 const struct GNUNET_CRYPTO_RsaSignature *signature)
1202 {
1203   
1204   struct GNSNameRecordBlock *nrb;
1205   GNUNET_HashCode name_hash;
1206   GNUNET_HashCode xor_hash;
1207   struct GNUNET_CRYPTO_HashAsciiEncoded xor_hash_string;
1208   uint32_t rd_payload_length;
1209   char* nrb_data = NULL;
1210
1211   /* we're done */
1212   if (NULL == name)
1213   {
1214     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Zone iteration finished\n");
1215     GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
1216     zone_update_taskid = GNUNET_SCHEDULER_add_now (&update_zone_dht_start,
1217                                                    NULL);
1218     return;
1219   }
1220   
1221   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1222              "Putting records for %s into the DHT\n", name);
1223   
1224   rd_payload_length = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
1225   
1226   nrb = GNUNET_malloc(rd_payload_length + strlen(name) + 1 
1227                       + sizeof(struct GNSNameRecordBlock));
1228   
1229   if (signature != NULL)
1230     nrb->signature = *signature;
1231   
1232   nrb->public_key = *key;
1233
1234   nrb->rd_count = htonl(rd_count);
1235   
1236   memset(&nrb[1], 0, strlen(name) + 1);
1237   memcpy(&nrb[1], name, strlen(name));
1238
1239   nrb_data = (char*)&nrb[1];
1240   nrb_data += strlen(name) + 1;
1241
1242   rd_payload_length += sizeof(struct GNSNameRecordBlock) +
1243     strlen(name) + 1;
1244
1245   if (-1 == GNUNET_NAMESTORE_records_serialize (rd_count,
1246                                                 rd,
1247                                                 rd_payload_length,
1248                                                 nrb_data))
1249   {
1250     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Record serialization failed!\n");
1251     return;
1252     //FIXME what to do
1253   }
1254
1255
1256   /*
1257    * calculate DHT key: H(name) xor H(pubkey)
1258    */
1259   GNUNET_CRYPTO_hash(name, strlen(name), &name_hash);
1260   GNUNET_CRYPTO_hash_xor(&zone_hash, &name_hash, &xor_hash);
1261   GNUNET_CRYPTO_hash_to_enc (&xor_hash, &xor_hash_string);
1262   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
1263              "putting records for %s under key: %s with size %d\n",
1264              name, (char*)&xor_hash_string, rd_payload_length);
1265
1266   GNUNET_DHT_put (dht_handle, &xor_hash,
1267                   DHT_GNS_REPLICATION_LEVEL,
1268                   GNUNET_DHT_RO_NONE,
1269                   GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
1270                   rd_payload_length,
1271                   (char*)nrb,
1272                   expiration,
1273                   DHT_OPERATION_TIMEOUT,
1274                   &record_dht_put,
1275                   NULL); //cls for cont
1276   
1277   num_public_records++;
1278
1279   /**
1280    * Reschedule periodic put
1281    */
1282   zone_update_taskid = GNUNET_SCHEDULER_add_delayed (dht_update_interval,
1283                                 &update_zone_dht_next,
1284                                 NULL);
1285
1286 }
1287
1288 /**
1289  * Periodically iterate over our zone and store everything in dht
1290  *
1291  * @param cls NULL
1292  * @param tc task context
1293  */
1294 static void
1295 update_zone_dht_start(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1296 {
1297   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Starting DHT zone update!\n");
1298   if (0 == num_public_records)
1299   {
1300     dht_update_interval = GNUNET_TIME_relative_multiply(
1301                                                       GNUNET_TIME_UNIT_SECONDS,
1302                                                       1);
1303   }
1304   else
1305   {
1306     dht_update_interval = GNUNET_TIME_relative_multiply(
1307                                                       GNUNET_TIME_UNIT_SECONDS,
1308                                                      (3600/num_public_records));
1309   }
1310   num_public_records = 0; //start counting again
1311   namestore_iter = GNUNET_NAMESTORE_zone_iteration_start (namestore_handle,
1312                                                           &zone_hash,
1313                                                           GNUNET_NAMESTORE_RF_AUTHORITY,
1314                                                           GNUNET_NAMESTORE_RF_PRIVATE,
1315                                                           &put_gns_record,
1316                                                           NULL);
1317 }
1318
1319 /**
1320  * Process GNS requests.
1321  *
1322  * @param cls closure
1323  * @param server the initialized server
1324  * @param c configuration to use
1325  */
1326 static void
1327 run (void *cls, struct GNUNET_SERVER_Handle *server,
1328      const struct GNUNET_CONFIGURATION_Handle *c)
1329 {
1330   
1331   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Initializing GNS\n");
1332
1333   char* keyfile;
1334   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
1335
1336   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (c, "gns",
1337                                              "ZONEKEY", &keyfile))
1338   {
1339     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1340                 "No private key for root zone specified%s!\n", keyfile);
1341     GNUNET_SCHEDULER_shutdown(0);
1342     return;
1343   }
1344
1345   zone_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
1346   GNUNET_CRYPTO_rsa_key_get_public (zone_key, &pkey);
1347
1348   GNUNET_CRYPTO_hash(&pkey, sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1349                      &zone_hash);
1350   
1351   nc = GNUNET_SERVER_notification_context_create (server, 1);
1352
1353   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1354                                 NULL);
1355
1356   if (GNUNET_YES ==
1357       GNUNET_CONFIGURATION_get_value_yesno (c, "gns",
1358                                             "HIJACK_DNS"))
1359   {
1360     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
1361                "DNS hijacking enabled... connecting to service.\n");
1362     /**
1363      * Do gnunet dns init here
1364      */
1365     dns_handle = GNUNET_DNS_connect(c,
1366                                     GNUNET_DNS_FLAG_PRE_RESOLUTION,
1367                                     &handle_dns_request, /* rh */
1368                                     NULL); /* Closure */
1369     if (NULL == dns_handle)
1370     {
1371       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1372                "Failed to connect to the dnsservice!\n");
1373     }
1374   }
1375
1376   
1377
1378   /**
1379    * handle to our local namestore
1380    */
1381   namestore_handle = GNUNET_NAMESTORE_connect(c);
1382
1383   if (NULL == namestore_handle)
1384   {
1385     //FIXME do error handling;
1386     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
1387                "Failed to connect to the namestore!\n");
1388     GNUNET_SCHEDULER_shutdown(0);
1389     return;
1390   }
1391   
1392   /**
1393    * handle to the dht
1394    */
1395   dht_handle = GNUNET_DHT_connect(c, 1); //FIXME get ht_len from cfg
1396
1397   if (NULL == dht_handle)
1398   {
1399     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Could not connect to DHT!\n");
1400   }
1401
1402   //put_some_records(); //FIXME for testing
1403   
1404   /**
1405    * Schedule periodic put
1406    * for our records
1407    * We have roughly an hour for all records;
1408    */
1409   dht_update_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
1410                                                       1);
1411   //zone_update_taskid = GNUNET_SCHEDULER_add_now (&update_zone_dht_start, NULL);
1412
1413 }
1414
1415
1416 /**
1417  * The main function for the GNS service.
1418  *
1419  * @param argc number of arguments from the command line
1420  * @param argv command line arguments
1421  * @return 0 ok, 1 on error
1422  */
1423 int
1424 main (int argc, char *const *argv)
1425 {
1426   int ret;
1427
1428   ret =
1429       (GNUNET_OK ==
1430        GNUNET_SERVICE_run (argc, argv, "gns", GNUNET_SERVICE_OPTION_NONE, &run,
1431                            NULL)) ? 0 : 1;
1432   return ret;
1433 }
1434
1435 /* end of gnunet-service-gns.c */