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_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 = exp;
356 memcpy (&get_result->key, key, sizeof (GNUNET_HashCode));
357 get_result->type = htons (type);
358 memcpy (&get_result[1], data, size);
359 send_reply_to_client (datacache_get_ctx->client, &get_result->header,
360 datacache_get_ctx->unique_id);
361 GNUNET_free (get_result);
367 * Server handler for initiating local dht get requests
369 * @param cls closure for service
370 * @param msg the actual get message
371 * @param message_context struct containing pertinent information about the get request
374 handle_dht_get (void *cls,
375 const struct GNUNET_MessageHeader *msg,
376 struct DHT_MessageContext *message_context)
378 const struct GNUNET_DHT_GetMessage *get_msg;
380 unsigned int results;
381 struct DatacacheGetContext datacache_get_context;
383 get_msg = (const struct GNUNET_DHT_GetMessage *) msg;
384 if (ntohs (get_msg->header.size) != sizeof (struct GNUNET_DHT_GetMessage))
390 get_type = ntohs (get_msg->type);
392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
393 "`%s': Received `%s' request from client, message type %u, key %s, uid %llu\n",
394 "DHT", "GET", get_type, GNUNET_h2s (message_context->key),
395 message_context->unique_id);
397 datacache_get_context.client = message_context->client;
398 datacache_get_context.unique_id = message_context->unique_id;
400 if (datacache != NULL)
402 GNUNET_DATACACHE_get (datacache, message_context->key, get_type,
403 &datacache_get_iterator, &datacache_get_context);
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405 "`%s': Found %d results for local `%s' request\n", "DHT",
411 * Server handler for initiating local dht find peer requests
413 * @param cls closure for service
414 * @param find_msg the actual find peer message
415 * @param message_context struct containing pertinent information about the request
419 handle_dht_find_peer (void *cls,
420 const struct GNUNET_MessageHeader *find_msg,
421 struct DHT_MessageContext *message_context)
423 struct GNUNET_MessageHeader *find_peer_result;
428 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
429 "`%s': Received `%s' request from client, key %s (msg size %d, we expected %d)\n",
430 "DHT", "FIND PEER", GNUNET_h2s (message_context->key),
431 ntohs (find_msg->size),
432 sizeof (struct GNUNET_MessageHeader));
434 if (my_hello == NULL)
437 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
438 "`%s': Our HELLO is null, can't return.\n",
443 /* Simplistic find_peer functionality, always return our hello */
444 hello_size = ntohs(my_hello->size);
445 tsize = hello_size + sizeof (struct GNUNET_MessageHeader);
447 if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
452 find_peer_result = GNUNET_malloc (tsize);
453 find_peer_result->type = htons (GNUNET_MESSAGE_TYPE_DHT_FIND_PEER_RESULT);
454 find_peer_result->size = htons (tsize);
455 memcpy (&find_peer_result[1], my_hello, hello_size);
457 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
458 "`%s': Sending hello size %d to client.\n",
461 send_reply_to_client(message_context->client, find_peer_result, message_context->unique_id);
462 GNUNET_free(find_peer_result);
467 * Server handler for initiating local dht put requests
469 * @param cls closure for service
470 * @param msg the actual put message
471 * @param message_context struct containing pertinent information about the request
474 handle_dht_put (void *cls,
475 const struct GNUNET_MessageHeader *msg,
476 struct DHT_MessageContext *message_context)
478 struct GNUNET_DHT_PutMessage *put_msg;
482 GNUNET_assert (ntohs (msg->size) >=
483 sizeof (struct GNUNET_DHT_PutMessage));
484 put_msg = (struct GNUNET_DHT_PutMessage *)msg;
485 put_type = ntohs (put_msg->type);
486 data_size = ntohs (put_msg->header.size) - sizeof (struct GNUNET_DHT_PutMessage);
488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489 "`%s': Received `%s' request from client, message type %d, key %s\n",
490 "DHT", "PUT", put_type, GNUNET_h2s (message_context->key));
492 if (datacache != NULL)
493 GNUNET_DATACACHE_put (datacache, message_context->key, data_size,
494 (char *) &put_msg[1], put_type,
495 GNUNET_TIME_absolute_ntoh(put_msg->expiration));
497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
498 "`%s': %s request received locally, but have no datacache!\n",
504 * Find a client if it exists, add it otherwise.
506 * @param client the server handle to the client
508 * @return the client if found, a new client otherwise
510 static struct ClientList *
511 find_active_client (struct GNUNET_SERVER_Client *client)
513 struct ClientList *pos = client_list;
514 struct ClientList *ret;
518 if (pos->client_handle == client)
523 ret = GNUNET_malloc (sizeof (struct ClientList));
524 ret->client_handle = client;
525 ret->next = client_list;
531 * Handler for any generic DHT messages, calls the appropriate handler
532 * depending on message type, sends confirmation if responses aren't otherwise
535 * @param cls closure for the service
536 * @param client the client we received this message from
537 * @param message the actual message received
540 handle_dht_start_message (void *cls, struct GNUNET_SERVER_Client *client,
541 const struct GNUNET_MessageHeader *message)
543 const struct GNUNET_DHT_RouteMessage *dht_msg = (const struct GNUNET_DHT_RouteMessage *) message;
544 const struct GNUNET_MessageHeader *enc_msg;
545 struct DHT_MessageContext *message_context;
549 enc_msg = (const struct GNUNET_MessageHeader *) &dht_msg[1];
550 enc_type = ntohs (enc_msg->type);
554 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
555 "`%s': Received `%s' request from client, message type %d, key %s, uid %llu\n",
556 "DHT", "GENERIC", enc_type, GNUNET_h2s (&dht_msg->key),
557 GNUNET_ntohll (dht_msg->unique_id));
560 message_context = GNUNET_malloc (sizeof (struct DHT_MessageContext));
561 message_context->client = find_active_client (client);
562 message_context->key = &dht_msg->key;
563 message_context->unique_id = GNUNET_ntohll (dht_msg->unique_id);
564 message_context->replication = ntohl (dht_msg->desired_replication_level);
565 message_context->msg_options = ntohl (dht_msg->options);
567 /* TODO: Steps to be added by students */
568 /* FIXME: Implement *remote* DHT operations here (forward request) */
569 /* Implement generic route function and call here. */
570 /* FIXME: *IF* handling should be local, then do this: */
571 /* 1. find if this peer is closest based on whatever metric the DHT uses
572 * 2. if this peer is closest _OR_ the message options indicate it should
573 * be processed everywhere _AND_ we want it processed everywhere, then
576 handle_locally = GNUNET_YES;
577 if (handle_locally == GNUNET_YES)
581 case GNUNET_MESSAGE_TYPE_DHT_GET:
582 handle_dht_get (cls, enc_msg,
585 case GNUNET_MESSAGE_TYPE_DHT_PUT:
586 handle_dht_put (cls, enc_msg,
589 case GNUNET_MESSAGE_TYPE_DHT_FIND_PEER:
590 handle_dht_find_peer (cls,
595 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
596 "`%s': Message type (%d) not handled\n", "DHT", enc_type);
599 GNUNET_free (message_context);
600 GNUNET_SERVER_receive_done (client, GNUNET_OK);
605 * Handler for any generic DHT stop messages, calls the appropriate handler
606 * depending on message type (if processed locally)
608 * @param cls closure for the service
609 * @param client the client we received this message from
610 * @param message the actual message received
612 * TODO: once messages are remembered by unique id, add code to
616 handle_dht_stop_message (void *cls, struct GNUNET_SERVER_Client *client,
617 const struct GNUNET_MessageHeader *message)
619 const struct GNUNET_DHT_StopMessage *dht_stop_msg =
620 (const struct GNUNET_DHT_StopMessage *) message;
623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624 "`%s': Received `%s' request from client, uid %llu\n", "DHT",
625 "GENERIC STOP", GNUNET_ntohll (dht_stop_msg->unique_id));
628 uid = GNUNET_ntohll(dht_stop_msg->unique_id);
629 /* TODO: actually stop... free associated resources for the request
630 * lookup request by uid and remove state. */
632 GNUNET_SERVER_receive_done (client, GNUNET_OK);
637 * Core handler for p2p route requests.
640 handle_dht_p2p_route_request (void *cls,
641 const struct GNUNET_PeerIdentity *peer,
642 const struct GNUNET_MessageHeader *message,
643 struct GNUNET_TIME_Relative latency, uint32_t distance)
646 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
647 "`%s': Received `%s' request from another peer\n", "DHT",
650 // FIXME: setup tracking for sending replies to peer (with timeout)
651 // FIXME: call code from handle_dht_start_message (refactor...)
657 * Core handler for p2p route results.
660 handle_dht_p2p_route_result (void *cls,
661 const struct GNUNET_PeerIdentity *peer,
662 const struct GNUNET_MessageHeader *message,
663 struct GNUNET_TIME_Relative latency, uint32_t distance)
666 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
667 "`%s': Received `%s' request from another peer\n", "DHT",
670 // FIXME: setup tracking for sending replies to peer
671 // FIXME: possibly call code from handle_dht_stop_message? (unique result?) (refactor...)
677 * Receive the HELLO from transport service,
678 * free current and replace if necessary.
681 * @param message HELLO message of peer
684 process_hello (void *cls, const struct GNUNET_MessageHeader *message)
687 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
688 "Received our `%s' from transport service\n",
692 GNUNET_assert (message != NULL);
693 GNUNET_free_non_null(my_hello);
694 my_hello = GNUNET_malloc(ntohs(message->size));
695 memcpy(my_hello, message, ntohs(message->size));
699 * Task run during shutdown.
705 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
707 if (transport_handle != NULL)
709 GNUNET_free_non_null(my_hello);
710 GNUNET_TRANSPORT_get_hello_cancel(transport_handle, &process_hello, NULL);
711 GNUNET_TRANSPORT_disconnect(transport_handle);
714 GNUNET_CORE_disconnect (coreAPI);
715 if (datacache != NULL)
716 GNUNET_DATACACHE_destroy (datacache);
717 if (my_short_id != NULL)
718 GNUNET_free(my_short_id);
723 * To be called on core init/fail.
725 * @param cls service closure
726 * @param server handle to the server for this service
727 * @param identity the public identity of this peer
728 * @param publicKey the public key of this peer
731 core_init (void *cls,
732 struct GNUNET_CORE_Handle *server,
733 const struct GNUNET_PeerIdentity *identity,
734 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey)
740 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
741 "%s: Connection to core FAILED!\n", "dht",
742 GNUNET_i2s (identity));
744 GNUNET_SCHEDULER_cancel (sched, cleanup_task);
745 GNUNET_SCHEDULER_add_now (sched, &shutdown_task, NULL);
749 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
750 "%s: Core connection initialized, I am peer: %s\n", "dht",
751 GNUNET_i2s (identity));
753 /* Copy our identity so we can use it */
754 memcpy (&my_identity, identity, sizeof (struct GNUNET_PeerIdentity));
755 my_short_id = GNUNET_strdup(GNUNET_i2s(&my_identity));
756 /* Set the server to local variable */
761 static struct GNUNET_SERVER_MessageHandler plugin_handlers[] = {
762 {&handle_dht_start_message, NULL, GNUNET_MESSAGE_TYPE_DHT_ROUTE, 0},
763 {&handle_dht_stop_message, NULL, GNUNET_MESSAGE_TYPE_DHT_STOP, 0},
768 static struct GNUNET_CORE_MessageHandler core_handlers[] = {
769 {&handle_dht_p2p_route_request, GNUNET_MESSAGE_TYPE_DHT_ROUTE, 0},
770 {&handle_dht_p2p_route_result, GNUNET_MESSAGE_TYPE_DHT_ROUTE_RESULT, 0},
775 * Method called whenever a peer connects.
778 * @param peer peer identity this notification is about
779 * @param latency reported latency of the connection with peer
780 * @param distance reported distance (DV) to peer
782 void handle_core_connect (void *cls,
783 const struct GNUNET_PeerIdentity * peer,
784 struct GNUNET_TIME_Relative latency,
789 size_t data_size = 42;
790 data = GNUNET_malloc (data_size);
791 memset (data, 43, data_size);
794 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795 "%s:%s Receives core connect message for peer %s distance %d!\n", my_short_id, "dht", GNUNET_i2s(peer), distance);
797 if (datacache != NULL)
799 ret = GNUNET_DATACACHE_put (datacache, &peer->hashPubKey, data_size,
801 GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
802 GNUNET_TIME_UNIT_MINUTES));
804 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
805 "%s Inserting data %s, type %d into datacache, return value was %d\n", my_short_id, GNUNET_h2s(&peer->hashPubKey), 130, ret);
813 * Process dht requests.
816 * @param scheduler scheduler to use
817 * @param server the initialized server
818 * @param c configuration to use
822 struct GNUNET_SCHEDULER_Handle *scheduler,
823 struct GNUNET_SERVER_Handle *server,
824 const struct GNUNET_CONFIGURATION_Handle *c)
828 datacache = GNUNET_DATACACHE_create (sched, cfg, "dhtcache");
829 GNUNET_SERVER_add_handlers (server, plugin_handlers);
830 coreAPI = GNUNET_CORE_connect (sched, /* Main scheduler */
831 cfg, /* Main configuration */
832 GNUNET_TIME_UNIT_FOREVER_REL,
833 NULL, /* FIXME: anything we want to pass around? */
834 &core_init, /* Call core_init once connected */
835 &handle_core_connect, /* Don't care about connects */
836 NULL, /* Don't care about disconnects */
837 NULL, /* Don't want notified about all incoming messages */
838 GNUNET_NO, /* For header only inbound notification */
839 NULL, /* Don't want notified about all outbound messages */
840 GNUNET_NO, /* For header only outbound notification */
841 core_handlers); /* Register these handlers */
844 transport_handle = GNUNET_TRANSPORT_connect(sched, cfg, NULL, NULL, NULL, NULL);
845 if (transport_handle != NULL)
846 GNUNET_TRANSPORT_get_hello (transport_handle, &process_hello, NULL);
848 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to connect to transport service!\n");
849 /* Scheduled the task to clean up when shutdown is called */
850 cleanup_task = GNUNET_SCHEDULER_add_delayed (sched,
851 GNUNET_TIME_UNIT_FOREVER_REL,
852 &shutdown_task, NULL);
856 * The main function for the dht service.
858 * @param argc number of arguments from the command line
859 * @param argv command line arguments
860 * @return 0 ok, 1 on error
863 main (int argc, char *const *argv)
866 GNUNET_SERVICE_run (argc,
869 GNUNET_SERVICE_OPTION_NONE,
870 &run, NULL)) ? 0 : 1;