- added makefile, borrowed lots of code from dht for client handling
[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  * Try reconnecting to the GNS service.
235  *
236  * @param cls GNUNET_GNS_Handle
237  * @param tc scheduler context
238  */
239 static void
240 try_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
241 {
242   struct GNUNET_GNS_Handle *handle = cls;
243
244 #if DEBUG_GNS
245   LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting with GNS %p\n", handle);
246 #endif
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;
250   else
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))
256   {
257 #if DEBUG_GNS
258     LOG (GNUNET_ERROR_TYPE_DEBUG, "GNS reconnect failed(!)\n");
259 #endif
260     return;
261   }
262   GNUNET_CONTAINER_multihashmap_iterate (handle->active_requests,
263                                          &add_request_to_pending, handle);
264   process_pending_messages (handle);
265 }
266
267
268 /**
269  * Try reconnecting to the GNS service.
270  *
271  * @param handle handle to gns to (possibly) disconnect and reconnect
272  */
273 static void
274 do_disconnect (struct GNUNET_GNS_Handle *handle)
275 {
276   if (handle->client == NULL)
277     return;
278   GNUNET_assert (handle->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
279   if (NULL != handle->th)
280     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
281   handle->th = NULL;
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);
289 }
290
291 /**
292  * Transmit the next pending message, called by notify_transmit_ready
293  */
294 static size_t
295 transmit_pending (void *cls, size_t size, void *buf);
296
297 /**
298  * Handler for messages received from the GNS service
299  *
300  * @param cls the 'struct GNUNET_GNS_Handle'
301  * @param msg the incoming message
302  */
303 static void
304 message_handler (void *cls, const struct GNUNET_MessageHeader *msg);
305
306 /**
307  * Try to send messages from list of messages to send
308  */
309 static void
310 process_pending_messages (struct GNUNET_GNS_Handle *handle)
311 {
312   struct PendingMessage *head;
313
314   if (handle->client == NULL)
315   {
316     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317                 "process_pending_messages called, but client is null, reconnecting\n");
318     do_disconnect (handle);
319     return;
320   }
321   if (handle->th != NULL)
322     return;
323   if (NULL == (head = handle->pending_head))
324     return;
325   handle->th =
326     GNUNET_CLIENT_notify_transmit_ready (handle->client,
327                                          ntohs (head->msg->size),
328                                          GNUNET_TIME_UNIT_FOREVER_REL,
329                                          GNUNET_YES, &transmit_pending,
330                                          handle);
331   if (NULL != handle->th)
332     return;
333   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
334               "notify_transmit_ready returned NULL, reconnecting\n");
335   do_disconnect (handle);
336 }
337
338
339 /**
340  * Transmit the next pending message, called by notify_transmit_ready
341  */
342 static size_t
343 transmit_pending (void *cls, size_t size, void *buf)
344 {
345   struct GNUNET_GNS_Handle *handle = cls;
346   struct PendingMessage *head;
347   size_t tsize;
348
349   handle->th = NULL;
350   if (buf == NULL)
351   {
352 #if DEBUG_GNS
353     LOG (GNUNET_ERROR_TYPE_DEBUG,
354          "Transmission to GNS service failed!  Reconnecting!\n");
355 #endif
356     do_disconnect (handle);
357     return 0;
358   }
359   if (NULL == (head = handle->pending_head))
360     return 0;
361
362   tsize = ntohs (head->msg->size);
363   if (size < tsize)
364   {
365     process_pending_messages (handle);
366     return 0;
367   }
368   memcpy (buf, head->msg, tsize);
369   GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
370                                head);
371   head->in_pending_queue = GNUNET_NO;
372   if (head->timeout_task != GNUNET_SCHEDULER_NO_TASK)
373   {
374     GNUNET_SCHEDULER_cancel (head->timeout_task);
375     head->timeout_task = GNUNET_SCHEDULER_NO_TASK;
376   }
377   if (GNUNET_YES == head->free_on_send)
378     GNUNET_free (head);
379   process_pending_messages (handle);
380 #if DEBUG_GNS
381   LOG (GNUNET_ERROR_TYPE_DEBUG,
382        "Forwarded request of %u bytes to GNS service\n", (unsigned int) tsize);
383 #endif
384   if (GNUNET_NO == handle->in_receive)
385   {
386 #if DEBUG_GNS
387     LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting to process replies from GNS\n");
388 #endif
389     handle->in_receive = GNUNET_YES;
390     GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
391                            GNUNET_TIME_UNIT_FOREVER_REL);
392   }
393   return tsize;
394 }
395
396 /**
397  * Process a given reply that might match the given
398  * request.
399  *
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
405  */
406 static int
407 process_reply (void *cls, const GNUNET_HashCode * key, void *value)
408 {
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;
413   size_t meta_length;
414   size_t msize;
415
416   if (gns_msg->unique_id != lookup_handle->unique_id)
417   {
418     /* UID mismatch */
419 #if DEBUG_GNS
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);
423 #endif
424     return GNUNET_YES;
425   }
426   msize = ntohs (gns_msg->header.size);
427   num_records = ntohl (gns_msg->num_records);
428   meta_length =
429     sizeof (struct GNUNET_GNS_ClientResultMessage) +
430     sizeof (struct GNUNET_GNS_Record) * (num_records);
431   if ((msize < meta_length) ||
432       (num_records >
433        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_GNS_Record)))
434   {
435     GNUNET_break (0);
436     return GNUNET_NO;
437   }
438 #if DEBUG_GNS
439   LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving %u byte reply for %s to application\n",
440        (unsigned int) (msize - meta_length), GNUNET_h2s (key));
441 #endif
442   records = (const struct GNUNET_GNS_Record *) &gns_msg[1];
443   lookup_handle->iter (lookup_handle->iter_cls, key, records, num_records);
444   return GNUNET_YES;
445 }
446
447
448 /**
449  * Handler for messages received from the GNS service
450  *
451  * @param cls the 'struct GNUNET_GNS_Handle'
452  * @param msg the incoming message
453  */
454 static void
455 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
456 {
457   struct GNUNET_GNS_Handle *handle = cls;
458   const struct GNUNET_GNS_ClientResultMessage *gns_msg;
459
460   if (msg == NULL)
461   {
462 #if DEBUG_GNS
463     LOG (GNUNET_ERROR_TYPE_DEBUG,
464          "Error receiving data from GNS service, reconnecting\n");
465 #endif
466     do_disconnect (handle);
467     return;
468   }
469   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT)
470   {
471     GNUNET_break (0);
472     do_disconnect (handle);
473     return;
474   }
475   if (ntohs (msg->size) < sizeof (struct GNUNET_GNS_ClientResultMessage))
476   {
477     GNUNET_break (0);
478     do_disconnect (handle);
479     return;
480   }
481   gns_msg = (const struct GNUNET_GNS_ClientResultMessage *) msg;
482 #if DEBUG_GNS
483   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply for `%s' from GNS service %p\n",
484        &gns_msg->name, handle);
485 #endif
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,
489                                               (void *) gns_msg);
490   GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
491                          GNUNET_TIME_UNIT_FOREVER_REL);
492 }
493
494
495 /**
496  * Initialize the connection with the GNS service.
497  *
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
501  */
502 struct GNUNET_GNS_Handle *
503 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
504                     unsigned int ht_len)
505 {
506   struct GNUNET_GNS_Handle *handle;
507
508   handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
509   handle->cfg = cfg;
510   handle->uid_gen =
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))
514   {
515     GNUNET_GNS_disconnect (handle);
516     return NULL;
517   }
518   return handle;
519 }
520
521
522 /**
523  * Shutdown connection with the GNS service.
524  *
525  * @param handle handle of the GNS connection to stop
526  */
527 void
528 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
529 {
530   /* disco from GNS */
531 }
532
533
534 /**
535  * Add a new record to the GNS.
536  *
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
541  */
542 void
543 GNUNET_GNS_add_record (struct GNUNET_GNS_Handle *handle,
544                        const char* name,
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)
549 {
550   /**
551    * build add record message
552    */
553   struct GNUNET_GNS_Record *record;
554
555   record = GNUNET_malloc(sizeof (struct GNUNET_GNS_Record));
556   /**
557    * TODO
558    * queue_record_msg(handle, record);
559    **/
560 }
561
562
563 /**
564  * Perform an asynchronous Lookup operation on the GNS.
565  *
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
572  */
573 struct GNUNET_GNS_LookupHandle *
574 GNUNET_GNS_lookup_start (struct GNUNET_GNS_Handle *handle,
575                       struct GNUNET_TIME_Relative timeout,
576                       const char * name,
577                       enum GNUNET_GNS_RecordType type,
578                       GNUNET_GNS_LookupIterator iter,
579                       void *iter_cls)
580 {
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;
584   size_t msize;
585   struct PendingMessage *pending;
586
587   msize = sizeof (struct GNUNET_GNS_ClientLookupMessage) + strlen(name);
588 #if DEBUG_GNS
589   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lookup for %s in GNS %p\n",
590        name, handle);
591 #endif
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));
601   handle->uid_gen++;
602   lookup_msg->unique_id = handle->uid_gen;
603   GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
604                                pending);
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;
615 }
616
617
618 /**
619  * Stop async GNS lookup.
620  *
621  * @param lookup_handle handle to the GNS lookup operation to stop
622  */
623 void
624 GNUNET_GNS_lookup_stop (struct GNUNET_GNS_LookupHandle *lookup_handle)
625 {
626   /* TODO Stop dht lookups */
627 }
628
629
630 /* end of gns_api.c */