- commit after up, ty!
[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 struct GNUNET_GNS_Record *records;
439   uint32_t num_records;
440   size_t meta_length;
441   size_t msize;
442
443   if (gns_msg->unique_id != lookup_handle->unique_id)
444   {
445     /* UID mismatch */
446 #if DEBUG_GNS
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);
450 #endif
451     return GNUNET_YES;
452   }
453   msize = ntohs (gns_msg->header.size);
454   num_records = ntohl (gns_msg->num_records);
455   meta_length =
456     sizeof (struct GNUNET_GNS_ClientResultMessage) +
457     sizeof (struct GNUNET_GNS_Record) * (num_records);
458   if ((msize < meta_length) ||
459       (num_records >
460        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_GNS_Record)))
461   {
462     GNUNET_break (0);
463     return GNUNET_NO;
464   }
465 #if DEBUG_GNS
466   LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving %u byte reply for %s to application\n",
467        (unsigned int) (msize - meta_length), GNUNET_h2s (key));
468 #endif
469   records = (const struct GNUNET_GNS_Record *) &gns_msg[1];
470   lookup_handle->iter (lookup_handle->iter_cls, key, records, num_records);
471   return GNUNET_YES;
472 }
473
474
475 /**
476  * Handler for messages received from the GNS service
477  *
478  * @param cls the 'struct GNUNET_GNS_Handle'
479  * @param msg the incoming message
480  */
481 static void
482 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
483 {
484   struct GNUNET_GNS_Handle *handle = cls;
485   const struct GNUNET_GNS_ClientResultMessage *gns_msg;
486
487   if (msg == NULL)
488   {
489 #if DEBUG_GNS
490     LOG (GNUNET_ERROR_TYPE_DEBUG,
491          "Error receiving data from GNS service, reconnecting\n");
492 #endif
493     do_disconnect (handle);
494     return;
495   }
496   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT)
497   {
498     GNUNET_break (0);
499     do_disconnect (handle);
500     return;
501   }
502   if (ntohs (msg->size) < sizeof (struct GNUNET_GNS_ClientResultMessage))
503   {
504     GNUNET_break (0);
505     do_disconnect (handle);
506     return;
507   }
508   gns_msg = (const struct GNUNET_GNS_ClientResultMessage *) msg;
509 #if DEBUG_GNS
510   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply for `%s' from GNS service %p\n",
511        &gns_msg->name, handle);
512 #endif
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,
516                                               (void *) gns_msg);
517   GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
518                          GNUNET_TIME_UNIT_FOREVER_REL);
519 }
520
521
522 /**
523  * Initialize the connection with the GNS service.
524  *
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
528  */
529 struct GNUNET_GNS_Handle *
530 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
531                     unsigned int ht_len)
532 {
533   struct GNUNET_GNS_Handle *handle;
534
535   handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
536   handle->cfg = cfg;
537   handle->uid_gen =
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))
541   {
542     GNUNET_GNS_disconnect (handle);
543     return NULL;
544   }
545   return handle;
546 }
547
548
549 /**
550  * Shutdown connection with the GNS service.
551  *
552  * @param handle handle of the GNS connection to stop
553  */
554 void
555 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
556 {
557   /* disco from GNS */
558 }
559
560
561 /**
562  * Add a new record to the GNS.
563  *
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
568  */
569 void
570 GNUNET_GNS_add_record (struct GNUNET_GNS_Handle *handle,
571                        const char* name,
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)
576 {
577   /**
578    * build add record message
579    */
580   struct GNUNET_GNS_Record *record;
581
582   record = GNUNET_malloc(sizeof (struct GNUNET_GNS_Record));
583   /**
584    * TODO
585    * queue_record_msg(handle, record);
586    **/
587 }
588
589
590 /**
591  * Perform an asynchronous Lookup operation on the GNS.
592  * TODO: Still not sure what we query for... "names" it is for now
593  *
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
600  */
601 struct GNUNET_GNS_LookupHandle *
602 GNUNET_GNS_lookup_start (struct GNUNET_GNS_Handle *handle,
603                          struct GNUNET_TIME_Relative timeout,
604                          const char * name,
605                          enum GNUNET_GNS_RecordType type,
606                          GNUNET_GNS_LookupIterator iter,
607                          void *iter_cls)
608 {
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;
612   GNUNET_HashCode key;
613   size_t msize;
614   struct PendingMessage *pending;
615
616   if (NULL == name)
617   {
618     return NULL;
619   }
620
621   GNUNET_CRYPTO_hash (name, strlen(name), &key);
622
623   msize = sizeof (struct GNUNET_GNS_ClientLookupMessage) + strlen(name);
624 #if DEBUG_GNS
625   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lookup for %s in GNS %p\n",
626        name, handle);
627 #endif
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));
638   handle->uid_gen++;
639   lookup_msg->unique_id = handle->uid_gen;
640   GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
641                                pending);
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,
649                                      lookup_handle,
650                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
651   process_pending_messages (handle);
652   return lookup_handle;
653 }
654
655
656 /**
657  * Stop async GNS lookup.
658  *
659  * @param lookup_handle handle to the GNS lookup operation to stop
660  */
661 void
662 GNUNET_GNS_lookup_stop (struct GNUNET_GNS_LookupHandle *lookup_handle)
663 {
664   /* TODO Stop dht lookups */
665 }
666
667
668 /* end of gns_api.c */