60c1ba4e493561e0d68361c11fa661728032802d
[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 2, 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
40
41 struct PendingMessages
42 {
43   /**
44    * Linked list of pending messages
45    */
46   struct PendingMessages *next;
47
48   /**
49    * Message that is pending
50    */
51   struct GNUNET_DV_SendMessage *msg;
52
53   /**
54    * Timeout for this message
55    */
56   struct GNUNET_TIME_Absolute timeout;
57
58 };
59
60
61
62 /**
63  * Handle for the service.
64  */
65 struct GNUNET_DV_Handle
66 {
67   /**
68    * Our scheduler.
69    */
70   struct GNUNET_SCHEDULER_Handle *sched;
71
72   /**
73    * Configuration to use.
74    */
75   const struct GNUNET_CONFIGURATION_Handle *cfg;
76
77   /**
78    * Socket (if available).
79    */
80   struct GNUNET_CLIENT_Connection *client;
81
82   /**
83    * Currently pending transmission request.
84    */
85   struct GNUNET_CLIENT_TransmitHandle *th;
86
87   /**
88    * List of the currently pending messages for the DV service.
89    */
90   struct PendingMessages *pending_list;
91
92   /**
93    * Message we are currently sending.
94    */
95   struct PendingMessages *current;
96
97   /**
98    * Kill off the connection and any pending messages.
99    */
100   int do_destroy;
101
102   /**
103    * Handler for messages we receive from the DV service
104    */
105   GNUNET_DV_MessageReceivedHandler receive_handler;
106
107   /**
108    * Closure for the receive handler
109    */
110   void *receive_cls;
111
112 };
113
114
115 struct StartContext
116 {
117
118   /**
119    * Start message
120    */
121   struct GNUNET_MessageHeader *message;
122
123   /**
124    * Handle to service, in case of timeout
125    */
126   struct GNUNET_DV_Handle *handle;
127 };
128
129
130 /**
131  * Try to (re)connect to the dv service.
132  *
133  * @return GNUNET_YES on success, GNUNET_NO on failure.
134  */
135 static int
136 try_connect (struct GNUNET_DV_Handle *ret)
137 {
138   if (ret->client != NULL)
139     return GNUNET_OK;
140   ret->client = GNUNET_CLIENT_connect (ret->sched, "dv", ret->cfg);
141   if (ret->client != NULL)
142     return GNUNET_YES;
143 #if DEBUG_STATISTICS
144   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145               _("Failed to connect to the dv service!\n"));
146 #endif
147   return GNUNET_NO;
148 }
149
150 static void process_pending_message(struct GNUNET_DV_Handle *handle);
151
152 /**
153  * Send complete, schedule next
154  */
155 static void
156 finish (struct GNUNET_DV_Handle *handle, int code)
157 {
158   struct PendingMessages *pos = handle->current;
159   handle->current = NULL;
160   process_pending_message (handle);
161
162   GNUNET_free (pos);
163 }
164
165
166 static size_t
167 transmit_pending (void *cls, size_t size, void *buf)
168 {
169   struct GNUNET_DV_Handle *handle = cls;
170   size_t ret;
171   size_t tsize;
172
173 #if DEBUG_DV
174   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "DV API: Transmit pending called with message type %d\n", ntohs(handle->current->msg->header.type));
175 #endif
176
177   if (buf == NULL)
178     {
179 #if DEBUG_DV
180       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "DV API: Transmit pending FAILED!\n\n\n");
181 #endif
182       finish(handle, GNUNET_SYSERR);
183       return 0;
184     }
185   handle->th = NULL;
186
187   ret = 0;
188
189   if (handle->current != NULL)
190   {
191     tsize = ntohs(handle->current->msg->header.size);
192     if (size >= tsize)
193     {
194       memcpy(buf, handle->current->msg, tsize);
195 #if DEBUG_DV
196       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "DV API: Copied %d bytes into buffer!\n\n\n", tsize);
197 #endif
198       finish(handle, GNUNET_OK);
199       return tsize;
200     }
201
202   }
203
204   return ret;
205 }
206
207 /**
208  * Try to send messages from list of messages to send
209  */
210 static void process_pending_message(struct GNUNET_DV_Handle *handle)
211 {
212
213   if (handle->current != NULL)
214     return;                     /* action already pending */
215   if (GNUNET_YES != try_connect (handle))
216     {
217       finish (handle, GNUNET_SYSERR);
218       return;
219     }
220
221   /* schedule next action */
222   handle->current = handle->pending_list;
223   if (NULL == handle->current)
224     {
225       if (handle->do_destroy)
226         {
227           handle->do_destroy = GNUNET_NO;
228           //GNUNET_DV_disconnect (handle); /* FIXME: replace with proper disconnect stuffs */
229         }
230       return;
231     }
232   handle->pending_list = handle->pending_list->next;
233   handle->current->next = NULL;
234
235   if (NULL ==
236       (handle->th = GNUNET_CLIENT_notify_transmit_ready (handle->client,
237                                                     ntohs(handle->current->msg->msgbuf_size),
238                                                     handle->current->msg->timeout,
239                                                     GNUNET_YES,
240                                                     &transmit_pending, handle)))
241     {
242 #if DEBUG_DV
243       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
244                   "Failed to transmit request to dv service.\n");
245 #endif
246       finish (handle, GNUNET_SYSERR);
247     }
248 }
249
250 /**
251  * Add a pending message to the linked list
252  *
253  * @param handle handle to the specified DV api
254  * @param msg the message to add to the list
255  */
256 static void add_pending(struct GNUNET_DV_Handle *handle, struct GNUNET_DV_SendMessage *msg)
257 {
258   struct PendingMessages *new_message;
259   struct PendingMessages *pos;
260   struct PendingMessages *last;
261
262   new_message = GNUNET_malloc(sizeof(struct PendingMessages));
263   new_message->msg = msg;
264
265   if (handle->pending_list != NULL)
266     {
267       pos = handle->pending_list;
268       while(pos != NULL)
269         {
270           last = pos;
271           pos = pos->next;
272         }
273       new_message->next = last->next; /* Should always be null */
274       last->next = new_message;
275     }
276   else
277     {
278       new_message->next = handle->pending_list; /* Will always be null */
279       handle->pending_list = new_message;
280     }
281
282   process_pending_message(handle);
283 }
284
285
286 void handle_message_receipt (void *cls,
287                              const struct GNUNET_MessageHeader * msg)
288 {
289   struct GNUNET_DV_Handle *handle = cls;
290   struct GNUNET_DV_MessageReceived *received_msg;
291   size_t packed_msg_len;
292   size_t sender_address_len;
293   char *sender_address;
294   char *packed_msg;
295   char *packed_msg_start;
296
297   if (msg == NULL)
298   {
299     return; /* Connection closed? */
300   }
301
302   GNUNET_assert(ntohs(msg->type) == GNUNET_MESSAGE_TYPE_TRANSPORT_DV_RECEIVE);
303
304   if (ntohs(msg->size) < sizeof(struct GNUNET_DV_MessageReceived))
305     return;
306
307   received_msg = (struct GNUNET_DV_MessageReceived *)msg;
308   packed_msg_len = ntohs(received_msg->msg_len);
309   sender_address_len = ntohs(received_msg->sender_address_len);
310
311   GNUNET_assert(ntohs(msg->size) == (sizeof(struct GNUNET_DV_MessageReceived) + packed_msg_len + sender_address_len));
312 #if DEBUG_DV
313   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "dv api receives message, size checks out!\n");
314 #endif
315   sender_address = GNUNET_malloc(sender_address_len);
316   memcpy(sender_address, &received_msg[1], sender_address_len);
317   packed_msg_start = (char *)&received_msg[1];
318   packed_msg = GNUNET_malloc(packed_msg_len);
319   memcpy(packed_msg, &packed_msg_start[sender_address_len], packed_msg_len);
320
321 #if DEBUG_DV
322   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "packed message type: %d or %d\n", ntohs(((struct GNUNET_MessageHeader *)packed_msg)->type), ((struct GNUNET_MessageHeader *)packed_msg)->type);
323 #endif
324   handle->receive_handler(handle->receive_cls,
325                           &received_msg->sender,
326                           packed_msg,
327                           packed_msg_len,
328                           ntohl(received_msg->distance),
329                           sender_address,
330                           sender_address_len);
331
332   GNUNET_free(sender_address);
333
334   GNUNET_CLIENT_receive (handle->client,
335                          &handle_message_receipt,
336                          handle, GNUNET_TIME_UNIT_FOREVER_REL);
337 }
338
339 /**
340  * Send a message from the plugin to the DV service indicating that
341  * a message should be sent via DV to some peer.
342  *
343  * @param dv_handle the handle to the DV api
344  * @param target the final target of the message
345  * @param msgbuf the msg(s) to send
346  * @param msgbuf_size the size of msgbuf
347  * @param priority priority to pass on to core when sending the message
348  * @param timeout how long can this message be delayed (pass through to core)
349  * @param addr the address of this peer (internally known to DV)
350  * @param addrlen the length of the peer address
351  *
352  */
353 int GNUNET_DV_send (struct GNUNET_DV_Handle *dv_handle,
354                     const struct GNUNET_PeerIdentity *target,
355                     const char *msgbuf,
356                     size_t msgbuf_size,
357                     unsigned int priority,
358                     struct GNUNET_TIME_Relative timeout,
359                     const void *addr,
360                     size_t addrlen)
361 {
362   struct GNUNET_DV_SendMessage *msg;
363   char *end_of_message;
364   /* FIXME: Copy message to end of thingy, can't just allocate dummy! */
365 #if DEBUG_DV
366   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "DV SEND called with message of size %d, address size %d, total size to send is %d\n", msgbuf_size, addrlen, sizeof(struct GNUNET_DV_SendMessage) + msgbuf_size + addrlen);
367 #endif
368   msg = GNUNET_malloc(sizeof(struct GNUNET_DV_SendMessage) + addrlen + msgbuf_size);
369   msg->header.size = htons(sizeof(struct GNUNET_DV_SendMessage) + addrlen + msgbuf_size);
370   msg->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND);
371   memcpy(&msg->target, target, sizeof(struct GNUNET_PeerIdentity));
372   msg->msgbuf_size = htons(msgbuf_size);
373   msg->priority = htonl(priority);
374   msg->timeout = timeout;
375   msg->addrlen = htons(addrlen);
376   memcpy(&msg[1], addr, addrlen);
377   end_of_message = (char *)&msg[1];
378   end_of_message = &end_of_message[addrlen];
379   memcpy(end_of_message, msgbuf, msgbuf_size);
380   add_pending(dv_handle, msg);
381
382   return GNUNET_OK;
383 }
384
385 /* Forward declaration */
386 void GNUNET_DV_disconnect(struct GNUNET_DV_Handle *handle);
387
388 static size_t
389 transmit_start (void *cls, size_t size, void *buf)
390 {
391   struct StartContext *start_context = cls;
392   struct GNUNET_DV_Handle *handle = start_context->handle;
393   size_t tsize;
394
395   if (buf == NULL)
396     {
397       GNUNET_free(start_context->message);
398       GNUNET_free(start_context);
399       GNUNET_DV_disconnect(handle);
400       return 0;
401     }
402
403   tsize = ntohs(start_context->message->size);
404   if (size >= tsize)
405   {
406     memcpy(buf, start_context->message, tsize);
407     return tsize;
408   }
409
410   return 0;
411 }
412
413 /**
414  * Connect to the DV service
415  *
416  * @param sched the scheduler to use
417  * @param cfg the configuration to use
418  * @param receive_handler method call when on receipt from the service
419  * @param receive_handler_cls closure for receive_handler
420  *
421  * @return handle to the DV service
422  */
423 struct GNUNET_DV_Handle *
424 GNUNET_DV_connect (struct GNUNET_SCHEDULER_Handle *sched,
425                   const struct GNUNET_CONFIGURATION_Handle *cfg,
426                   GNUNET_DV_MessageReceivedHandler receive_handler,
427                   void *receive_handler_cls)
428 {
429   struct GNUNET_DV_Handle *handle;
430   struct GNUNET_MessageHeader *start_message;
431   struct StartContext *start_context;
432   handle = GNUNET_malloc(sizeof(struct GNUNET_DV_Handle));
433
434   handle->cfg = cfg;
435   handle->sched = sched;
436   handle->pending_list = NULL;
437   handle->current = NULL;
438   handle->do_destroy = GNUNET_NO;
439   handle->th = NULL;
440   handle->client = GNUNET_CLIENT_connect(sched, "dv", cfg);
441   handle->receive_handler = receive_handler;
442   handle->receive_cls = receive_handler_cls;
443
444   if (handle->client == NULL)
445     {
446       GNUNET_free(handle);
447       return NULL;
448     }
449
450   start_message = GNUNET_malloc(sizeof(struct GNUNET_MessageHeader));
451   start_message->size = htons(sizeof(struct GNUNET_MessageHeader));
452   start_message->type = htons(GNUNET_MESSAGE_TYPE_DV_START);
453
454   start_context = GNUNET_malloc(sizeof(struct StartContext));
455   start_context->handle = handle;
456   start_context->message = start_message;
457   GNUNET_CLIENT_notify_transmit_ready (handle->client,
458                                        sizeof(struct GNUNET_MessageHeader),
459                                        GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 60),
460                                        GNUNET_YES,
461                                        &transmit_start, start_context);
462
463   GNUNET_CLIENT_receive (handle->client,
464                          &handle_message_receipt,
465                          handle, GNUNET_TIME_UNIT_FOREVER_REL);
466
467   return handle;
468 }
469
470 /**
471  * Disconnect from the DV service
472  *
473  * @param handle the current handle to the service to disconnect
474  */
475 void GNUNET_DV_disconnect(struct GNUNET_DV_Handle *handle)
476 {
477   struct PendingMessages *pos;
478
479   GNUNET_assert(handle != NULL);
480
481   if (handle->th != NULL) /* We have a live transmit request in the Aether */
482     {
483       GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
484       handle->th = NULL;
485     }
486   if (handle->current != NULL) /* We are trying to send something now, clean it up */
487     GNUNET_free(handle->current);
488   while (NULL != (pos = handle->pending_list)) /* Remove all pending sends from the list */
489     {
490       handle->pending_list = pos->next;
491       GNUNET_free(pos);
492     }
493   if (handle->client != NULL) /* Finally, disconnect from the service */
494     {
495       GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
496       handle->client = NULL;
497     }
498
499   GNUNET_free (handle);
500 }
501
502 /* end of dv_api.c */