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.
23 * @brief library to access the GNS service
24 * @author Martin Schanzenbach
28 #include "gnunet_util_lib.h"
29 #include "gnunet_constants.h"
30 #include "gnunet_arm_service.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_protocols.h"
33 #include "gnunet_dht_service.h"
35 #include "gnunet_gns_service.h"
37 #define DEBUG_GNS_API GNUNET_EXTRA_LOGGING
39 #define LOG(kind,...) GNUNET_log_from (kind, "gns-api",__VA_ARGS__)
41 /* TODO into gnunet_protocols */
42 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP 23
43 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT 24
46 * Entry in our list of messages to be (re-)transmitted.
51 * This is a doubly-linked list.
53 struct PendingMessage *prev;
56 * This is a doubly-linked list.
58 struct PendingMessage *next;
61 * Message that is pending, allocated at the end
64 const struct GNUNET_MessageHeader *msg;
67 * Handle to the GNS API context.
69 struct GNUNET_GNS_Handle *handle;
72 * Continuation to call when the request has been
73 * transmitted (for the first time) to the service; can be NULL.
75 GNUNET_SCHEDULER_Task cont;
83 * Timeout task for this message
85 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
88 * Unique ID for this request
93 * Free the saved message once sent, set to GNUNET_YES for messages
94 * that do not receive responses; GNUNET_NO if this pending message
95 * is aliased from a 'struct GNUNET_DHT_RouteHandle' and will be freed
101 * GNUNET_YES if this message is in our pending queue right now.
103 int in_pending_queue;
108 * Handle to a Lookup request
110 struct GNUNET_GNS_LookupHandle
114 * Iterator to call on data receipt
116 GNUNET_GNS_LookupIterator iter;
119 * Closure for the iterator callback
124 * Main handle to this GNS api
126 struct GNUNET_GNS_Handle *gns_handle;
129 * Key that this get request is for
134 * Unique identifier for this request (for key collisions).
138 struct PendingMessage *message;
145 struct GNUNET_GNS_Record
147 enum GNUNET_GNS_RecordType type;
151 * Connection to the GNS service.
153 struct GNUNET_GNS_Handle
157 * Configuration to use.
159 const struct GNUNET_CONFIGURATION_Handle *cfg;
162 * Socket (if available).
164 struct GNUNET_CLIENT_Connection *client;
167 * Currently pending transmission request (or NULL).
169 struct GNUNET_CLIENT_TransmitHandle *th;
172 * Head of linked list of messages we would like to transmit.
174 struct PendingMessage *pending_head;
177 * Tail of linked list of messages we would like to transmit.
179 struct PendingMessage *pending_tail;
182 * Hash map containing the current outstanding unique requests.
184 struct GNUNET_CONTAINER_MultiHashMap *active_requests;
186 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
189 * How quickly should we retry? Used for exponential back-off on
192 struct GNUNET_TIME_Relative retry_time;
195 * Generator for unique ids.
200 * Did we start our receive loop yet?
206 * Try to send messages from list of messages to send
207 * @param handle GNS_Handle
210 process_pending_messages (struct GNUNET_GNS_Handle *handle);
213 * Try to (re)connect to the GNS service.
215 * @return GNUNET_YES on success, GNUNET_NO on failure.
218 try_connect (struct GNUNET_GNS_Handle *handle)
220 if (handle->client != NULL)
222 handle->in_receive = GNUNET_NO;
223 handle->client = GNUNET_CLIENT_connect ("gns", handle->cfg);
224 if (handle->client == NULL)
226 LOG (GNUNET_ERROR_TYPE_WARNING,
227 _("Failed to connect to the GNS service!\n"));
234 * Add the request corresponding to the given handle
235 * to the pending queue (if it is not already in there).
237 * @param cls the 'struct GNUNET_GNS_Handle*'
238 * @param key key for the request (not used)
239 * @param value the 'struct GNUNET_GNS_LookupHandle*'
240 * @return GNUNET_YES (always)
243 add_request_to_pending (void *cls, const GNUNET_HashCode * key, void *value)
245 struct GNUNET_GNS_Handle *handle = cls;
246 struct GNUNET_GNS_LookupHandle *rh = value;
248 if (GNUNET_NO == rh->message->in_pending_queue)
251 LOG (GNUNET_ERROR_TYPE_DEBUG,
252 "Retransmitting request related to %s to GNS %p\n", GNUNET_h2s(key),
255 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
257 rh->message->in_pending_queue = GNUNET_YES;
263 * Try reconnecting to the GNS service.
265 * @param cls GNUNET_GNS_Handle
266 * @param tc scheduler context
269 try_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
271 struct GNUNET_GNS_Handle *handle = cls;
274 LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting with GNS %p\n", handle);
276 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
277 if (handle->retry_time.rel_value < GNUNET_CONSTANTS_SERVICE_RETRY.rel_value)
278 handle->retry_time = GNUNET_CONSTANTS_SERVICE_RETRY;
280 handle->retry_time = GNUNET_TIME_relative_multiply (handle->retry_time, 2);
281 if (handle->retry_time.rel_value > GNUNET_CONSTANTS_SERVICE_TIMEOUT.rel_value)
282 handle->retry_time = GNUNET_CONSTANTS_SERVICE_TIMEOUT;
283 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
284 if (GNUNET_YES != try_connect (handle))
287 LOG (GNUNET_ERROR_TYPE_DEBUG, "GNS reconnect failed(!)\n");
291 GNUNET_CONTAINER_multihashmap_iterate (handle->active_requests,
292 &add_request_to_pending, handle);
293 process_pending_messages (handle);
298 * Try reconnecting to the GNS service.
300 * @param handle handle to gns to (possibly) disconnect and reconnect
303 do_disconnect (struct GNUNET_GNS_Handle *handle)
305 if (handle->client == NULL)
307 GNUNET_assert (handle->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
308 if (NULL != handle->th)
309 GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
312 "Disconnecting from GNS service, will try to reconnect in %llu ms\n",
313 (unsigned long long) handle->retry_time.rel_value);
314 GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
315 handle->client = NULL;
316 handle->reconnect_task =
317 GNUNET_SCHEDULER_add_delayed (handle->retry_time, &try_reconnect, handle);
321 * Transmit the next pending message, called by notify_transmit_ready
324 transmit_pending (void *cls, size_t size, void *buf);
327 * Handler for messages received from the GNS service
329 * @param cls the 'struct GNUNET_GNS_Handle'
330 * @param msg the incoming message
333 message_handler (void *cls, const struct GNUNET_MessageHeader *msg);
336 * Try to send messages from list of messages to send
339 process_pending_messages (struct GNUNET_GNS_Handle *handle)
341 struct PendingMessage *head;
343 if (handle->client == NULL)
345 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
346 "process_pending_messages called, but client is null, reconnecting\n");
347 do_disconnect (handle);
350 if (handle->th != NULL)
352 if (NULL == (head = handle->pending_head))
355 GNUNET_CLIENT_notify_transmit_ready (handle->client,
356 ntohs (head->msg->size),
357 GNUNET_TIME_UNIT_FOREVER_REL,
358 GNUNET_YES, &transmit_pending,
360 if (NULL != handle->th)
362 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
363 "notify_transmit_ready returned NULL, reconnecting\n");
364 do_disconnect (handle);
369 * Transmit the next pending message, called by notify_transmit_ready
372 transmit_pending (void *cls, size_t size, void *buf)
374 struct GNUNET_GNS_Handle *handle = cls;
375 struct PendingMessage *head;
381 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
382 "Transmission to GNS service failed! Reconnecting!\n");
383 do_disconnect (handle);
386 if (NULL == (head = handle->pending_head))
389 tsize = ntohs (head->msg->size);
392 process_pending_messages (handle);
395 memcpy (buf, head->msg, tsize);
396 GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
398 head->in_pending_queue = GNUNET_NO;
399 if (head->timeout_task != GNUNET_SCHEDULER_NO_TASK)
401 GNUNET_SCHEDULER_cancel (head->timeout_task);
402 head->timeout_task = GNUNET_SCHEDULER_NO_TASK;
404 if (GNUNET_YES == head->free_on_send)
406 process_pending_messages (handle);
408 LOG (GNUNET_ERROR_TYPE_DEBUG,
409 "Forwarded request of %u bytes to GNS service\n", (unsigned int) tsize);
411 if (GNUNET_NO == handle->in_receive)
414 LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting to process replies from GNS\n");
416 handle->in_receive = GNUNET_YES;
417 GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
418 GNUNET_TIME_UNIT_FOREVER_REL);
424 * Process a given reply that might match the given
427 * @param cls the 'struct GNUNET_GNS_ClientResultMessage'
428 * @param key query of the request
429 * @param value the 'struct GNUNET_GNS_LookupHandle' of a request matching the same key
430 * @return GNUNET_YES to continue to iterate over all results,
431 * GNUNET_NO if the reply is malformed
434 process_reply (void *cls, const GNUNET_HashCode * key, void *value)
436 const struct GNUNET_GNS_ClientResultMessage *gns_msg = cls;
437 struct GNUNET_GNS_LookupHandle *lookup_handle = value;
438 const char *name = (const char*) &lookup_handle[1];
439 const struct GNUNET_GNS_Record *records;
440 uint32_t num_records;
444 if (gns_msg->unique_id != lookup_handle->unique_id)
448 LOG (GNUNET_ERROR_TYPE_DEBUG,
449 "Ignoring reply for %s: UID mismatch: %llu/%llu\n", GNUNET_h2s (key),
450 gns_msg->unique_id, lookup_handle->unique_id);
454 msize = ntohs (gns_msg->header.size);
455 num_records = ntohl (gns_msg->num_records);
457 sizeof (struct GNUNET_GNS_ClientResultMessage) +
458 sizeof (struct GNUNET_GNS_Record) * (num_records);
459 if ((msize < meta_length) ||
461 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_GNS_Record)))
467 LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving %u byte reply for %s to application\n",
468 (unsigned int) (msize - meta_length), GNUNET_h2s (key));
470 records = (const struct GNUNET_GNS_Record *) &gns_msg[1];
471 lookup_handle->iter (lookup_handle->iter_cls, name, records, num_records);
477 * Handler for messages received from the GNS service
479 * @param cls the 'struct GNUNET_GNS_Handle'
480 * @param msg the incoming message
483 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
485 struct GNUNET_GNS_Handle *handle = cls;
486 const struct GNUNET_GNS_ClientResultMessage *gns_msg;
491 LOG (GNUNET_ERROR_TYPE_DEBUG,
492 "Error receiving data from GNS service, reconnecting\n");
494 do_disconnect (handle);
497 if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT)
500 do_disconnect (handle);
503 if (ntohs (msg->size) < sizeof (struct GNUNET_GNS_ClientResultMessage))
506 do_disconnect (handle);
509 gns_msg = (const struct GNUNET_GNS_ClientResultMessage *) msg;
511 LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply for `%s' from GNS service %p\n",
512 &gns_msg->name, handle);
514 /* TODO uniquely identify requests... maybe hash(name) or uid */
515 GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests,
516 &gns_msg->key, &process_reply,
518 GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
519 GNUNET_TIME_UNIT_FOREVER_REL);
524 * Initialize the connection with the GNS service.
526 * @param cfg configuration to use
527 * @param ht_len size of the internal hash table to use for parallel requests
528 * @return handle to the GNS service, or NULL on error
530 struct GNUNET_GNS_Handle *
531 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
534 struct GNUNET_GNS_Handle *handle;
536 handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
539 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
540 handle->active_requests = GNUNET_CONTAINER_multihashmap_create (ht_len);
541 if (GNUNET_NO == try_connect (handle))
543 GNUNET_GNS_disconnect (handle);
551 * Shutdown connection with the GNS service.
553 * @param handle handle of the GNS connection to stop
556 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
563 * Perform an asynchronous Lookup operation on the GNS.
565 * - Still not sure what we query for... "names" it is for now
566 * - Do we need such sophisticated message queueing like dht? simplify?
568 * @param handle handle to the GNS service
569 * @param timeout how long to wait for transmission of this request to the service
570 * @param name the name to look up
571 * @param iter function to call on each result
572 * @param iter_cls closure for iter
573 * @return handle to stop the async get
575 struct GNUNET_GNS_LookupHandle *
576 GNUNET_GNS_lookup_start (struct GNUNET_GNS_Handle *handle,
577 struct GNUNET_TIME_Relative timeout,
579 enum GNUNET_GNS_RecordType type,
580 GNUNET_GNS_LookupIterator iter,
583 /* IPC to look for local entries, start dht lookup, return lookup_handle */
584 struct GNUNET_GNS_ClientLookupMessage *lookup_msg;
585 struct GNUNET_GNS_LookupHandle *lookup_handle;
588 struct PendingMessage *pending;
595 GNUNET_CRYPTO_hash (name, strlen(name), &key);
597 msize = sizeof (struct GNUNET_GNS_ClientLookupMessage) + strlen(name);
599 LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lookup for %s in GNS %p\n",
602 pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
603 lookup_msg = (struct GNUNET_GNS_ClientLookupMessage *) &pending[1];
604 pending->msg = &lookup_msg->header;
605 pending->handle = handle;
606 pending->free_on_send = GNUNET_NO;
607 lookup_msg->header.size = htons (msize);
608 lookup_msg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP);
609 lookup_msg->key = key;
610 memcpy(&lookup_msg[1], name, strlen(name));
612 lookup_msg->unique_id = handle->uid_gen;
613 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
615 pending->in_pending_queue = GNUNET_YES;
616 lookup_handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_LookupHandle));
617 lookup_handle->iter = iter;
618 lookup_handle->iter_cls = iter_cls;
619 lookup_handle->message = pending;
620 lookup_handle->unique_id = lookup_msg->unique_id;
621 GNUNET_CONTAINER_multihashmap_put (handle->active_requests, &lookup_msg->key,
623 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
624 process_pending_messages (handle);
625 return lookup_handle;
630 * Stop async GNS lookup.
632 * @param lookup_handle handle to the GNS lookup operation to stop
635 GNUNET_GNS_lookup_stop (struct GNUNET_GNS_LookupHandle *lookup_handle)
637 /* TODO Stop dht lookups */
641 /* end of gns_api.c */