-more DV hacking
[oweals/gnunet.git] / src / dv / dv_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009--2013 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_util_lib.h"
29 #include "gnunet_dv_service.h"
30 #include "gnunet_protocols.h"
31 #include "dv.h"
32 #include "gnunet_transport_plugin.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "dv-api",__VA_ARGS__)
35
36
37 /**
38  * Handle for a send operation.
39  */
40 struct GNUNET_DV_TransmitHandle
41 {
42   /**
43    * Kept in a DLL.
44    */
45   struct GNUNET_DV_TransmitHandle *next;
46
47   /**
48    * Kept in a DLL.
49    */
50   struct GNUNET_DV_TransmitHandle *prev;
51
52   /**
53    * Handle to the service.
54    */
55   struct GNUNET_DV_ServiceHandle *sh;
56
57   /**
58    * Function to call upon completion.
59    */
60   GNUNET_DV_MessageSentCallback cb;
61
62   /**
63    * Closure for 'cb'.
64    */
65   void *cb_cls;
66   
67   /**
68    * The actual message (allocated at the end of this struct).
69    */
70   const struct GNUNET_MessageHeader *msg;
71
72   /**
73    * Destination for the message.
74    */
75   struct GNUNET_PeerIdentity target;
76
77 };
78
79
80 /**
81  * Handle to the DV service.
82  */
83 struct GNUNET_DV_ServiceHandle
84 {
85
86   /**
87    * Connection to DV service.
88    */
89   struct GNUNET_CLIENT_Connection *client;
90
91   /**
92    * Active request for transmission to DV service.
93    */
94   struct GNUNET_CLIENT_TransmitHandle *th;
95
96   /**
97    * Our configuration.
98    */
99   const struct GNUNET_CONFIGURATION_Handle *cfg;
100
101   /**
102    * Closure for the callbacks.
103    */
104   void *cls;
105   
106   /**
107    * Function to call on connect events.
108    */
109   GNUNET_DV_ConnectCallback connect_cb;
110
111   /**
112    * Function to call on disconnect events.
113    */
114   GNUNET_DV_DisconnectCallback disconnect_cb;
115
116   /**
117    * Function to call on receiving messages events.
118    */
119   GNUNET_DV_MessageReceivedCallback message_cb;
120
121   /**
122    * Head of messages to transmit.
123    */
124   struct GNUNET_DV_TransmitHandle *th_head;
125
126   /**
127    * Tail of messages to transmit.
128    */
129   struct GNUNET_DV_TransmitHandle *th_tail;
130
131   /**
132    * Mapping of peer identities to TransmitHandles to invoke
133    * upon successful transmission.  The respective
134    * transmissions have already been done.
135    */
136   struct GNUNET_CONTAINER_MultiHashMap *send_callbacks;
137
138   /**
139    * Current unique ID
140    */
141   uint32_t uid_gen;
142
143 };
144
145
146 /**
147  * Disconnect and then reconnect to the DV service.
148  *
149  * @param sh service handle
150  */
151 static void
152 reconnect (struct GNUNET_DV_ServiceHandle *sh);
153
154
155 /**
156  * Gives a message from our queue to the DV service.
157  *
158  * @param cls handle to the dv service (struct GNUNET_DV_ServiceHandle)
159  * @param size how many bytes can we send
160  * @param buf where to copy the message to send
161  * @return how many bytes we copied to buf
162  */
163 static size_t
164 transmit_pending (void *cls, size_t size, void *buf)
165 {
166   struct GNUNET_DV_ServiceHandle *sh = cls;
167   char *cbuf = buf;
168   struct GNUNET_DV_TransmitHandle *th;
169   size_t ret;
170   size_t tsize;
171
172   sh->th = NULL;
173   if (NULL == buf)
174   {
175     reconnect (sh);
176     return 0;
177   }
178   ret = 0;
179   while ( (NULL != (th = sh->th_head)) &&
180           (size - ret >= (tsize = ntohs (th->msg->size)) ))
181   {
182     GNUNET_CONTAINER_DLL_remove (sh->th_head,
183                                  sh->th_tail,
184                                  th);
185     memcpy (&cbuf[ret], th->msg, tsize);
186     ret += tsize;
187     (void) GNUNET_CONTAINER_multihashmap_put (sh->send_callbacks,
188                                               &th->target.hashPubKey,
189                                               th,
190                                               GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
191   }
192   return ret;
193 }
194
195
196 /**
197  * Start sending messages from our queue to the service.
198  *
199  * @param sh service handle
200  */
201 static void
202 start_transmit (struct GNUNET_DV_ServiceHandle *sh)
203 {
204   if (NULL != sh->th)
205     return;
206   if (NULL == sh->th_head)
207     return; 
208   sh->th =
209     GNUNET_CLIENT_notify_transmit_ready (sh->client,
210                                          ntohs (sh->th_head->msg->size),
211                                          GNUNET_TIME_UNIT_FOREVER_REL,
212                                          GNUNET_NO, 
213                                          &transmit_pending, sh);
214 }
215
216
217 /**
218  * Handles a message sent from the DV service to us.
219  * Parse it out and give it to the plugin.
220  *
221  * @param cls the handle to the DV API
222  * @param msg the message that was received
223  */
224 static void
225 handle_message_receipt (void *cls, 
226                         const struct GNUNET_MessageHeader *msg)
227 {
228   struct GNUNET_DV_ServiceHandle *sh = cls;
229   const struct GNUNET_DV_ConnectMessage *cm;
230   const struct GNUNET_DV_DisconnectMessage *dm;
231   const struct GNUNET_DV_ReceivedMessage *rm;
232   const struct GNUNET_MessageHeader *payload;
233
234   if (NULL == msg)
235   {
236     /* Connection closed */
237     reconnect (sh);
238     return;
239   }
240   switch (ntohs (msg->type))
241   {
242   case GNUNET_MESSAGE_TYPE_DV_CONNECT:
243     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ConnectMessage))
244     {
245       GNUNET_break (0);
246       reconnect (sh);
247       return;
248     }
249     cm = (const struct GNUNET_DV_ConnectMessage *) msg;
250     sh->connect_cb (sh->cls,
251                     &cm->peer,
252                     ntohl (cm->distance));
253     break;
254   case GNUNET_MESSAGE_TYPE_DV_DISCONNECT:
255     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DisconnectMessage))
256     {
257       GNUNET_break (0);
258       reconnect (sh);
259       return;
260     }
261     dm = (const struct GNUNET_DV_DisconnectMessage *) msg;
262     sh->disconnect_cb (sh->cls,
263                        &dm->peer);
264     break;
265   case GNUNET_MESSAGE_TYPE_DV_RECV:
266     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_ReceivedMessage) + sizeof (struct GNUNET_MessageHeader))
267     {
268       GNUNET_break (0);
269       reconnect (sh);
270       return;
271     }
272     rm = (const struct GNUNET_DV_ReceivedMessage *) msg;
273     payload = (const struct GNUNET_MessageHeader *) &rm[1];
274     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (payload->size))
275     {
276       GNUNET_break (0);
277       reconnect (sh);
278       return;
279     }
280     sh->message_cb (sh->cls,
281                     &rm->sender,
282                     ntohl (rm->distance),
283                     payload);
284     break;
285   default:
286     reconnect (sh);
287     break;
288   }
289   GNUNET_CLIENT_receive (sh->client, 
290                          &handle_message_receipt, sh,
291                          GNUNET_TIME_UNIT_FOREVER_REL);
292 }
293
294
295 /**
296  * Transmit the start message to the DV service.
297  *
298  * @param cls the 'struct GNUNET_DV_ServiceHandle'
299  * @param size number of bytes available in buf
300  * @param buf where to copy the message
301  * @return number of bytes written to buf
302  */ 
303 static size_t
304 transmit_start (void *cls,
305                 size_t size,
306                 void *buf)
307 {
308   struct GNUNET_DV_ServiceHandle *sh = cls;
309   struct GNUNET_MessageHeader start_message;
310
311   sh->th = NULL;
312   if (NULL == buf)
313   {
314     GNUNET_break (0);
315     reconnect (sh);
316     return 0;
317   }
318   GNUNET_assert (size >= sizeof (start_message));
319   start_message.size = htons (sizeof (struct GNUNET_MessageHeader));
320   start_message.type = htons (GNUNET_MESSAGE_TYPE_DV_START);
321   memcpy (buf, &start_message, sizeof (start_message));
322   GNUNET_CLIENT_receive (sh->client,
323                          &handle_message_receipt, sh,
324                          GNUNET_TIME_UNIT_FOREVER_REL);
325   start_transmit (sh);
326   return sizeof (start_message);
327 }
328
329
330 /**
331  * We got disconnected from the service and thus all of the
332  * pending send callbacks will never be confirmed.  Clean up.
333  *
334  * @param cls the 'struct GNUNET_DV_ServiceHandle'
335  * @param key a peer identity
336  * @param value a 'struct GNUNET_DV_TransmitHandle' to clean up
337  * @return GNUNET_OK (continue to iterate)
338  */
339 static int
340 cleanup_send_cb (void *cls,
341                  const struct GNUNET_HashCode *key,
342                  void *value)
343 {
344   struct GNUNET_DV_ServiceHandle *sh = cls;
345   struct GNUNET_DV_TransmitHandle *th = value;
346
347   GNUNET_assert (GNUNET_YES ==
348                  GNUNET_CONTAINER_multihashmap_remove (sh->send_callbacks,
349                                                        key,
350                                                        th));
351   th->cb (th->cb_cls, GNUNET_SYSERR);
352   GNUNET_free (th);
353   return GNUNET_OK;
354 }
355
356
357 /**
358  * Disconnect and then reconnect to the DV service.
359  *
360  * @param sh service handle
361  */
362 static void
363 reconnect (struct GNUNET_DV_ServiceHandle *sh)
364
365   if (NULL != sh->th)
366   {
367     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
368     sh->th = NULL;
369   }
370   if (NULL != sh->client)
371   {
372     GNUNET_CLIENT_disconnect (sh->client);
373     sh->client = NULL;
374   }
375   GNUNET_CONTAINER_multihashmap_iterate (sh->send_callbacks,
376                                          &cleanup_send_cb,
377                                          sh);
378   sh->client = GNUNET_CLIENT_connect ("dv", sh->cfg);
379   if (NULL == sh->client)
380   {
381     GNUNET_break (0);
382     return;
383   }
384   sh->th = GNUNET_CLIENT_notify_transmit_ready (sh->client,
385                                                 sizeof (struct GNUNET_MessageHeader),
386                                                 GNUNET_TIME_UNIT_FOREVER_REL,
387                                                 GNUNET_YES,
388                                                 &transmit_start,
389                                                 sh);
390 }
391
392
393 /**
394  * Connect to the DV service.
395  *
396  * @param cfg configuration
397  * @param cls closure for callbacks
398  * @param connect_cb function to call on connects
399  * @param disconnect_cb function to call on disconnects
400  * @param message_cb function to call if we receive messages
401  * @return handle to access the service
402  */
403 struct GNUNET_DV_ServiceHandle *
404 GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
405                            void *cls,
406                            GNUNET_DV_ConnectCallback connect_cb,
407                            GNUNET_DV_DisconnectCallback disconnect_cb,
408                            GNUNET_DV_MessageReceivedCallback message_cb)
409 {
410   struct GNUNET_DV_ServiceHandle *sh;
411
412   sh = GNUNET_malloc (sizeof (struct GNUNET_DV_ServiceHandle));
413   sh->cfg = cfg;
414   sh->cls = cls;
415   sh->connect_cb = connect_cb;
416   sh->disconnect_cb = disconnect_cb;
417   sh->message_cb = message_cb;
418   sh->send_callbacks = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_YES);
419   reconnect (sh);
420   return sh;
421 }
422
423
424 /**
425  * Disconnect from DV service.
426  *
427  * @param sh service handle
428  */
429 void
430 GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh)
431 {
432   struct GNUNET_DV_TransmitHandle *pos;
433   
434   if (NULL == sh)
435     return;
436   if (NULL != sh->th)
437   {
438     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
439     sh->th = NULL;
440   }
441   while (NULL != (pos = sh->th_head))
442   {
443     GNUNET_CONTAINER_DLL_remove (sh->th_head,
444                                  sh->th_tail,
445                                  pos);
446     GNUNET_free (pos);
447   }
448   if (NULL != sh->client) 
449   {
450     GNUNET_CLIENT_disconnect (sh->client);
451     sh->client = NULL;
452   }
453   GNUNET_CONTAINER_multihashmap_iterate (sh->send_callbacks,
454                                          &cleanup_send_cb,
455                                          sh);
456   GNUNET_CONTAINER_multihashmap_destroy (sh->send_callbacks);
457   GNUNET_free (sh);
458 }
459
460
461 /**
462  * Send a message via DV service.
463  *
464  * @param sh service handle
465  * @param target intended recpient
466  * @param msg message payload
467  * @param cb function to invoke when done
468  * @param cb_cls closure for 'cb'
469  * @return handle to cancel the operation
470  */
471 struct GNUNET_DV_TransmitHandle *
472 GNUNET_DV_send (struct GNUNET_DV_ServiceHandle *sh,
473                 const struct GNUNET_PeerIdentity *target,
474                 const struct GNUNET_MessageHeader *msg,
475                 GNUNET_DV_MessageSentCallback cb,
476                 void *cb_cls)
477 {
478   struct GNUNET_DV_TransmitHandle *th;
479   struct GNUNET_DV_SendMessage *sm;
480
481   if (ntohs (msg->size) + sizeof (struct GNUNET_DV_SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
482   {
483     GNUNET_break (0);
484     return NULL;
485   }
486   th = GNUNET_malloc (sizeof (struct GNUNET_DV_TransmitHandle) +
487                       sizeof (struct GNUNET_DV_SendMessage) +
488                       ntohs (msg->size));
489   th->sh = sh;
490   th->target = *target;
491   th->cb = cb;
492   th->cb_cls = cb_cls;
493   th->msg = (const struct GNUNET_MessageHeader *) &th[1];
494   sm = (struct GNUNET_DV_SendMessage *) &th[1];
495   sm->header.type = htons (GNUNET_MESSAGE_TYPE_DV_SEND);
496   sm->header.size = htons (sizeof (struct GNUNET_DV_SendMessage) + 
497                            ntohs (msg->size));
498   /* use memcpy here as 'target' may not be sufficiently aligned */
499   memcpy (&sm->target, target, sizeof (struct GNUNET_PeerIdentity));
500   memcpy (&sm[1], msg, ntohs (msg->size));
501   GNUNET_CONTAINER_DLL_insert (sh->th_head,
502                                sh->th_tail,
503                                th);
504   start_transmit (sh);
505   return th;
506 }
507
508
509 /**
510  * Abort send operation (naturally, the message may have
511  * already been transmitted; this only stops the 'cb'
512  * from being called again).
513  *
514  * @param th send operation to cancel
515  */
516 void
517 GNUNET_DV_send_cancel (struct GNUNET_DV_TransmitHandle *th)
518 {
519   struct GNUNET_DV_ServiceHandle *sh = th->sh;
520   int ret;
521
522   ret = GNUNET_CONTAINER_multihashmap_remove (sh->send_callbacks,
523                                               &th->target.hashPubKey,
524                                               th);
525   if (GNUNET_YES != ret)
526     GNUNET_CONTAINER_DLL_remove (sh->th_head,
527                                  sh->th_tail,
528                                  th);
529   GNUNET_free (th);
530 }
531
532
533 /* end of dv_api.c */