use LOG macro in dv_api.c
[oweals/gnunet.git] / src / dv / dv_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 dv/dv_api.c
23  * @brief library to access the DV service
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  */
27 #include "platform.h"
28 #include "gnunet_bandwidth_lib.h"
29 #include "gnunet_client_lib.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_container_lib.h"
32 #include "gnunet_arm_service.h"
33 #include "gnunet_hello_lib.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_server_lib.h"
36 #include "gnunet_time_lib.h"
37 #include "gnunet_dv_service.h"
38 #include "dv.h"
39 #include "gnunet_transport_plugin.h"
40
41 #define LOG(kind,...) GNUNET_log_from (kind, "dv-api",__VA_ARGS__)
42
43 /**
44  * Store ready to send messages
45  */
46 struct PendingMessages
47 {
48   /**
49    * Linked list of pending messages
50    */
51   struct PendingMessages *next;
52
53   /**
54    * Message that is pending
55    */
56   struct GNUNET_DV_SendMessage *msg;
57
58   /**
59    * Timeout for this message
60    */
61   struct GNUNET_TIME_Absolute timeout;
62
63 };
64
65 /**
66  * Handle for the service.
67  */
68 struct GNUNET_DV_Handle
69 {
70
71   /**
72    * Configuration to use.
73    */
74   const struct GNUNET_CONFIGURATION_Handle *cfg;
75
76   /**
77    * Socket (if available).
78    */
79   struct GNUNET_CLIENT_Connection *client;
80
81   /**
82    * Currently pending transmission request.
83    */
84   struct GNUNET_CLIENT_TransmitHandle *th;
85
86   /**
87    * List of the currently pending messages for the DV service.
88    */
89   struct PendingMessages *pending_list;
90
91   /**
92    * Message we are currently sending.
93    */
94   struct PendingMessages *current;
95
96   /**
97    * Handler for messages we receive from the DV service
98    */
99   GNUNET_DV_MessageReceivedHandler receive_handler;
100
101   /**
102    * Closure for the receive handler
103    */
104   void *receive_cls;
105
106   /**
107    * Current unique ID
108    */
109   uint32_t uid_gen;
110
111   /**
112    * Hashmap containing outstanding send requests awaiting confirmation.
113    */
114   struct GNUNET_CONTAINER_MultiHashMap *send_callbacks;
115
116 };
117
118
119 struct StartContext
120 {
121   /**
122    * Start message
123    */
124   struct GNUNET_MessageHeader *message;
125
126   /**
127    * Handle to service, in case of timeout
128    */
129   struct GNUNET_DV_Handle *handle;
130 };
131
132 struct SendCallbackContext
133 {
134   /**
135    * The continuation to call once a message is confirmed sent (or failed)
136    */
137   GNUNET_TRANSPORT_TransmitContinuation cont;
138
139   /**
140    * Closure to call with send continuation.
141    */
142   void *cont_cls;
143
144   /**
145    * Target of the message.
146    */
147   struct GNUNET_PeerIdentity target;
148 };
149
150 /**
151  * Convert unique ID to hash code.
152  *
153  * @param uid unique ID to convert
154  * @param hash set to uid (extended with zeros)
155  */
156 static void
157 hash_from_uid (uint32_t uid, GNUNET_HashCode * hash)
158 {
159   memset (hash, 0, sizeof (GNUNET_HashCode));
160   *((uint32_t *) hash) = uid;
161 }
162
163 /**
164  * Try to (re)connect to the dv service.
165  *
166  * @param ret handle to the (disconnected) dv service
167  *
168  * @return GNUNET_YES on success, GNUNET_NO on failure.
169  */
170 static int
171 try_connect (struct GNUNET_DV_Handle *ret)
172 {
173   if (ret->client != NULL)
174     return GNUNET_OK;
175   ret->client = GNUNET_CLIENT_connect ("dv", ret->cfg);
176   if (ret->client != NULL)
177     return GNUNET_YES;
178 #if DEBUG_DV_MESSAGES
179   LOG (GNUNET_ERROR_TYPE_DEBUG, _("Failed to connect to the dv service!\n"));
180 #endif
181   return GNUNET_NO;
182 }
183
184 static void
185 process_pending_message (struct GNUNET_DV_Handle *handle);
186
187 /**
188  * Send complete, schedule next
189  *
190  * @param handle handle to the dv service
191  * @param code return code for send (unused)
192  */
193 static void
194 finish (struct GNUNET_DV_Handle *handle, int code)
195 {
196   struct PendingMessages *pos = handle->current;
197
198   handle->current = NULL;
199   process_pending_message (handle);
200
201   GNUNET_free (pos->msg);
202   GNUNET_free (pos);
203 }
204
205 /**
206  * Notification that we can send data
207  *
208  * @param cls handle to the dv service (struct GNUNET_DV_Handle)
209  * @param size how many bytes can we send
210  * @param buf where to copy the message to send
211  *
212  * @return how many bytes we copied to buf
213  */
214 static size_t
215 transmit_pending (void *cls, size_t size, void *buf)
216 {
217   struct GNUNET_DV_Handle *handle = cls;
218   size_t ret;
219   size_t tsize;
220
221 #if DEBUG_DV
222   if (handle->current != NULL)
223     LOG (GNUNET_ERROR_TYPE_DEBUG,
224          "DV API: Transmit pending called with message type %d\n",
225          ntohs (handle->current->msg->header.type));
226 #endif
227
228   if (buf == NULL)
229   {
230 #if DEBUG_DV
231     LOG (GNUNET_ERROR_TYPE_DEBUG, "DV API: Transmit pending FAILED!\n\n\n");
232 #endif
233     finish (handle, GNUNET_SYSERR);
234     return 0;
235   }
236   handle->th = NULL;
237
238   ret = 0;
239
240   if (handle->current != NULL)
241   {
242     tsize = ntohs (handle->current->msg->header.size);
243     if (size >= tsize)
244     {
245       memcpy (buf, handle->current->msg, tsize);
246 #if DEBUG_DV
247       LOG (GNUNET_ERROR_TYPE_DEBUG,
248            "DV API: Copied %d bytes into buffer!\n\n\n", tsize);
249 #endif
250       finish (handle, GNUNET_OK);
251       return tsize;
252     }
253
254   }
255
256   return ret;
257 }
258
259 /**
260  * Try to send messages from list of messages to send
261  *
262  * @param handle handle to the distance vector service
263  */
264 static void
265 process_pending_message (struct GNUNET_DV_Handle *handle)
266 {
267
268   if (handle->current != NULL)
269     return;                     /* action already pending */
270   if (GNUNET_YES != try_connect (handle))
271   {
272     finish (handle, GNUNET_SYSERR);
273     return;
274   }
275
276   /* schedule next action */
277   handle->current = handle->pending_list;
278   if (NULL == handle->current)
279   {
280     return;
281   }
282   handle->pending_list = handle->pending_list->next;
283   handle->current->next = NULL;
284
285   if (NULL ==
286       (handle->th =
287        GNUNET_CLIENT_notify_transmit_ready (handle->client,
288                                             ntohs (handle->current->msg->
289                                                    header.size),
290                                             handle->current->msg->timeout,
291                                             GNUNET_YES, &transmit_pending,
292                                             handle)))
293   {
294 #if DEBUG_DV
295     LOG (GNUNET_ERROR_TYPE_DEBUG,
296          "Failed to transmit request to dv service.\n");
297 #endif
298     finish (handle, GNUNET_SYSERR);
299   }
300 }
301
302 /**
303  * Add a pending message to the linked list
304  *
305  * @param handle handle to the specified DV api
306  * @param msg the message to add to the list
307  */
308 static void
309 add_pending (struct GNUNET_DV_Handle *handle, struct GNUNET_DV_SendMessage *msg)
310 {
311   struct PendingMessages *new_message;
312   struct PendingMessages *pos;
313   struct PendingMessages *last;
314
315   new_message = GNUNET_malloc (sizeof (struct PendingMessages));
316   new_message->msg = msg;
317
318   if (handle->pending_list != NULL)
319   {
320     pos = handle->pending_list;
321     while (pos != NULL)
322     {
323       last = pos;
324       pos = pos->next;
325     }
326     last->next = new_message;
327   }
328   else
329   {
330     handle->pending_list = new_message;
331   }
332
333   process_pending_message (handle);
334 }
335
336 /**
337  * Handles a message sent from the DV service to us.
338  * Parse it out and give it to the plugin.
339  *
340  * @param cls the handle to the DV API
341  * @param msg the message that was received
342  */
343 void
344 handle_message_receipt (void *cls, const struct GNUNET_MessageHeader *msg)
345 {
346   struct GNUNET_DV_Handle *handle = cls;
347   struct GNUNET_DV_MessageReceived *received_msg;
348   struct GNUNET_DV_SendResultMessage *send_result_msg;
349   size_t packed_msg_len;
350   size_t sender_address_len;
351   char *sender_address;
352   char *packed_msg;
353   char *packed_msg_start;
354   GNUNET_HashCode uidhash;
355   struct SendCallbackContext *send_ctx;
356
357   if (msg == NULL)
358   {
359 #if DEBUG_DV_MESSAGES
360     LOG (GNUNET_ERROR_TYPE_DEBUG, "DV_API receive: connection closed\n");
361 #endif
362     return;                     /* Connection closed? */
363   }
364
365   GNUNET_assert ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_TRANSPORT_DV_RECEIVE)
366                  || (ntohs (msg->type) ==
367                      GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND_RESULT));
368
369   switch (ntohs (msg->type))
370   {
371   case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_RECEIVE:
372     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_MessageReceived))
373       return;
374
375     received_msg = (struct GNUNET_DV_MessageReceived *) msg;
376     packed_msg_len = ntohl (received_msg->msg_len);
377     sender_address_len =
378         ntohs (msg->size) - packed_msg_len -
379         sizeof (struct GNUNET_DV_MessageReceived);
380     GNUNET_assert (sender_address_len > 0);
381     sender_address = GNUNET_malloc (sender_address_len);
382     memcpy (sender_address, &received_msg[1], sender_address_len);
383     packed_msg_start = (char *) &received_msg[1];
384     packed_msg = GNUNET_malloc (packed_msg_len);
385     memcpy (packed_msg, &packed_msg_start[sender_address_len], packed_msg_len);
386
387 #if DEBUG_DV_MESSAGES
388     LOG (GNUNET_ERROR_TYPE_DEBUG,
389          "DV_API receive: packed message type: %d or %d\n",
390          ntohs (((struct GNUNET_MessageHeader *) packed_msg)->type),
391          ((struct GNUNET_MessageHeader *) packed_msg)->type);
392     LOG (GNUNET_ERROR_TYPE_DEBUG,
393          "DV_API receive: message sender reported as %s\n",
394          GNUNET_i2s (&received_msg->sender));
395     LOG (GNUNET_ERROR_TYPE_DEBUG, "DV_API receive: distance is %u\n",
396          ntohl (received_msg->distance));
397 #endif
398
399     handle->receive_handler (handle->receive_cls, &received_msg->sender,
400                              packed_msg, packed_msg_len,
401                              ntohl (received_msg->distance), sender_address,
402                              sender_address_len);
403
404     GNUNET_free (sender_address);
405     break;
406   case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND_RESULT:
407     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_SendResultMessage))
408       return;
409
410     send_result_msg = (struct GNUNET_DV_SendResultMessage *) msg;
411     hash_from_uid (ntohl (send_result_msg->uid), &uidhash);
412     send_ctx =
413         GNUNET_CONTAINER_multihashmap_get (handle->send_callbacks, &uidhash);
414
415     if ((send_ctx != NULL) && (send_ctx->cont != NULL))
416     {
417       if (ntohl (send_result_msg->result) == 0)
418       {
419         send_ctx->cont (send_ctx->cont_cls, &send_ctx->target, GNUNET_OK);
420       }
421       else
422       {
423         send_ctx->cont (send_ctx->cont_cls, &send_ctx->target, GNUNET_SYSERR);
424       }
425     }
426     GNUNET_free_non_null (send_ctx);
427     break;
428   default:
429     break;
430   }
431   GNUNET_CLIENT_receive (handle->client, &handle_message_receipt, handle,
432                          GNUNET_TIME_UNIT_FOREVER_REL);
433 }
434
435 /**
436  * Send a message from the plugin to the DV service indicating that
437  * a message should be sent via DV to some peer.
438  *
439  * @param dv_handle the handle to the DV api
440  * @param target the final target of the message
441  * @param msgbuf the msg(s) to send
442  * @param msgbuf_size the size of msgbuf
443  * @param priority priority to pass on to core when sending the message
444  * @param timeout how long can this message be delayed (pass through to core)
445  * @param addr the address of this peer (internally known to DV)
446  * @param addrlen the length of the peer address
447  * @param cont continuation to call once the message has been sent (or failed)
448  * @param cont_cls closure for continuation
449  *
450  */
451 int
452 GNUNET_DV_send (struct GNUNET_DV_Handle *dv_handle,
453                 const struct GNUNET_PeerIdentity *target, const char *msgbuf,
454                 size_t msgbuf_size, unsigned int priority,
455                 struct GNUNET_TIME_Relative timeout, const void *addr,
456                 size_t addrlen, GNUNET_TRANSPORT_TransmitContinuation cont,
457                 void *cont_cls)
458 {
459   struct GNUNET_DV_SendMessage *msg;
460   struct SendCallbackContext *send_ctx;
461   char *end_of_message;
462   GNUNET_HashCode uidhash;
463   int msize;
464
465 #if DEBUG_DV_MESSAGES
466   dv_handle->uid_gen =
467       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, UINT32_MAX);
468 #else
469   dv_handle->uid_gen++;
470 #endif
471
472   msize = sizeof (struct GNUNET_DV_SendMessage) + addrlen + msgbuf_size;
473   msg = GNUNET_malloc (msize);
474   msg->header.size = htons (msize);
475   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND);
476   memcpy (&msg->target, target, sizeof (struct GNUNET_PeerIdentity));
477   msg->priority = htonl (priority);
478   msg->timeout = timeout;
479   msg->addrlen = htonl (addrlen);
480   msg->uid = htonl (dv_handle->uid_gen);
481   memcpy (&msg[1], addr, addrlen);
482   end_of_message = (char *) &msg[1];
483   end_of_message = &end_of_message[addrlen];
484   memcpy (end_of_message, msgbuf, msgbuf_size);
485   add_pending (dv_handle, msg);
486   send_ctx = GNUNET_malloc (sizeof (struct SendCallbackContext));
487   send_ctx->cont = cont;
488   send_ctx->cont_cls = cont_cls;
489   memcpy (&send_ctx->target, target, sizeof (struct GNUNET_PeerIdentity));
490   hash_from_uid (dv_handle->uid_gen, &uidhash);
491   GNUNET_CONTAINER_multihashmap_put (dv_handle->send_callbacks, &uidhash,
492                                      send_ctx,
493                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
494
495   return GNUNET_OK;
496 }
497
498 /**
499  * Callback to transmit a start message to
500  * the DV service, once we can send
501  *
502  * @param cls struct StartContext
503  * @param size how much can we send
504  * @param buf where to copy the message
505  *
506  * @return number of bytes copied to buf
507  */
508 static size_t
509 transmit_start (void *cls, size_t size, void *buf)
510 {
511   struct StartContext *start_context = cls;
512   struct GNUNET_DV_Handle *handle = start_context->handle;
513   size_t tsize;
514
515 #if DEBUG_DV
516   LOG (GNUNET_ERROR_TYPE_DEBUG, "DV API: sending start request to service\n");
517 #endif
518   if (buf == NULL)
519   {
520     GNUNET_free (start_context->message);
521     GNUNET_free (start_context);
522     GNUNET_DV_disconnect (handle);
523     return 0;
524   }
525
526   tsize = ntohs (start_context->message->size);
527   if (size >= tsize)
528   {
529     memcpy (buf, start_context->message, tsize);
530     GNUNET_free (start_context->message);
531     GNUNET_free (start_context);
532     GNUNET_CLIENT_receive (handle->client, &handle_message_receipt, handle,
533                            GNUNET_TIME_UNIT_FOREVER_REL);
534
535
536     return tsize;
537   }
538
539   return 0;
540 }
541
542 /**
543  * Connect to the DV service
544  *
545  * @param cfg the configuration to use
546  * @param receive_handler method call when on receipt from the service
547  * @param receive_handler_cls closure for receive_handler
548  *
549  * @return handle to the DV service
550  */
551 struct GNUNET_DV_Handle *
552 GNUNET_DV_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
553                    GNUNET_DV_MessageReceivedHandler receive_handler,
554                    void *receive_handler_cls)
555 {
556   struct GNUNET_DV_Handle *handle;
557   struct GNUNET_MessageHeader *start_message;
558   struct StartContext *start_context;
559
560   handle = GNUNET_malloc (sizeof (struct GNUNET_DV_Handle));
561
562   handle->cfg = cfg;
563   handle->pending_list = NULL;
564   handle->current = NULL;
565   handle->th = NULL;
566   handle->client = GNUNET_CLIENT_connect ("dv", cfg);
567   handle->receive_handler = receive_handler;
568   handle->receive_cls = receive_handler_cls;
569
570   if (handle->client == NULL)
571   {
572     GNUNET_free (handle);
573     return NULL;
574   }
575
576   start_message = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader));
577   start_message->size = htons (sizeof (struct GNUNET_MessageHeader));
578   start_message->type = htons (GNUNET_MESSAGE_TYPE_DV_START);
579
580   start_context = GNUNET_malloc (sizeof (struct StartContext));
581   start_context->handle = handle;
582   start_context->message = start_message;
583   GNUNET_CLIENT_notify_transmit_ready (handle->client,
584                                        sizeof (struct GNUNET_MessageHeader),
585                                        GNUNET_TIME_relative_multiply
586                                        (GNUNET_TIME_UNIT_SECONDS, 60),
587                                        GNUNET_YES, &transmit_start,
588                                        start_context);
589
590   handle->send_callbacks = GNUNET_CONTAINER_multihashmap_create (100);
591
592   return handle;
593 }
594
595 /**
596  * Disconnect from the DV service
597  *
598  * @param handle the current handle to the service to disconnect
599  */
600 void
601 GNUNET_DV_disconnect (struct GNUNET_DV_Handle *handle)
602 {
603   struct PendingMessages *pos;
604
605   GNUNET_assert (handle != NULL);
606
607   if (handle->th != NULL)       /* We have a live transmit request in the Aether */
608   {
609     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
610     handle->th = NULL;
611   }
612   if (handle->current != NULL)  /* We are trying to send something now, clean it up */
613     GNUNET_free (handle->current);
614   while (NULL != (pos = handle->pending_list))  /* Remove all pending sends from the list */
615   {
616     handle->pending_list = pos->next;
617     GNUNET_free (pos);
618   }
619   if (handle->client != NULL)   /* Finally, disconnect from the service */
620   {
621     GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
622     handle->client = NULL;
623   }
624
625   GNUNET_free (handle);
626 }
627
628 /* end of dv_api.c */