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