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 struct GNUNET_GNS_Record *records;
439 uint32_t num_records;
443 if (gns_msg->unique_id != lookup_handle->unique_id)
447 LOG (GNUNET_ERROR_TYPE_DEBUG,
448 "Ignoring reply for %s: UID mismatch: %llu/%llu\n", GNUNET_h2s (key),
449 gns_msg->unique_id, lookup_handle->unique_id);
453 msize = ntohs (gns_msg->header.size);
454 num_records = ntohl (gns_msg->num_records);
456 sizeof (struct GNUNET_GNS_ClientResultMessage) +
457 sizeof (struct GNUNET_GNS_Record) * (num_records);
458 if ((msize < meta_length) ||
460 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_GNS_Record)))
466 LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving %u byte reply for %s to application\n",
467 (unsigned int) (msize - meta_length), GNUNET_h2s (key));
469 records = (const struct GNUNET_GNS_Record *) &gns_msg[1];
470 lookup_handle->iter (lookup_handle->iter_cls, key, records, num_records);
476 * Handler for messages received from the GNS service
478 * @param cls the 'struct GNUNET_GNS_Handle'
479 * @param msg the incoming message
482 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
484 struct GNUNET_GNS_Handle *handle = cls;
485 const struct GNUNET_GNS_ClientResultMessage *gns_msg;
490 LOG (GNUNET_ERROR_TYPE_DEBUG,
491 "Error receiving data from GNS service, reconnecting\n");
493 do_disconnect (handle);
496 if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT)
499 do_disconnect (handle);
502 if (ntohs (msg->size) < sizeof (struct GNUNET_GNS_ClientResultMessage))
505 do_disconnect (handle);
508 gns_msg = (const struct GNUNET_GNS_ClientResultMessage *) msg;
510 LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply for `%s' from GNS service %p\n",
511 &gns_msg->name, handle);
513 /* TODO uniquely identify requests... maybe hash(name) or uid */
514 GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests,
515 &gns_msg->key, &process_reply,
517 GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
518 GNUNET_TIME_UNIT_FOREVER_REL);
523 * Initialize the connection with the GNS service.
525 * @param cfg configuration to use
526 * @param ht_len size of the internal hash table to use for parallel requests
527 * @return handle to the GNS service, or NULL on error
529 struct GNUNET_GNS_Handle *
530 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
533 struct GNUNET_GNS_Handle *handle;
535 handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
538 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
539 handle->active_requests = GNUNET_CONTAINER_multihashmap_create (ht_len);
540 if (GNUNET_NO == try_connect (handle))
542 GNUNET_GNS_disconnect (handle);
550 * Shutdown connection with the GNS service.
552 * @param handle handle of the GNS connection to stop
555 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
562 * Add a new record to the GNS.
564 * @param handle handle to GNS service
565 * @param record the record to store
566 * @param exp desired expiration time for the value
567 * @param timeout how long to wait for transmission of this request
570 GNUNET_GNS_add_record (struct GNUNET_GNS_Handle *handle,
572 enum GNUNET_GNS_RecordType type,
573 size_t size, const char *data,
574 struct GNUNET_TIME_Absolute exp,
575 struct GNUNET_TIME_Relative timeout)
578 * build add record message
580 struct GNUNET_GNS_Record *record;
582 record = GNUNET_malloc(sizeof (struct GNUNET_GNS_Record));
585 * queue_record_msg(handle, record);
591 * Perform an asynchronous Lookup operation on the GNS.
592 * TODO: Still not sure what we query for... "names" it is for now
594 * @param handle handle to the GNS service
595 * @param timeout how long to wait for transmission of this request to the service
596 * @param name the name to look up
597 * @param iter function to call on each result
598 * @param iter_cls closure for iter
599 * @return handle to stop the async get
601 struct GNUNET_GNS_LookupHandle *
602 GNUNET_GNS_lookup_start (struct GNUNET_GNS_Handle *handle,
603 struct GNUNET_TIME_Relative timeout,
605 enum GNUNET_GNS_RecordType type,
606 GNUNET_GNS_LookupIterator iter,
609 /* IPC to look for local entries, start dht lookup, return lookup_handle */
610 struct GNUNET_GNS_ClientLookupMessage *lookup_msg;
611 struct GNUNET_GNS_LookupHandle *lookup_handle;
614 struct PendingMessage *pending;
621 GNUNET_CRYPTO_hash (name, strlen(name), &key);
623 msize = sizeof (struct GNUNET_GNS_ClientLookupMessage) + strlen(name);
625 LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lookup for %s in GNS %p\n",
628 pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
629 lookup_msg = (struct GNUNET_GNS_ClientLookupMessage *) &pending[1];
630 pending->msg = &lookup_msg->header;
631 pending->handle = handle;
632 pending->free_on_send = GNUNET_NO;
633 lookup_msg->header.size = htons (msize);
634 lookup_msg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP);
635 lookup_msg->namelen = strlen(name);
636 lookup_msg->key = key;
637 memcpy(&lookup_msg[1], name, strlen(name));
639 lookup_msg->unique_id = handle->uid_gen;
640 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
642 pending->in_pending_queue = GNUNET_YES;
643 lookup_handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_LookupHandle));
644 lookup_handle->iter = iter;
645 lookup_handle->iter_cls = iter_cls;
646 lookup_handle->message = pending;
647 lookup_handle->unique_id = lookup_msg->unique_id;
648 GNUNET_CONTAINER_multihashmap_put (handle->active_requests, &lookup_msg->key,
650 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
651 process_pending_messages (handle);
652 return lookup_handle;
657 * Stop async GNS lookup.
659 * @param lookup_handle handle to the GNS lookup operation to stop
662 GNUNET_GNS_lookup_stop (struct GNUNET_GNS_LookupHandle *lookup_handle)
664 /* TODO Stop dht lookups */
668 /* end of gns_api.c */