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