-fixes
[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   /* name to shorten */
64   char* name;
65
66 };
67
68
69 /**
70  * Handle to a get auhtority operation from api
71  */
72 struct ClientGetAuthHandle
73 {
74   /* the requesting client that */
75   struct GNUNET_SERVER_Client *client;
76
77   /* request id */
78   uint64_t unique_id;
79
80   /* name to lookup authority */
81   char* name;
82
83 };
84
85
86 /**
87  * Handle to a lookup operation from api
88  */
89 struct ClientLookupHandle
90 {
91   /* the requesting client that */
92   struct GNUNET_SERVER_Client *client;
93
94   /* request id */
95   uint64_t unique_id;
96
97   /* request type */
98   enum GNUNET_GNS_RecordType type;
99
100   /* the name to look up */
101   char* name; //Needed?
102 };
103
104 /**
105  * Our handle to the DHT
106  */
107 static struct GNUNET_DHT_Handle *dht_handle;
108
109 /**
110  * Our zone's private key
111  */
112 struct GNUNET_CRYPTO_RsaPrivateKey *zone_key;
113
114 /**
115  * Our handle to the namestore service
116  * FIXME maybe need a second handle for iteration
117  */
118 struct GNUNET_NAMESTORE_Handle *namestore_handle;
119
120 /**
121  * Handle to iterate over our authoritative zone in namestore
122  */
123 struct GNUNET_NAMESTORE_ZoneIterator *namestore_iter;
124
125 /**
126  * The configuration the GNS service is running with
127  */
128 const struct GNUNET_CONFIGURATION_Handle *GNS_cfg;
129
130 /**
131  * Our notification context.
132  */
133 static struct GNUNET_SERVER_NotificationContext *nc;
134
135 /**
136  * Our zone hash
137  */
138 struct GNUNET_CRYPTO_ShortHashCode zone_hash;
139
140 /**
141  * Useful for zone update for DHT put
142  */
143 static int num_public_records =  3600;
144
145 /* dht update interval FIXME define? */
146 static struct GNUNET_TIME_Relative dht_update_interval;
147
148 /* zone update task */
149 GNUNET_SCHEDULER_TaskIdentifier zone_update_taskid = GNUNET_SCHEDULER_NO_TASK;
150
151 /* automatic pkey import for name shortening */
152 static int auto_import_pkey;
153
154 /* lookup timeout */
155 static struct GNUNET_TIME_Relative default_lookup_timeout;
156
157 /**
158  * Continue shutdown
159  */
160 static void
161 on_resolver_cleanup(void)
162 {
163   GNUNET_NAMESTORE_disconnect(namestore_handle, 1);
164   GNUNET_DHT_disconnect(dht_handle);
165 }
166
167 /**
168  * Task run during shutdown.
169  *
170  * @param cls unused
171  * @param tc unused
172  */
173 static void
174 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
175 {
176
177   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
178              "Shutting down!");
179   /* Kill zone task for it may make the scheduler hang */
180   if (zone_update_taskid)
181     GNUNET_SCHEDULER_cancel(zone_update_taskid);
182   
183   GNUNET_SERVER_notification_context_destroy (nc);
184   
185   gns_interceptor_stop();
186   gns_resolver_cleanup(&on_resolver_cleanup);
187
188 }
189
190
191 /**
192  * Method called periodicattluy that triggers
193  * iteration over root zone
194  *
195  * @param cls closure
196  * @param tc task context
197  */
198 static void
199 update_zone_dht_next(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
200 {
201   GNUNET_NAMESTORE_zone_iterator_next(namestore_iter);
202 }
203
204 /**
205  * Continuation for DHT put
206  *
207  * @param cls closure
208  * @param tc task context
209  */
210 static void
211 record_dht_put(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
212 {
213   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "put request transmitted\n");
214 }
215
216 /* prototype */
217 static void
218 update_zone_dht_start(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
219
220 /**
221  * Function used to put all records successively into the DHT.
222  *
223  * @param cls the closure (NULL)
224  * @param key the public key of the authority (ours)
225  * @param expiration lifetime of the namestore entry
226  * @param name the name of the records
227  * @param rd_count the number of records in data
228  * @param rd the record data
229  * @param signature the signature for the record data
230  */
231 static void
232 put_gns_record(void *cls,
233                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *key,
234                 struct GNUNET_TIME_Absolute expiration,
235                 const char *name,
236                 unsigned int rd_count,
237                 const struct GNUNET_NAMESTORE_RecordData *rd,
238                 const struct GNUNET_CRYPTO_RsaSignature *signature)
239 {
240   
241   struct GNSNameRecordBlock *nrb;
242   struct GNUNET_CRYPTO_ShortHashCode name_hash;
243   GNUNET_HashCode xor_hash;
244   GNUNET_HashCode name_hash_double;
245   GNUNET_HashCode zone_hash_double;
246   struct GNUNET_CRYPTO_HashAsciiEncoded xor_hash_string;
247   uint32_t rd_payload_length;
248   char* nrb_data = NULL;
249   size_t namelen;
250
251   /* we're done */
252   if (NULL == name)
253   {
254     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
255                "Zone iteration finished. Rescheduling put in %ds\n",
256                GNUNET_GNS_DHT_MAX_UPDATE_INTERVAL);
257     zone_update_taskid = GNUNET_SCHEDULER_add_delayed (
258                                         GNUNET_TIME_relative_multiply(
259                                             GNUNET_TIME_UNIT_SECONDS,
260                                             GNUNET_GNS_DHT_MAX_UPDATE_INTERVAL
261                                             ),
262                                             &update_zone_dht_start,
263                                             NULL);
264     return;
265   }
266   
267   namelen = strlen(name) + 1;
268   
269   if (signature == NULL)
270   {
271     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
272                "No signature for %s record data provided! Skipping...\n",
273                name);
274     zone_update_taskid = GNUNET_SCHEDULER_add_now (&update_zone_dht_next,
275                                                    NULL);
276     return;
277
278   }
279   
280   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
281              "Putting records for %s into the DHT\n", name);
282   
283   rd_payload_length = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
284   
285   nrb = GNUNET_malloc(rd_payload_length + namelen
286                       + sizeof(struct GNSNameRecordBlock));
287   
288   nrb->signature = *signature;
289   
290   nrb->public_key = *key;
291
292   nrb->rd_count = htonl(rd_count);
293   
294   memcpy(&nrb[1], name, namelen);
295
296   nrb_data = (char*)&nrb[1];
297   nrb_data += namelen;
298
299   rd_payload_length += sizeof(struct GNSNameRecordBlock) + namelen;
300
301   if (-1 == GNUNET_NAMESTORE_records_serialize (rd_count,
302                                                 rd,
303                                                 rd_payload_length,
304                                                 nrb_data))
305   {
306     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
307                "Record serialization failed! Skipping...\n");
308     GNUNET_free(nrb);
309     zone_update_taskid = GNUNET_SCHEDULER_add_now (&update_zone_dht_next,
310                                                    NULL);
311     return;
312   }
313
314
315   /*
316    * calculate DHT key: H(name) xor H(pubkey)
317    */
318   GNUNET_CRYPTO_short_hash(name, strlen(name), &name_hash);
319   GNUNET_CRYPTO_short_hash_double (&name_hash, &name_hash_double);
320   GNUNET_CRYPTO_short_hash_double (&zone_hash, &zone_hash_double);
321   GNUNET_CRYPTO_hash_xor(&zone_hash_double, &name_hash_double, &xor_hash);
322   GNUNET_CRYPTO_hash_to_enc (&xor_hash, &xor_hash_string);
323
324   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
325              "putting records for %s under key: %s with size %d\n",
326              name, (char*)&xor_hash_string, rd_payload_length);
327
328   GNUNET_DHT_put (dht_handle, &xor_hash,
329                   DHT_GNS_REPLICATION_LEVEL,
330                   GNUNET_DHT_RO_NONE,
331                   GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
332                   rd_payload_length,
333                   (char*)nrb,
334                   expiration,
335                   DHT_OPERATION_TIMEOUT,
336                   &record_dht_put,
337                   NULL); //cls for cont
338   
339   num_public_records++;
340
341   /**
342    * Reschedule periodic put
343    */
344   zone_update_taskid = GNUNET_SCHEDULER_add_delayed (dht_update_interval,
345                                 &update_zone_dht_next,
346                                 NULL);
347
348   GNUNET_free(nrb);
349
350 }
351
352 /**
353  * Periodically iterate over our zone and store everything in dht
354  *
355  * @param cls NULL
356  * @param tc task context
357  */
358 static void
359 update_zone_dht_start(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
360 {
361   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Scheduling DHT zone update!\n");
362   if (0 == num_public_records)
363   {
364     dht_update_interval = GNUNET_TIME_relative_multiply(
365                                             GNUNET_TIME_UNIT_SECONDS,
366                                             GNUNET_GNS_DHT_MAX_UPDATE_INTERVAL);
367     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
368                "No records in db. Adjusted DHT update interval to %ds\n",
369                GNUNET_GNS_DHT_MAX_UPDATE_INTERVAL);
370   }
371   else
372   {
373     
374     dht_update_interval = GNUNET_TIME_relative_multiply(
375                                                       GNUNET_TIME_UNIT_SECONDS,
376                                                      (3600/num_public_records));
377     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
378                "Adjusted DHT update interval to %ds!\n",
379                (3600/num_public_records));
380   }
381
382   /* start counting again */
383   num_public_records = 0;
384   namestore_iter = GNUNET_NAMESTORE_zone_iteration_start (namestore_handle,
385                                                  &zone_hash,
386                                                  GNUNET_NAMESTORE_RF_AUTHORITY,
387                                                  GNUNET_NAMESTORE_RF_PRIVATE,
388                                                  &put_gns_record,
389                                                  NULL);
390 }
391
392
393 /* END DHT ZONE PROPAGATION */
394
395 /**
396  * Send shorten response back to client
397  * 
398  * @param cls the closure containing a client shorten handle
399  * @param name the shortened name result or NULL if cannot be shortened
400  */
401 static void
402 send_shorten_response(void* cls, const char* name)
403 {
404   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message with %s\n",
405               "SHORTEN_RESULT", name);
406   struct GNUNET_GNS_ClientShortenResultMessage *rmsg;
407   struct ClientShortenHandle *csh = (struct ClientShortenHandle *)cls;
408   
409   if (name == NULL)
410   {
411     name = "";
412   }
413
414   rmsg = GNUNET_malloc(sizeof(struct GNUNET_GNS_ClientShortenResultMessage)
415                        + strlen(name) + 1);
416   
417   rmsg->id = csh->unique_id;
418   rmsg->header.type = htons(GNUNET_MESSAGE_TYPE_GNS_SHORTEN_RESULT);
419   rmsg->header.size = 
420     htons(sizeof(struct GNUNET_GNS_ClientShortenResultMessage) +
421           strlen(name) + 1);
422
423   strcpy((char*)&rmsg[1], name);
424
425   GNUNET_SERVER_notification_context_unicast (nc, csh->client,
426                               (const struct GNUNET_MessageHeader *) rmsg,
427                               GNUNET_NO);
428   GNUNET_SERVER_receive_done (csh->client, GNUNET_OK);
429   
430   GNUNET_free(rmsg);
431   GNUNET_free_non_null(csh->name);
432   GNUNET_free(csh);
433
434 }
435
436 /**
437  * Handle a shorten message from the api
438  *
439  * @param cls the closure
440  * @param client the client
441  * @param message the message
442  */
443 static void handle_shorten(void *cls,
444                            struct GNUNET_SERVER_Client * client,
445                            const struct GNUNET_MessageHeader * message)
446 {
447   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "SHORTEN");
448
449   size_t msg_size = 0;
450   struct ClientShortenHandle *csh;
451   char name[MAX_DNS_NAME_LENGTH];
452   char* nameptr = name;
453
454   if (ntohs (message->size) < sizeof (struct GNUNET_GNS_ClientShortenMessage))
455   {
456     GNUNET_break_op (0);
457     GNUNET_SERVER_receive_done (client, GNUNET_OK);
458     return;
459   }
460
461   GNUNET_SERVER_notification_context_add (nc, client);
462
463   struct GNUNET_GNS_ClientShortenMessage *sh_msg =
464     (struct GNUNET_GNS_ClientShortenMessage *) message;
465   
466   msg_size = ntohs(message->size);
467
468   if (msg_size > GNUNET_SERVER_MAX_MESSAGE_SIZE)
469   {
470     GNUNET_break_op (0);
471     GNUNET_SERVER_receive_done (client, GNUNET_OK);
472     return;
473   }
474
475   csh = GNUNET_malloc(sizeof(struct ClientShortenHandle));
476   csh->client = client;
477   csh->unique_id = sh_msg->id;
478   
479   GNUNET_STRINGS_utf8_tolower((char*)&sh_msg[1], &nameptr);
480
481   if (strlen (name) < strlen(GNUNET_GNS_TLD)) {
482     csh->name = NULL;
483     send_shorten_response(csh, name);
484     return;
485   }
486   
487   if (!is_gnunet_tld(name) && !is_zkey_tld(name))
488   {
489     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490                 "%s is not our domain. Returning\n", name);
491     csh->name = NULL;
492     send_shorten_response(csh, name);
493     return;
494   }
495   
496   /* Start shortening */
497   if (GNUNET_YES == auto_import_pkey)
498     gns_resolver_shorten_name(zone_hash, name, zone_key,
499                               &send_shorten_response, csh);
500   else
501     gns_resolver_shorten_name(zone_hash, name, NULL,
502                               &send_shorten_response, csh);
503 }
504
505
506 /**
507  * Send get authority response back to client
508  * 
509  * @param cls the closure containing a client get auth handle
510  * @param name the shortened name result or NULL if cannot be shortened
511  */
512 static void
513 send_get_auth_response(void *cls, const char* name)
514 {
515   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message with %s\n",
516               "GET_AUTH_RESULT", name);
517   struct GNUNET_GNS_ClientGetAuthResultMessage *rmsg;
518   struct ClientGetAuthHandle *cah = (struct ClientGetAuthHandle *)cls;
519   
520   if (name == NULL)
521   {
522     name = "";
523   }
524
525   rmsg = GNUNET_malloc(sizeof(struct GNUNET_GNS_ClientGetAuthResultMessage)
526                        + strlen(name) + 1);
527   
528   rmsg->id = cah->unique_id;
529   rmsg->header.type = htons(GNUNET_MESSAGE_TYPE_GNS_GET_AUTH_RESULT);
530   rmsg->header.size = 
531     htons(sizeof(struct GNUNET_GNS_ClientGetAuthResultMessage) +
532           strlen(name) + 1);
533
534   strcpy((char*)&rmsg[1], name);
535
536   GNUNET_SERVER_notification_context_unicast (nc, cah->client,
537                               (const struct GNUNET_MessageHeader *) rmsg,
538                               GNUNET_NO);
539   GNUNET_SERVER_receive_done (cah->client, GNUNET_OK);
540   
541   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up handles...\n");
542
543   GNUNET_free(rmsg);
544   GNUNET_free_non_null(cah->name);
545   GNUNET_free(cah);
546
547   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "done.\n");
548
549 }
550
551
552 /**
553  * Handle a get authority message from the api
554  *
555  * @param cls the closure
556  * @param client the client
557  * @param message the message
558  */
559 static void handle_get_authority(void *cls,
560                            struct GNUNET_SERVER_Client * client,
561                            const struct GNUNET_MessageHeader * message)
562 {
563   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "GET_AUTH");
564
565   size_t msg_size = 0;
566   struct ClientGetAuthHandle *cah;
567   char name[MAX_DNS_NAME_LENGTH];
568   char* nameptr = name;
569
570   if (ntohs (message->size) < sizeof (struct GNUNET_GNS_ClientGetAuthMessage))
571   {
572     GNUNET_break_op (0);
573     GNUNET_SERVER_receive_done (client, GNUNET_OK);
574     return;
575   }
576
577   GNUNET_SERVER_notification_context_add (nc, client);
578
579   struct GNUNET_GNS_ClientGetAuthMessage *sh_msg =
580     (struct GNUNET_GNS_ClientGetAuthMessage *) message;
581   
582   msg_size = ntohs(message->size);
583
584   if (msg_size > GNUNET_SERVER_MAX_MESSAGE_SIZE)
585   {
586     GNUNET_break_op (0);
587     GNUNET_SERVER_receive_done (client, GNUNET_OK);
588     return;
589   }
590   
591   GNUNET_STRINGS_utf8_tolower((char*)&sh_msg[1], &nameptr);
592
593
594   cah = GNUNET_malloc(sizeof(struct ClientGetAuthHandle));
595   cah->client = client;
596   cah->unique_id = sh_msg->id;
597
598   if (strlen(name) < strlen(GNUNET_GNS_TLD))
599   {
600     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601                 "%s is too short. Returning\n", name);
602     cah->name = NULL;
603     send_get_auth_response(cah, name);
604     return;
605   }
606
607   if (strcmp(name+strlen(name)-strlen(GNUNET_GNS_TLD),
608              GNUNET_GNS_TLD) != 0)
609   {
610     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
611                 "%s is not our domain. Returning\n", name);
612     cah->name = NULL;
613     send_get_auth_response(cah, name);
614     return;
615   }
616
617   if (strcmp(name, GNUNET_GNS_TLD) == 0)
618   {
619     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
620                 "%s is us. Returning\n", name);
621     cah->name = NULL;
622     send_get_auth_response(cah, name);
623     return;
624   }
625   
626   cah->name = GNUNET_malloc(strlen(name)
627                             - strlen(GNUNET_GNS_TLD) + 1);
628   memset(cah->name, 0,
629          strlen(name)-strlen(GNUNET_GNS_TLD) + 1);
630   memcpy(cah->name, name,
631          strlen(name)-strlen(GNUNET_GNS_TLD));
632
633   /* Start delegation resolution in our namestore */
634   gns_resolver_get_authority(zone_hash, name, &send_get_auth_response, cah);
635 }
636
637
638 /**
639  * Reply to client with the result from our lookup.
640  *
641  * @param cls the closure (our client lookup handle)
642  * @param rd_count the number of records
643  * @param rd the record data
644  */
645 static void
646 send_lookup_response(void* cls,
647                      uint32_t rd_count,
648                      const struct GNUNET_NAMESTORE_RecordData *rd)
649 {
650   struct ClientLookupHandle* clh = (struct ClientLookupHandle*)cls;
651   struct GNUNET_GNS_ClientLookupResultMessage *rmsg;
652   size_t len;
653   
654   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' message with %d results\n",
655               "LOOKUP_RESULT", rd_count);
656   
657   len = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
658   rmsg = GNUNET_malloc(len+sizeof(struct GNUNET_GNS_ClientLookupResultMessage));
659   
660   rmsg->id = clh->unique_id;
661   rmsg->rd_count = htonl(rd_count);
662   rmsg->header.type = htons(GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
663   rmsg->header.size = 
664     htons(len+sizeof(struct GNUNET_GNS_ClientLookupResultMessage));
665
666   GNUNET_NAMESTORE_records_serialize (rd_count, rd, len, (char*)&rmsg[1]);
667   
668   GNUNET_SERVER_notification_context_unicast (nc, clh->client,
669                                 (const struct GNUNET_MessageHeader *) rmsg,
670                                 GNUNET_NO);
671   GNUNET_SERVER_receive_done (clh->client, GNUNET_OK);
672   
673   GNUNET_free(rmsg);
674   GNUNET_free(clh->name);
675   GNUNET_free(clh);
676
677 }
678
679
680 /**
681  * Handle lookup requests from client
682  *
683  * @param cls the closure
684  * @param client the client
685  * @param message the message
686  */
687 static void
688 handle_lookup(void *cls,
689               struct GNUNET_SERVER_Client * client,
690               const struct GNUNET_MessageHeader * message)
691 {
692   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", "LOOKUP");
693
694   size_t msg_size = 0;
695   size_t namelen;
696   char name[MAX_DNS_NAME_LENGTH];
697   struct ClientLookupHandle *clh;
698   char* nameptr = name;
699
700   if (ntohs (message->size) < sizeof (struct GNUNET_GNS_ClientLookupMessage))
701   {
702     GNUNET_break_op (0);
703     GNUNET_SERVER_receive_done (client, GNUNET_OK);
704     return;
705   }
706
707   GNUNET_SERVER_notification_context_add (nc, client);
708
709   struct GNUNET_GNS_ClientLookupMessage *sh_msg =
710     (struct GNUNET_GNS_ClientLookupMessage *) message;
711   
712   msg_size = ntohs(message->size);
713
714   if (msg_size > GNUNET_SERVER_MAX_MESSAGE_SIZE)
715   {
716     GNUNET_break_op (0);
717     GNUNET_SERVER_receive_done (client, GNUNET_OK);
718     return;
719   }
720   
721   GNUNET_STRINGS_utf8_tolower((char*)&sh_msg[1], &nameptr);
722   namelen = strlen(name)+1;
723   clh = GNUNET_malloc(sizeof(struct ClientLookupHandle));
724   clh->client = client;
725   clh->name = GNUNET_malloc(namelen);
726   strcpy(clh->name, name);
727   clh->unique_id = sh_msg->id;
728   clh->type = ntohl(sh_msg->type);
729   
730   if (GNUNET_YES == auto_import_pkey)
731   {
732     gns_resolver_lookup_record(zone_hash, clh->type, name,
733                                zone_key,
734                                default_lookup_timeout,
735                                &send_lookup_response, clh);
736   }
737   else
738   {
739     gns_resolver_lookup_record(zone_hash, clh->type, name,
740                                NULL,
741                                default_lookup_timeout,
742                                &send_lookup_response, clh);
743   }
744 }
745
746
747
748 /**
749  * Process GNS requests.
750  *
751  * @param cls closure)
752  * @param server the initialized server
753  * @param c configuration to use
754  */
755 static void
756 run (void *cls, struct GNUNET_SERVER_Handle *server,
757      const struct GNUNET_CONFIGURATION_Handle *c)
758 {
759   
760   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Initializing GNS\n");
761   
762   char* keyfile;
763   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
764   unsigned long long max_parallel_bg_queries = 0;
765   unsigned long long default_lookup_timeout_secs = 0;
766
767   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
768     {&handle_shorten, NULL, GNUNET_MESSAGE_TYPE_GNS_SHORTEN, 0},
769     {&handle_lookup, NULL, GNUNET_MESSAGE_TYPE_GNS_LOOKUP, 0},
770     {&handle_get_authority, NULL, GNUNET_MESSAGE_TYPE_GNS_GET_AUTH, 0}
771   };
772
773   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c, "gns",
774                                              "ZONEKEY", &keyfile))
775   {
776     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
777                 "No private key for root zone specified!\n");
778     GNUNET_SCHEDULER_shutdown(0);
779     return;
780   }
781
782   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
783              "Using keyfile %s for root zone.\n", keyfile);
784
785   zone_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
786   GNUNET_CRYPTO_rsa_key_get_public (zone_key, &pkey);
787
788   GNUNET_CRYPTO_short_hash(&pkey,
789                      sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
790                      &zone_hash);
791   GNUNET_free(keyfile);
792   
793   /**
794    * handle to our local namestore
795    */
796   namestore_handle = GNUNET_NAMESTORE_connect(c);
797
798   if (NULL == namestore_handle)
799   {
800     //FIXME do error handling;
801     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
802                "Failed to connect to the namestore!\n");
803     GNUNET_SCHEDULER_shutdown(0);
804     return;
805   }
806   
807   /**
808    * handle to the dht
809    */
810   dht_handle = GNUNET_DHT_connect(c, 1); //FIXME get ht_len from cfg
811
812   if (NULL == dht_handle)
813   {
814     GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Could not connect to DHT!\n");
815   }
816
817   if (GNUNET_YES ==
818       GNUNET_CONFIGURATION_get_value_yesno (c, "gns",
819                                             "AUTO_IMPORT_PKEY"))
820   {
821     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
822                "Automatic PKEY import is enabled.\n");
823     auto_import_pkey = GNUNET_YES;
824
825   }
826
827   if (GNUNET_OK ==
828       GNUNET_CONFIGURATION_get_value_number(c, "gns",
829                                             "MAX_PARALLEL_BACKGROUND_QUERIES",
830                                             &max_parallel_bg_queries))
831   {
832     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
833                "Number of allowed parallel background queries: %d\n",
834                max_parallel_bg_queries);
835   }
836
837   if (GNUNET_OK ==
838       GNUNET_CONFIGURATION_get_value_number(c, "gns",
839                                             "DEFAULT_LOOKUP_TIMEOUT",
840                                             &default_lookup_timeout_secs))
841   {
842     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
843                "Default lookup timeout: %ds\n", default_lookup_timeout_secs);
844     default_lookup_timeout = GNUNET_TIME_relative_multiply(
845                                             GNUNET_TIME_UNIT_SECONDS,
846                                             default_lookup_timeout_secs);
847   }
848   
849   if (gns_resolver_init(namestore_handle, dht_handle, zone_hash,
850                         max_parallel_bg_queries)
851       == GNUNET_SYSERR)
852   {
853     GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
854                "Unable to initialize resolver!\n");
855     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
856     return;
857   }
858
859   if (GNUNET_YES ==
860       GNUNET_CONFIGURATION_get_value_yesno (c, "gns", "HIJACK_DNS"))
861   {
862     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
863                "DNS hijacking enabled... connecting to service.\n");
864
865     if (gns_interceptor_init(zone_hash, zone_key, c) == GNUNET_SYSERR)
866     {
867       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
868                "Failed to enable the dns interceptor!\n");
869     }
870   }
871   
872   //put_some_records(); //FIXME for testing
873   
874   /**
875    * Schedule periodic put
876    * for our records
877    * We have roughly an hour for all records;
878    */
879   dht_update_interval = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS,
880                                                       1);
881   zone_update_taskid = GNUNET_SCHEDULER_add_now (&update_zone_dht_start, NULL);
882
883   GNUNET_SERVER_add_handlers (server, handlers);
884   
885   //FIXME
886   //GNUNET_SERVER_disconnect_notify (server,
887   //                                 &client_disconnect_notification,
888   //                                 NULL);
889
890   nc = GNUNET_SERVER_notification_context_create (server, 1);
891
892   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
893                                 NULL);
894 }
895
896
897 /**
898  * The main function for the GNS service.
899  *
900  * @param argc number of arguments from the command line
901  * @param argv command line arguments
902  * @return 0 ok, 1 on error
903  */
904 int
905 main (int argc, char *const *argv)
906 {
907   int ret;
908
909   ret =
910       (GNUNET_OK ==
911        GNUNET_SERVICE_run (argc, argv, "gns", GNUNET_SERVICE_OPTION_NONE, &run,
912                            NULL)) ? 0 : 1;
913   return ret;
914 }
915
916 /* end of gnunet-service-gns.c */