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