separate SD calculations
[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    * Payload size in bytes
151    */
152   size_t payload_size;
153
154   /**
155    * DV message size
156    */
157   size_t msg_size;
158 };
159
160 /**
161  * Convert unique ID to hash code.
162  *
163  * @param uid unique ID to convert
164  * @param hash set to uid (extended with zeros)
165  */
166 static void
167 hash_from_uid (uint32_t uid, struct GNUNET_HashCode * hash)
168 {
169   memset (hash, 0, sizeof (struct GNUNET_HashCode));
170   *((uint32_t *) hash) = uid;
171 }
172
173 /**
174  * Try to (re)connect to the dv service.
175  *
176  * @param ret handle to the (disconnected) dv service
177  *
178  * @return GNUNET_YES on success, GNUNET_NO on failure.
179  */
180 static int
181 try_connect (struct GNUNET_DV_Handle *ret)
182 {
183   if (ret->client != NULL)
184     return GNUNET_OK;
185   ret->client = GNUNET_CLIENT_connect ("dv", ret->cfg);
186   if (ret->client != NULL)
187     return GNUNET_YES;
188 #if DEBUG_DV_MESSAGES
189   LOG (GNUNET_ERROR_TYPE_DEBUG, _("Failed to connect to the dv service!\n"));
190 #endif
191   return GNUNET_NO;
192 }
193
194 static void
195 process_pending_message (struct GNUNET_DV_Handle *handle);
196
197 /**
198  * Send complete, schedule next
199  *
200  * @param handle handle to the dv service
201  * @param code return code for send (unused)
202  */
203 static void
204 finish (struct GNUNET_DV_Handle *handle, int code)
205 {
206   struct PendingMessages *pos = handle->current;
207
208   handle->current = NULL;
209   process_pending_message (handle);
210
211   GNUNET_free (pos->msg);
212   GNUNET_free (pos);
213 }
214
215 /**
216  * Notification that we can send data
217  *
218  * @param cls handle to the dv service (struct GNUNET_DV_Handle)
219  * @param size how many bytes can we send
220  * @param buf where to copy the message to send
221  *
222  * @return how many bytes we copied to buf
223  */
224 static size_t
225 transmit_pending (void *cls, size_t size, void *buf)
226 {
227   struct GNUNET_DV_Handle *handle = cls;
228   size_t ret;
229   size_t tsize;
230
231 #if DEBUG_DV
232   if (handle->current != NULL)
233     LOG (GNUNET_ERROR_TYPE_DEBUG,
234          "DV API: Transmit pending called with message type %d\n",
235          ntohs (handle->current->msg->header.type));
236 #endif
237
238   if (buf == NULL)
239   {
240 #if DEBUG_DV
241     LOG (GNUNET_ERROR_TYPE_DEBUG, "DV API: Transmit pending FAILED!\n\n\n");
242 #endif
243     finish (handle, GNUNET_SYSERR);
244     return 0;
245   }
246   handle->th = NULL;
247
248   ret = 0;
249
250   if (handle->current != NULL)
251   {
252     tsize = ntohs (handle->current->msg->header.size);
253     if (size >= tsize)
254     {
255       memcpy (buf, handle->current->msg, tsize);
256 #if DEBUG_DV
257       LOG (GNUNET_ERROR_TYPE_DEBUG,
258            "DV API: Copied %d bytes into buffer!\n\n\n", tsize);
259 #endif
260       finish (handle, GNUNET_OK);
261       return tsize;
262     }
263
264   }
265
266   return ret;
267 }
268
269 /**
270  * Try to send messages from list of messages to send
271  *
272  * @param handle handle to the distance vector service
273  */
274 static void
275 process_pending_message (struct GNUNET_DV_Handle *handle)
276 {
277
278   if (handle->current != NULL)
279     return;                     /* action already pending */
280   if (GNUNET_YES != try_connect (handle))
281   {
282     finish (handle, GNUNET_SYSERR);
283     return;
284   }
285
286   /* schedule next action */
287   handle->current = handle->pending_list;
288   if (NULL == handle->current)
289   {
290     return;
291   }
292   handle->pending_list = handle->pending_list->next;
293   handle->current->next = NULL;
294
295   if (NULL ==
296       (handle->th =
297        GNUNET_CLIENT_notify_transmit_ready (handle->client,
298                                             ntohs (handle->current->msg->
299                                                    header.size),
300                                             handle->current->msg->timeout,
301                                             GNUNET_YES, &transmit_pending,
302                                             handle)))
303   {
304 #if DEBUG_DV
305     LOG (GNUNET_ERROR_TYPE_DEBUG,
306          "Failed to transmit request to dv service.\n");
307 #endif
308     finish (handle, GNUNET_SYSERR);
309   }
310 }
311
312 /**
313  * Add a pending message to the linked list
314  *
315  * @param handle handle to the specified DV api
316  * @param msg the message to add to the list
317  */
318 static void
319 add_pending (struct GNUNET_DV_Handle *handle, struct GNUNET_DV_SendMessage *msg)
320 {
321   struct PendingMessages *new_message;
322   struct PendingMessages *pos;
323   struct PendingMessages *last;
324
325   new_message = GNUNET_malloc (sizeof (struct PendingMessages));
326   new_message->msg = msg;
327
328   if (handle->pending_list != NULL)
329   {
330     pos = handle->pending_list;
331     while (pos != NULL)
332     {
333       last = pos;
334       pos = pos->next;
335     }
336     last->next = new_message;
337   }
338   else
339   {
340     handle->pending_list = new_message;
341   }
342
343   process_pending_message (handle);
344 }
345
346 /**
347  * Handles a message sent from the DV service to us.
348  * Parse it out and give it to the plugin.
349  *
350  * @param cls the handle to the DV API
351  * @param msg the message that was received
352  */
353 void
354 handle_message_receipt (void *cls, const struct GNUNET_MessageHeader *msg)
355 {
356   struct GNUNET_DV_Handle *handle = cls;
357   struct GNUNET_DV_MessageReceived *received_msg;
358   struct GNUNET_DV_SendResultMessage *send_result_msg;
359   size_t packed_msg_len;
360   size_t sender_address_len;
361   char *sender_address;
362   char *packed_msg;
363   char *packed_msg_start;
364   struct GNUNET_HashCode uidhash;
365   struct SendCallbackContext *send_ctx;
366
367   if (msg == NULL)
368   {
369 #if DEBUG_DV_MESSAGES
370     LOG (GNUNET_ERROR_TYPE_DEBUG, "DV_API receive: connection closed\n");
371 #endif
372     return;                     /* Connection closed? */
373   }
374
375   GNUNET_assert ((ntohs (msg->type) == GNUNET_MESSAGE_TYPE_TRANSPORT_DV_RECEIVE)
376                  || (ntohs (msg->type) ==
377                      GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND_RESULT));
378
379   switch (ntohs (msg->type))
380   {
381   case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_RECEIVE:
382     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_MessageReceived))
383       return;
384
385     received_msg = (struct GNUNET_DV_MessageReceived *) msg;
386     packed_msg_len = ntohl (received_msg->msg_len);
387     sender_address_len =
388         ntohs (msg->size) - packed_msg_len -
389         sizeof (struct GNUNET_DV_MessageReceived);
390     GNUNET_assert (sender_address_len > 0);
391     sender_address = GNUNET_malloc (sender_address_len);
392     memcpy (sender_address, &received_msg[1], sender_address_len);
393     packed_msg_start = (char *) &received_msg[1];
394     packed_msg = GNUNET_malloc (packed_msg_len);
395     memcpy (packed_msg, &packed_msg_start[sender_address_len], packed_msg_len);
396
397 #if DEBUG_DV_MESSAGES
398     LOG (GNUNET_ERROR_TYPE_DEBUG,
399          "DV_API receive: packed message type: %d or %d\n",
400          ntohs (((struct GNUNET_MessageHeader *) packed_msg)->type),
401          ((struct GNUNET_MessageHeader *) packed_msg)->type);
402     LOG (GNUNET_ERROR_TYPE_DEBUG,
403          "DV_API receive: message sender reported as %s\n",
404          GNUNET_i2s (&received_msg->sender));
405     LOG (GNUNET_ERROR_TYPE_DEBUG, "DV_API receive: distance is %u\n",
406          ntohl (received_msg->distance));
407 #endif
408
409     handle->receive_handler (handle->receive_cls, &received_msg->sender,
410                              packed_msg, packed_msg_len,
411                              ntohl (received_msg->distance), sender_address,
412                              sender_address_len);
413
414     GNUNET_free (sender_address);
415     break;
416   case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND_RESULT:
417     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_SendResultMessage))
418       return;
419
420     send_result_msg = (struct GNUNET_DV_SendResultMessage *) msg;
421     hash_from_uid (ntohl (send_result_msg->uid), &uidhash);
422     send_ctx =
423         GNUNET_CONTAINER_multihashmap_get (handle->send_callbacks, &uidhash);
424
425     if ((send_ctx != NULL) && (send_ctx->cont != NULL))
426     {
427       if (ntohl (send_result_msg->result) == 0)
428       {
429         send_ctx->cont (send_ctx->cont_cls, &send_ctx->target, GNUNET_OK,
430                         send_ctx->payload_size, send_ctx->msg_size);
431       }
432       else
433       {
434         send_ctx->cont (send_ctx->cont_cls, &send_ctx->target, GNUNET_SYSERR,
435                         send_ctx->payload_size, 0);
436       }
437     }
438     GNUNET_free_non_null (send_ctx);
439     break;
440   default:
441     break;
442   }
443   GNUNET_CLIENT_receive (handle->client, &handle_message_receipt, handle,
444                          GNUNET_TIME_UNIT_FOREVER_REL);
445 }
446
447 /**
448  * Send a message from the plugin to the DV service indicating that
449  * a message should be sent via DV to some peer.
450  *
451  * @param dv_handle the handle to the DV api
452  * @param target the final target of the message
453  * @param msgbuf the msg(s) to send
454  * @param msgbuf_size the size of msgbuf
455  * @param priority priority to pass on to core when sending the message
456  * @param timeout how long can this message be delayed (pass through to core)
457  * @param addr the address of this peer (internally known to DV)
458  * @param addrlen the length of the peer address
459  * @param cont continuation to call once the message has been sent (or failed)
460  * @param cont_cls closure for continuation
461  *
462  */
463 int
464 GNUNET_DV_send (struct GNUNET_DV_Handle *dv_handle,
465                 const struct GNUNET_PeerIdentity *target, const char *msgbuf,
466                 size_t msgbuf_size, unsigned int priority,
467                 struct GNUNET_TIME_Relative timeout, const void *addr,
468                 size_t addrlen, GNUNET_TRANSPORT_TransmitContinuation cont,
469                 void *cont_cls)
470 {
471   struct GNUNET_DV_SendMessage *msg;
472   struct SendCallbackContext *send_ctx;
473   char *end_of_message;
474   struct GNUNET_HashCode uidhash;
475   int msize;
476
477 #if DEBUG_DV_MESSAGES
478   dv_handle->uid_gen =
479       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, UINT32_MAX);
480 #else
481   dv_handle->uid_gen++;
482 #endif
483
484   msize = sizeof (struct GNUNET_DV_SendMessage) + addrlen + msgbuf_size;
485   msg = GNUNET_malloc (msize);
486   msg->header.size = htons (msize);
487   msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND);
488   memcpy (&msg->target, target, sizeof (struct GNUNET_PeerIdentity));
489   msg->priority = htonl (priority);
490   msg->timeout = timeout;
491   msg->addrlen = htonl (addrlen);
492   msg->uid = htonl (dv_handle->uid_gen);
493   memcpy (&msg[1], addr, addrlen);
494   end_of_message = (char *) &msg[1];
495   end_of_message = &end_of_message[addrlen];
496   memcpy (end_of_message, msgbuf, msgbuf_size);
497   add_pending (dv_handle, msg);
498   send_ctx = GNUNET_malloc (sizeof (struct SendCallbackContext));
499   send_ctx->payload_size = msgbuf_size;
500   send_ctx->msg_size = msize;
501   send_ctx->cont = cont;
502   send_ctx->cont_cls = cont_cls;
503   memcpy (&send_ctx->target, target, sizeof (struct GNUNET_PeerIdentity));
504   hash_from_uid (dv_handle->uid_gen, &uidhash);
505   GNUNET_CONTAINER_multihashmap_put (dv_handle->send_callbacks, &uidhash,
506                                      send_ctx,
507                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
508
509   return GNUNET_OK;
510 }
511
512 /**
513  * Callback to transmit a start message to
514  * the DV service, once we can send
515  *
516  * @param cls struct StartContext
517  * @param size how much can we send
518  * @param buf where to copy the message
519  *
520  * @return number of bytes copied to buf
521  */
522 static size_t
523 transmit_start (void *cls, size_t size, void *buf)
524 {
525   struct StartContext *start_context = cls;
526   struct GNUNET_DV_Handle *handle = start_context->handle;
527   size_t tsize;
528
529 #if DEBUG_DV
530   LOG (GNUNET_ERROR_TYPE_DEBUG, "DV API: sending start request to service\n");
531 #endif
532   if (buf == NULL)
533   {
534     GNUNET_free (start_context->message);
535     GNUNET_free (start_context);
536     GNUNET_DV_disconnect (handle);
537     return 0;
538   }
539
540   tsize = ntohs (start_context->message->size);
541   if (size >= tsize)
542   {
543     memcpy (buf, start_context->message, tsize);
544     GNUNET_free (start_context->message);
545     GNUNET_free (start_context);
546     GNUNET_CLIENT_receive (handle->client, &handle_message_receipt, handle,
547                            GNUNET_TIME_UNIT_FOREVER_REL);
548
549
550     return tsize;
551   }
552
553   return 0;
554 }
555
556 /**
557  * Connect to the DV service
558  *
559  * @param cfg the configuration to use
560  * @param receive_handler method call when on receipt from the service
561  * @param receive_handler_cls closure for receive_handler
562  *
563  * @return handle to the DV service
564  */
565 struct GNUNET_DV_Handle *
566 GNUNET_DV_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
567                    GNUNET_DV_MessageReceivedHandler receive_handler,
568                    void *receive_handler_cls)
569 {
570   struct GNUNET_DV_Handle *handle;
571   struct GNUNET_MessageHeader *start_message;
572   struct StartContext *start_context;
573
574   handle = GNUNET_malloc (sizeof (struct GNUNET_DV_Handle));
575
576   handle->cfg = cfg;
577   handle->pending_list = NULL;
578   handle->current = NULL;
579   handle->th = NULL;
580   handle->client = GNUNET_CLIENT_connect ("dv", cfg);
581   handle->receive_handler = receive_handler;
582   handle->receive_cls = receive_handler_cls;
583
584   if (handle->client == NULL)
585   {
586     GNUNET_free (handle);
587     return NULL;
588   }
589
590   start_message = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader));
591   start_message->size = htons (sizeof (struct GNUNET_MessageHeader));
592   start_message->type = htons (GNUNET_MESSAGE_TYPE_DV_START);
593
594   start_context = GNUNET_malloc (sizeof (struct StartContext));
595   start_context->handle = handle;
596   start_context->message = start_message;
597   GNUNET_CLIENT_notify_transmit_ready (handle->client,
598                                        sizeof (struct GNUNET_MessageHeader),
599                                        GNUNET_TIME_relative_multiply
600                                        (GNUNET_TIME_UNIT_SECONDS, 60),
601                                        GNUNET_YES, &transmit_start,
602                                        start_context);
603
604   handle->send_callbacks = GNUNET_CONTAINER_multihashmap_create (100, GNUNET_NO);
605
606   return handle;
607 }
608
609 /**
610  * Disconnect from the DV service
611  *
612  * @param handle the current handle to the service to disconnect
613  */
614 void
615 GNUNET_DV_disconnect (struct GNUNET_DV_Handle *handle)
616 {
617   struct PendingMessages *pos;
618
619   GNUNET_assert (handle != NULL);
620
621   if (handle->th != NULL)       /* We have a live transmit request in the Aether */
622   {
623     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
624     handle->th = NULL;
625   }
626   if (handle->current != NULL)  /* We are trying to send something now, clean it up */
627     GNUNET_free (handle->current);
628   while (NULL != (pos = handle->pending_list))  /* Remove all pending sends from the list */
629   {
630     handle->pending_list = pos->next;
631     GNUNET_free (pos);
632   }
633   if (handle->client != NULL)   /* Finally, disconnect from the service */
634   {
635     GNUNET_CLIENT_disconnect (handle->client);
636     handle->client = NULL;
637   }
638
639   GNUNET_free (handle);
640 }
641
642 /* end of dv_api.c */