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 3, 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 static 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;
73 * Short id of the peer, for printing
75 static char *my_short_id;
80 static struct GNUNET_MessageHeader *my_hello;
83 * Task to run when we shut down, cleaning up all our trash
85 static GNUNET_SCHEDULER_TaskIdentifier cleanup_task;
89 * Linked list of messages to send to clients.
94 * Pointer to next item in the list
96 struct PendingMessage *next;
99 * Pointer to previous item in the list
101 struct PendingMessage *prev;
104 * Actual message to be sent; // avoid allocation
106 const struct GNUNET_MessageHeader *msg; // msg = (cast) &pm[1]; // memcpy (&pm[1], data, len);
111 * Struct containing information about a client,
112 * handle to connect to it, and any pending messages
113 * that need to be sent to it.
118 * Linked list of active clients
120 struct ClientList *next;
123 * The handle to this client
125 struct GNUNET_SERVER_Client *client_handle;
128 * Handle to the current transmission request, NULL
131 struct GNUNET_CONNECTION_TransmitHandle *transmit_handle;
134 * Linked list of pending messages for this client
136 struct PendingMessage *pending_head;
139 * Tail of linked list of pending messages for this client
141 struct PendingMessage *pending_tail;
146 * Context for handling results from a get request.
148 struct DatacacheGetContext
151 * The client to send the result to.
153 struct ClientList *client;
156 * The unique id of this request
158 unsigned long long unique_id;
162 * Context containing information about a DHT message received.
164 struct DHT_MessageContext
167 * The client this request was received from.
169 struct ClientList *client;
172 * The key this request was about
174 const GNUNET_HashCode *key;
177 * The unique identifier of this request
179 unsigned long long unique_id;
182 * Desired replication level
187 * Any message options for this request
193 * List of active clients.
195 static struct ClientList *client_list;
198 * Forward declaration.
200 static size_t send_generic_reply (void *cls, size_t size, void *buf);
204 * Task run to check for messages that need to be sent to a client.
206 * @param client a ClientList, containing the client and any messages to be sent to it
209 process_pending_messages (struct ClientList *client)
211 if (client->pending_head == NULL)
213 if (client->transmit_handle != NULL)
215 client->transmit_handle =
216 GNUNET_SERVER_notify_transmit_ready (client->client_handle,
217 ntohs (client->pending_head->msg->
219 GNUNET_TIME_UNIT_FOREVER_REL,
220 &send_generic_reply, client);
224 * Callback called as a result of issuing a GNUNET_SERVER_notify_transmit_ready
225 * request. A ClientList is passed as closure, take the head of the list
226 * and copy it into buf, which has the result of sending the message to the
229 * @param cls closure to this call
230 * @param size maximum number of bytes available to send
231 * @param buf where to copy the actual message to
233 * @return the number of bytes actually copied, 0 indicates failure
236 send_generic_reply (void *cls, size_t size, void *buf)
238 struct ClientList *client = cls;
240 struct PendingMessage *reply;
244 client->transmit_handle = NULL;
247 /* client disconnected */
251 while ( (NULL != (reply = client->pending_head)) &&
252 (size >= off + (msize = ntohs (reply->msg->size))))
254 GNUNET_CONTAINER_DLL_remove (client->pending_head,
255 client->pending_tail,
257 memcpy (&cbuf[off], reply->msg, msize);
261 process_pending_messages (client);
267 * Add a PendingMessage to the clients list of messages to be sent
269 * @param client the active client to send the message to
270 * @param pending_message the actual message to send
273 add_pending_message (struct ClientList *client,
274 struct PendingMessage *pending_message)
276 GNUNET_CONTAINER_DLL_insert_after (client->pending_head,
277 client->pending_tail,
278 client->pending_tail,
280 process_pending_messages (client);
285 * Called when a reply needs to be sent to a client, as
286 * a result it found to a GET or FIND PEER request.
288 * @param client the client to send the reply to
289 * @param message the encapsulated message to send
290 * @param uid the unique identifier of this request
293 send_reply_to_client (struct ClientList *client,
294 const struct GNUNET_MessageHeader *message,
295 unsigned long long uid)
297 struct GNUNET_DHT_RouteResultMessage *reply;
298 struct PendingMessage *pending_message;
302 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303 "`%s': Sending reply to client.\n", "DHT");
305 msize = ntohs (message->size);
306 tsize = sizeof (struct GNUNET_DHT_RouteResultMessage) + msize;
307 if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
313 pending_message = GNUNET_malloc (sizeof (struct PendingMessage) + tsize);
314 pending_message->msg = (struct GNUNET_MessageHeader *)&pending_message[1];
315 reply = (struct GNUNET_DHT_RouteResultMessage *)&pending_message[1];
316 reply->header.type = htons (GNUNET_MESSAGE_TYPE_LOCAL_DHT_ROUTE_RESULT);
317 reply->header.size = htons (tsize);
318 reply->unique_id = GNUNET_htonll (uid);
319 memcpy (&reply[1], message, msize);
321 add_pending_message (client, pending_message);
326 * Iterator for local get request results,
328 * @param cls closure for iterator, a DatacacheGetContext
329 * @param exp when does this value expire?
330 * @param key the key this data is stored under
331 * @param size the size of the data identified by key
332 * @param data the actual data
333 * @param type the type of the data
335 * @return GNUNET_OK to continue iteration, anything else
339 datacache_get_iterator (void *cls,
340 struct GNUNET_TIME_Absolute exp,
341 const GNUNET_HashCode * key,
342 uint32_t size, const char *data, uint32_t type)
344 struct DatacacheGetContext *datacache_get_ctx = cls;
345 struct GNUNET_DHT_GetResultMessage *get_result;
347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
348 "`%s': Received `%s' response from datacache\n", "DHT", "GET");
351 GNUNET_malloc (sizeof (struct GNUNET_DHT_GetResultMessage) + size);
352 get_result->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_GET_RESULT);
353 get_result->header.size =
354 htons (sizeof (struct GNUNET_DHT_GetResultMessage) + size);
355 get_result->expiration = GNUNET_TIME_absolute_hton (exp);
356 get_result->type = htons (type);
357 memcpy (&get_result[1], data, size);
358 send_reply_to_client (datacache_get_ctx->client, &get_result->header,
359 datacache_get_ctx->unique_id);
360 GNUNET_free (get_result);
366 * Server handler for initiating local dht get requests
368 * @param cls closure for service
369 * @param msg the actual get message
370 * @param message_context struct containing pertinent information about the get request
373 handle_dht_get (void *cls,
374 const struct GNUNET_MessageHeader *msg,
375 struct DHT_MessageContext *message_context)
377 const struct GNUNET_DHT_GetMessage *get_msg;
379 unsigned int results;
380 struct DatacacheGetContext datacache_get_context;
382 get_msg = (const struct GNUNET_DHT_GetMessage *) msg;
383 if (ntohs (get_msg->header.size) != sizeof (struct GNUNET_DHT_GetMessage))
389 get_type = ntohs (get_msg->type);
391 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
392 "`%s': Received `%s' request from client, message type %u, key %s, uid %llu\n",
393 "DHT", "GET", get_type, GNUNET_h2s (message_context->key),
394 message_context->unique_id);
396 datacache_get_context.client = message_context->client;
397 datacache_get_context.unique_id = message_context->unique_id;
399 if (datacache != NULL)
401 GNUNET_DATACACHE_get (datacache, message_context->key, get_type,
402 &datacache_get_iterator, &datacache_get_context);
403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404 "`%s': Found %d results for local `%s' request\n", "DHT",
410 * Server handler for initiating local dht find peer requests
412 * @param cls closure for service
413 * @param find_msg the actual find peer message
414 * @param message_context struct containing pertinent information about the request
418 handle_dht_find_peer (void *cls,
419 const struct GNUNET_MessageHeader *find_msg,
420 struct DHT_MessageContext *message_context)
422 struct GNUNET_MessageHeader *find_peer_result;
427 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428 "`%s': Received `%s' request from client, key %s (msg size %d, we expected %d)\n",
429 "DHT", "FIND PEER", GNUNET_h2s (message_context->key),
430 ntohs (find_msg->size),
431 sizeof (struct GNUNET_MessageHeader));
433 if (my_hello == NULL)
436 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
437 "`%s': Our HELLO is null, can't return.\n",
442 /* Simplistic find_peer functionality, always return our hello */
443 hello_size = ntohs(my_hello->size);
444 tsize = hello_size + sizeof (struct GNUNET_MessageHeader);
446 if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
451 find_peer_result = GNUNET_malloc (tsize);
452 find_peer_result->type = htons (GNUNET_MESSAGE_TYPE_DHT_FIND_PEER_RESULT);
453 find_peer_result->size = htons (tsize);
454 memcpy (&find_peer_result[1], my_hello, hello_size);
456 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
457 "`%s': Sending hello size %d to client.\n",
460 send_reply_to_client(message_context->client, find_peer_result, message_context->unique_id);
461 GNUNET_free(find_peer_result);
466 * Server handler for initiating local dht put requests
468 * @param cls closure for service
469 * @param msg the actual put message
470 * @param message_context struct containing pertinent information about the request
473 handle_dht_put (void *cls,
474 const struct GNUNET_MessageHeader *msg,
475 struct DHT_MessageContext *message_context)
477 struct GNUNET_DHT_PutMessage *put_msg;
481 GNUNET_assert (ntohs (msg->size) >=
482 sizeof (struct GNUNET_DHT_PutMessage));
483 put_msg = (struct GNUNET_DHT_PutMessage *)msg;
484 put_type = ntohs (put_msg->type);
485 data_size = ntohs (put_msg->header.size) - sizeof (struct GNUNET_DHT_PutMessage);
487 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488 "`%s': Received `%s' request from client, message type %d, key %s\n",
489 "DHT", "PUT", put_type, GNUNET_h2s (message_context->key));
491 if (datacache != NULL)
492 GNUNET_DATACACHE_put (datacache, message_context->key, data_size,
493 (char *) &put_msg[1], put_type,
494 GNUNET_TIME_absolute_ntoh(put_msg->expiration));
496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497 "`%s': %s request received locally, but have no datacache!\n",
503 * Find a client if it exists, add it otherwise.
505 * @param client the server handle to the client
507 * @return the client if found, a new client otherwise
509 static struct ClientList *
510 find_active_client (struct GNUNET_SERVER_Client *client)
512 struct ClientList *pos = client_list;
513 struct ClientList *ret;
517 if (pos->client_handle == client)
522 ret = GNUNET_malloc (sizeof (struct ClientList));
523 ret->client_handle = client;
524 ret->next = client_list;
530 * Handler for any generic DHT messages, calls the appropriate handler
531 * depending on message type, sends confirmation if responses aren't otherwise
534 * @param cls closure for the service
535 * @param client the client we received this message from
536 * @param message the actual message received
539 handle_dht_start_message (void *cls, struct GNUNET_SERVER_Client *client,
540 const struct GNUNET_MessageHeader *message)
542 const struct GNUNET_DHT_RouteMessage *dht_msg = (const struct GNUNET_DHT_RouteMessage *) message;
543 const struct GNUNET_MessageHeader *enc_msg;
544 struct DHT_MessageContext *message_context;
548 enc_msg = (const struct GNUNET_MessageHeader *) &dht_msg[1];
549 enc_type = ntohs (enc_msg->type);
553 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
554 "`%s': Received `%s' request from client, message type %d, key %s, uid %llu\n",
555 "DHT", "GENERIC", enc_type, GNUNET_h2s (&dht_msg->key),
556 GNUNET_ntohll (dht_msg->unique_id));
559 message_context = GNUNET_malloc (sizeof (struct DHT_MessageContext));
560 message_context->client = find_active_client (client);
561 message_context->key = &dht_msg->key;
562 message_context->unique_id = GNUNET_ntohll (dht_msg->unique_id);
563 message_context->replication = ntohl (dht_msg->desired_replication_level);
564 message_context->msg_options = ntohl (dht_msg->options);
566 /* TODO: Steps to be added by students */
567 /* FIXME: Implement *remote* DHT operations here (forward request) */
568 /* Implement generic route function and call here. */
569 /* FIXME: *IF* handling should be local, then do this: */
570 /* 1. find if this peer is closest based on whatever metric the DHT uses
571 * 2. if this peer is closest _OR_ the message options indicate it should
572 * be processed everywhere _AND_ we want it processed everywhere, then
575 handle_locally = GNUNET_NO;
576 if (handle_locally == GNUNET_YES)
580 case GNUNET_MESSAGE_TYPE_DHT_GET:
581 handle_dht_get (cls, enc_msg,
584 case GNUNET_MESSAGE_TYPE_DHT_PUT:
585 handle_dht_put (cls, enc_msg,
588 case GNUNET_MESSAGE_TYPE_DHT_FIND_PEER:
589 handle_dht_find_peer (cls,
594 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
595 "`%s': Message type (%d) not handled\n", "DHT", enc_type);
599 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
600 "`%s': Message type (%d) not handled\n", "DHT", enc_type);
601 GNUNET_free (message_context);
602 GNUNET_SERVER_receive_done (client, GNUNET_OK);
607 * Handler for any generic DHT stop messages, calls the appropriate handler
608 * depending on message type (if processed locally)
610 * @param cls closure for the service
611 * @param client the client we received this message from
612 * @param message the actual message received
614 * TODO: once messages are remembered by unique id, add code to
618 handle_dht_stop_message (void *cls, struct GNUNET_SERVER_Client *client,
619 const struct GNUNET_MessageHeader *message)
621 const struct GNUNET_DHT_StopMessage *dht_stop_msg =
622 (const struct GNUNET_DHT_StopMessage *) message;
625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626 "`%s': Received `%s' request from client, uid %llu\n", "DHT",
627 "GENERIC STOP", GNUNET_ntohll (dht_stop_msg->unique_id));
630 uid = GNUNET_ntohll(dht_stop_msg->unique_id);
631 /* TODO: actually stop... free associated resources for the request
632 * lookup request by uid and remove state. */
634 GNUNET_SERVER_receive_done (client, GNUNET_OK);
639 * Core handler for p2p route requests.
642 handle_dht_p2p_route_request (void *cls,
643 const struct GNUNET_PeerIdentity *peer,
644 const struct GNUNET_MessageHeader *message,
645 struct GNUNET_TIME_Relative latency, uint32_t distance)
648 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
649 "`%s': Received `%s' request from another peer\n", "DHT",
652 // FIXME: setup tracking for sending replies to peer (with timeout)
653 // FIXME: call code from handle_dht_start_message (refactor...)
659 * Core handler for p2p route results.
662 handle_dht_p2p_route_result (void *cls,
663 const struct GNUNET_PeerIdentity *peer,
664 const struct GNUNET_MessageHeader *message,
665 struct GNUNET_TIME_Relative latency, uint32_t distance)
668 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
669 "`%s': Received `%s' request from another peer\n", "DHT",
672 // FIXME: setup tracking for sending replies to peer
673 // FIXME: possibly call code from handle_dht_stop_message? (unique result?) (refactor...)
679 * Receive the HELLO from transport service,
680 * free current and replace if necessary.
683 * @param message HELLO message of peer
686 process_hello (void *cls, const struct GNUNET_MessageHeader *message)
689 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
690 "Received our `%s' from transport service\n",
694 GNUNET_assert (message != NULL);
695 GNUNET_free_non_null(my_hello);
696 my_hello = GNUNET_malloc(ntohs(message->size));
697 memcpy(my_hello, message, ntohs(message->size));
701 * Task run during shutdown.
707 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
709 if (transport_handle != NULL)
711 GNUNET_free_non_null(my_hello);
712 GNUNET_TRANSPORT_get_hello_cancel(transport_handle, &process_hello, NULL);
713 GNUNET_TRANSPORT_disconnect(transport_handle);
716 GNUNET_CORE_disconnect (coreAPI);
717 if (datacache != NULL)
718 GNUNET_DATACACHE_destroy (datacache);
719 if (my_short_id != NULL)
720 GNUNET_free(my_short_id);
725 * To be called on core init/fail.
727 * @param cls service closure
728 * @param server handle to the server for this service
729 * @param identity the public identity of this peer
730 * @param publicKey the public key of this peer
733 core_init (void *cls,
734 struct GNUNET_CORE_Handle *server,
735 const struct GNUNET_PeerIdentity *identity,
736 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
742 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
743 "%s: Connection to core FAILED!\n", "dht",
744 GNUNET_i2s (identity));
746 GNUNET_SCHEDULER_cancel (sched, cleanup_task);
747 GNUNET_SCHEDULER_add_now (sched, &shutdown_task, NULL);
751 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
752 "%s: Core connection initialized, I am peer: %s\n", "dht",
753 GNUNET_i2s (identity));
755 /* Copy our identity so we can use it */
756 memcpy (&my_identity, identity, sizeof (struct GNUNET_PeerIdentity));
757 my_short_id = GNUNET_strdup(GNUNET_i2s(&my_identity));
758 /* Set the server to local variable */
763 static struct GNUNET_SERVER_MessageHandler plugin_handlers[] = {
764 {&handle_dht_start_message, NULL, GNUNET_MESSAGE_TYPE_LOCAL_DHT_ROUTE, 0},
765 {&handle_dht_stop_message, NULL, GNUNET_MESSAGE_TYPE_DHT_ROUTE_STOP, 0},
770 static struct GNUNET_CORE_MessageHandler core_handlers[] = {
771 {&handle_dht_p2p_route_request, GNUNET_MESSAGE_TYPE_P2P_DHT_ROUTE, 0},
772 {&handle_dht_p2p_route_result, GNUNET_MESSAGE_TYPE_P2P_DHT_ROUTE_RESULT, 0},
777 * Method called whenever a peer connects.
780 * @param peer peer identity this notification is about
781 * @param latency reported latency of the connection with peer
782 * @param distance reported distance (DV) to peer
784 void handle_core_connect (void *cls,
785 const struct GNUNET_PeerIdentity * peer,
786 struct GNUNET_TIME_Relative latency,
791 size_t data_size = 42;
792 data = GNUNET_malloc (data_size);
793 memset (data, 43, data_size);
796 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
797 "%s:%s Receives core connect message for peer %s distance %d!\n", my_short_id, "dht", GNUNET_i2s(peer), distance);
799 if (datacache != NULL)
801 ret = GNUNET_DATACACHE_put (datacache, &peer->hashPubKey, data_size,
803 GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
804 GNUNET_TIME_UNIT_MINUTES));
806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807 "%s Inserting data %s, type %d into datacache, return value was %d\n", my_short_id, GNUNET_h2s(&peer->hashPubKey), 130, ret);
815 * Process dht requests.
818 * @param scheduler scheduler to use
819 * @param server the initialized server
820 * @param c configuration to use
824 struct GNUNET_SCHEDULER_Handle *scheduler,
825 struct GNUNET_SERVER_Handle *server,
826 const struct GNUNET_CONFIGURATION_Handle *c)
830 datacache = GNUNET_DATACACHE_create (sched, cfg, "dhtcache");
831 GNUNET_SERVER_add_handlers (server, plugin_handlers);
832 coreAPI = GNUNET_CORE_connect (sched, /* Main scheduler */
833 cfg, /* Main configuration */
834 GNUNET_TIME_UNIT_FOREVER_REL,
835 NULL, /* FIXME: anything we want to pass around? */
836 &core_init, /* Call core_init once connected */
837 &handle_core_connect, /* Don't care about connects */
838 NULL, /* Don't care about disconnects */
839 NULL, /* Don't care about peer status changes */
840 NULL, /* Don't want notified about all incoming messages */
841 GNUNET_NO, /* For header only inbound notification */
842 NULL, /* Don't want notified about all outbound messages */
843 GNUNET_NO, /* For header only outbound notification */
844 core_handlers); /* Register these handlers */
847 transport_handle = GNUNET_TRANSPORT_connect(sched, cfg, NULL, NULL, NULL, NULL);
848 if (transport_handle != NULL)
849 GNUNET_TRANSPORT_get_hello (transport_handle, &process_hello, NULL);
851 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to connect to transport service!\n");
852 /* Scheduled the task to clean up when shutdown is called */
853 cleanup_task = GNUNET_SCHEDULER_add_delayed (sched,
854 GNUNET_TIME_UNIT_FOREVER_REL,
855 &shutdown_task, NULL);
859 * The main function for the dht service.
861 * @param argc number of arguments from the command line
862 * @param argv command line arguments
863 * @return 0 ok, 1 on error
866 main (int argc, char *const *argv)
869 GNUNET_SERVICE_run (argc,
872 GNUNET_SERVICE_OPTION_NONE,
873 &run, NULL)) ? 0 : 1;