8c536b207f913e0698f8b6cfd53f3ab8f2fd3e0f
[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 transport/dv_api.c
23  * @brief library to access the DV service
24  * @author Christian Grothoff
25  * @author Not 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  * Handle for the service.
62  */
63 struct GNUNET_DV_Handle
64 {
65   /**
66    * Our scheduler.
67    */
68   struct GNUNET_SCHEDULER_Handle *sched;
69
70   /**
71    * Configuration to use.
72    */
73   const struct GNUNET_CONFIGURATION_Handle *cfg;
74
75   /**
76    * Socket (if available).
77    */
78   struct GNUNET_CLIENT_Connection *client;
79
80   /**
81    * Currently pending transmission request.
82    */
83   struct GNUNET_CLIENT_TransmitHandle *th;
84
85   /**
86    * List of the currently pending messages for the DV service.
87    */
88   struct PendingMessages *pending_list;
89
90   /**
91    * Message we are currently sending.
92    */
93   struct PendingMessages *current;
94
95   /**
96    * Kill off the connection and any pending messages.
97    */
98   int do_destroy;
99
100   /**
101    * Handler for messages we receive from the DV service
102    */
103   GNUNET_DV_MessageReceivedHandler receive_handler;
104
105   /**
106    * Closure for the receive handler
107    */
108   void *receive_cls;
109
110 };
111
112
113 /**
114  * Try to (re)connect to the dv service.
115  *
116  * @return GNUNET_YES on success, GNUNET_NO on failure.
117  */
118 static int
119 try_connect (struct GNUNET_DV_Handle *ret)
120 {
121   if (ret->client != NULL)
122     return GNUNET_OK;
123   ret->client = GNUNET_CLIENT_connect (ret->sched, "dv", ret->cfg);
124   if (ret->client != NULL)
125     return GNUNET_YES;
126 #if DEBUG_STATISTICS
127   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
128               _("Failed to connect to the dv service!\n"));
129 #endif
130   return GNUNET_NO;
131 }
132
133 static void process_pending_message(struct GNUNET_DV_Handle *handle);
134
135 /**
136  * Send complete, schedule next
137  */
138 static void
139 finish (struct GNUNET_DV_Handle *handle, int code)
140 {
141   struct PendingMessages *pos = handle->current;
142   handle->current = NULL;
143   process_pending_message (handle);
144
145   GNUNET_free (pos);
146 }
147
148
149 static size_t
150 transmit_pending (void *cls, size_t size, void *buf)
151 {
152   struct GNUNET_DV_Handle *handle = cls;
153   size_t ret;
154
155   if (buf == NULL)
156     {
157       finish(handle, GNUNET_SYSERR);
158       return 0;
159     }
160   handle->th = NULL;
161
162   return ret;
163 }
164
165 /**
166  * Try to send messages from list of messages to send
167  */
168 static void process_pending_message(struct GNUNET_DV_Handle *handle)
169 {
170   struct GNUNET_TIME_Relative timeout;
171
172   if (handle->current != NULL)
173     return;                     /* action already pending */
174   if (GNUNET_YES != try_connect (handle))
175     {
176       finish (handle, GNUNET_SYSERR);
177       return;
178     }
179
180   /* schedule next action */
181   handle->current = handle->pending_list;
182   if (NULL == handle->current)
183     {
184       if (handle->do_destroy)
185         {
186           handle->do_destroy = GNUNET_NO;
187           //GNUNET_DV_disconnect (handle); /* FIXME: replace with proper disconnect stuffs */
188         }
189       return;
190     }
191   handle->pending_list = handle->pending_list->next;
192   handle->current->next = NULL;
193
194   timeout = GNUNET_TIME_absolute_get_remaining (handle->current->timeout);
195   if (NULL ==
196       (handle->th = GNUNET_CLIENT_notify_transmit_ready (handle->client,
197                                                     ntohs(handle->current->msg->msgbuf_size),
198                                                     timeout,
199                                                     GNUNET_YES,
200                                                     &transmit_pending, handle)))
201     {
202 #if DEBUG_STATISTICS
203       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
204                   "Failed to transmit request to dv service.\n");
205 #endif
206       finish (handle, GNUNET_SYSERR);
207     }
208 }
209
210 /**
211  * Add a pending message to the linked list
212  *
213  * @param handle handle to the specified DV api
214  * @param msg the message to add to the list
215  */
216 static void add_pending(struct GNUNET_DV_Handle *handle, struct GNUNET_DV_SendMessage *msg)
217 {
218   struct PendingMessages *new_message;
219   struct PendingMessages *pos;
220   struct PendingMessages *last;
221
222   new_message = GNUNET_malloc(sizeof(struct PendingMessages));
223   new_message->msg = msg;
224
225   if (handle->pending_list != NULL)
226     {
227       pos = handle->pending_list;
228       while(pos != NULL)
229         {
230           last = pos;
231           pos = pos->next;
232         }
233       new_message->next = last->next; /* Should always be null */
234       last->next = new_message;
235     }
236   else
237     {
238       new_message->next = handle->pending_list; /* Will always be null */
239       handle->pending_list = new_message;
240     }
241
242   process_pending_message(handle);
243 }
244
245
246
247
248 void handle_message_receipt (void *cls,
249                              const struct GNUNET_MessageHeader * msg)
250 {
251   struct GNUNET_DV_Handle *handle = cls;
252   struct GNUNET_DV_MessageReceived *received_msg;
253   char *sender_address;
254
255   GNUNET_assert(ntohs(msg->type) == GNUNET_MESSAGE_TYPE_TRANSPORT_DV_RECEIVE);
256
257   if (ntohs(msg->size) < sizeof(struct GNUNET_DV_MessageReceived))
258     return;
259
260   received_msg = (struct GNUNET_DV_MessageReceived *)msg;
261   GNUNET_assert(ntohs(msg->size) == (sizeof(struct GNUNET_DV_MessageReceived) + ntohs(received_msg->msg->size) + ntohs(received_msg->sender_address_len)));
262
263   sender_address = GNUNET_malloc(ntohs(received_msg->sender_address_len));
264   sender_address = memcpy(sender_address, &received_msg[1], ntohs(received_msg->sender_address_len));
265
266   handle->receive_handler(handle->receive_cls,
267                           received_msg->sender,
268                           received_msg->msg,
269                           ntohl(received_msg->distance),
270                           sender_address,
271                           ntohs(received_msg->sender_address_len));
272
273   GNUNET_free(sender_address);
274
275   GNUNET_CLIENT_receive (handle->client,
276                          &handle_message_receipt,
277                          handle, GNUNET_TIME_UNIT_FOREVER_REL);
278 }
279
280 /**
281  * Send a message from the plugin to the DV service indicating that
282  * a message should be sent via DV to some peer.
283  *
284  * @target the final target of the message
285  * @msgbuf the msg(s) to send
286  * @msgbuf_size the size of msgbuf
287  * @priority priority to pass on to core when sending the message
288  * @timeout how long can this message be delayed (pass through to core)
289  * @addr the address of this peer (internally known to DV)
290  * @addrlen the length of the peer address
291  *
292  */
293 int GNUNET_DV_send (struct GNUNET_DV_Handle *dv_handle,
294                     const struct GNUNET_PeerIdentity *target,
295                     const char *msgbuf,
296                     size_t msgbuf_size,
297                     unsigned int priority,
298                     struct GNUNET_TIME_Relative timeout,
299                     const void *addr,
300                     size_t addrlen)
301 {
302   struct GNUNET_DV_SendMessage *msg;
303
304   msg = GNUNET_malloc(sizeof(struct GNUNET_DV_SendMessage) + msgbuf_size + addrlen);
305   msg->header.size = htons(sizeof(struct GNUNET_DV_SendMessage) + msgbuf_size + addrlen);
306   msg->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_DV_SEND);
307   memcpy(&msg->target, target, sizeof(struct GNUNET_PeerIdentity));
308   msg->msgbuf = GNUNET_malloc(msgbuf_size);
309   memcpy(msg->msgbuf, msgbuf, msgbuf_size);
310   msg->msgbuf_size = htons(msgbuf_size);
311   msg->priority = htonl(priority);
312   msg->timeout = timeout;
313   msg->addrlen = htons(addrlen);
314   memcpy(&msg[1], addr, addrlen);
315
316   add_pending(dv_handle, msg);
317   process_pending_message(dv_handle);
318
319   return GNUNET_OK;
320 }
321
322 /**
323  * Connect to the DV service
324  *
325  * @param sched the scheduler to use
326  * @param cfg the configuration to use
327  *
328  * @return handle to the DV service
329  */
330 struct GNUNET_DV_Handle *
331 GNUNET_DV_connect (struct GNUNET_SCHEDULER_Handle *sched,
332                   const struct GNUNET_CONFIGURATION_Handle *cfg,
333                   GNUNET_DV_MessageReceivedHandler receive_handler,
334                   void *receive_handler_cls)
335 {
336   struct GNUNET_DV_Handle *handle;
337
338   handle = GNUNET_malloc(sizeof(struct GNUNET_DV_Handle));
339
340   handle->cfg = cfg;
341   handle->sched = sched;
342   handle->pending_list = NULL;
343   handle->current = NULL;
344   handle->do_destroy = GNUNET_NO;
345   handle->th = NULL;
346   handle->client = GNUNET_CLIENT_connect(sched, "dv", cfg);
347   handle->receive_handler = receive_handler;
348   handle->receive_cls = receive_handler_cls;
349
350   if (handle->client == NULL)
351     return NULL;
352
353   GNUNET_CLIENT_receive (handle->client,
354                          &handle_message_receipt,
355                          handle, GNUNET_TIME_UNIT_FOREVER_REL);
356
357   return handle;
358 }
359
360 /**
361  * Disconnect from the DV service
362  *
363  * @param handle the current handle to the service to disconnect
364  */
365 void GNUNET_DV_disconnect(struct GNUNET_DV_Handle *handle)
366 {
367   struct PendingMessages *pos;
368
369   GNUNET_assert(handle != NULL);
370
371   if (handle->th != NULL) /* We have a live transmit request in the Aether */
372     {
373       GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
374       handle->th = NULL;
375     }
376   if (handle->current != NULL) /* We are trying to send something now, clean it up */
377     GNUNET_free(handle->current);
378   while (NULL != (pos = handle->pending_list)) /* Remove all pending sends from the list */
379     {
380       handle->pending_list = pos->next;
381       GNUNET_free(pos);
382     }
383   if (handle->client != NULL) /* Finally, disconnect from the service */
384     {
385       GNUNET_CLIENT_disconnect (handle->client);
386       handle->client = NULL;
387     }
388
389   GNUNET_free (handle);
390 }
391
392 /* end of dv_api.c */