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 * TODO: Do we really need a client API?
25 * @brief library to access the GNS service
26 * @author Martin Schanzenbach
30 #include "gnunet_util_lib.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_arm_service.h"
33 #include "gnunet_hello_lib.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_dht_service.h"
37 #include "gnunet_gns_service.h"
39 #define DEBUG_GNS_API GNUNET_EXTRA_LOGGING
41 #define LOG(kind,...) GNUNET_log_from (kind, "gns-api",__VA_ARGS__)
43 /* TODO into gnunet_protocols */
44 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP 23
45 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT 24
48 * Entry in our list of messages to be (re-)transmitted.
53 * This is a doubly-linked list.
55 struct PendingMessage *prev;
58 * This is a doubly-linked list.
60 struct PendingMessage *next;
63 * Message that is pending, allocated at the end
66 const struct GNUNET_MessageHeader *msg;
69 * Handle to the GNS API context.
71 struct GNUNET_GNS_Handle *handle;
74 * Continuation to call when the request has been
75 * transmitted (for the first time) to the service; can be NULL.
77 GNUNET_SCHEDULER_Task cont;
85 * Timeout task for this message
87 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
90 * Unique ID for this request
95 * Free the saved message once sent, set to GNUNET_YES for messages
96 * that do not receive responses; GNUNET_NO if this pending message
97 * is aliased from a 'struct GNUNET_DHT_RouteHandle' and will be freed
103 * GNUNET_YES if this message is in our pending queue right now.
105 int in_pending_queue;
110 * Handle to a Lookup request
112 struct GNUNET_GNS_LookupHandle
116 * Iterator to call on data receipt
118 GNUNET_GNS_LookupIterator iter;
121 * Closure for the iterator callback
126 * Main handle to this GNS api
128 struct GNUNET_GNS_Handle *gns_handle;
131 * Key that this get request is for
136 * Unique identifier for this request (for key collisions).
140 struct PendingMessage *message;
147 struct GNUNET_GNS_Record
149 enum GNUNET_GNS_RecordType type;
150 /* Followed by data array. Format determined by type */
155 * Connection to the GNS service.
157 struct GNUNET_GNS_Handle
161 * Configuration to use.
163 const struct GNUNET_CONFIGURATION_Handle *cfg;
166 * Socket (if available).
168 struct GNUNET_CLIENT_Connection *client;
171 * Currently pending transmission request (or NULL).
173 struct GNUNET_CLIENT_TransmitHandle *th;
176 * Head of linked list of messages we would like to transmit.
178 struct PendingMessage *pending_head;
181 * Tail of linked list of messages we would like to transmit.
183 struct PendingMessage *pending_tail;
186 * Hash map containing the current outstanding unique requests.
188 struct GNUNET_CONTAINER_MultiHashMap *active_requests;
190 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
193 * How quickly should we retry? Used for exponential back-off on
196 struct GNUNET_TIME_Relative retry_time;
199 * Generator for unique ids.
204 * Did we start our receive loop yet?
210 * Try to send messages from list of messages to send
211 * @param handle GNS_Handle
214 process_pending_messages (struct GNUNET_GNS_Handle *handle);
217 * Try to (re)connect to the GNS service.
219 * @return GNUNET_YES on success, GNUNET_NO on failure.
222 try_connect (struct GNUNET_GNS_Handle *handle)
224 if (handle->client != NULL)
226 handle->in_receive = GNUNET_NO;
227 handle->client = GNUNET_CLIENT_connect ("gns", handle->cfg);
228 if (handle->client == NULL)
230 LOG (GNUNET_ERROR_TYPE_WARNING,
231 _("Failed to connect to the GNS service!\n"));
238 * Add the request corresponding to the given handle
239 * to the pending queue (if it is not already in there).
241 * @param cls the 'struct GNUNET_GNS_Handle*'
242 * @param key key for the request (not used)
243 * @param value the 'struct GNUNET_GNS_LookupHandle*'
244 * @return GNUNET_YES (always)
247 add_request_to_pending (void *cls, const GNUNET_HashCode * key, void *value)
249 struct GNUNET_GNS_Handle *handle = cls;
250 struct GNUNET_GNS_LookupHandle *rh = value;
252 if (GNUNET_NO == rh->message->in_pending_queue)
255 LOG (GNUNET_ERROR_TYPE_DEBUG,
256 "Retransmitting request related to %s to GNS %p\n", GNUNET_h2s(key),
259 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
261 rh->message->in_pending_queue = GNUNET_YES;
267 * Try reconnecting to the GNS service.
269 * @param cls GNUNET_GNS_Handle
270 * @param tc scheduler context
273 try_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
275 struct GNUNET_GNS_Handle *handle = cls;
278 LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting with GNS %p\n", handle);
280 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
281 if (handle->retry_time.rel_value < GNUNET_CONSTANTS_SERVICE_RETRY.rel_value)
282 handle->retry_time = GNUNET_CONSTANTS_SERVICE_RETRY;
284 handle->retry_time = GNUNET_TIME_relative_multiply (handle->retry_time, 2);
285 if (handle->retry_time.rel_value > GNUNET_CONSTANTS_SERVICE_TIMEOUT.rel_value)
286 handle->retry_time = GNUNET_CONSTANTS_SERVICE_TIMEOUT;
287 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
288 if (GNUNET_YES != try_connect (handle))
291 LOG (GNUNET_ERROR_TYPE_DEBUG, "GNS reconnect failed(!)\n");
295 GNUNET_CONTAINER_multihashmap_iterate (handle->active_requests,
296 &add_request_to_pending, handle);
297 process_pending_messages (handle);
302 * Try reconnecting to the GNS service.
304 * @param handle handle to gns to (possibly) disconnect and reconnect
307 do_disconnect (struct GNUNET_GNS_Handle *handle)
309 if (handle->client == NULL)
311 GNUNET_assert (handle->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
312 if (NULL != handle->th)
313 GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
316 "Disconnecting from GNS service, will try to reconnect in %llu ms\n",
317 (unsigned long long) handle->retry_time.rel_value);
318 GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
319 handle->client = NULL;
320 handle->reconnect_task =
321 GNUNET_SCHEDULER_add_delayed (handle->retry_time, &try_reconnect, handle);
325 * Transmit the next pending message, called by notify_transmit_ready
328 transmit_pending (void *cls, size_t size, void *buf);
331 * Handler for messages received from the GNS service
333 * @param cls the 'struct GNUNET_GNS_Handle'
334 * @param msg the incoming message
337 message_handler (void *cls, const struct GNUNET_MessageHeader *msg);
340 * Try to send messages from list of messages to send
343 process_pending_messages (struct GNUNET_GNS_Handle *handle)
345 struct PendingMessage *head;
347 if (handle->client == NULL)
349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
350 "process_pending_messages called, but client is null, reconnecting\n");
351 do_disconnect (handle);
354 if (handle->th != NULL)
356 if (NULL == (head = handle->pending_head))
359 GNUNET_CLIENT_notify_transmit_ready (handle->client,
360 ntohs (head->msg->size),
361 GNUNET_TIME_UNIT_FOREVER_REL,
362 GNUNET_YES, &transmit_pending,
364 if (NULL != handle->th)
366 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
367 "notify_transmit_ready returned NULL, reconnecting\n");
368 do_disconnect (handle);
373 * Transmit the next pending message, called by notify_transmit_ready
376 transmit_pending (void *cls, size_t size, void *buf)
378 struct GNUNET_GNS_Handle *handle = cls;
379 struct PendingMessage *head;
385 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386 "Transmission to GNS service failed! Reconnecting!\n");
387 do_disconnect (handle);
390 if (NULL == (head = handle->pending_head))
393 tsize = ntohs (head->msg->size);
396 process_pending_messages (handle);
399 memcpy (buf, head->msg, tsize);
400 GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
402 head->in_pending_queue = GNUNET_NO;
403 if (head->timeout_task != GNUNET_SCHEDULER_NO_TASK)
405 GNUNET_SCHEDULER_cancel (head->timeout_task);
406 head->timeout_task = GNUNET_SCHEDULER_NO_TASK;
408 if (GNUNET_YES == head->free_on_send)
410 process_pending_messages (handle);
412 LOG (GNUNET_ERROR_TYPE_DEBUG,
413 "Forwarded request of %u bytes to GNS service\n", (unsigned int) tsize);
415 if (GNUNET_NO == handle->in_receive)
418 LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting to process replies from GNS\n");
420 handle->in_receive = GNUNET_YES;
421 GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
422 GNUNET_TIME_UNIT_FOREVER_REL);
428 * Process a given reply that might match the given
431 * @param cls the 'struct GNUNET_GNS_ClientResultMessage'
432 * @param key query of the request
433 * @param value the 'struct GNUNET_GNS_LookupHandle' of a request matching the same key
434 * @return GNUNET_YES to continue to iterate over all results,
435 * GNUNET_NO if the reply is malformed
438 process_reply (void *cls, const GNUNET_HashCode * key, void *value)
440 const struct GNUNET_GNS_ClientResultMessage *gns_msg = cls;
441 struct GNUNET_GNS_LookupHandle *lookup_handle = value;
442 const char *name = (const char*) &lookup_handle[1];
443 const struct GNUNET_GNS_Record *records;
444 uint32_t num_records;
448 if (gns_msg->unique_id != lookup_handle->unique_id)
452 LOG (GNUNET_ERROR_TYPE_DEBUG,
453 "Ignoring reply for %s: UID mismatch: %llu/%llu\n", GNUNET_h2s (key),
454 gns_msg->unique_id, lookup_handle->unique_id);
458 msize = ntohs (gns_msg->header.size);
459 num_records = ntohl (gns_msg->num_records);
461 sizeof (struct GNUNET_GNS_ClientResultMessage) +
462 sizeof (struct GNUNET_GNS_Record) * (num_records);
463 if ((msize < meta_length) ||
465 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_GNS_Record)))
471 LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving %u byte reply for %s to application\n",
472 (unsigned int) (msize - meta_length), GNUNET_h2s (key));
474 records = (const struct GNUNET_GNS_Record *) &gns_msg[1];
475 lookup_handle->iter (lookup_handle->iter_cls, name, records, num_records);
481 * Handler for messages received from the GNS service
483 * @param cls the 'struct GNUNET_GNS_Handle'
484 * @param msg the incoming message
487 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
489 struct GNUNET_GNS_Handle *handle = cls;
490 const struct GNUNET_GNS_ClientResultMessage *gns_msg;
495 LOG (GNUNET_ERROR_TYPE_DEBUG,
496 "Error receiving data from GNS service, reconnecting\n");
498 do_disconnect (handle);
501 if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT)
504 do_disconnect (handle);
507 if (ntohs (msg->size) < sizeof (struct GNUNET_GNS_ClientResultMessage))
510 do_disconnect (handle);
513 gns_msg = (const struct GNUNET_GNS_ClientResultMessage *) msg;
515 LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply for `%s' from GNS service %p\n",
516 &gns_msg->name, handle);
518 /* TODO uniquely identify requests... maybe hash(name) or uid */
519 GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests,
520 &gns_msg->key, &process_reply,
522 GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
523 GNUNET_TIME_UNIT_FOREVER_REL);
528 * Initialize the connection with the GNS service.
530 * @param cfg configuration to use
531 * @param ht_len size of the internal hash table to use for parallel requests
532 * @return handle to the GNS service, or NULL on error
534 struct GNUNET_GNS_Handle *
535 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
538 struct GNUNET_GNS_Handle *handle;
540 handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
543 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
544 handle->active_requests = GNUNET_CONTAINER_multihashmap_create (ht_len);
545 if (GNUNET_NO == try_connect (handle))
547 GNUNET_GNS_disconnect (handle);
555 * Shutdown connection with the GNS service.
557 * @param handle handle of the GNS connection to stop
560 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
567 * Perform an asynchronous Lookup operation on the GNS.
569 * - Still not sure what we query for... "names" it is for now
570 * - Do we need such sophisticated message queueing like dht? simplify?
572 * @param handle handle to the GNS service
573 * @param timeout how long to wait for transmission of this request to the service
574 * @param name the name to look up
575 * @param iter function to call on each result
576 * @param iter_cls closure for iter
577 * @return handle to stop the async get
579 struct GNUNET_GNS_LookupHandle *
580 GNUNET_GNS_lookup_start (struct GNUNET_GNS_Handle *handle,
581 struct GNUNET_TIME_Relative timeout,
583 enum GNUNET_GNS_RecordType type,
584 GNUNET_GNS_LookupIterator iter,
587 /* IPC to look for local entries, start dht lookup, return lookup_handle */
588 struct GNUNET_GNS_ClientLookupMessage *lookup_msg;
589 struct GNUNET_GNS_LookupHandle *lookup_handle;
592 struct PendingMessage *pending;
599 GNUNET_CRYPTO_hash (name, strlen(name), &key);
601 msize = sizeof (struct GNUNET_GNS_ClientLookupMessage) + strlen(name);
603 LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lookup for %s in GNS %p\n",
606 pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
607 lookup_msg = (struct GNUNET_GNS_ClientLookupMessage *) &pending[1];
608 pending->msg = &lookup_msg->header;
609 pending->handle = handle;
610 pending->free_on_send = GNUNET_NO;
611 lookup_msg->header.size = htons (msize);
612 lookup_msg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP);
613 lookup_msg->key = key;
614 memcpy(&lookup_msg[1], name, strlen(name));
616 lookup_msg->unique_id = handle->uid_gen;
617 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
619 pending->in_pending_queue = GNUNET_YES;
620 lookup_handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_LookupHandle));
621 lookup_handle->iter = iter;
622 lookup_handle->iter_cls = iter_cls;
623 lookup_handle->message = pending;
624 lookup_handle->unique_id = lookup_msg->unique_id;
625 GNUNET_CONTAINER_multihashmap_put (handle->active_requests, &lookup_msg->key,
627 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
628 process_pending_messages (handle);
629 return lookup_handle;
634 * Stop async GNS lookup.
636 * @param lookup_handle handle to the GNS lookup operation to stop
639 GNUNET_GNS_lookup_stop (struct GNUNET_GNS_LookupHandle *lookup_handle)
641 /* TODO Stop dht lookups */
645 /* end of gns_api.c */