service does simple put and get into datacache, test case verifies it works
[oweals/gnunet.git] / src / dht / gnunet-service-dht.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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 2, 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  * @file dht/gnunet-service-dht.c
23  * @brief main DHT service shell, building block for DHT implementations
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  */
27
28 #include "platform.h"
29 #include "gnunet_client_lib.h"
30 #include "gnunet_getopt_lib.h"
31 #include "gnunet_os_lib.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_service_lib.h"
34 #include "gnunet_core_service.h"
35 #include "gnunet_signal_lib.h"
36 #include "gnunet_util_lib.h"
37 #include "gnunet_datacache_lib.h"
38 #include "dht.h"
39
40 /**
41  * Handle to the datacache service (for inserting/retrieving data)
42  */
43 struct GNUNET_DATACACHE_Handle *datacache;
44
45 /**
46  * The main scheduler to use for the DHT service
47  */
48 static struct GNUNET_SCHEDULER_Handle *sched;
49
50 /**
51  * The configuration the DHT service is running with
52  */
53 static const struct GNUNET_CONFIGURATION_Handle *cfg;
54
55 /**
56  * Timeout for transmissions to clients
57  */
58 static struct GNUNET_TIME_Relative client_transmit_timeout;
59
60 /**
61  * Handle to the core service
62  */
63 static struct GNUNET_CORE_Handle *coreAPI;
64
65 /**
66  * The identity of our peer.
67  */
68 static struct GNUNET_PeerIdentity my_identity;
69
70 /**
71  * Task to run when we shut down, cleaning up all our trash
72  */
73 static GNUNET_SCHEDULER_TaskIdentifier cleanup_task;
74
75 /**
76  * Context for handling results from a get request.
77  */
78 struct DatacacheGetContext
79 {
80   /**
81    * The client to send the result to.
82    */
83   struct GNUNET_SERVER_Client *client;
84
85   /**
86    * The unique id of this request
87    */
88   unsigned long long unique_id;
89 };
90
91
92 struct DHT_MessageContext
93 {
94   /**
95    * The client this request was received from.
96    */
97   struct GNUNET_SERVER_Client *client;
98
99   /**
100    * The key this request was about
101    */
102   GNUNET_HashCode *key;
103
104   /**
105    * The unique identifier of this request
106    */
107   unsigned long long unique_id;
108
109   /**
110    * Desired replication level
111    */
112   size_t replication;
113
114   /**
115    * Any message options for this request
116    */
117   size_t msg_options;
118 };
119
120 /**
121  * Server handler for handling locally received dht requests
122  */
123 static void
124 handle_dht_start_message (void *cls, struct GNUNET_SERVER_Client *client,
125                           const struct GNUNET_MessageHeader *message);
126
127 static void
128 handle_dht_stop_message (void *cls, struct GNUNET_SERVER_Client *client,
129                          const struct GNUNET_MessageHeader *message);
130
131 static struct GNUNET_SERVER_MessageHandler plugin_handlers[] = {
132   {&handle_dht_start_message, NULL, GNUNET_MESSAGE_TYPE_DHT, 0},
133   {&handle_dht_stop_message, NULL, GNUNET_MESSAGE_TYPE_DHT_STOP, 0},
134   {NULL, NULL, 0, 0}
135 };
136
137
138 /**
139  * Core handler for p2p dht get requests.
140  */
141 static int handle_dht_p2p_get (void *cls,
142                                const struct GNUNET_PeerIdentity *peer,
143                                const struct GNUNET_MessageHeader *message,
144                                struct GNUNET_TIME_Relative latency,
145                                uint32_t distance);
146
147 /**
148  * Core handler for p2p dht put requests.
149  */
150 static int handle_dht_p2p_put (void *cls,
151                                const struct GNUNET_PeerIdentity *peer,
152                                const struct GNUNET_MessageHeader *message,
153                                struct GNUNET_TIME_Relative latency,
154                                uint32_t distance);
155
156 /**
157  * Core handler for p2p dht find peer requests.
158  */
159 static int handle_dht_p2p_find_peer (void *cls,
160                                      const struct GNUNET_PeerIdentity *peer,
161                                      const struct GNUNET_MessageHeader
162                                      *message,
163                                      struct GNUNET_TIME_Relative latency,
164                                      uint32_t distance);
165
166 static struct GNUNET_CORE_MessageHandler core_handlers[] = {
167   {&handle_dht_p2p_get, GNUNET_MESSAGE_TYPE_DHT_GET, 0},
168   {&handle_dht_p2p_put, GNUNET_MESSAGE_TYPE_DHT_PUT, 0},
169   {&handle_dht_p2p_find_peer, GNUNET_MESSAGE_TYPE_DHT_FIND_PEER, 0},
170   {NULL, 0, 0}
171 };
172
173
174 static size_t
175 send_reply (void *cls, size_t size, void *buf)
176 {
177   struct GNUNET_DHT_Message *reply = cls;
178
179   if (buf == NULL)              /* Message timed out, that's crappy... */
180     {
181       GNUNET_free (reply);
182       return 0;
183     }
184
185   if (size >= ntohs (reply->header.size))
186     {
187       memcpy (buf, reply, ntohs (reply->header.size));
188       return ntohs (reply->header.size);
189     }
190   else
191     return 0;
192 }
193
194
195 static void
196 send_reply_to_client (struct GNUNET_SERVER_Client *client,
197                       struct GNUNET_MessageHeader *message,
198                       unsigned long long uid)
199 {
200   struct GNUNET_DHT_Message *reply;
201   size_t msize;
202   size_t tsize;
203 #if DEBUG_DHT
204   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205               "`%s': Sending reply to client.\n", "DHT");
206 #endif
207   msize = ntohs (message->size);
208   tsize = sizeof (struct GNUNET_DHT_Message) + msize;
209   reply = GNUNET_malloc (tsize);
210   reply->header.type = htons (GNUNET_MESSAGE_TYPE_DHT);
211   reply->header.size = htons (tsize);
212   if (uid != 0)
213     reply->unique = htons (GNUNET_YES);
214   reply->unique_id = GNUNET_htonll (uid);
215   memcpy (&reply[1], message, msize);
216
217   GNUNET_SERVER_notify_transmit_ready (client,
218                                        tsize,
219                                        GNUNET_TIME_relative_multiply
220                                        (GNUNET_TIME_UNIT_SECONDS, 5),
221                                        &send_reply, reply);
222
223 }
224
225
226 /**
227  * Iterator for local get request results, return
228  * GNUNET_OK to continue iteration, anything else
229  * to stop iteration.
230  */
231 static int
232 datacache_get_iterator (void *cls,
233                         struct GNUNET_TIME_Absolute exp,
234                         const GNUNET_HashCode * key,
235                         uint32_t size, const char *data, uint32_t type)
236 {
237   struct DatacacheGetContext *datacache_get_ctx = cls;
238   struct GNUNET_DHT_GetResultMessage *get_result;
239
240   get_result =
241     GNUNET_malloc (sizeof (struct GNUNET_DHT_GetResultMessage) + size);
242   get_result->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_GET_RESULT);
243   get_result->header.size =
244     htons (sizeof (struct GNUNET_DHT_GetResultMessage) + size);
245   get_result->data_size = htons (size);
246   get_result->expiration = exp;
247   memcpy (&get_result->key, key, sizeof (GNUNET_HashCode));
248   get_result->type = htons (type);
249   memcpy (&get_result[1], data, size);
250
251   send_reply_to_client (datacache_get_ctx->client, &get_result->header,
252                         datacache_get_ctx->unique_id);
253
254   GNUNET_free (get_result);
255   return GNUNET_OK;
256 }
257
258 /**
259  * Server handler for initiating local dht get requests
260  */
261 static void
262 handle_dht_get (void *cls, struct GNUNET_DHT_GetMessage *get_msg,
263                 struct DHT_MessageContext *message_context)
264 {
265 #if DEBUG_DHT
266   GNUNET_HashCode get_key;
267 #endif
268   size_t get_type;
269   unsigned int results;
270   struct DatacacheGetContext *datacache_get_context;
271
272   GNUNET_assert (ntohs (get_msg->header.size) >=
273                  sizeof (struct GNUNET_DHT_GetMessage));
274   get_type = ntohs (get_msg->type);
275
276 #if DEBUG_DHT
277   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
278               "`%s': Received `%s' request from client, message type %d, key %s, uid %llu\n",
279               "DHT", "GET", get_type, GNUNET_h2s (&get_key),
280               message_context->unique_id);
281 #endif
282
283   datacache_get_context = GNUNET_malloc (sizeof (struct DatacacheGetContext));
284   datacache_get_context->client = message_context->client;
285   datacache_get_context->unique_id = message_context->unique_id;
286
287   results = 0;
288   if (datacache != NULL)
289     results =
290       GNUNET_DATACACHE_get (datacache, message_context->key, get_type,
291                             datacache_get_iterator, datacache_get_context);
292
293 #if DEBUG_DHT
294   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
295               "`%s': Found %d results for local `%s' request\n", "DHT",
296               results, "GET");
297 #endif
298   GNUNET_free (datacache_get_context);
299   /* FIXME: Implement get functionality here */
300 }
301
302
303 /**
304  * Server handler for initiating local dht find peer requests
305  */
306 static void
307 handle_dht_find_peer (void *cls, struct GNUNET_DHT_FindPeerMessage *find_msg,
308                       struct DHT_MessageContext *message_context)
309 {
310 #if DEBUG_DHT
311   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
312               "`%s': Received `%s' request from client, key %s (msg size %d, we expected %d)\n",
313               "DHT", "FIND PEER", GNUNET_h2s (message_context->key),
314               ntohs (find_msg->header.size),
315               sizeof (struct GNUNET_DHT_FindPeerMessage));
316 #endif
317
318   GNUNET_assert (ntohs (find_msg->header.size) >=
319                  sizeof (struct GNUNET_DHT_FindPeerMessage));
320
321   /* FIXME: Implement find peer functionality here */
322 }
323
324
325 /**
326  * Server handler for initiating local dht put requests
327  */
328 static void
329 handle_dht_put (void *cls, struct GNUNET_DHT_PutMessage *put_msg,
330                 struct DHT_MessageContext *message_context)
331 {
332   size_t put_type;
333   size_t data_size;
334
335   GNUNET_assert (ntohs (put_msg->header.size) >=
336                  sizeof (struct GNUNET_DHT_PutMessage));
337
338   put_type = ntohs (put_msg->type);
339   data_size = ntohs (put_msg->data_size);
340 #if DEBUG_DHT
341   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342               "`%s': %s msg total size is %d, data size %d, struct size %d\n",
343               "DHT", "PUT", ntohs (put_msg->header.size), data_size,
344               sizeof (struct GNUNET_DHT_PutMessage));
345 #endif
346   GNUNET_assert (ntohs (put_msg->header.size) ==
347                  sizeof (struct GNUNET_DHT_PutMessage) + data_size);
348
349 #if DEBUG_DHT
350   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
351               "`%s': Received `%s' request from client, message type %d, key %s\n",
352               "DHT", "PUT", put_type, GNUNET_h2s (message_context->key));
353 #endif
354
355   /**
356    * Simplest DHT functionality, store any message we receive a put request for.
357    */
358   if (datacache != NULL)
359     GNUNET_DATACACHE_put (datacache, message_context->key, data_size,
360                           (char *) &put_msg[1], put_type,
361                           put_msg->expiration);
362   /**
363    * FIXME: Implement dht put request functionality here!
364    */
365
366 }
367
368 /**
369  * Context for sending receipt confirmations. Not used yet.
370  */
371 struct SendConfirmationContext
372 {
373   /**
374    * The message to send.
375    */
376   struct GNUNET_DHT_StopMessage *message;
377
378   /**
379    * Transmit handle.
380    */
381   struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
382 };
383
384 static size_t
385 send_confirmation (void *cls, size_t size, void *buf)
386 {
387   struct GNUNET_DHT_StopMessage *confirmation_message = cls;
388
389   if (buf == NULL)              /* Message timed out, that's crappy... */
390     {
391       GNUNET_free (confirmation_message);
392       return 0;
393     }
394
395   if (size >= ntohs (confirmation_message->header.size))
396     {
397       memcpy (buf, confirmation_message,
398               ntohs (confirmation_message->header.size));
399       return ntohs (confirmation_message->header.size);
400     }
401   else
402     return 0;
403 }
404
405
406 static void
407 send_client_receipt_confirmation (struct GNUNET_SERVER_Client *client,
408                                   uint64_t uid)
409 {
410   struct GNUNET_DHT_StopMessage *confirm_message;
411
412 #if DEBUG_DHT
413   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
414               "`%s': Sending receipt confirmation for uid %llu\n", "DHT",
415               uid);
416 #endif
417   confirm_message = GNUNET_malloc (sizeof (struct GNUNET_DHT_StopMessage));
418   confirm_message->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_STOP);
419   confirm_message->header.size =
420     htons (sizeof (struct GNUNET_DHT_StopMessage));
421   confirm_message->unique_id = GNUNET_htonll (uid);
422
423   GNUNET_SERVER_notify_transmit_ready (client,
424                                        sizeof (struct GNUNET_DHT_StopMessage),
425                                        GNUNET_TIME_relative_multiply
426                                        (GNUNET_TIME_UNIT_SECONDS, 5),
427                                        &send_confirmation, confirm_message);
428
429 }
430
431 static void
432 handle_dht_start_message (void *cls, struct GNUNET_SERVER_Client *client,
433                           const struct GNUNET_MessageHeader *message)
434 {
435   struct GNUNET_DHT_Message *dht_msg = (struct GNUNET_DHT_Message *) message;
436   struct GNUNET_MessageHeader *enc_msg;
437   struct DHT_MessageContext *message_context;
438   size_t enc_type;
439
440   enc_msg = (struct GNUNET_MessageHeader *) &dht_msg[1];
441   enc_type = ntohs (enc_msg->type);
442
443
444 #if DEBUG_DHT
445   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446               "`%s': Received `%s' request from client, message type %d, key %s, uid %llu\n",
447               "DHT", "GENERIC", enc_type, GNUNET_h2s (&dht_msg->key),
448               GNUNET_ntohll (dht_msg->unique_id));
449 #endif
450
451   message_context = GNUNET_malloc (sizeof (struct DHT_MessageContext));
452   message_context->client = client;
453   message_context->key = &dht_msg->key;
454   message_context->unique_id = GNUNET_ntohll (dht_msg->unique_id);
455   message_context->replication = ntohs (dht_msg->desired_replication_level);
456   message_context->msg_options = ntohs (dht_msg->options);
457
458   switch (enc_type)
459     {
460     case GNUNET_MESSAGE_TYPE_DHT_GET:
461       handle_dht_get (cls, (struct GNUNET_DHT_GetMessage *) enc_msg,
462                       message_context);
463       break;
464     case GNUNET_MESSAGE_TYPE_DHT_PUT:
465       handle_dht_put (cls, (struct GNUNET_DHT_PutMessage *) enc_msg,
466                       message_context);
467       send_client_receipt_confirmation (client,
468                                         GNUNET_ntohll (dht_msg->unique_id));
469       break;
470     case GNUNET_MESSAGE_TYPE_DHT_FIND_PEER:
471       handle_dht_find_peer (cls,
472                             (struct GNUNET_DHT_FindPeerMessage *) enc_msg,
473                             message_context);
474       break;
475     default:
476       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
477                   "`%s': Message type (%d) not handled\n", "DHT", enc_type);
478     }
479
480   GNUNET_free (message_context);
481   GNUNET_SERVER_receive_done (client, GNUNET_OK);
482
483 }
484
485
486 static void
487 handle_dht_stop_message (void *cls, struct GNUNET_SERVER_Client *client,
488                          const struct GNUNET_MessageHeader *message)
489 {
490   struct GNUNET_DHT_StopMessage *dht_stop_msg =
491     (struct GNUNET_DHT_StopMessage *) message;
492
493 #if DEBUG_DHT
494   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495               "`%s': Received `%s' request from client, uid %llu\n", "DHT",
496               "GENERIC STOP", GNUNET_ntohll (dht_stop_msg->unique_id));
497 #endif
498
499   /* TODO: Put in demultiplexing here */
500
501   send_client_receipt_confirmation (client,
502                                     GNUNET_ntohll (dht_stop_msg->unique_id));
503   GNUNET_SERVER_receive_done (client, GNUNET_OK);
504 }
505
506
507 /**
508  * Core handler for p2p dht get requests.
509  */
510 static int
511 handle_dht_p2p_get (void *cls,
512                     const struct GNUNET_PeerIdentity *peer,
513                     const struct GNUNET_MessageHeader *message,
514                     struct GNUNET_TIME_Relative latency, uint32_t distance)
515 {
516 #if DEBUG_DHT
517   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518               "`%s': Received `%s' request from another peer\n", "DHT",
519               "GET");
520 #endif
521
522   return GNUNET_YES;
523 }
524
525 /**
526  * Core handler for p2p dht put requests.
527  */
528 static int
529 handle_dht_p2p_put (void *cls,
530                     const struct GNUNET_PeerIdentity *peer,
531                     const struct GNUNET_MessageHeader *message,
532                     struct GNUNET_TIME_Relative latency, uint32_t distance)
533 {
534 #if DEBUG_DHT
535   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
536               "`%s': Received `%s' request from another peer\n", "DHT",
537               "PUT");
538 #endif
539
540   return GNUNET_YES;
541 }
542
543 /**
544  * Core handler for p2p dht find peer requests.
545  */
546 static int
547 handle_dht_p2p_find_peer (void *cls,
548                           const struct GNUNET_PeerIdentity *peer,
549                           const struct GNUNET_MessageHeader *message,
550                           struct GNUNET_TIME_Relative latency,
551                           uint32_t distance)
552 {
553 #if DEBUG_DHT
554   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
555               "`%s': Received `%s' request from another peer\n", "DHT",
556               "FIND PEER");
557 #endif
558
559   return GNUNET_YES;
560 }
561
562 /**
563  * Task run during shutdown.
564  *
565  * @param cls unused
566  * @param tc unused
567  */
568 static void
569 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
570 {
571   GNUNET_CORE_disconnect (coreAPI);
572 }
573
574 /**
575  * To be called on core init/fail.
576  */
577 void
578 core_init (void *cls,
579            struct GNUNET_CORE_Handle *server,
580            const struct GNUNET_PeerIdentity *identity,
581            const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
582 {
583
584   if (server == NULL)
585     {
586       GNUNET_SCHEDULER_cancel (sched, cleanup_task);
587       GNUNET_SCHEDULER_add_now (sched, &shutdown_task, NULL);
588       return;
589     }
590 #if DEBUG_DHT
591   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
592               "%s: Core connection initialized, I am peer: %s\n", "dht",
593               GNUNET_i2s (identity));
594 #endif
595   memcpy (&my_identity, identity, sizeof (struct GNUNET_PeerIdentity));
596   coreAPI = server;
597 }
598
599 /**
600  * Process dht requests.
601  *
602  * @param cls closure
603  * @param scheduler scheduler to use
604  * @param server the initialized server
605  * @param c configuration to use
606  */
607 static void
608 run (void *cls,
609      struct GNUNET_SCHEDULER_Handle *scheduler,
610      struct GNUNET_SERVER_Handle *server,
611      const struct GNUNET_CONFIGURATION_Handle *c)
612 {
613   sched = scheduler;
614   cfg = c;
615
616   datacache = GNUNET_DATACACHE_create (sched, cfg, "dhtcache");
617
618   client_transmit_timeout =
619     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5);
620   GNUNET_SERVER_add_handlers (server, plugin_handlers);
621
622   coreAPI = GNUNET_CORE_connect (sched, /* Main scheduler */
623                                  cfg,   /* Main configuration */
624                                  client_transmit_timeout,       /* Delay for connecting */
625                                  NULL,  /* FIXME: anything we want to pass around? */
626                                  &core_init,    /* Call core_init once connected */
627                                  NULL,  /* Don't care about pre-connects */
628                                  NULL,  /* Don't care about connects */
629                                  NULL,  /* Don't care about disconnects */
630                                  NULL,  /* Don't want notified about all incoming messages */
631                                  GNUNET_NO,     /* For header only inbound notification */
632                                  NULL,  /* Don't want notified about all outbound messages */
633                                  GNUNET_NO,     /* For header only outbound notification */
634                                  core_handlers);        /* Register these handlers */
635
636   if (coreAPI == NULL)
637     return;
638
639   /* Scheduled the task to clean up when shutdown is called */
640   cleanup_task = GNUNET_SCHEDULER_add_delayed (sched,
641                                                GNUNET_TIME_UNIT_FOREVER_REL,
642                                                &shutdown_task, NULL);
643 }
644
645
646 /**
647  * The main function for the dht service.
648  *
649  * @param argc number of arguments from the command line
650  * @param argv command line arguments
651  * @return 0 ok, 1 on error
652  */
653 int
654 main (int argc, char *const *argv)
655 {
656   return (GNUNET_OK ==
657           GNUNET_SERVICE_run (argc,
658                               argv,
659                               "dht",
660                               GNUNET_SERVICE_OPTION_NONE,
661                               &run, NULL)) ? 0 : 1;
662 }