-generate and process ACKs
[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    * UID of our message, if any.
79    */
80   uint32_t uid;
81   
82 };
83
84
85 /**
86  * Handle to the DV service.
87  */
88 struct GNUNET_DV_ServiceHandle
89 {
90
91   /**
92    * Connection to DV service.
93    */
94   struct GNUNET_CLIENT_Connection *client;
95
96   /**
97    * Active request for transmission to DV service.
98    */
99   struct GNUNET_CLIENT_TransmitHandle *th;
100
101   /**
102    * Our configuration.
103    */
104   const struct GNUNET_CONFIGURATION_Handle *cfg;
105
106   /**
107    * Closure for the callbacks.
108    */
109   void *cls;
110   
111   /**
112    * Function to call on connect events.
113    */
114   GNUNET_DV_ConnectCallback connect_cb;
115
116   /**
117    * Function to call on disconnect events.
118    */
119   GNUNET_DV_DisconnectCallback disconnect_cb;
120
121   /**
122    * Function to call on receiving messages events.
123    */
124   GNUNET_DV_MessageReceivedCallback message_cb;
125
126   /**
127    * Head of messages to transmit.
128    */
129   struct GNUNET_DV_TransmitHandle *th_head;
130
131   /**
132    * Tail of messages to transmit.
133    */
134   struct GNUNET_DV_TransmitHandle *th_tail;
135
136   /**
137    * Mapping of peer identities to TransmitHandles to invoke
138    * upon successful transmission.  The respective
139    * transmissions have already been done.
140    */
141   struct GNUNET_CONTAINER_MultiHashMap *send_callbacks;
142
143   /**
144    * Current unique ID
145    */
146   uint32_t uid_gen;
147
148 };
149
150
151 /**
152  * Disconnect and then reconnect to the DV service.
153  *
154  * @param sh service handle
155  */
156 static void
157 reconnect (struct GNUNET_DV_ServiceHandle *sh);
158
159
160 /**
161  * Gives a message from our queue to the DV service.
162  *
163  * @param cls handle to the dv service (struct GNUNET_DV_ServiceHandle)
164  * @param size how many bytes can we send
165  * @param buf where to copy the message to send
166  * @return how many bytes we copied to buf
167  */
168 static size_t
169 transmit_pending (void *cls, size_t size, void *buf)
170 {
171   struct GNUNET_DV_ServiceHandle *sh = cls;
172   char *cbuf = buf;
173   struct GNUNET_DV_TransmitHandle *th;
174   size_t ret;
175   size_t tsize;
176
177   sh->th = NULL;
178   if (NULL == buf)
179   {
180     reconnect (sh);
181     return 0;
182   }
183   ret = 0;
184   while ( (NULL != (th = sh->th_head)) &&
185           (size - ret >= (tsize = ntohs (th->msg->size)) ))
186   {
187     GNUNET_CONTAINER_DLL_remove (sh->th_head,
188                                  sh->th_tail,
189                                  th);
190     memcpy (&cbuf[ret], th->msg, tsize);
191     ret += tsize;
192     if (NULL != th->cb)
193     {
194       (void) GNUNET_CONTAINER_multihashmap_put (sh->send_callbacks,
195                                                 &th->target.hashPubKey,
196                                                 th,
197                                                 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
198     }
199     else
200     {
201       GNUNET_free (th);
202     }
203   }
204   return ret;
205 }
206
207
208 /**
209  * Start sending messages from our queue to the service.
210  *
211  * @param sh service handle
212  */
213 static void
214 start_transmit (struct GNUNET_DV_ServiceHandle *sh)
215 {
216   if (NULL != sh->th)
217     return;
218   if (NULL == sh->th_head)
219     return; 
220   sh->th =
221     GNUNET_CLIENT_notify_transmit_ready (sh->client,
222                                          ntohs (sh->th_head->msg->size),
223                                          GNUNET_TIME_UNIT_FOREVER_REL,
224                                          GNUNET_NO, 
225                                          &transmit_pending, sh);
226 }
227
228
229 /**
230  * Closure for 'process_ack'.
231  */
232 struct AckContext
233 {
234   /**
235    * The ACK message.
236    */
237   const struct GNUNET_DV_AckMessage *ack;
238
239   /**
240    * Our service handle.
241    */
242   struct GNUNET_DV_ServiceHandle *sh;
243 };
244
245
246 /**
247  * We got an ACK.  Check if it matches the given transmit handle, and if
248  * so call the continuation.
249  *
250  * @param cls the 'struct AckContext'
251  * @param key peer identity
252  * @param value the 'struct GNUNET_DV_TransmitHandle'
253  * @return GNUNET_OK if the ACK did not match (continue to iterate)
254  */
255 static int
256 process_ack (void *cls,
257              const struct GNUNET_HashCode *key,
258              void *value)
259 {
260   struct AckContext *ctx = cls;
261   struct GNUNET_DV_TransmitHandle *th = value;
262
263   if (th->uid != ntohl (ctx->ack->uid))
264     return GNUNET_OK;
265   GNUNET_assert (GNUNET_YES ==
266                  GNUNET_CONTAINER_multihashmap_remove (ctx->sh->send_callbacks,
267                                                        key,
268                                                        th));
269   /* FIXME: should distinguish between success and failure here... */
270   th->cb (th->cb_cls,
271           GNUNET_OK);
272   GNUNET_free (th);
273   return GNUNET_NO;
274 }
275
276
277 /**
278  * Handles a message sent from the DV service to us.
279  * Parse it out and give it to the plugin.
280  *
281  * @param cls the handle to the DV API
282  * @param msg the message that was received
283  */
284 static void
285 handle_message_receipt (void *cls, 
286                         const struct GNUNET_MessageHeader *msg)
287 {
288   struct GNUNET_DV_ServiceHandle *sh = cls;
289   const struct GNUNET_DV_ConnectMessage *cm;
290   const struct GNUNET_DV_DisconnectMessage *dm;
291   const struct GNUNET_DV_ReceivedMessage *rm;
292   const struct GNUNET_MessageHeader *payload;
293   const struct GNUNET_DV_AckMessage *ack;
294   struct AckContext ctx;
295   
296   if (NULL == msg)
297   {
298     /* Connection closed */
299     reconnect (sh);
300     return;
301   }
302   switch (ntohs (msg->type))
303   {
304   case GNUNET_MESSAGE_TYPE_DV_CONNECT:
305     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ConnectMessage))
306     {
307       GNUNET_break (0);
308       reconnect (sh);
309       return;
310     }
311     cm = (const struct GNUNET_DV_ConnectMessage *) msg;
312     sh->connect_cb (sh->cls,
313                     &cm->peer,
314                     ntohl (cm->distance));
315     break;
316   case GNUNET_MESSAGE_TYPE_DV_DISCONNECT:
317     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DisconnectMessage))
318     {
319       GNUNET_break (0);
320       reconnect (sh);
321       return;
322     }
323     dm = (const struct GNUNET_DV_DisconnectMessage *) msg;
324     sh->disconnect_cb (sh->cls,
325                        &dm->peer);
326     break;
327   case GNUNET_MESSAGE_TYPE_DV_RECV:
328     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_ReceivedMessage) + sizeof (struct GNUNET_MessageHeader))
329     {
330       GNUNET_break (0);
331       reconnect (sh);
332       return;
333     }
334     rm = (const struct GNUNET_DV_ReceivedMessage *) msg;
335     payload = (const struct GNUNET_MessageHeader *) &rm[1];
336     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (payload->size))
337     {
338       GNUNET_break (0);
339       reconnect (sh);
340       return;
341     }
342     sh->message_cb (sh->cls,
343                     &rm->sender,
344                     ntohl (rm->distance),
345                     payload);
346     break;
347   case GNUNET_MESSAGE_TYPE_DV_SEND_ACK:
348     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_AckMessage))
349     {
350       GNUNET_break (0);
351       reconnect (sh);
352       return;
353     }
354     ack = (const struct GNUNET_DV_AckMessage *) msg;
355     ctx.ack = ack;
356     ctx.sh = sh;
357     GNUNET_CONTAINER_multihashmap_get_multiple (sh->send_callbacks,
358                                                 &ack->target.hashPubKey,
359                                                 &process_ack,
360                                                 &ctx);
361     return;
362   default:
363     reconnect (sh);
364     break;
365   }
366   GNUNET_CLIENT_receive (sh->client, 
367                          &handle_message_receipt, sh,
368                          GNUNET_TIME_UNIT_FOREVER_REL);
369 }
370
371
372 /**
373  * Transmit the start message to the DV service.
374  *
375  * @param cls the 'struct GNUNET_DV_ServiceHandle'
376  * @param size number of bytes available in buf
377  * @param buf where to copy the message
378  * @return number of bytes written to buf
379  */ 
380 static size_t
381 transmit_start (void *cls,
382                 size_t size,
383                 void *buf)
384 {
385   struct GNUNET_DV_ServiceHandle *sh = cls;
386   struct GNUNET_MessageHeader start_message;
387
388   sh->th = NULL;
389   if (NULL == buf)
390   {
391     GNUNET_break (0);
392     reconnect (sh);
393     return 0;
394   }
395   GNUNET_assert (size >= sizeof (start_message));
396   start_message.size = htons (sizeof (struct GNUNET_MessageHeader));
397   start_message.type = htons (GNUNET_MESSAGE_TYPE_DV_START);
398   memcpy (buf, &start_message, sizeof (start_message));
399   GNUNET_CLIENT_receive (sh->client,
400                          &handle_message_receipt, sh,
401                          GNUNET_TIME_UNIT_FOREVER_REL);
402   start_transmit (sh);
403   return sizeof (start_message);
404 }
405
406
407 /**
408  * We got disconnected from the service and thus all of the
409  * pending send callbacks will never be confirmed.  Clean up.
410  *
411  * @param cls the 'struct GNUNET_DV_ServiceHandle'
412  * @param key a peer identity
413  * @param value a 'struct GNUNET_DV_TransmitHandle' to clean up
414  * @return GNUNET_OK (continue to iterate)
415  */
416 static int
417 cleanup_send_cb (void *cls,
418                  const struct GNUNET_HashCode *key,
419                  void *value)
420 {
421   struct GNUNET_DV_ServiceHandle *sh = cls;
422   struct GNUNET_DV_TransmitHandle *th = value;
423
424   GNUNET_assert (GNUNET_YES ==
425                  GNUNET_CONTAINER_multihashmap_remove (sh->send_callbacks,
426                                                        key,
427                                                        th));
428   th->cb (th->cb_cls, GNUNET_SYSERR);
429   GNUNET_free (th);
430   return GNUNET_OK;
431 }
432
433
434 /**
435  * Disconnect and then reconnect to the DV service.
436  *
437  * @param sh service handle
438  */
439 static void
440 reconnect (struct GNUNET_DV_ServiceHandle *sh)
441
442   if (NULL != sh->th)
443   {
444     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
445     sh->th = NULL;
446   }
447   if (NULL != sh->client)
448   {
449     GNUNET_CLIENT_disconnect (sh->client);
450     sh->client = NULL;
451   }
452   GNUNET_CONTAINER_multihashmap_iterate (sh->send_callbacks,
453                                          &cleanup_send_cb,
454                                          sh);
455   sh->client = GNUNET_CLIENT_connect ("dv", sh->cfg);
456   if (NULL == sh->client)
457   {
458     GNUNET_break (0);
459     return;
460   }
461   sh->th = GNUNET_CLIENT_notify_transmit_ready (sh->client,
462                                                 sizeof (struct GNUNET_MessageHeader),
463                                                 GNUNET_TIME_UNIT_FOREVER_REL,
464                                                 GNUNET_YES,
465                                                 &transmit_start,
466                                                 sh);
467 }
468
469
470 /**
471  * Connect to the DV service.
472  *
473  * @param cfg configuration
474  * @param cls closure for callbacks
475  * @param connect_cb function to call on connects
476  * @param disconnect_cb function to call on disconnects
477  * @param message_cb function to call if we receive messages
478  * @return handle to access the service
479  */
480 struct GNUNET_DV_ServiceHandle *
481 GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
482                            void *cls,
483                            GNUNET_DV_ConnectCallback connect_cb,
484                            GNUNET_DV_DisconnectCallback disconnect_cb,
485                            GNUNET_DV_MessageReceivedCallback message_cb)
486 {
487   struct GNUNET_DV_ServiceHandle *sh;
488
489   sh = GNUNET_malloc (sizeof (struct GNUNET_DV_ServiceHandle));
490   sh->cfg = cfg;
491   sh->cls = cls;
492   sh->connect_cb = connect_cb;
493   sh->disconnect_cb = disconnect_cb;
494   sh->message_cb = message_cb;
495   sh->send_callbacks = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_YES);
496   reconnect (sh);
497   return sh;
498 }
499
500
501 /**
502  * Disconnect from DV service.
503  *
504  * @param sh service handle
505  */
506 void
507 GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh)
508 {
509   struct GNUNET_DV_TransmitHandle *pos;
510   
511   if (NULL == sh)
512     return;
513   if (NULL != sh->th)
514   {
515     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
516     sh->th = NULL;
517   }
518   while (NULL != (pos = sh->th_head))
519   {
520     GNUNET_CONTAINER_DLL_remove (sh->th_head,
521                                  sh->th_tail,
522                                  pos);
523     GNUNET_free (pos);
524   }
525   if (NULL != sh->client) 
526   {
527     GNUNET_CLIENT_disconnect (sh->client);
528     sh->client = NULL;
529   }
530   GNUNET_CONTAINER_multihashmap_iterate (sh->send_callbacks,
531                                          &cleanup_send_cb,
532                                          sh);
533   GNUNET_CONTAINER_multihashmap_destroy (sh->send_callbacks);
534   GNUNET_free (sh);
535 }
536
537
538 /**
539  * Send a message via DV service.
540  *
541  * @param sh service handle
542  * @param target intended recpient
543  * @param msg message payload
544  * @param cb function to invoke when done
545  * @param cb_cls closure for 'cb'
546  * @return handle to cancel the operation
547  */
548 struct GNUNET_DV_TransmitHandle *
549 GNUNET_DV_send (struct GNUNET_DV_ServiceHandle *sh,
550                 const struct GNUNET_PeerIdentity *target,
551                 const struct GNUNET_MessageHeader *msg,
552                 GNUNET_DV_MessageSentCallback cb,
553                 void *cb_cls)
554 {
555   struct GNUNET_DV_TransmitHandle *th;
556   struct GNUNET_DV_SendMessage *sm;
557
558   if (ntohs (msg->size) + sizeof (struct GNUNET_DV_SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
559   {
560     GNUNET_break (0);
561     return NULL;
562   }
563   th = GNUNET_malloc (sizeof (struct GNUNET_DV_TransmitHandle) +
564                       sizeof (struct GNUNET_DV_SendMessage) +
565                       ntohs (msg->size));
566   th->sh = sh;
567   th->target = *target;
568   th->cb = cb;
569   th->cb_cls = cb_cls;
570   th->msg = (const struct GNUNET_MessageHeader *) &th[1];
571   sm = (struct GNUNET_DV_SendMessage *) &th[1];
572   sm->header.type = htons (GNUNET_MESSAGE_TYPE_DV_SEND);
573   sm->header.size = htons (sizeof (struct GNUNET_DV_SendMessage) + 
574                            ntohs (msg->size));
575   if (0 == sh->uid_gen)
576     sh->uid_gen = 1;
577   th->uid = sh->uid_gen;
578   sm->uid = htonl (sh->uid_gen++);
579   /* use memcpy here as 'target' may not be sufficiently aligned */
580   memcpy (&sm->target, target, sizeof (struct GNUNET_PeerIdentity));
581   memcpy (&sm[1], msg, ntohs (msg->size));
582   GNUNET_CONTAINER_DLL_insert (sh->th_head,
583                                sh->th_tail,
584                                th);
585   start_transmit (sh);
586   return th;
587 }
588
589
590 /**
591  * Abort send operation (naturally, the message may have
592  * already been transmitted; this only stops the 'cb'
593  * from being called again).
594  *
595  * @param th send operation to cancel
596  */
597 void
598 GNUNET_DV_send_cancel (struct GNUNET_DV_TransmitHandle *th)
599 {
600   struct GNUNET_DV_ServiceHandle *sh = th->sh;
601   int ret;
602
603   ret = GNUNET_CONTAINER_multihashmap_remove (sh->send_callbacks,
604                                               &th->target.hashPubKey,
605                                               th);
606   if (GNUNET_YES != ret)
607     GNUNET_CONTAINER_DLL_remove (sh->th_head,
608                                  sh->th_tail,
609                                  th);
610   GNUNET_free (th);
611 }
612
613
614 /* end of dv_api.c */