namestore stub api added
[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  * A GNS Record.
146  */
147 struct GNUNET_GNS_Record
148 {
149   enum GNUNET_GNS_RecordType type;
150   /* Followed by data array. Format determined by type */
151   char *data;
152 };
153
154 /**
155  * Connection to the GNS service.
156  */
157 struct GNUNET_GNS_Handle
158 {
159
160   /**
161    * Configuration to use.
162    */
163   const struct GNUNET_CONFIGURATION_Handle *cfg;
164
165   /**
166    * Socket (if available).
167    */
168   struct GNUNET_CLIENT_Connection *client;
169
170   /**
171    * Currently pending transmission request (or NULL).
172    */
173   struct GNUNET_CLIENT_TransmitHandle *th;
174
175   /**
176    * Head of linked list of messages we would like to transmit.
177    */
178   struct PendingMessage *pending_head;
179
180   /**
181    * Tail of linked list of messages we would like to transmit.
182    */
183   struct PendingMessage *pending_tail;
184
185   /**
186    * Hash map containing the current outstanding unique requests.
187    */
188   struct GNUNET_CONTAINER_MultiHashMap *active_requests;
189
190   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
191
192   /**
193    * How quickly should we retry?  Used for exponential back-off on
194    * connect-errors.
195    */
196   struct GNUNET_TIME_Relative retry_time;
197
198   /**
199    * Generator for unique ids.
200    */
201   uint64_t uid_gen;
202
203   /**
204    * Did we start our receive loop yet?
205    */
206   int in_receive;
207 };
208
209 /**
210  * Try to send messages from list of messages to send
211  * @param handle GNS_Handle
212  */
213 static void
214 process_pending_messages (struct GNUNET_GNS_Handle *handle);
215
216 /**
217  * Try to (re)connect to the GNS service.
218  *
219  * @return GNUNET_YES on success, GNUNET_NO on failure.
220  */
221 static int
222 try_connect (struct GNUNET_GNS_Handle *handle)
223 {
224   if (handle->client != NULL)
225     return GNUNET_OK;
226   handle->in_receive = GNUNET_NO;
227   handle->client = GNUNET_CLIENT_connect ("gns", handle->cfg);
228   if (handle->client == NULL)
229   {
230     LOG (GNUNET_ERROR_TYPE_WARNING,
231          _("Failed to connect to the GNS service!\n"));
232     return GNUNET_NO;
233   }
234   return GNUNET_YES;
235 }
236
237 /**
238  * Add the request corresponding to the given handle
239  * to the pending queue (if it is not already in there).
240  *
241  * @param cls the 'struct GNUNET_GNS_Handle*'
242  * @param key key for the request (not used)
243  * @param value the 'struct GNUNET_GNS_LookupHandle*'
244  * @return GNUNET_YES (always)
245  */
246 static int
247 add_request_to_pending (void *cls, const GNUNET_HashCode * key, void *value)
248 {
249   struct GNUNET_GNS_Handle *handle = cls;
250   struct GNUNET_GNS_LookupHandle *rh = value;
251
252   if (GNUNET_NO == rh->message->in_pending_queue)
253   {
254 #if DEBUG_DHT
255     LOG (GNUNET_ERROR_TYPE_DEBUG,
256          "Retransmitting request related to %s to GNS %p\n", GNUNET_h2s(key),
257          handle);
258 #endif
259     GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
260                                  rh->message);
261     rh->message->in_pending_queue = GNUNET_YES;
262   }
263   return GNUNET_YES;
264 }
265
266 /**
267  * Try reconnecting to the GNS service.
268  *
269  * @param cls GNUNET_GNS_Handle
270  * @param tc scheduler context
271  */
272 static void
273 try_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
274 {
275   struct GNUNET_GNS_Handle *handle = cls;
276
277 #if DEBUG_GNS
278   LOG (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting with GNS %p\n", handle);
279 #endif
280   handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
281   if (handle->retry_time.rel_value < GNUNET_CONSTANTS_SERVICE_RETRY.rel_value)
282     handle->retry_time = GNUNET_CONSTANTS_SERVICE_RETRY;
283   else
284     handle->retry_time = GNUNET_TIME_relative_multiply (handle->retry_time, 2);
285   if (handle->retry_time.rel_value > GNUNET_CONSTANTS_SERVICE_TIMEOUT.rel_value)
286     handle->retry_time = GNUNET_CONSTANTS_SERVICE_TIMEOUT;
287   handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
288   if (GNUNET_YES != try_connect (handle))
289   {
290 #if DEBUG_GNS
291     LOG (GNUNET_ERROR_TYPE_DEBUG, "GNS reconnect failed(!)\n");
292 #endif
293     return;
294   }
295   GNUNET_CONTAINER_multihashmap_iterate (handle->active_requests,
296                                          &add_request_to_pending, handle);
297   process_pending_messages (handle);
298 }
299
300
301 /**
302  * Try reconnecting to the GNS service.
303  *
304  * @param handle handle to gns to (possibly) disconnect and reconnect
305  */
306 static void
307 do_disconnect (struct GNUNET_GNS_Handle *handle)
308 {
309   if (handle->client == NULL)
310     return;
311   GNUNET_assert (handle->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
312   if (NULL != handle->th)
313     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
314   handle->th = NULL;
315   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
316               "Disconnecting from GNS service, will try to reconnect in %llu ms\n",
317               (unsigned long long) handle->retry_time.rel_value);
318   GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
319   handle->client = NULL;
320   handle->reconnect_task =
321       GNUNET_SCHEDULER_add_delayed (handle->retry_time, &try_reconnect, handle);
322 }
323
324 /**
325  * Transmit the next pending message, called by notify_transmit_ready
326  */
327 static size_t
328 transmit_pending (void *cls, size_t size, void *buf);
329
330 /**
331  * Handler for messages received from the GNS service
332  *
333  * @param cls the 'struct GNUNET_GNS_Handle'
334  * @param msg the incoming message
335  */
336 static void
337 message_handler (void *cls, const struct GNUNET_MessageHeader *msg);
338
339 /**
340  * Try to send messages from list of messages to send
341  */
342 static void
343 process_pending_messages (struct GNUNET_GNS_Handle *handle)
344 {
345   struct PendingMessage *head;
346
347   if (handle->client == NULL)
348   {
349     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
350                 "process_pending_messages called, but client is null, reconnecting\n");
351     do_disconnect (handle);
352     return;
353   }
354   if (handle->th != NULL)
355     return;
356   if (NULL == (head = handle->pending_head))
357     return;
358   handle->th =
359     GNUNET_CLIENT_notify_transmit_ready (handle->client,
360                                          ntohs (head->msg->size),
361                                          GNUNET_TIME_UNIT_FOREVER_REL,
362                                          GNUNET_YES, &transmit_pending,
363                                          handle);
364   if (NULL != handle->th)
365     return;
366   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
367               "notify_transmit_ready returned NULL, reconnecting\n");
368   do_disconnect (handle);
369 }
370
371
372 /**
373  * Transmit the next pending message, called by notify_transmit_ready
374  */
375 static size_t
376 transmit_pending (void *cls, size_t size, void *buf)
377 {
378   struct GNUNET_GNS_Handle *handle = cls;
379   struct PendingMessage *head;
380   size_t tsize;
381
382   handle->th = NULL;
383   if (buf == NULL)
384   {
385     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386          "Transmission to GNS service failed!  Reconnecting!\n");
387     do_disconnect (handle);
388     return 0;
389   }
390   if (NULL == (head = handle->pending_head))
391     return 0;
392
393   tsize = ntohs (head->msg->size);
394   if (size < tsize)
395   {
396     process_pending_messages (handle);
397     return 0;
398   }
399   memcpy (buf, head->msg, tsize);
400   GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
401                                head);
402   head->in_pending_queue = GNUNET_NO;
403   if (head->timeout_task != GNUNET_SCHEDULER_NO_TASK)
404   {
405     GNUNET_SCHEDULER_cancel (head->timeout_task);
406     head->timeout_task = GNUNET_SCHEDULER_NO_TASK;
407   }
408   if (GNUNET_YES == head->free_on_send)
409     GNUNET_free (head);
410   process_pending_messages (handle);
411 #if DEBUG_GNS
412   LOG (GNUNET_ERROR_TYPE_DEBUG,
413        "Forwarded request of %u bytes to GNS service\n", (unsigned int) tsize);
414 #endif
415   if (GNUNET_NO == handle->in_receive)
416   {
417 #if DEBUG_GNS
418     LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting to process replies from GNS\n");
419 #endif
420     handle->in_receive = GNUNET_YES;
421     GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
422                            GNUNET_TIME_UNIT_FOREVER_REL);
423   }
424   return tsize;
425 }
426
427 /**
428  * Process a given reply that might match the given
429  * request.
430  *
431  * @param cls the 'struct GNUNET_GNS_ClientResultMessage'
432  * @param key query of the request
433  * @param value the 'struct GNUNET_GNS_LookupHandle' of a request matching the same key
434  * @return GNUNET_YES to continue to iterate over all results,
435  *         GNUNET_NO if the reply is malformed
436  */
437 static int
438 process_reply (void *cls, const GNUNET_HashCode * key, void *value)
439 {
440   const struct GNUNET_GNS_ClientResultMessage *gns_msg = cls;
441   struct GNUNET_GNS_LookupHandle *lookup_handle = value;
442   const char *name = (const char*) &lookup_handle[1];
443   const struct GNUNET_GNS_Record *records;
444   uint32_t num_records;
445   size_t meta_length;
446   size_t msize;
447
448   if (gns_msg->unique_id != lookup_handle->unique_id)
449   {
450     /* UID mismatch */
451 #if DEBUG_GNS
452     LOG (GNUNET_ERROR_TYPE_DEBUG,
453          "Ignoring reply for %s: UID mismatch: %llu/%llu\n", GNUNET_h2s (key),
454          gns_msg->unique_id, lookup_handle->unique_id);
455 #endif
456     return GNUNET_YES;
457   }
458   msize = ntohs (gns_msg->header.size);
459   num_records = ntohl (gns_msg->num_records);
460   meta_length =
461     sizeof (struct GNUNET_GNS_ClientResultMessage) +
462     sizeof (struct GNUNET_GNS_Record) * (num_records);
463   if ((msize < meta_length) ||
464       (num_records >
465        GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_GNS_Record)))
466   {
467     GNUNET_break (0);
468     return GNUNET_NO;
469   }
470 #if DEBUG_GNS
471   LOG (GNUNET_ERROR_TYPE_DEBUG, "Giving %u byte reply for %s to application\n",
472        (unsigned int) (msize - meta_length), GNUNET_h2s (key));
473 #endif
474   records = (const struct GNUNET_GNS_Record *) &gns_msg[1];
475   lookup_handle->iter (lookup_handle->iter_cls, name, records, num_records);
476   return GNUNET_YES;
477 }
478
479
480 /**
481  * Handler for messages received from the GNS service
482  *
483  * @param cls the 'struct GNUNET_GNS_Handle'
484  * @param msg the incoming message
485  */
486 static void
487 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
488 {
489   struct GNUNET_GNS_Handle *handle = cls;
490   const struct GNUNET_GNS_ClientResultMessage *gns_msg;
491
492   if (msg == NULL)
493   {
494 #if DEBUG_GNS
495     LOG (GNUNET_ERROR_TYPE_DEBUG,
496          "Error receiving data from GNS service, reconnecting\n");
497 #endif
498     do_disconnect (handle);
499     return;
500   }
501   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_GNS_CLIENT_RESULT)
502   {
503     GNUNET_break (0);
504     do_disconnect (handle);
505     return;
506   }
507   if (ntohs (msg->size) < sizeof (struct GNUNET_GNS_ClientResultMessage))
508   {
509     GNUNET_break (0);
510     do_disconnect (handle);
511     return;
512   }
513   gns_msg = (const struct GNUNET_GNS_ClientResultMessage *) msg;
514 #if DEBUG_GNS
515   LOG (GNUNET_ERROR_TYPE_DEBUG, "Received reply for `%s' from GNS service %p\n",
516        &gns_msg->name, handle);
517 #endif
518   /* TODO uniquely identify requests... maybe hash(name) or uid */
519   GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests,
520                                               &gns_msg->key, &process_reply,
521                                               (void *) gns_msg);
522   GNUNET_CLIENT_receive (handle->client, &message_handler, handle,
523                          GNUNET_TIME_UNIT_FOREVER_REL);
524 }
525
526
527 /**
528  * Initialize the connection with the GNS service.
529  *
530  * @param cfg configuration to use
531  * @param ht_len size of the internal hash table to use for parallel requests
532  * @return handle to the GNS service, or NULL on error
533  */
534 struct GNUNET_GNS_Handle *
535 GNUNET_GNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
536                     unsigned int ht_len)
537 {
538   struct GNUNET_GNS_Handle *handle;
539
540   handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_Handle));
541   handle->cfg = cfg;
542   handle->uid_gen =
543       GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
544   handle->active_requests = GNUNET_CONTAINER_multihashmap_create (ht_len);
545   if (GNUNET_NO == try_connect (handle))
546   {
547     GNUNET_GNS_disconnect (handle);
548     return NULL;
549   }
550   return handle;
551 }
552
553
554 /**
555  * Shutdown connection with the GNS service.
556  *
557  * @param handle handle of the GNS connection to stop
558  */
559 void
560 GNUNET_GNS_disconnect (struct GNUNET_GNS_Handle *handle)
561 {
562   /* disco from GNS */
563 }
564
565
566 /**
567  * Perform an asynchronous Lookup operation on the GNS.
568  * TODO:
569  *    - Still not sure what we query for... "names" it is for now
570  *    - Do we need such sophisticated message queueing like dht? simplify?
571  *
572  * @param handle handle to the GNS service
573  * @param timeout how long to wait for transmission of this request to the service
574  * @param name the name to look up
575  * @param iter function to call on each result
576  * @param iter_cls closure for iter
577  * @return handle to stop the async get
578  */
579 struct GNUNET_GNS_LookupHandle *
580 GNUNET_GNS_lookup_start (struct GNUNET_GNS_Handle *handle,
581                          struct GNUNET_TIME_Relative timeout,
582                          const char * name,
583                          enum GNUNET_GNS_RecordType type,
584                          GNUNET_GNS_LookupIterator iter,
585                          void *iter_cls)
586 {
587   /* IPC to look for local entries, start dht lookup, return lookup_handle */
588   struct GNUNET_GNS_ClientLookupMessage *lookup_msg;
589   struct GNUNET_GNS_LookupHandle *lookup_handle;
590   GNUNET_HashCode key;
591   size_t msize;
592   struct PendingMessage *pending;
593
594   if (NULL == name)
595   {
596     return NULL;
597   }
598
599   GNUNET_CRYPTO_hash (name, strlen(name), &key);
600
601   msize = sizeof (struct GNUNET_GNS_ClientLookupMessage) + strlen(name);
602 #if DEBUG_GNS
603   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting lookup for %s in GNS %p\n",
604        name, handle);
605 #endif
606   pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
607   lookup_msg = (struct GNUNET_GNS_ClientLookupMessage *) &pending[1];
608   pending->msg = &lookup_msg->header;
609   pending->handle = handle;
610   pending->free_on_send = GNUNET_NO;
611   lookup_msg->header.size = htons (msize);
612   lookup_msg->header.type = htons (GNUNET_MESSAGE_TYPE_GNS_CLIENT_LOOKUP);
613   lookup_msg->key = key;
614   memcpy(&lookup_msg[1], name, strlen(name));
615   handle->uid_gen++;
616   lookup_msg->unique_id = handle->uid_gen;
617   GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
618                                pending);
619   pending->in_pending_queue = GNUNET_YES;
620   lookup_handle = GNUNET_malloc (sizeof (struct GNUNET_GNS_LookupHandle));
621   lookup_handle->iter = iter;
622   lookup_handle->iter_cls = iter_cls;
623   lookup_handle->message = pending;
624   lookup_handle->unique_id = lookup_msg->unique_id;
625   GNUNET_CONTAINER_multihashmap_put (handle->active_requests, &lookup_msg->key,
626                                      lookup_handle,
627                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
628   process_pending_messages (handle);
629   return lookup_handle;
630 }
631
632
633 /**
634  * Stop async GNS lookup.
635  *
636  * @param lookup_handle handle to the GNS lookup operation to stop
637  */
638 void
639 GNUNET_GNS_lookup_stop (struct GNUNET_GNS_LookupHandle *lookup_handle)
640 {
641   /* TODO Stop dht lookups */
642 }
643
644
645 /* end of gns_api.c */