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 * Try reconnecting to the GNS service.
236 * @param cls GNUNET_GNS_Handle
237 * @param tc scheduler context
240 try_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
242 struct GNUNET_GNS_Handle *handle = cls;
245 LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting with GNS %p\n", handle);
247 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
248 if (handle->retry_time.rel_value < GNUNET_CONSTANTS_SERVICE_RETRY.rel_value)
249 handle->retry_time = GNUNET_CONSTANTS_SERVICE_RETRY;
251 handle->retry_time = GNUNET_TIME_relative_multiply (handle->retry_time, 2);
252 if (handle->retry_time.rel_value > GNUNET_CONSTANTS_SERVICE_TIMEOUT.rel_value)
253 handle->retry_time = GNUNET_CONSTANTS_SERVICE_TIMEOUT;
254 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
255 if (GNUNET_YES != try_connect (handle))
258 LOG (GNUNET_ERROR_TYPE_DEBUG, "GNS reconnect failed(!)\n");
262 GNUNET_CONTAINER_multihashmap_iterate (handle->active_requests,
263 &add_request_to_pending, handle);
264 process_pending_messages (handle);
269 * Try reconnecting to the GNS service.
271 * @param handle handle to gns to (possibly) disconnect and reconnect
274 do_disconnect (struct GNUNET_GNS_Handle *handle)
276 if (handle->client == NULL)
278 GNUNET_assert (handle->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
279 if (NULL != handle->th)
280 GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
283 "Disconnecting from GNS service, will try to reconnect in %llu ms\n",
284 (unsigned long long) handle->retry_time.rel_value);
285 GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
286 handle->client = NULL;
287 handle->reconnect_task =
288 GNUNET_SCHEDULER_add_delayed (handle->retry_time, &try_reconnect, handle);
292 * Transmit the next pending message, called by notify_transmit_ready
295 transmit_pending (void *cls, size_t size, void *buf);
298 * Handler for messages received from the GNS service
300 * @param cls the 'struct GNUNET_GNS_Handle'
301 * @param msg the incoming message
304 message_handler (void *cls, const struct GNUNET_MessageHeader *msg);
307 * Try to send messages from list of messages to send
310 process_pending_messages (struct GNUNET_GNS_Handle *handle)
312 struct PendingMessage *head;
314 if (handle->client == NULL)
316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317 "process_pending_messages called, but client is null, reconnecting\n");
318 do_disconnect (handle);
321 if (handle->th != NULL)
323 if (NULL == (head = handle->pending_head))
326 GNUNET_CLIENT_notify_transmit_ready (handle->client,
327 ntohs (head->msg->size),
328 GNUNET_TIME_UNIT_FOREVER_REL,
329 GNUNET_YES, &transmit_pending,
331 if (NULL != handle->th)
333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
334 "notify_transmit_ready returned NULL, reconnecting\n");
335 do_disconnect (handle);
340 * Transmit the next pending message, called by notify_transmit_ready
343 transmit_pending (void *cls, size_t size, void *buf)
345 struct GNUNET_GNS_Handle *handle = cls;
346 struct PendingMessage *head;
353 LOG (GNUNET_ERROR_TYPE_DEBUG,
354 "Transmission to GNS service failed! Reconnecting!\n");
356 do_disconnect (handle);
359 if (NULL == (head = handle->pending_head))
362 tsize = ntohs (head->msg->size);
365 process_pending_messages (handle);
368 memcpy (buf, head->msg, tsize);
369 GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
371 head->in_pending_queue = GNUNET_NO;
372 if (head->timeout_task != GNUNET_SCHEDULER_NO_TASK)
374 GNUNET_SCHEDULER_cancel (head->timeout_task);
375 head->timeout_task = GNUNET_SCHEDULER_NO_TASK;
377 if (GNUNET_YES == head->free_on_send)
379 process_pending_messages (handle);
381 LOG (GNUNET_ERROR_TYPE_DEBUG,
382 "Forwarded request of %u bytes to GNS service\n", (unsigned int) tsize);
384 if (GNUNET_NO == handle->in_receive)
387 LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting to process replies from GNS\n");
389 handle->in_receive = GNUNET_YES;
390 GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
391 GNUNET_TIME_UNIT_FOREVER_REL);
397 * Process a given reply that might match the given
400 * @param cls the 'struct GNUNET_GNS_ClientResultMessage'
401 * @param key query of the request
402 * @param value the 'struct GNUNET_GNS_LookupHandle' of a request matching the same key
403 * @return GNUNET_YES to continue to iterate over all results,
404 * GNUNET_NO if the reply is malformed
407 process_reply (void *cls, const GNUNET_HashCode * key, void *value)
409 const struct GNUNET_GNS_ClientResultMessage *gns_msg = cls;
410 struct GNUNET_GNS_LookupHandle *lookup_handle = value;
411 const struct GNUNET_GNS_Record *records;
412 uint32_t num_records;
416 if (gns_msg->unique_id != lookup_handle->unique_id)
420 LOG (GNUNET_ERROR_TYPE_DEBUG,
421 "Ignoring reply for %s: UID mismatch: %llu/%llu\n", GNUNET_h2s (key),
422 gns_msg->unique_id, lookup_handle->unique_id);
426 msize = ntohs (gns_msg->header.size);
427 num_records = ntohl (gns_msg->num_records);
429 sizeof (struct GNUNET_GNS_ClientResultMessage) +
430 sizeof (struct GNUNET_GNS_Record) * (num_records);
431 if ((msize < meta_length) ||
433 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_GNS_Record)))
439 LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving %u byte reply for %s to application\n",
440 (unsigned int) (msize - meta_length), GNUNET_h2s (key));
442 records = (const struct GNUNET_GNS_Record *) &gns_msg[1];
443 lookup_handle->iter (lookup_handle->iter_cls, key, records, num_records);
449 * Handler for messages received from the GNS service
451 * @param cls the 'struct GNUNET_GNS_Handle'
452 * @param msg the incoming message
455 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
457 struct GNUNET_GNS_Handle *handle = cls;
458 const struct GNUNET_GNS_ClientResultMessage *gns_msg;
463 LOG (GNUNET_ERROR_TYPE_DEBUG,
464 "Error receiving data from GNS service, reconnecting\n");
466 do_disconnect (handle);
469 if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT)
472 do_disconnect (handle);
475 if (ntohs (msg->size) < sizeof (struct GNUNET_GNS_ClientResultMessage))
478 do_disconnect (handle);
481 gns_msg = (const struct GNUNET_GNS_ClientResultMessage *) msg;
483 LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply for `%s' from GNS service %p\n",
484 &gns_msg->name, handle);
486 /* TODO uniquely identify requests... maybe hash(name) or uid */
487 GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests,
488 &gns_msg->key, &process_reply,
490 GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
491 GNUNET_TIME_UNIT_FOREVER_REL);
496 * Initialize the connection with the GNS service.
498 * @param cfg configuration to use
499 * @param ht_len size of the internal hash table to use for parallel requests
500 * @return handle to the GNS service, or NULL on error
502 struct GNUNET_GNS_Handle *
503 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
506 struct GNUNET_GNS_Handle *handle;
508 handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
511 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
512 handle->active_requests = GNUNET_CONTAINER_multihashmap_create (ht_len);
513 if (GNUNET_NO == try_connect (handle))
515 GNUNET_GNS_disconnect (handle);
523 * Shutdown connection with the GNS service.
525 * @param handle handle of the GNS connection to stop
528 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
535 * Add a new record to the GNS.
537 * @param handle handle to GNS service
538 * @param record the record to store
539 * @param exp desired expiration time for the value
540 * @param timeout how long to wait for transmission of this request
543 GNUNET_GNS_add_record (struct GNUNET_GNS_Handle *handle,
545 enum GNUNET_GNS_RecordType type,
546 size_t size, const char *data,
547 struct GNUNET_TIME_Absolute exp,
548 struct GNUNET_TIME_Relative timeout)
551 * build add record message
553 struct GNUNET_GNS_Record *record;
555 record = GNUNET_malloc(sizeof (struct GNUNET_GNS_Record));
558 * queue_record_msg(handle, record);
564 * Perform an asynchronous Lookup operation on the GNS.
566 * @param handle handle to the GNS service
567 * @param timeout how long to wait for transmission of this request to the service
568 * @param name the name to look up
569 * @param iter function to call on each result
570 * @param iter_cls closure for iter
571 * @return handle to stop the async get
573 struct GNUNET_GNS_LookupHandle *
574 GNUNET_GNS_lookup_start (struct GNUNET_GNS_Handle *handle,
575 struct GNUNET_TIME_Relative timeout,
577 enum GNUNET_GNS_RecordType type,
578 GNUNET_GNS_LookupIterator iter,
581 /* IPC to look for local entries, start dht lookup, return lookup_handle */
582 struct GNUNET_GNS_ClientLookupMessage *lookup_msg;
583 struct GNUNET_GNS_LookupHandle *lookup_handle;
585 struct PendingMessage *pending;
587 msize = sizeof (struct GNUNET_GNS_ClientLookupMessage) + strlen(name);
589 LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lookup for %s in GNS %p\n",
592 pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
593 lookup_msg = (struct GNUNET_GNS_ClientLookupMessage *) &pending[1];
594 pending->msg = &lookup_msg->header;
595 pending->handle = handle;
596 pending->free_on_send = GNUNET_NO;
597 lookup_msg->header.size = htons (msize);
598 lookup_msg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP);
599 lookup_msg->namelen = strlen(name);
600 memcpy(&lookup_msg[1], name, strlen(name));
602 lookup_msg->unique_id = handle->uid_gen;
603 GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
605 pending->in_pending_queue = GNUNET_YES;
606 lookup_handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_LookupHandle));
607 lookup_handle->iter = iter;
608 lookup_handle->iter_cls = iter_cls;
609 lookup_handle->message = pending;
610 lookup_handle->unique_id = lookup_msg->unique_id;
611 GNUNET_CONTAINER_multihashmap_put (handle->active_requests, key, lookup_handle,
612 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
613 process_pending_messages (handle);
614 return lookup_handle;
619 * Stop async GNS lookup.
621 * @param lookup_handle handle to the GNS lookup operation to stop
624 GNUNET_GNS_lookup_stop (struct GNUNET_GNS_LookupHandle *lookup_handle)
626 /* TODO Stop dht lookups */
630 /* end of gns_api.c */