2 This file is part of GNUnet.
3 (C) 2009, 2010 Christian Grothoff (and other contributing authors)
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.
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.
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.
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
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 "gnunet_transport_service.h"
39 #include "gnunet_hello_lib.h"
43 * Handle to the datacache service (for inserting/retrieving data)
45 struct GNUNET_DATACACHE_Handle *datacache;
48 * The main scheduler to use for the DHT service
50 static struct GNUNET_SCHEDULER_Handle *sched;
53 * The configuration the DHT service is running with
55 static const struct GNUNET_CONFIGURATION_Handle *cfg;
58 * Handle to the core service
60 static struct GNUNET_CORE_Handle *coreAPI;
63 * Handle to the transport service, for getting our hello
65 static struct GNUNET_TRANSPORT_Handle *transport_handle;
68 * The identity of our peer.
70 static struct GNUNET_PeerIdentity my_identity;
75 static struct GNUNET_MessageHeader *my_hello;
78 * Task to run when we shut down, cleaning up all our trash
80 static GNUNET_SCHEDULER_TaskIdentifier cleanup_task;
84 * Linked list of messages to send to clients.
89 * Pointer to next item in the list
91 struct PendingMessage *next;
94 * Pointer to previous item in the list
96 struct PendingMessage *prev;
99 * Actual message to be sent; // avoid allocation
101 const struct GNUNET_MessageHeader *msg; // msg = (cast) &pm[1]; // memcpy (&pm[1], data, len);
106 * Struct containing information about a client,
107 * handle to connect to it, and any pending messages
108 * that need to be sent to it.
113 * Linked list of active clients
115 struct ClientList *next;
118 * The handle to this client
120 struct GNUNET_SERVER_Client *client_handle;
123 * Handle to the current transmission request, NULL
126 struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
129 * Linked list of pending messages for this client
131 struct PendingMessage *pending_head;
134 * Tail of linked list of pending messages for this client
136 struct PendingMessage *pending_tail;
141 * Context for handling results from a get request.
143 struct DatacacheGetContext
146 * The client to send the result to.
148 struct ClientList *client;
151 * The unique id of this request
153 unsigned long long unique_id;
157 * Context containing information about a DHT message received.
159 struct DHT_MessageContext
162 * The client this request was received from.
164 struct ClientList *client;
167 * The key this request was about
169 const GNUNET_HashCode *key;
172 * The unique identifier of this request
174 unsigned long long unique_id;
177 * Desired replication level
182 * Any message options for this request
188 * List of active clients.
190 static struct ClientList *client_list;
193 * Forward declaration.
195 static size_t send_generic_reply (void *cls, size_t size, void *buf);
199 * Task run to check for messages that need to be sent to a client.
201 * @param client a ClientList, containing the client and any messages to be sent to it
204 process_pending_messages (struct ClientList *client)
206 if (client->pending_head == NULL)
208 if (client->transmit_handle != NULL)
210 client->transmit_handle =
211 GNUNET_SERVER_notify_transmit_ready (client->client_handle,
212 ntohs (client->pending_head->msg->
214 GNUNET_TIME_UNIT_FOREVER_REL,
215 &send_generic_reply, client);
219 * Callback called as a result of issuing a GNUNET_SERVER_notify_transmit_ready
220 * request. A ClientList is passed as closure, take the head of the list
221 * and copy it into buf, which has the result of sending the message to the
224 * @param cls closure to this call
225 * @param size maximum number of bytes available to send
226 * @param buf where to copy the actual message to
228 * @return the number of bytes actually copied, 0 indicates failure
231 send_generic_reply (void *cls, size_t size, void *buf)
233 struct ClientList *client = cls;
235 struct PendingMessage *reply;
239 client->transmit_handle = NULL;
242 /* client disconnected */
246 while ( (NULL != (reply = client->pending_head)) &&
247 (size >= off + (msize = ntohs (reply->msg->size))))
249 GNUNET_CONTAINER_DLL_remove (client->pending_head,
250 client->pending_tail,
252 memcpy (&cbuf[off], reply->msg, msize);
256 process_pending_messages (client);
262 * Add a PendingMessage to the clients list of messages to be sent
264 * @param client the active client to send the message to
265 * @param pending_message the actual message to send
268 add_pending_message (struct ClientList *client,
269 struct PendingMessage *pending_message)
271 GNUNET_CONTAINER_DLL_insert_after (client->pending_head,
272 client->pending_tail,
273 client->pending_tail,
275 process_pending_messages (client);
280 * Called when a reply needs to be sent to a client, as
281 * a result it found to a GET or FIND PEER request.
283 * @param client the client to send the reply to
284 * @param message the encapsulated message to send
285 * @param uid the unique identifier of this request
288 send_reply_to_client (struct ClientList *client,
289 const struct GNUNET_MessageHeader *message,
290 unsigned long long uid)
292 struct GNUNET_DHT_RouteResultMessage *reply;
293 struct PendingMessage *pending_message;
297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298 "`%s': Sending reply to client.\n", "DHT");
300 msize = ntohs (message->size);
301 tsize = sizeof (struct GNUNET_DHT_RouteResultMessage) + msize;
302 if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
308 pending_message = GNUNET_malloc (sizeof (struct PendingMessage) + tsize);
309 pending_message->msg = (struct GNUNET_MessageHeader *)&pending_message[1];
310 reply = (struct GNUNET_DHT_RouteResultMessage *)&pending_message[1];
311 reply->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_ROUTE_RESULT);
312 reply->header.size = htons (tsize);
313 reply->unique_id = GNUNET_htonll (uid);
314 memcpy (&reply[1], message, msize);
316 add_pending_message (client, pending_message);
321 * Iterator for local get request results,
323 * @param cls closure for iterator, a DatacacheGetContext
324 * @param exp when does this value expire?
325 * @param key the key this data is stored under
326 * @param size the size of the data identified by key
327 * @param data the actual data
328 * @param type the type of the data
330 * @return GNUNET_OK to continue iteration, anything else
334 datacache_get_iterator (void *cls,
335 struct GNUNET_TIME_Absolute exp,
336 const GNUNET_HashCode * key,
337 uint32_t size, const char *data, uint32_t type)
339 struct DatacacheGetContext *datacache_get_ctx = cls;
340 struct GNUNET_DHT_GetResultMessage *get_result;
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
343 "`%s': Received `%s' response from datacache\n", "DHT", "GET");
346 GNUNET_malloc (sizeof (struct GNUNET_DHT_GetResultMessage) + size);
347 get_result->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_GET_RESULT);
348 get_result->header.size =
349 htons (sizeof (struct GNUNET_DHT_GetResultMessage) + size);
350 get_result->expiration = exp;
351 memcpy (&get_result->key, key, sizeof (GNUNET_HashCode));
352 get_result->type = htons (type);
353 memcpy (&get_result[1], data, size);
354 send_reply_to_client (datacache_get_ctx->client, &get_result->header,
355 datacache_get_ctx->unique_id);
356 GNUNET_free (get_result);
362 * Server handler for initiating local dht get requests
364 * @param cls closure for service
365 * @param msg the actual get message
366 * @param message_context struct containing pertinent information about the get request
369 handle_dht_get (void *cls,
370 const struct GNUNET_MessageHeader *msg,
371 struct DHT_MessageContext *message_context)
373 const struct GNUNET_DHT_GetMessage *get_msg;
375 unsigned int results;
376 struct DatacacheGetContext datacache_get_context;
378 get_msg = (const struct GNUNET_DHT_GetMessage *) msg;
379 if (ntohs (get_msg->header.size) != sizeof (struct GNUNET_DHT_GetMessage))
385 get_type = ntohs (get_msg->type);
387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
388 "`%s': Received `%s' request from client, message type %u, key %s, uid %llu\n",
389 "DHT", "GET", get_type, GNUNET_h2s (message_context->key),
390 message_context->unique_id);
392 datacache_get_context.client = message_context->client;
393 datacache_get_context.unique_id = message_context->unique_id;
395 if (datacache != NULL)
397 GNUNET_DATACACHE_get (datacache, message_context->key, get_type,
398 &datacache_get_iterator, &datacache_get_context);
399 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400 "`%s': Found %d results for local `%s' request\n", "DHT",
406 * Server handler for initiating local dht find peer requests
408 * @param cls closure for service
409 * @param find_msg the actual find peer message
410 * @param message_context struct containing pertinent information about the request
414 handle_dht_find_peer (void *cls,
415 const struct GNUNET_MessageHeader *find_msg,
416 struct DHT_MessageContext *message_context)
418 struct GNUNET_MessageHeader *find_peer_result;
423 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
424 "`%s': Received `%s' request from client, key %s (msg size %d, we expected %d)\n",
425 "DHT", "FIND PEER", GNUNET_h2s (message_context->key),
426 ntohs (find_msg->size),
427 sizeof (struct GNUNET_MessageHeader));
429 if (my_hello == NULL)
432 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
433 "`%s': Our HELLO is null, can't return.\n",
438 /* Simplistic find_peer functionality, always return our hello */
439 hello_size = ntohs(my_hello->size);
440 tsize = hello_size + sizeof (struct GNUNET_MessageHeader);
442 find_peer_result = GNUNET_malloc (tsize);
443 find_peer_result->type = htons (GNUNET_MESSAGE_TYPE_DHT_FIND_PEER_RESULT);
444 find_peer_result->size = htons (tsize);
445 memcpy (&find_peer_result[1], my_hello, hello_size);
447 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448 "`%s': Sending hello size %d to client.\n",
451 send_reply_to_client(message_context->client, find_peer_result, message_context->unique_id);
452 GNUNET_free(find_peer_result);
457 * Server handler for initiating local dht put requests
459 * @param cls closure for service
460 * @param put_msg the actual put message
461 * @param message_context struct containing pertinent information about the request
464 handle_dht_put (void *cls,
465 const struct GNUNET_MessageHeader *msg,
466 struct DHT_MessageContext *message_context)
468 struct GNUNET_DHT_PutMessage *put_msg;
472 GNUNET_assert (ntohs (msg->size) >=
473 sizeof (struct GNUNET_DHT_PutMessage));
474 put_msg = (struct GNUNET_DHT_PutMessage *)msg;
475 put_type = ntohs (put_msg->type);
476 data_size = ntohs (put_msg->header.size) - sizeof (struct GNUNET_DHT_PutMessage);
478 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
479 "`%s': Received `%s' request from client, message type %d, key %s\n",
480 "DHT", "PUT", put_type, GNUNET_h2s (message_context->key));
482 if (datacache != NULL)
483 GNUNET_DATACACHE_put (datacache, message_context->key, data_size,
484 (char *) &put_msg[1], put_type,
485 GNUNET_TIME_absolute_ntoh(put_msg->expiration));
487 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488 "`%s': %s request received locally, but have no datacache!\n",
494 * Find a client if it exists, add it otherwise.
496 * @param client the server handle to the client
498 * @return the client if found, a new client otherwise
500 static struct ClientList *
501 find_active_client (struct GNUNET_SERVER_Client *client)
503 struct ClientList *pos = client_list;
504 struct ClientList *ret;
508 if (pos->client_handle == client)
513 ret = GNUNET_malloc (sizeof (struct ClientList));
514 ret->client_handle = client;
515 ret->next = client_list;
521 * Handler for any generic DHT messages, calls the appropriate handler
522 * depending on message type, sends confirmation if responses aren't otherwise
525 * @param cls closure for the service
526 * @param client the client we received this message from
527 * @param message the actual message received
530 handle_dht_start_message (void *cls, struct GNUNET_SERVER_Client *client,
531 const struct GNUNET_MessageHeader *message)
533 const struct GNUNET_DHT_RouteMessage *dht_msg = (const struct GNUNET_DHT_RouteMessage *) message;
534 const struct GNUNET_MessageHeader *enc_msg;
535 struct DHT_MessageContext *message_context;
539 enc_msg = (const struct GNUNET_MessageHeader *) &dht_msg[1];
540 enc_type = ntohs (enc_msg->type);
544 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
545 "`%s': Received `%s' request from client, message type %d, key %s, uid %llu\n",
546 "DHT", "GENERIC", enc_type, GNUNET_h2s (&dht_msg->key),
547 GNUNET_ntohll (dht_msg->unique_id));
550 message_context = GNUNET_malloc (sizeof (struct DHT_MessageContext));
551 message_context->client = find_active_client (client);
552 message_context->key = &dht_msg->key;
553 message_context->unique_id = GNUNET_ntohll (dht_msg->unique_id);
554 message_context->replication = ntohl (dht_msg->desired_replication_level);
555 message_context->msg_options = ntohl (dht_msg->options);
557 /* TODO: Steps to be added by students */
558 /* FIXME: Implement *remote* DHT operations here (forward request) */
559 /* Implement generic route function and call here. */
560 /* FIXME: *IF* handling should be local, then do this: */
561 /* 1. find if this peer is closest based on whatever metric the DHT uses
562 * 2. if this peer is closest _OR_ the message options indicate it should
563 * be processed everywhere _AND_ we want it processed everywhere, then
566 handle_locally = GNUNET_YES;
567 if (handle_locally == GNUNET_YES)
571 case GNUNET_MESSAGE_TYPE_DHT_GET:
572 handle_dht_get (cls, enc_msg,
575 case GNUNET_MESSAGE_TYPE_DHT_PUT:
576 handle_dht_put (cls, enc_msg,
579 case GNUNET_MESSAGE_TYPE_DHT_FIND_PEER:
580 handle_dht_find_peer (cls,
585 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
586 "`%s': Message type (%d) not handled\n", "DHT", enc_type);
589 GNUNET_free (message_context);
590 GNUNET_SERVER_receive_done (client, GNUNET_OK);
595 * Handler for any generic DHT stop messages, calls the appropriate handler
596 * depending on message type (if processed locally)
598 * @param cls closure for the service
599 * @param client the client we received this message from
600 * @param message the actual message received
602 * TODO: once message are remembered by unique id, add code to
606 handle_dht_stop_message (void *cls, struct GNUNET_SERVER_Client *client,
607 const struct GNUNET_MessageHeader *message)
609 const struct GNUNET_DHT_StopMessage *dht_stop_msg =
610 (const struct GNUNET_DHT_StopMessage *) message;
613 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614 "`%s': Received `%s' request from client, uid %llu\n", "DHT",
615 "GENERIC STOP", GNUNET_ntohll (dht_stop_msg->unique_id));
618 uid = GNUNET_ntohll(dht_stop_msg->unique_id);
619 /* TODO: actually stop... free associated resources for the request
620 * lookup request by uid and remove state. */
622 GNUNET_SERVER_receive_done (client, GNUNET_OK);
627 * Core handler for p2p route requests.
630 handle_dht_p2p_route_request (void *cls,
631 const struct GNUNET_PeerIdentity *peer,
632 const struct GNUNET_MessageHeader *message,
633 struct GNUNET_TIME_Relative latency, uint32_t distance)
636 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
637 "`%s': Received `%s' request from another peer\n", "DHT",
640 // FIXME: setup tracking for sending replies to peer (with timeout)
641 // FIXME: call code from handle_dht_start_message (refactor...)
647 * Core handler for p2p route results.
650 handle_dht_p2p_route_result (void *cls,
651 const struct GNUNET_PeerIdentity *peer,
652 const struct GNUNET_MessageHeader *message,
653 struct GNUNET_TIME_Relative latency, uint32_t distance)
656 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
657 "`%s': Received `%s' request from another peer\n", "DHT",
660 // FIXME: setup tracking for sending replies to peer
661 // FIXME: possibly call code from handle_dht_stop_message? (unique result?) (refactor...)
667 * Receive the HELLO from transport service,
668 * free current and replace if necessary.
671 * @param message HELLO message of peer
674 process_hello (void *cls, const struct GNUNET_MessageHeader *message)
677 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
678 "Received our `%s' from transport service\n",
682 GNUNET_assert (message != NULL);
683 GNUNET_free_non_null(my_hello);
684 my_hello = GNUNET_malloc(ntohs(message->size));
685 memcpy(my_hello, message, ntohs(message->size));
689 * Task run during shutdown.
695 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
697 if (transport_handle != NULL)
699 GNUNET_free_non_null(my_hello);
700 GNUNET_TRANSPORT_get_hello_cancel(transport_handle, &process_hello, NULL);
701 GNUNET_TRANSPORT_disconnect(transport_handle);
704 GNUNET_CORE_disconnect (coreAPI);
709 * To be called on core init/fail.
711 * @param cls service closure
712 * @param server handle to the server for this service
713 * @param identity the public identity of this peer
714 * @param publicKey the public key of this peer
717 core_init (void *cls,
718 struct GNUNET_CORE_Handle *server,
719 const struct GNUNET_PeerIdentity *identity,
720 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
726 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727 "%s: Connection to core FAILED!\n", "dht",
728 GNUNET_i2s (identity));
730 GNUNET_SCHEDULER_cancel (sched, cleanup_task);
731 GNUNET_SCHEDULER_add_now (sched, &shutdown_task, NULL);
735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736 "%s: Core connection initialized, I am peer: %s\n", "dht",
737 GNUNET_i2s (identity));
739 /* Copy our identity so we can use it */
740 memcpy (&my_identity, identity, sizeof (struct GNUNET_PeerIdentity));
741 /* Set the server to local variable */
746 static struct GNUNET_SERVER_MessageHandler plugin_handlers[] = {
747 {&handle_dht_start_message, NULL, GNUNET_MESSAGE_TYPE_DHT_ROUTE, 0},
748 {&handle_dht_stop_message, NULL, GNUNET_MESSAGE_TYPE_DHT_STOP, 0},
753 static struct GNUNET_CORE_MessageHandler core_handlers[] = {
754 {&handle_dht_p2p_route_request, GNUNET_MESSAGE_TYPE_DHT_ROUTE, 0},
755 {&handle_dht_p2p_route_result, GNUNET_MESSAGE_TYPE_DHT_ROUTE_RESULT, 0},
761 * Process dht requests.
764 * @param scheduler scheduler to use
765 * @param server the initialized server
766 * @param c configuration to use
770 struct GNUNET_SCHEDULER_Handle *scheduler,
771 struct GNUNET_SERVER_Handle *server,
772 const struct GNUNET_CONFIGURATION_Handle *c)
776 datacache = GNUNET_DATACACHE_create (sched, cfg, "dhtcache");
777 GNUNET_SERVER_add_handlers (server, plugin_handlers);
778 coreAPI = GNUNET_CORE_connect (sched, /* Main scheduler */
779 cfg, /* Main configuration */
780 GNUNET_TIME_UNIT_FOREVER_REL,
781 NULL, /* FIXME: anything we want to pass around? */
782 &core_init, /* Call core_init once connected */
783 NULL, /* Don't care about connects */
784 NULL, /* Don't care about disconnects */
785 NULL, /* Don't want notified about all incoming messages */
786 GNUNET_NO, /* For header only inbound notification */
787 NULL, /* Don't want notified about all outbound messages */
788 GNUNET_NO, /* For header only outbound notification */
789 core_handlers); /* Register these handlers */
792 transport_handle = GNUNET_TRANSPORT_connect(sched, cfg, NULL, NULL, NULL, NULL);
793 if (transport_handle != NULL)
794 GNUNET_TRANSPORT_get_hello (transport_handle, &process_hello, NULL);
796 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to connect to transport service!\n");
797 /* Scheduled the task to clean up when shutdown is called */
798 cleanup_task = GNUNET_SCHEDULER_add_delayed (sched,
799 GNUNET_TIME_UNIT_FOREVER_REL,
800 &shutdown_task, NULL);
805 * The main function for the dht service.
807 * @param argc number of arguments from the command line
808 * @param argv command line arguments
809 * @return 0 ok, 1 on error
812 main (int argc, char *const *argv)
815 GNUNET_SERVICE_run (argc,
818 GNUNET_SERVICE_OPTION_NONE,
819 &run, NULL)) ? 0 : 1;