-new comments for resolver
[oweals/gnunet.git] / src / gns / gns_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * TODO: Do we really need a client API?
23  *
24  * @file gns/gns_api.c
25  * @brief library to access the GNS service
26  * @author Martin Schanzenbach
27  */
28
29 #include "platform.h"
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"
36 #include "gns.h"
37 #include "gnunet_gns_service.h"
38
39 #define DEBUG_GNS_API GNUNET_EXTRA_LOGGING
40
41 #define LOG(kind,...) GNUNET_log_from (kind, "gns-api",__VA_ARGS__)
42
43 /* TODO into gnunet_protocols */
44 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP 23
45 #define GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT 24
46
47 /**
48  * Entry in our list of messages to be (re-)transmitted.
49  */
50 struct PendingMessage
51 {
52   /**
53    * This is a doubly-linked list.
54    */
55   struct PendingMessage *prev;
56
57   /**
58    * This is a doubly-linked list.
59    */
60   struct PendingMessage *next;
61
62   /**
63    * Message that is pending, allocated at the end
64    * of this struct.
65    */
66   const struct GNUNET_MessageHeader *msg;
67
68   /**
69    * Handle to the GNS API context.
70    */
71   struct GNUNET_GNS_Handle *handle;
72
73   /**
74    * Continuation to call when the request has been
75    * transmitted (for the first time) to the service; can be NULL.
76    */
77   GNUNET_SCHEDULER_Task cont;
78
79   /**
80    * Closure for 'cont'.
81    */
82   void *cont_cls;
83
84   /**
85    * Timeout task for this message
86    */
87   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
88
89   /**
90    * Unique ID for this request
91    */
92   uint64_t unique_id;
93
94   /**
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
98    * from there.
99    */
100
101   int free_on_send;
102   /**
103    * GNUNET_YES if this message is in our pending queue right now.
104    */
105   int in_pending_queue;
106
107 };
108
109 /**
110  * Handle to a Lookup request
111  */
112 struct GNUNET_GNS_LookupHandle
113 {
114
115   /**
116    * Iterator to call on data receipt
117    */
118   GNUNET_GNS_LookupIterator iter;
119
120   /**
121    * Closure for the iterator callback
122    */
123   void *iter_cls;
124
125   /**
126    * Main handle to this GNS api
127    */
128   struct GNUNET_GNS_Handle *gns_handle;
129
130   /**
131    * Key that this get request is for
132    */
133   GNUNET_HashCode key;
134
135   /**
136    * Unique identifier for this request (for key collisions).
137    */
138   uint64_t unique_id;
139
140   struct PendingMessage *message;
141
142 };
143
144 /**
145  * Connection to the GNS service.
146  */
147 struct GNUNET_GNS_Handle
148 {
149
150   /**
151    * Configuration to use.
152    */
153   const struct GNUNET_CONFIGURATION_Handle *cfg;
154
155   /**
156    * Socket (if available).
157    */
158   struct GNUNET_CLIENT_Connection *client;
159
160   /**
161    * Currently pending transmission request (or NULL).
162    */
163   struct GNUNET_CLIENT_TransmitHandle *th;
164
165   /**
166    * Head of linked list of messages we would like to transmit.
167    */
168   struct PendingMessage *pending_head;
169
170   /**
171    * Tail of linked list of messages we would like to transmit.
172    */
173   struct PendingMessage *pending_tail;
174
175   /**
176    * Hash map containing the current outstanding unique requests.
177    */
178   struct GNUNET_CONTAINER_MultiHashMap *active_requests;
179
180   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
181
182   /**
183    * How quickly should we retry?  Used for exponential back-off on
184    * connect-errors.
185    */
186   struct GNUNET_TIME_Relative retry_time;
187
188   /**
189    * Generator for unique ids.
190    */
191   uint64_t uid_gen;
192
193   /**
194    * Did we start our receive loop yet?
195    */
196   int in_receive;
197 };
198
199 /**
200  * Try to send messages from list of messages to send
201  * @param handle GNS_Handle
202  */
203 static void
204 process_pending_messages (struct GNUNET_GNS_Handle *handle);
205
206 /**
207  * Try to (re)connect to the GNS service.
208  *
209  * @return GNUNET_YES on success, GNUNET_NO on failure.
210  */
211 static int
212 try_connect (struct GNUNET_GNS_Handle *handle)
213 {
214   if (handle->client != NULL)
215     return GNUNET_OK;
216   handle->in_receive = GNUNET_NO;
217   handle->client = GNUNET_CLIENT_connect ("gns", handle->cfg);
218   if (handle->client == NULL)
219   {
220     LOG (GNUNET_ERROR_TYPE_WARNING,
221          _("Failed to connect to the GNS service!\n"));
222     return GNUNET_NO;
223   }
224   return GNUNET_YES;
225 }
226
227 /**
228  * Add the request corresponding to the given handle
229  * to the pending queue (if it is not already in there).
230  *
231  * @param cls the 'struct GNUNET_GNS_Handle*'
232  * @param key key for the request (not used)
233  * @param value the 'struct GNUNET_GNS_LookupHandle*'
234  * @return GNUNET_YES (always)
235  */
236 static int
237 add_request_to_pending (void *cls, const GNUNET_HashCode * key, void *value)
238 {
239   struct GNUNET_GNS_Handle *handle = cls;
240   struct GNUNET_GNS_LookupHandle *rh = value;
241
242   if (GNUNET_NO == rh->message->in_pending_queue)
243   {
244 #if DEBUG_DHT
245     LOG (GNUNET_ERROR_TYPE_DEBUG,
246          "Retransmitting request related to %s to GNS %p\n", GNUNET_h2s(key),
247          handle);
248 #endif
249     GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
250                                  rh->message);
251     rh->message->in_pending_queue = GNUNET_YES;
252   }
253   return GNUNET_YES;
254 }
255
256 /**
257  * Try reconnecting to the GNS service.
258  *
259  * @param cls GNUNET_GNS_Handle
260  * @param tc scheduler context
261  */
262 static void
263 try_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
264 {
265   struct GNUNET_GNS_Handle *handle = cls;
266
267 #if DEBUG_GNS
268   LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting with GNS %p\n", handle);
269 #endif
270   handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
271   if (handle->retry_time.rel_value < GNUNET_CONSTANTS_SERVICE_RETRY.rel_value)
272     handle->retry_time = GNUNET_CONSTANTS_SERVICE_RETRY;
273   else
274     handle->retry_time = GNUNET_TIME_relative_multiply (handle->retry_time, 2);
275   if (handle->retry_time.rel_value > GNUNET_CONSTANTS_SERVICE_TIMEOUT.rel_value)
276     handle->retry_time = GNUNET_CONSTANTS_SERVICE_TIMEOUT;
277   handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
278   if (GNUNET_YES != try_connect (handle))
279   {
280 #if DEBUG_GNS
281     LOG (GNUNET_ERROR_TYPE_DEBUG, "GNS reconnect failed(!)\n");
282 #endif
283     return;
284   }
285   GNUNET_CONTAINER_multihashmap_iterate (handle->active_requests,
286                                          &add_request_to_pending, handle);
287   process_pending_messages (handle);
288 }
289
290
291 /**
292  * Try reconnecting to the GNS service.
293  *
294  * @param handle handle to gns to (possibly) disconnect and reconnect
295  */
296 static void
297 do_disconnect (struct GNUNET_GNS_Handle *handle)
298 {
299   if (handle->client == NULL)
300     return;
301   GNUNET_assert (handle->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
302   if (NULL != handle->th)
303     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
304   handle->th = NULL;
305   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
306               "Disconnecting from GNS service, will try to reconnect in %llu ms\n",
307               (unsigned long long) handle->retry_time.rel_value);
308   GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
309   handle->client = NULL;
310   handle->reconnect_task =
311       GNUNET_SCHEDULER_add_delayed (handle->retry_time, &try_reconnect, handle);
312 }
313
314 /**
315  * Transmit the next pending message, called by notify_transmit_ready
316  */
317 static size_t
318 transmit_pending (void *cls, size_t size, void *buf);
319
320 /**
321  * Handler for messages received from the GNS service
322  *
323  * @param cls the 'struct GNUNET_GNS_Handle'
324  * @param msg the incoming message
325  */
326 static void
327 message_handler (void *cls, const struct GNUNET_MessageHeader *msg);
328
329 /**
330  * Try to send messages from list of messages to send
331  */
332 static void
333 process_pending_messages (struct GNUNET_GNS_Handle *handle)
334 {
335   struct PendingMessage *head;
336
337   if (handle->client == NULL)
338   {
339     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
340                 "process_pending_messages called, but client is null, reconnecting\n");
341     do_disconnect (handle);
342     return;
343   }
344   if (handle->th != NULL)
345     return;
346   if (NULL == (head = handle->pending_head))
347     return;
348   handle->th =
349     GNUNET_CLIENT_notify_transmit_ready (handle->client,
350                                          ntohs (head->msg->size),
351                                          GNUNET_TIME_UNIT_FOREVER_REL,
352                                          GNUNET_YES, &transmit_pending,
353                                          handle);
354   if (NULL != handle->th)
355     return;
356   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
357               "notify_transmit_ready returned NULL, reconnecting\n");
358   do_disconnect (handle);
359 }
360
361
362 /**
363  * Transmit the next pending message, called by notify_transmit_ready
364  */
365 static size_t
366 transmit_pending (void *cls, size_t size, void *buf)
367 {
368   struct GNUNET_GNS_Handle *handle = cls;
369   struct PendingMessage *head;
370   size_t tsize;
371
372   handle->th = NULL;
373   if (buf == NULL)
374   {
375     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
376          "Transmission to GNS service failed!  Reconnecting!\n");
377     do_disconnect (handle);
378     return 0;
379   }
380   if (NULL == (head = handle->pending_head))
381     return 0;
382
383   tsize = ntohs (head->msg->size);
384   if (size < tsize)
385   {
386     process_pending_messages (handle);
387     return 0;
388   }
389   memcpy (buf, head->msg, tsize);
390   GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
391                                head);
392   head->in_pending_queue = GNUNET_NO;
393   if (head->timeout_task != GNUNET_SCHEDULER_NO_TASK)
394   {
395     GNUNET_SCHEDULER_cancel (head->timeout_task);
396     head->timeout_task = GNUNET_SCHEDULER_NO_TASK;
397   }
398   if (GNUNET_YES == head->free_on_send)
399     GNUNET_free (head);
400   process_pending_messages (handle);
401 #if DEBUG_GNS
402   LOG (GNUNET_ERROR_TYPE_DEBUG,
403        "Forwarded request of %u bytes to GNS service\n", (unsigned int) tsize);
404 #endif
405   if (GNUNET_NO == handle->in_receive)
406   {
407 #if DEBUG_GNS
408     LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting to process replies from GNS\n");
409 #endif
410     handle->in_receive = GNUNET_YES;
411     GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
412                            GNUNET_TIME_UNIT_FOREVER_REL);
413   }
414   return tsize;
415 }
416
417 /**
418  * Process a given reply that might match the given
419  * request.
420  *
421  * @param cls the 'struct GNUNET_GNS_ClientResultMessage'
422  * @param key query of the request
423  * @param value the 'struct GNUNET_GNS_LookupHandle' of a request matching the same key
424  * @return GNUNET_YES to continue to iterate over all results,
425  *         GNUNET_NO if the reply is malformed
426  */
427 static int
428 process_reply (void *cls, const GNUNET_HashCode * key, void *value)
429 {
430   const struct GNUNET_GNS_ClientResultMessage *gns_msg = cls;
431   struct GNUNET_GNS_LookupHandle *lookup_handle = value;
432   const char *name = (const char*) &lookup_handle[1];
433   const struct GNUNET_GNS_Record *records;
434   uint32_t num_records;
435   size_t meta_length;
436   size_t msize;
437
438   if (gns_msg->unique_id != lookup_handle->unique_id)
439   {
440     /* UID mismatch */
441 #if DEBUG_GNS
442     LOG (GNUNET_ERROR_TYPE_DEBUG,
443          "Ignoring reply for %s: UID mismatch: %llu/%llu\n", GNUNET_h2s (key),
444          gns_msg->unique_id, lookup_handle->unique_id);
445 #endif
446     return GNUNET_YES;
447   }
448   msize = ntohs (gns_msg->header.size);
449   num_records = ntohl (gns_msg->num_records);
450   meta_length =
451     sizeof (struct GNUNET_GNS_ClientResultMessage) +
452     sizeof (struct GNUNET_GNS_Record) * (num_records);
453   if ((msize < meta_length) ||
454       (num_records >
455        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_GNS_Record)))
456   {
457     GNUNET_break (0);
458     return GNUNET_NO;
459   }
460 #if DEBUG_GNS
461   LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving %u byte reply for %s to application\n",
462        (unsigned int) (msize - meta_length), GNUNET_h2s (key));
463 #endif
464   records = (const struct GNUNET_GNS_Record *) &gns_msg[1];
465   lookup_handle->iter (lookup_handle->iter_cls, name, records, num_records);
466   return GNUNET_YES;
467 }
468
469
470 /**
471  * Handler for messages received from the GNS service
472  *
473  * @param cls the 'struct GNUNET_GNS_Handle'
474  * @param msg the incoming message
475  */
476 static void
477 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
478 {
479   struct GNUNET_GNS_Handle *handle = cls;
480   const struct GNUNET_GNS_ClientResultMessage *gns_msg;
481
482   if (msg == NULL)
483   {
484 #if DEBUG_GNS
485     LOG (GNUNET_ERROR_TYPE_DEBUG,
486          "Error receiving data from GNS service, reconnecting\n");
487 #endif
488     do_disconnect (handle);
489     return;
490   }
491   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT)
492   {
493     GNUNET_break (0);
494     do_disconnect (handle);
495     return;
496   }
497   if (ntohs (msg->size) < sizeof (struct GNUNET_GNS_ClientResultMessage))
498   {
499     GNUNET_break (0);
500     do_disconnect (handle);
501     return;
502   }
503   gns_msg = (const struct GNUNET_GNS_ClientResultMessage *) msg;
504 #if DEBUG_GNS
505   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply for `%s' from GNS service %p\n",
506        &gns_msg->name, handle);
507 #endif
508   /* TODO uniquely identify requests... maybe hash(name) or uid */
509   GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests,
510                                               &gns_msg->key, &process_reply,
511                                               (void *) gns_msg);
512   GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
513                          GNUNET_TIME_UNIT_FOREVER_REL);
514 }
515
516
517 /**
518  * Initialize the connection with the GNS service.
519  *
520  * @param cfg configuration to use
521  * @param ht_len size of the internal hash table to use for parallel requests
522  * @return handle to the GNS service, or NULL on error
523  */
524 struct GNUNET_GNS_Handle *
525 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
526                     unsigned int ht_len)
527 {
528   struct GNUNET_GNS_Handle *handle;
529
530   handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
531   handle->cfg = cfg;
532   handle->uid_gen =
533       GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
534   handle->active_requests = GNUNET_CONTAINER_multihashmap_create (ht_len);
535   if (GNUNET_NO == try_connect (handle))
536   {
537     GNUNET_GNS_disconnect (handle);
538     return NULL;
539   }
540   return handle;
541 }
542
543
544 /**
545  * Shutdown connection with the GNS service.
546  *
547  * @param handle handle of the GNS connection to stop
548  */
549 void
550 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
551 {
552   /* disco from GNS */
553 }
554
555
556 /**
557  * Perform an asynchronous Lookup operation on the GNS.
558  * TODO:
559  *    - Still not sure what we query for... "names" it is for now
560  *    - Do we need such sophisticated message queueing like dht? simplify?
561  *
562  * @param handle handle to the GNS service
563  * @param timeout how long to wait for transmission of this request to the service
564  * @param name the name to look up
565  * @param iter function to call on each result
566  * @param iter_cls closure for iter
567  * @return handle to stop the async get
568  */
569 struct GNUNET_GNS_LookupHandle *
570 GNUNET_GNS_lookup_start (struct GNUNET_GNS_Handle *handle,
571                          struct GNUNET_TIME_Relative timeout,
572                          const char * name,
573                          enum GNUNET_GNS_RecordType type,
574                          GNUNET_GNS_LookupIterator iter,
575                          void *iter_cls)
576 {
577   /* IPC to look for local entries, start dht lookup, return lookup_handle */
578   struct GNUNET_GNS_ClientLookupMessage *lookup_msg;
579   struct GNUNET_GNS_LookupHandle *lookup_handle;
580   GNUNET_HashCode key;
581   size_t msize;
582   struct PendingMessage *pending;
583
584   if (NULL == name)
585   {
586     return NULL;
587   }
588
589   GNUNET_CRYPTO_hash (name, strlen(name), &key);
590
591   msize = sizeof (struct GNUNET_GNS_ClientLookupMessage) + strlen(name);
592 #if DEBUG_GNS
593   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lookup for %s in GNS %p\n",
594        name, handle);
595 #endif
596   pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
597   lookup_msg = (struct GNUNET_GNS_ClientLookupMessage *) &pending[1];
598   pending->msg = &lookup_msg->header;
599   pending->handle = handle;
600   pending->free_on_send = GNUNET_NO;
601   lookup_msg->header.size = htons (msize);
602   lookup_msg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP);
603   lookup_msg->key = key;
604   memcpy(&lookup_msg[1], name, strlen(name));
605   handle->uid_gen++;
606   lookup_msg->unique_id = handle->uid_gen;
607   GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
608                                pending);
609   pending->in_pending_queue = GNUNET_YES;
610   lookup_handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_LookupHandle));
611   lookup_handle->iter = iter;
612   lookup_handle->iter_cls = iter_cls;
613   lookup_handle->message = pending;
614   lookup_handle->unique_id = lookup_msg->unique_id;
615   GNUNET_CONTAINER_multihashmap_put (handle->active_requests, &lookup_msg->key,
616                                      lookup_handle,
617                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
618   process_pending_messages (handle);
619   return lookup_handle;
620 }
621
622
623 /**
624  * Stop async GNS lookup.
625  *
626  * @param lookup_handle handle to the GNS lookup operation to stop
627  */
628 void
629 GNUNET_GNS_lookup_stop (struct GNUNET_GNS_LookupHandle *lookup_handle)
630 {
631   /* TODO Stop dht lookups */
632 }
633
634
635 /* end of gns_api.c */