-nack support
[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   th->cb (th->cb_cls,
270           (ntohs (ctx->ack->header.type) == GNUNET_MESSAGE_TYPE_DV_SEND_ACK)
271           ? GNUNET_OK
272           : GNUNET_SYSERR);
273   GNUNET_free (th);
274   return GNUNET_NO;
275 }
276
277
278 /**
279  * Handles a message sent from the DV service to us.
280  * Parse it out and give it to the plugin.
281  *
282  * @param cls the handle to the DV API
283  * @param msg the message that was received
284  */
285 static void
286 handle_message_receipt (void *cls, 
287                         const struct GNUNET_MessageHeader *msg)
288 {
289   struct GNUNET_DV_ServiceHandle *sh = cls;
290   const struct GNUNET_DV_ConnectMessage *cm;
291   const struct GNUNET_DV_DisconnectMessage *dm;
292   const struct GNUNET_DV_ReceivedMessage *rm;
293   const struct GNUNET_MessageHeader *payload;
294   const struct GNUNET_DV_AckMessage *ack;
295   struct AckContext ctx;
296   
297   if (NULL == msg)
298   {
299     /* Connection closed */
300     reconnect (sh);
301     return;
302   }
303   switch (ntohs (msg->type))
304   {
305   case GNUNET_MESSAGE_TYPE_DV_CONNECT:
306     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ConnectMessage))
307     {
308       GNUNET_break (0);
309       reconnect (sh);
310       return;
311     }
312     cm = (const struct GNUNET_DV_ConnectMessage *) msg;
313     sh->connect_cb (sh->cls,
314                     &cm->peer,
315                     ntohl (cm->distance));
316     break;
317   case GNUNET_MESSAGE_TYPE_DV_DISCONNECT:
318     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DisconnectMessage))
319     {
320       GNUNET_break (0);
321       reconnect (sh);
322       return;
323     }
324     dm = (const struct GNUNET_DV_DisconnectMessage *) msg;
325     sh->disconnect_cb (sh->cls,
326                        &dm->peer);
327     break;
328   case GNUNET_MESSAGE_TYPE_DV_RECV:
329     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_ReceivedMessage) + sizeof (struct GNUNET_MessageHeader))
330     {
331       GNUNET_break (0);
332       reconnect (sh);
333       return;
334     }
335     rm = (const struct GNUNET_DV_ReceivedMessage *) msg;
336     payload = (const struct GNUNET_MessageHeader *) &rm[1];
337     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (payload->size))
338     {
339       GNUNET_break (0);
340       reconnect (sh);
341       return;
342     }
343     sh->message_cb (sh->cls,
344                     &rm->sender,
345                     ntohl (rm->distance),
346                     payload);
347     break;
348   case GNUNET_MESSAGE_TYPE_DV_SEND_ACK:
349   case GNUNET_MESSAGE_TYPE_DV_SEND_NACK:
350     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_AckMessage))
351     {
352       GNUNET_break (0);
353       reconnect (sh);
354       return;
355     }
356     ack = (const struct GNUNET_DV_AckMessage *) msg;
357     ctx.ack = ack;
358     ctx.sh = sh;
359     GNUNET_CONTAINER_multihashmap_get_multiple (sh->send_callbacks,
360                                                 &ack->target.hashPubKey,
361                                                 &process_ack,
362                                                 &ctx);
363     break;
364   case GNUNET_MESSAGE_TYPE_DV_DISTANCE_CHANGED:
365     GNUNET_break (0);
366     break;
367   default:
368     reconnect (sh);
369     break;
370   }
371   GNUNET_CLIENT_receive (sh->client, 
372                          &handle_message_receipt, sh,
373                          GNUNET_TIME_UNIT_FOREVER_REL);
374 }
375
376
377 /**
378  * Transmit the start message to the DV service.
379  *
380  * @param cls the 'struct GNUNET_DV_ServiceHandle'
381  * @param size number of bytes available in buf
382  * @param buf where to copy the message
383  * @return number of bytes written to buf
384  */ 
385 static size_t
386 transmit_start (void *cls,
387                 size_t size,
388                 void *buf)
389 {
390   struct GNUNET_DV_ServiceHandle *sh = cls;
391   struct GNUNET_MessageHeader start_message;
392
393   sh->th = NULL;
394   if (NULL == buf)
395   {
396     GNUNET_break (0);
397     reconnect (sh);
398     return 0;
399   }
400   GNUNET_assert (size >= sizeof (start_message));
401   start_message.size = htons (sizeof (struct GNUNET_MessageHeader));
402   start_message.type = htons (GNUNET_MESSAGE_TYPE_DV_START);
403   memcpy (buf, &start_message, sizeof (start_message));
404   GNUNET_CLIENT_receive (sh->client,
405                          &handle_message_receipt, sh,
406                          GNUNET_TIME_UNIT_FOREVER_REL);
407   start_transmit (sh);
408   return sizeof (start_message);
409 }
410
411
412 /**
413  * We got disconnected from the service and thus all of the
414  * pending send callbacks will never be confirmed.  Clean up.
415  *
416  * @param cls the 'struct GNUNET_DV_ServiceHandle'
417  * @param key a peer identity
418  * @param value a 'struct GNUNET_DV_TransmitHandle' to clean up
419  * @return GNUNET_OK (continue to iterate)
420  */
421 static int
422 cleanup_send_cb (void *cls,
423                  const struct GNUNET_HashCode *key,
424                  void *value)
425 {
426   struct GNUNET_DV_ServiceHandle *sh = cls;
427   struct GNUNET_DV_TransmitHandle *th = value;
428
429   GNUNET_assert (GNUNET_YES ==
430                  GNUNET_CONTAINER_multihashmap_remove (sh->send_callbacks,
431                                                        key,
432                                                        th));
433   th->cb (th->cb_cls, GNUNET_SYSERR);
434   GNUNET_free (th);
435   return GNUNET_OK;
436 }
437
438
439 /**
440  * Disconnect and then reconnect to the DV service.
441  *
442  * @param sh service handle
443  */
444 static void
445 reconnect (struct GNUNET_DV_ServiceHandle *sh)
446
447   if (NULL != sh->th)
448   {
449     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
450     sh->th = NULL;
451   }
452   if (NULL != sh->client)
453   {
454     GNUNET_CLIENT_disconnect (sh->client);
455     sh->client = NULL;
456   }
457   GNUNET_CONTAINER_multihashmap_iterate (sh->send_callbacks,
458                                          &cleanup_send_cb,
459                                          sh);
460   sh->client = GNUNET_CLIENT_connect ("dv", sh->cfg);
461   if (NULL == sh->client)
462   {
463     GNUNET_break (0);
464     return;
465   }
466   sh->th = GNUNET_CLIENT_notify_transmit_ready (sh->client,
467                                                 sizeof (struct GNUNET_MessageHeader),
468                                                 GNUNET_TIME_UNIT_FOREVER_REL,
469                                                 GNUNET_YES,
470                                                 &transmit_start,
471                                                 sh);
472 }
473
474
475 /**
476  * Connect to the DV service.
477  *
478  * @param cfg configuration
479  * @param cls closure for callbacks
480  * @param connect_cb function to call on connects
481  * @param disconnect_cb function to call on disconnects
482  * @param message_cb function to call if we receive messages
483  * @return handle to access the service
484  */
485 struct GNUNET_DV_ServiceHandle *
486 GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
487                            void *cls,
488                            GNUNET_DV_ConnectCallback connect_cb,
489                            GNUNET_DV_DisconnectCallback disconnect_cb,
490                            GNUNET_DV_MessageReceivedCallback message_cb)
491 {
492   struct GNUNET_DV_ServiceHandle *sh;
493
494   sh = GNUNET_malloc (sizeof (struct GNUNET_DV_ServiceHandle));
495   sh->cfg = cfg;
496   sh->cls = cls;
497   sh->connect_cb = connect_cb;
498   sh->disconnect_cb = disconnect_cb;
499   sh->message_cb = message_cb;
500   sh->send_callbacks = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_YES);
501   reconnect (sh);
502   return sh;
503 }
504
505
506 /**
507  * Disconnect from DV service.
508  *
509  * @param sh service handle
510  */
511 void
512 GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh)
513 {
514   struct GNUNET_DV_TransmitHandle *pos;
515   
516   if (NULL == sh)
517     return;
518   if (NULL != sh->th)
519   {
520     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
521     sh->th = NULL;
522   }
523   while (NULL != (pos = sh->th_head))
524   {
525     GNUNET_CONTAINER_DLL_remove (sh->th_head,
526                                  sh->th_tail,
527                                  pos);
528     GNUNET_free (pos);
529   }
530   if (NULL != sh->client) 
531   {
532     GNUNET_CLIENT_disconnect (sh->client);
533     sh->client = NULL;
534   }
535   GNUNET_CONTAINER_multihashmap_iterate (sh->send_callbacks,
536                                          &cleanup_send_cb,
537                                          sh);
538   GNUNET_CONTAINER_multihashmap_destroy (sh->send_callbacks);
539   GNUNET_free (sh);
540 }
541
542
543 /**
544  * Send a message via DV service.
545  *
546  * @param sh service handle
547  * @param target intended recpient
548  * @param msg message payload
549  * @param cb function to invoke when done
550  * @param cb_cls closure for 'cb'
551  * @return handle to cancel the operation
552  */
553 struct GNUNET_DV_TransmitHandle *
554 GNUNET_DV_send (struct GNUNET_DV_ServiceHandle *sh,
555                 const struct GNUNET_PeerIdentity *target,
556                 const struct GNUNET_MessageHeader *msg,
557                 GNUNET_DV_MessageSentCallback cb,
558                 void *cb_cls)
559 {
560   struct GNUNET_DV_TransmitHandle *th;
561   struct GNUNET_DV_SendMessage *sm;
562
563   if (ntohs (msg->size) + sizeof (struct GNUNET_DV_SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
564   {
565     GNUNET_break (0);
566     return NULL;
567   }
568   th = GNUNET_malloc (sizeof (struct GNUNET_DV_TransmitHandle) +
569                       sizeof (struct GNUNET_DV_SendMessage) +
570                       ntohs (msg->size));
571   th->sh = sh;
572   th->target = *target;
573   th->cb = cb;
574   th->cb_cls = cb_cls;
575   th->msg = (const struct GNUNET_MessageHeader *) &th[1];
576   sm = (struct GNUNET_DV_SendMessage *) &th[1];
577   sm->header.type = htons (GNUNET_MESSAGE_TYPE_DV_SEND);
578   sm->header.size = htons (sizeof (struct GNUNET_DV_SendMessage) + 
579                            ntohs (msg->size));
580   if (0 == sh->uid_gen)
581     sh->uid_gen = 1;
582   th->uid = sh->uid_gen;
583   sm->uid = htonl (sh->uid_gen++);
584   /* use memcpy here as 'target' may not be sufficiently aligned */
585   memcpy (&sm->target, target, sizeof (struct GNUNET_PeerIdentity));
586   memcpy (&sm[1], msg, ntohs (msg->size));
587   GNUNET_CONTAINER_DLL_insert (sh->th_head,
588                                sh->th_tail,
589                                th);
590   start_transmit (sh);
591   return th;
592 }
593
594
595 /**
596  * Abort send operation (naturally, the message may have
597  * already been transmitted; this only stops the 'cb'
598  * from being called again).
599  *
600  * @param th send operation to cancel
601  */
602 void
603 GNUNET_DV_send_cancel (struct GNUNET_DV_TransmitHandle *th)
604 {
605   struct GNUNET_DV_ServiceHandle *sh = th->sh;
606   int ret;
607
608   ret = GNUNET_CONTAINER_multihashmap_remove (sh->send_callbacks,
609                                               &th->target.hashPubKey,
610                                               th);
611   if (GNUNET_YES != ret)
612     GNUNET_CONTAINER_DLL_remove (sh->th_head,
613                                  sh->th_tail,
614                                  th);
615   GNUNET_free (th);
616 }
617
618
619 /* end of dv_api.c */