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.
24 * @brief library to access the GNS service
25 * @author Martin Schanzenbach
29 #include "gnunet_util_lib.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_arm_service.h"
32 #include "gnunet_hello_lib.h"
33 #include "gnunet_protocols.h"
34 #include "gnunet_dht_service.h"
36 #include "gnunet_gns_service.h"
38 #define DEBUG_GNS_API GNUNET_EXTRA_LOGGING
40 #define LOG(kind,...) GNUNET_log_from (kind, "gns-api",__VA_ARGS__)
42 /* TODO into gnunet_protocols */
43 #define GNUNET_MESSAGE_TYPE_GNS_LOOKUP 23
44 #define GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT 24
45 #define GNUNET_MESSAGE_TYPE_GNS_SHORTEN 25
46 #define GNUNET_MESSAGE_TYPE_GNS_SHORTEN_RESULT 26
49 * Entry in our list of messages to be (re-)transmitted.
54 * This is a doubly-linked list.
56 struct PendingMessage *prev;
59 * This is a doubly-linked list.
61 struct PendingMessage *next;
64 * Message that is pending, allocated at the end
67 const struct GNUNET_MessageHeader *msg;
70 * Handle to the GNS API context.
72 struct GNUNET_GNS_Handle *handle;
75 * Continuation to call when the request has been
76 * transmitted (for the first time) to the service; can be NULL.
78 GNUNET_SCHEDULER_Task cont;
86 * Unique ID for this request
91 * Free the saved message once sent, set to GNUNET_YES for messages
92 * that do not receive responses; GNUNET_NO if this pending message
93 * is aliased from a 'struct GNUNET_DHT_RouteHandle' and will be freed
99 * GNUNET_YES if this message is in our pending queue right now.
101 int in_pending_queue;
106 * Handle to a Lookup request
108 struct GNUNET_GNS_LookupHandle
112 * Iterator to call on data receipt
114 GNUNET_GNS_LookupIterator iter;
117 * Closure for the iterator callback
122 * Main handle to this GNS api
124 struct GNUNET_GNS_Handle *gns_handle;
127 * Key that this get request is for
132 * Unique identifier for this request (for key collisions).
136 struct PendingMessage *message;
142 * Handle to a shorten request
144 struct GNUNET_GNS_ShortenHandle
148 * Processor to call on data receipt
150 GNUNET_GNS_ShortenResultProcessor proc;
153 * Closure for the processor
158 * Main handle to this GNS api
160 struct GNUNET_GNS_Handle *gns_handle;
163 * Key that this get request is for
168 * Unique identifier for this request (for key collisions).
172 struct PendingMessage *message;
178 * Connection to the GNS service.
180 struct GNUNET_GNS_Handle
184 * Configuration to use.
186 const struct GNUNET_CONFIGURATION_Handle *cfg;
189 * Socket (if available).
191 struct GNUNET_CLIENT_Connection *client;
194 * Currently pending transmission request (or NULL).
196 struct GNUNET_CLIENT_TransmitHandle *th;
199 * Head of linked list of messages we would like to transmit.
201 struct PendingMessage *pending_head;
204 * Tail of linked list of messages we would like to transmit.
206 struct PendingMessage *pending_tail;
209 * Hash maps containing the current outstanding unique requests.
211 struct GNUNET_CONTAINER_MultiHashMap *active_lookup_requests;
212 struct GNUNET_CONTAINER_MultiHashMap *active_shorten_requests;
214 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
217 * How quickly should we retry? Used for exponential back-off on
220 struct GNUNET_TIME_Relative retry_time;
223 * Generator for unique ids.
228 * Did we start our receive loop yet?
234 * Try to send messages from list of messages to send
235 * @param handle GNS_Handle
238 process_pending_messages (struct GNUNET_GNS_Handle *handle);
241 * Try to (re)connect to the GNS service.
243 * @return GNUNET_YES on success, GNUNET_NO on failure.
246 try_connect (struct GNUNET_GNS_Handle *handle)
248 if (handle->client != NULL)
250 handle->in_receive = GNUNET_NO;
251 handle->client = GNUNET_CLIENT_connect ("gns", handle->cfg);
252 if (handle->client == NULL)
254 LOG (GNUNET_ERROR_TYPE_WARNING,
255 _("Failed to connect to the GNS service!\n"));
262 * Add the request corresponding to the given handle
263 * to the pending queue (if it is not already in there).
265 * @param cls the 'struct GNUNET_GNS_Handle*'
266 * @param key key for the request (not used)
267 * @param value the 'struct GNUNET_GNS_LookupHandle*'
268 * @return GNUNET_YES (always)
271 add_lookup_request_to_pending (void *cls, const GNUNET_HashCode * key,
274 struct GNUNET_GNS_Handle *handle = cls;
275 struct GNUNET_GNS_LookupHandle *lh = value;
277 if (GNUNET_NO == lh->message->in_pending_queue)
279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
280 "Retransmitting request related to %s to GNS %p\n", GNUNET_h2s(key),
282 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
284 lh->message->in_pending_queue = GNUNET_YES;
291 * Add the request corresponding to the given handle
292 * to the pending queue (if it is not already in there).
294 * @param cls the 'struct GNUNET_GNS_Handle*'
295 * @param key key for the request (not used)
296 * @param value the 'struct GNUNET_GNS_ShortenHandle*'
297 * @return GNUNET_YES (always)
300 add_shorten_request_to_pending (void *cls, const GNUNET_HashCode * key,
303 struct GNUNET_GNS_Handle *handle = cls;
304 struct GNUNET_GNS_ShortenHandle *sh = value;
306 if (GNUNET_NO == sh->message->in_pending_queue)
308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
309 "Retransmitting shorten request related to %s to GNS %p\n",
312 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
314 sh->message->in_pending_queue = GNUNET_YES;
321 * Try reconnecting to the GNS service.
323 * @param cls GNUNET_GNS_Handle
324 * @param tc scheduler context
327 try_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
329 struct GNUNET_GNS_Handle *handle = cls;
331 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting with GNS %p\n", handle);
332 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
333 if (handle->retry_time.rel_value < GNUNET_CONSTANTS_SERVICE_RETRY.rel_value)
334 handle->retry_time = GNUNET_CONSTANTS_SERVICE_RETRY;
336 handle->retry_time = GNUNET_TIME_relative_multiply (handle->retry_time, 2);
337 if (handle->retry_time.rel_value > GNUNET_CONSTANTS_SERVICE_TIMEOUT.rel_value)
338 handle->retry_time = GNUNET_CONSTANTS_SERVICE_TIMEOUT;
339 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
340 if (GNUNET_YES != try_connect (handle))
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GNS reconnect failed(!)\n");
345 GNUNET_CONTAINER_multihashmap_iterate (handle->active_lookup_requests,
346 &add_lookup_request_to_pending, handle);
347 GNUNET_CONTAINER_multihashmap_iterate (handle->active_shorten_requests,
348 &add_shorten_request_to_pending, handle);
349 process_pending_messages (handle);
354 * Try reconnecting to the GNS service.
356 * @param handle handle to gns to (possibly) disconnect and reconnect
359 do_disconnect (struct GNUNET_GNS_Handle *handle)
361 if (handle->client == NULL)
363 GNUNET_assert (handle->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
364 if (NULL != handle->th)
365 GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
367 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368 "Disconnecting from GNS service, will try to reconnect in %llu ms\n",
369 (unsigned long long) handle->retry_time.rel_value);
370 GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
371 handle->client = NULL;
372 handle->reconnect_task =
373 GNUNET_SCHEDULER_add_delayed (handle->retry_time, &try_reconnect, handle);
377 * Transmit the next pending message, called by notify_transmit_ready
380 transmit_pending (void *cls, size_t size, void *buf);
383 * Handler for messages received from the GNS service
385 * @param cls the 'struct GNUNET_GNS_Handle'
386 * @param msg the incoming message
389 message_handler (void *cls, const struct GNUNET_MessageHeader *msg);
392 * Try to send messages from list of messages to send
395 process_pending_messages (struct GNUNET_GNS_Handle *handle)
397 struct PendingMessage *head;
399 if (handle->client == NULL)
401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402 "process_pending_messages called, but client is null, reconnecting\n");
403 do_disconnect (handle);
407 if (handle->th != NULL)
410 if (NULL == (head = handle->pending_head))
414 GNUNET_CLIENT_notify_transmit_ready (handle->client,
415 ntohs (head->msg->size),
416 GNUNET_TIME_UNIT_FOREVER_REL,
417 GNUNET_YES, &transmit_pending,
419 if (NULL != handle->th)
421 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
422 "notify_transmit_ready returned NULL, reconnecting\n");
423 do_disconnect (handle);
428 * Transmit the next pending message, called by notify_transmit_ready
431 transmit_pending (void *cls, size_t size, void *buf)
433 struct GNUNET_GNS_Handle *handle = cls;
434 struct PendingMessage *head;
441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442 "Transmission to GNS service failed! Reconnecting!\n");
443 do_disconnect (handle);
447 if (NULL == (head = handle->pending_head))
450 tsize = ntohs (head->msg->size);
454 process_pending_messages (handle);
458 memcpy (buf, head->msg, tsize);
460 GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
463 head->in_pending_queue = GNUNET_NO;
465 if (GNUNET_YES == head->free_on_send)
468 process_pending_messages (handle);
470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471 "Forwarded request of %u bytes to GNS service\n", (unsigned int) tsize);
473 if (GNUNET_NO == handle->in_receive)
475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476 "Starting to process replies from GNS\n");
477 handle->in_receive = GNUNET_YES;
478 GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
479 GNUNET_TIME_UNIT_FOREVER_REL);
485 * Process a given reply that might match the given
488 * @param cls the 'struct GNUNET_GNS_ClientResultMessage'
489 * @param key query of the request
490 * @param value the 'struct GNUNET_GNS_LookupHandle' of a request matching the same key
491 * @return GNUNET_YES to continue to iterate over all results,
492 * GNUNET_NO if the reply is malformed
495 process_shorten_reply (void *cls, const GNUNET_HashCode * key, void *value)
497 const struct GNUNET_GNS_ClientShortenResultMessage *gns_msg = cls;
498 struct GNUNET_GNS_ShortenHandle *shorten_handle = value;
499 const char *name = (const char*)&((struct GNUNET_GNS_ClientShortenMessage *) &((shorten_handle->message)[1]))[1]; //FIXME
500 const char *short_name;
502 if (ntohs (((struct GNUNET_MessageHeader*)gns_msg)->size) <
503 sizeof (struct GNUNET_GNS_ClientShortenResultMessage))
506 do_disconnect (shorten_handle->gns_handle);
510 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
511 "Received reply for `%s' from GNS service\n",
514 if (gns_msg->unique_id != shorten_handle->unique_id)
517 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518 "Ignoring reply for %s: UID mismatch: %llu/%llu\n", GNUNET_h2s (key),
519 gns_msg->unique_id, shorten_handle->unique_id);
522 short_name = (char*)&gns_msg[1];
524 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
525 "Giving shorten reply %s for %s to application\n",
528 shorten_handle->proc (shorten_handle->proc_cls, name, short_name);
530 GNUNET_CLIENT_receive (shorten_handle->gns_handle->client,
531 &message_handler, shorten_handle->gns_handle,
532 GNUNET_TIME_UNIT_FOREVER_REL);
538 * Process a given reply to the lookup request
540 * @param cls the 'struct GNUNET_GNS_ClientResultMessage'
541 * @param key query of the request
542 * @param value the 'struct GNUNET_GNS_LookupHandle' of a request matching the same key
543 * @return GNUNET_YES to continue to iterate over all results,
544 * GNUNET_NO if the reply is malformed
547 process_lookup_reply (void *cls, const GNUNET_HashCode * key, void *value)
549 const struct GNUNET_GNS_ClientLookupResultMessage *gns_msg = cls;
550 struct GNUNET_GNS_LookupHandle *lookup_handle = value;
551 const char *name = (const char*) &lookup_handle[1];
552 struct GNUNET_NAMESTORE_RecordData *rd;
555 if (ntohs (((struct GNUNET_MessageHeader*)gns_msg)->size) <
556 sizeof (struct GNUNET_GNS_ClientLookupResultMessage))
559 do_disconnect (lookup_handle->gns_handle);
563 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
564 "Received lookup reply for `%s' from GNS service %p\n",
565 name, lookup_handle->gns_handle);
567 if (gns_msg->unique_id != lookup_handle->unique_id)
570 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
571 "Ignoring reply for %s: UID mismatch: %llu/%llu\n", GNUNET_h2s (key),
572 gns_msg->unique_id, lookup_handle->unique_id);
576 rd_count = ntohl(gns_msg->rd_count);
577 rd = (struct GNUNET_NAMESTORE_RecordData*)&gns_msg[1];
578 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
579 "Giving lookup reply for %s to application\n",
582 lookup_handle->iter (lookup_handle->iter_cls, name, rd_count, rd);
584 GNUNET_CLIENT_receive (lookup_handle->gns_handle->client, &message_handler,
585 lookup_handle->gns_handle,
586 GNUNET_TIME_UNIT_FOREVER_REL);
591 * Handler for messages received from the GNS service
593 * @param cls the 'struct GNUNET_GNS_Handle'
594 * @param msg the incoming message
597 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
599 struct GNUNET_GNS_Handle *handle = cls;
600 const struct GNUNET_GNS_ClientLookupResultMessage *lookup_msg;
601 const struct GNUNET_GNS_ClientShortenResultMessage *shorten_msg;
603 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
607 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
608 "Error receiving data from GNS service, reconnecting\n");
609 do_disconnect (handle);
613 if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT)
615 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
617 lookup_msg = (const struct GNUNET_GNS_ClientLookupResultMessage *) msg;
618 GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_lookup_requests,
620 &process_lookup_reply,
621 (void *) lookup_msg);
623 else if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_GNS_SHORTEN_RESULT)
625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626 "Got shorten msg\n");
627 shorten_msg = (const struct GNUNET_GNS_ClientShortenResultMessage *) msg;
628 GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_shorten_requests,
630 &process_shorten_reply,
631 (void *) shorten_msg);
636 do_disconnect (handle);
644 * Initialize the connection with the GNS service.
646 * @param cfg configuration to use
647 * @param ht_len size of the internal hash table to use for parallel requests
648 * @return handle to the GNS service, or NULL on error
650 struct GNUNET_GNS_Handle *
651 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
653 struct GNUNET_GNS_Handle *handle;
655 handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
658 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
659 handle->active_lookup_requests =
660 GNUNET_CONTAINER_multihashmap_create (5);
661 handle->active_shorten_requests =
662 GNUNET_CONTAINER_multihashmap_create (5);
663 if (GNUNET_NO == try_connect (handle))
665 GNUNET_GNS_disconnect (handle);
673 * Shutdown connection with the GNS service.
675 * @param handle handle of the GNS connection to stop
678 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
685 * Perform an asynchronous Lookup operation on the GNS.
687 * @param handle handle to the GNS service
688 * @param name the name to look up
689 * @param iter function to call on each result
690 * @param iter_cls closure for iter
691 * @return handle to stop the async get
693 struct GNUNET_GNS_LookupHandle *
694 GNUNET_GNS_lookup_start (struct GNUNET_GNS_Handle *handle,
696 enum GNUNET_GNS_RecordType type,
697 GNUNET_GNS_LookupIterator iter,
700 /* IPC to look for local entries, start dht lookup, return lookup_handle */
701 struct GNUNET_GNS_ClientLookupMessage *lookup_msg;
702 struct GNUNET_GNS_LookupHandle *lookup_handle;
705 struct PendingMessage *pending;
712 GNUNET_CRYPTO_hash (name, strlen(name), &key);
714 msize = sizeof (struct GNUNET_GNS_ClientLookupMessage) + strlen(name);
715 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting lookup for %s in GNS %p\n",
717 pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
718 lookup_msg = (struct GNUNET_GNS_ClientLookupMessage *) &pending[1];
719 pending->msg = &lookup_msg->header;
720 pending->handle = handle;
721 pending->free_on_send = GNUNET_NO;
722 lookup_msg->header.size = htons (msize);
723 lookup_msg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_LOOKUP);
724 lookup_msg->key = key;
725 memcpy(&lookup_msg[1], name, strlen(name));
727 lookup_msg->unique_id = handle->uid_gen;
728 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
730 pending->in_pending_queue = GNUNET_YES;
731 lookup_handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_LookupHandle));
732 lookup_handle->iter = iter;
733 lookup_handle->iter_cls = iter_cls;
734 lookup_handle->message = pending;
735 lookup_handle->unique_id = lookup_msg->unique_id;
736 GNUNET_CONTAINER_multihashmap_put (handle->active_lookup_requests,
739 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
740 process_pending_messages (handle);
741 return lookup_handle;
746 * Stop async GNS lookup.
748 * @param lookup_handle handle to the GNS lookup operation to stop
751 GNUNET_GNS_lookup_stop (struct GNUNET_GNS_LookupHandle *lookup_handle)
753 /* TODO Stop gns lookups */
758 * Perform a name shortening operation on the GNS.
760 * @param handle handle to the GNS service
761 * @param name the name to look up
762 * @param proc function to call on result
763 * @param proc_cls closure for processor
764 * @return handle to the operation
766 struct GNUNET_GNS_ShortenHandle *
767 GNUNET_GNS_shorten (struct GNUNET_GNS_Handle *handle,
769 GNUNET_GNS_ShortenResultProcessor proc,
772 /* IPC to shorten gns names, return shorten_handle */
773 struct GNUNET_GNS_ClientShortenMessage *shorten_msg;
774 struct GNUNET_GNS_ShortenHandle *shorten_handle;
776 struct PendingMessage *pending;
783 msize = sizeof (struct GNUNET_GNS_ClientShortenMessage) + strlen(name) +1;
784 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to shorten %s in GNS\n", name);
785 pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
786 memset(pending, 0, (sizeof (struct PendingMessage) + msize));
787 shorten_msg = (struct GNUNET_GNS_ClientShortenMessage *) &pending[1];
788 pending->msg = &shorten_msg->header;
789 pending->handle = handle;
790 pending->free_on_send = GNUNET_NO;
791 shorten_msg->header.size = htons (msize);
792 shorten_msg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_SHORTEN);
793 memcpy(&shorten_msg[1], name, strlen(name));
795 shorten_msg->unique_id = handle->uid_gen;
796 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
798 pending->in_pending_queue = GNUNET_YES;
799 shorten_handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_ShortenHandle));
800 shorten_handle->proc = proc;
801 shorten_handle->proc_cls = proc_cls;
802 shorten_handle->message = pending;
803 shorten_handle->unique_id = shorten_msg->unique_id;
804 GNUNET_CONTAINER_multihashmap_put (handle->active_shorten_requests,
807 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
808 process_pending_messages (handle);
809 return shorten_handle;
814 /* end of gns_api.c */