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