-style 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  * Information we track for each peer.
39  */
40 struct ConnectedPeer;
41
42
43 /**
44  * Handle for a send operation.
45  */
46 struct GNUNET_DV_TransmitHandle
47 {
48   /**
49    * Kept in a DLL.
50    */
51   struct GNUNET_DV_TransmitHandle *next;
52
53   /**
54    * Kept in a DLL.
55    */
56   struct GNUNET_DV_TransmitHandle *prev;
57
58   /**
59    * Handle to the service.
60    */
61   struct GNUNET_DV_ServiceHandle *sh;
62
63   /**
64    * Function to call upon completion.
65    */
66   GNUNET_DV_MessageSentCallback cb;
67
68   /**
69    * Closure for @a cb.
70    */
71   void *cb_cls;
72
73   /**
74    * The actual message (allocated at the end of this struct).
75    */
76   const struct GNUNET_MessageHeader *msg;
77
78   /**
79    * Destination for the message.
80    */
81   struct ConnectedPeer *target;
82
83   /**
84    * UID of our message, if any.
85    */
86   uint32_t uid;
87
88 };
89
90
91 /**
92  * Information we track for each peer.
93  */
94 struct ConnectedPeer
95 {
96
97   /**
98    * Identity of the peer.
99    */
100   struct GNUNET_PeerIdentity pid;
101
102   /**
103    * Head of DLL of transmission handles where we need
104    * to invoke a continuation when we are informed about
105    * successful transmission.  The respective request
106    * has already been sent to the DV service.
107    */
108   struct GNUNET_DV_TransmitHandle *head;
109
110   /**
111    * Tail of DLL of transmission handles where we need
112    * to invoke a continuation when we are informed about
113    * successful transmission.  The respective request
114    * has already been sent to the DV service.
115    */
116   struct GNUNET_DV_TransmitHandle *tail;
117
118 };
119
120
121 /**
122  * Handle to the DV service.
123  */
124 struct GNUNET_DV_ServiceHandle
125 {
126
127   /**
128    * Connection to DV service.
129    */
130   struct GNUNET_CLIENT_Connection *client;
131
132   /**
133    * Active request for transmission to DV service.
134    */
135   struct GNUNET_CLIENT_TransmitHandle *th;
136
137   /**
138    * Our configuration.
139    */
140   const struct GNUNET_CONFIGURATION_Handle *cfg;
141
142   /**
143    * Closure for the callbacks.
144    */
145   void *cls;
146
147   /**
148    * Function to call on connect events.
149    */
150   GNUNET_DV_ConnectCallback connect_cb;
151
152   /**
153    * Function to call on distance change events.
154    */
155   GNUNET_DV_DistanceChangedCallback distance_cb;
156
157   /**
158    * Function to call on disconnect events.
159    */
160   GNUNET_DV_DisconnectCallback disconnect_cb;
161
162   /**
163    * Function to call on receiving messages events.
164    */
165   GNUNET_DV_MessageReceivedCallback message_cb;
166
167   /**
168    * Head of messages to transmit.
169    */
170   struct GNUNET_DV_TransmitHandle *th_head;
171
172   /**
173    * Tail of messages to transmit.
174    */
175   struct GNUNET_DV_TransmitHandle *th_tail;
176
177   /**
178    * Information tracked per connected peer.  Maps peer
179    * identities to `struct ConnectedPeer` entries.
180    */
181   struct GNUNET_CONTAINER_MultiPeerMap *peers;
182
183   /**
184    * Current unique ID
185    */
186   uint32_t uid_gen;
187
188 };
189
190
191 /**
192  * Disconnect and then reconnect to the DV service.
193  *
194  * @param sh service handle
195  */
196 static void
197 reconnect (struct GNUNET_DV_ServiceHandle *sh);
198
199
200 /**
201  * Gives a message from our queue to the DV service.
202  *
203  * @param cls handle to the dv service (`struct GNUNET_DV_ServiceHandle`)
204  * @param size how many bytes can we send
205  * @param buf where to copy the message to send
206  * @return how many bytes we copied to @a buf
207  */
208 static size_t
209 transmit_pending (void *cls, size_t size, void *buf)
210 {
211   struct GNUNET_DV_ServiceHandle *sh = cls;
212   char *cbuf = buf;
213   struct GNUNET_DV_TransmitHandle *th;
214   size_t ret;
215   size_t tsize;
216
217   sh->th = NULL;
218   if (NULL == buf)
219   {
220     reconnect (sh);
221     return 0;
222   }
223   ret = 0;
224   while ( (NULL != (th = sh->th_head)) &&
225           (size - ret >= (tsize = ntohs (th->msg->size)) ))
226   {
227     GNUNET_CONTAINER_DLL_remove (sh->th_head,
228                                  sh->th_tail,
229                                  th);
230     memcpy (&cbuf[ret], th->msg, tsize);
231     LOG (GNUNET_ERROR_TYPE_DEBUG,
232          "Passing %u bytes of type %u to DV service\n",
233          tsize,
234          ntohs (th->msg->type));
235     th->msg = NULL;
236     ret += tsize;
237     if (NULL != th->cb)
238     {
239       GNUNET_CONTAINER_DLL_insert (th->target->head,
240                                    th->target->tail,
241                                    th);
242     }
243     else
244     {
245       GNUNET_free (th);
246     }
247   }
248   return ret;
249 }
250
251
252 /**
253  * Start sending messages from our queue to the service.
254  *
255  * @param sh service handle
256  */
257 static void
258 start_transmit (struct GNUNET_DV_ServiceHandle *sh)
259 {
260   if (NULL != sh->th)
261     return;
262   if (NULL == sh->th_head)
263     return;
264   sh->th =
265     GNUNET_CLIENT_notify_transmit_ready (sh->client,
266                                          ntohs (sh->th_head->msg->size),
267                                          GNUNET_TIME_UNIT_FOREVER_REL,
268                                          GNUNET_NO,
269                                          &transmit_pending, sh);
270 }
271
272
273 /**
274  * We got disconnected from the service and thus all of the
275  * pending send callbacks will never be confirmed.  Clean up.
276  *
277  * @param cls the 'struct GNUNET_DV_ServiceHandle'
278  * @param key a peer identity
279  * @param value a `struct ConnectedPeer` to clean up
280  * @return #GNUNET_OK (continue to iterate)
281  */
282 static int
283 cleanup_send_cb (void *cls,
284                  const struct GNUNET_PeerIdentity *key,
285                  void *value)
286 {
287   struct GNUNET_DV_ServiceHandle *sh = cls;
288   struct ConnectedPeer *peer = value;
289   struct GNUNET_DV_TransmitHandle *th;
290
291   GNUNET_assert (GNUNET_YES ==
292                  GNUNET_CONTAINER_multipeermap_remove (sh->peers,
293                                                        key,
294                                                        peer));
295   sh->disconnect_cb (sh->cls,
296                      key);
297   while (NULL != (th = peer->head))
298   {
299     GNUNET_CONTAINER_DLL_remove (peer->head, peer->tail, th);
300     th->cb (th->cb_cls, GNUNET_SYSERR);
301     GNUNET_free (th);
302   }
303   GNUNET_free (peer);
304   return GNUNET_OK;
305 }
306
307
308 /**
309  * Handles a message sent from the DV service to us.
310  * Parse it out and give it to the plugin.
311  *
312  * @param cls the handle to the DV API
313  * @param msg the message that was received
314  */
315 static void
316 handle_message_receipt (void *cls,
317                         const struct GNUNET_MessageHeader *msg)
318 {
319   struct GNUNET_DV_ServiceHandle *sh = cls;
320   const struct GNUNET_DV_ConnectMessage *cm;
321   const struct GNUNET_DV_DistanceUpdateMessage *dum;
322   const struct GNUNET_DV_DisconnectMessage *dm;
323   const struct GNUNET_DV_ReceivedMessage *rm;
324   const struct GNUNET_MessageHeader *payload;
325   const struct GNUNET_DV_AckMessage *ack;
326   struct GNUNET_DV_TransmitHandle *th;
327   struct GNUNET_DV_TransmitHandle *tn;
328   struct ConnectedPeer *peer;
329
330   if (NULL == msg)
331   {
332     /* Connection closed */
333     reconnect (sh);
334     return;
335   }
336   LOG (GNUNET_ERROR_TYPE_DEBUG,
337        "Received message of type %u with %u bytes from DV service\n",
338        (unsigned int) ntohs (msg->type),
339        (unsigned int) ntohs (msg->size));
340   switch (ntohs (msg->type))
341   {
342   case GNUNET_MESSAGE_TYPE_DV_CONNECT:
343     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ConnectMessage))
344     {
345       GNUNET_break (0);
346       reconnect (sh);
347       return;
348     }
349     cm = (const struct GNUNET_DV_ConnectMessage *) msg;
350     peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
351                                               &cm->peer);
352     if (NULL != peer)
353     {
354       GNUNET_break (0);
355       reconnect (sh);
356       return;
357     }
358     peer = GNUNET_new (struct ConnectedPeer);
359     peer->pid = cm->peer;
360     GNUNET_assert (GNUNET_OK ==
361                    GNUNET_CONTAINER_multipeermap_put (sh->peers,
362                                                       &peer->pid,
363                                                       peer,
364                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
365     sh->connect_cb (sh->cls,
366                     &cm->peer,
367                     ntohl (cm->distance),
368                     (enum GNUNET_ATS_Network_Type) ntohl (cm->network));
369     break;
370   case GNUNET_MESSAGE_TYPE_DV_DISTANCE_CHANGED:
371     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DistanceUpdateMessage))
372     {
373       GNUNET_break (0);
374       reconnect (sh);
375       return;
376     }
377     dum = (const struct GNUNET_DV_DistanceUpdateMessage *) msg;
378     sh->distance_cb (sh->cls,
379                      &dum->peer,
380                      ntohl (dum->distance),
381                      (enum GNUNET_ATS_Network_Type) ntohl (dum->network));
382     break;
383   case GNUNET_MESSAGE_TYPE_DV_DISCONNECT:
384     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DisconnectMessage))
385     {
386       GNUNET_break (0);
387       reconnect (sh);
388       return;
389     }
390     dm = (const struct GNUNET_DV_DisconnectMessage *) msg;
391     peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
392                                               &dm->peer);
393     if (NULL == peer)
394     {
395       GNUNET_break (0);
396       reconnect (sh);
397       return;
398     }
399     tn = sh->th_head;
400     while (NULL != (th = tn))
401     {
402       tn = th->next;
403       if (peer == th->target)
404       {
405         GNUNET_CONTAINER_DLL_remove (sh->th_head,
406                                      sh->th_tail,
407                                      th);
408         th->cb (th->cb_cls, GNUNET_SYSERR);
409         GNUNET_free (th);
410       }
411     }
412     cleanup_send_cb (sh, &dm->peer, peer);
413     break;
414   case GNUNET_MESSAGE_TYPE_DV_RECV:
415     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_ReceivedMessage) + sizeof (struct GNUNET_MessageHeader))
416     {
417       GNUNET_break (0);
418       reconnect (sh);
419       return;
420     }
421     rm = (const struct GNUNET_DV_ReceivedMessage *) msg;
422     payload = (const struct GNUNET_MessageHeader *) &rm[1];
423     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (payload->size))
424     {
425       GNUNET_break (0);
426       reconnect (sh);
427       return;
428     }
429     if (NULL ==
430         GNUNET_CONTAINER_multipeermap_get (sh->peers,
431                                            &rm->sender))
432     {
433       GNUNET_break (0);
434       reconnect (sh);
435       return;
436     }
437     sh->message_cb (sh->cls,
438                     &rm->sender,
439                     ntohl (rm->distance),
440                     payload);
441     break;
442   case GNUNET_MESSAGE_TYPE_DV_SEND_ACK:
443   case GNUNET_MESSAGE_TYPE_DV_SEND_NACK:
444     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_AckMessage))
445     {
446       GNUNET_break (0);
447       reconnect (sh);
448       return;
449     }
450     ack = (const struct GNUNET_DV_AckMessage *) msg;
451     peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
452                                               &ack->target);
453     if (NULL == peer)
454       break; /* this happens, just ignore */
455     for (th = peer->head; NULL != th; th = th->next)
456     {
457       if (th->uid != ntohl (ack->uid))
458         continue;
459       LOG (GNUNET_ERROR_TYPE_DEBUG,
460            "Matched ACK for message to peer %s\n",
461            GNUNET_i2s (&ack->target));
462       GNUNET_CONTAINER_DLL_remove (peer->head,
463                                    peer->tail,
464                                    th);
465       th->cb (th->cb_cls,
466               (ntohs (ack->header.type) == GNUNET_MESSAGE_TYPE_DV_SEND_ACK)
467               ? GNUNET_OK
468               : GNUNET_SYSERR);
469       GNUNET_free (th);
470       break;
471     }
472     break;
473   default:
474     reconnect (sh);
475     break;
476   }
477   LOG (GNUNET_ERROR_TYPE_DEBUG,
478        "Received message, continuing receive loop for %p\n",
479        sh->client);
480   GNUNET_CLIENT_receive (sh->client,
481                          &handle_message_receipt, sh,
482                          GNUNET_TIME_UNIT_FOREVER_REL);
483 }
484
485
486 /**
487  * Transmit the start message to the DV service.
488  *
489  * @param cls the `struct GNUNET_DV_ServiceHandle *`
490  * @param size number of bytes available in buf
491  * @param buf where to copy the message
492  * @return number of bytes written to buf
493  */
494 static size_t
495 transmit_start (void *cls,
496                 size_t size,
497                 void *buf)
498 {
499   struct GNUNET_DV_ServiceHandle *sh = cls;
500   struct GNUNET_MessageHeader start_message;
501
502   sh->th = NULL;
503   if (NULL == buf)
504   {
505     GNUNET_break (0);
506     reconnect (sh);
507     return 0;
508   }
509   GNUNET_assert (size >= sizeof (start_message));
510   start_message.size = htons (sizeof (struct GNUNET_MessageHeader));
511   start_message.type = htons (GNUNET_MESSAGE_TYPE_DV_START);
512   memcpy (buf, &start_message, sizeof (start_message));
513   LOG (GNUNET_ERROR_TYPE_DEBUG,
514        "Transmitting START request, starting receive loop for %p\n",
515        sh->client);
516   GNUNET_CLIENT_receive (sh->client,
517                          &handle_message_receipt, sh,
518                          GNUNET_TIME_UNIT_FOREVER_REL);
519   start_transmit (sh);
520   return sizeof (start_message);
521 }
522
523
524 /**
525  * Disconnect and then reconnect to the DV service.
526  *
527  * @param sh service handle
528  */
529 static void
530 reconnect (struct GNUNET_DV_ServiceHandle *sh)
531 {
532   if (NULL != sh->th)
533   {
534     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
535     sh->th = NULL;
536   }
537   LOG (GNUNET_ERROR_TYPE_DEBUG,
538        "Disconnecting from DV service at %p\n",
539        sh->client);
540   if (NULL != sh->client)
541   {
542     GNUNET_CLIENT_disconnect (sh->client);
543     sh->client = NULL;
544   }
545   GNUNET_CONTAINER_multipeermap_iterate (sh->peers,
546                                          &cleanup_send_cb,
547                                          sh);
548   LOG (GNUNET_ERROR_TYPE_DEBUG,
549        "Connecting to DV service\n");
550   sh->client = GNUNET_CLIENT_connect ("dv", sh->cfg);
551   if (NULL == sh->client)
552   {
553     GNUNET_break (0);
554     return;
555   }
556   sh->th = GNUNET_CLIENT_notify_transmit_ready (sh->client,
557                                                 sizeof (struct GNUNET_MessageHeader),
558                                                 GNUNET_TIME_UNIT_FOREVER_REL,
559                                                 GNUNET_YES,
560                                                 &transmit_start,
561                                                 sh);
562 }
563
564
565 /**
566  * Connect to the DV service.
567  *
568  * @param cfg configuration
569  * @param cls closure for callbacks
570  * @param connect_cb function to call on connects
571  * @param distance_cb function to call if distances change
572  * @param disconnect_cb function to call on disconnects
573  * @param message_cb function to call if we receive messages
574  * @return handle to access the service
575  */
576 struct GNUNET_DV_ServiceHandle *
577 GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
578                            void *cls,
579                            GNUNET_DV_ConnectCallback connect_cb,
580                            GNUNET_DV_DistanceChangedCallback distance_cb,
581                            GNUNET_DV_DisconnectCallback disconnect_cb,
582                            GNUNET_DV_MessageReceivedCallback message_cb)
583 {
584   struct GNUNET_DV_ServiceHandle *sh;
585
586   sh = GNUNET_new (struct GNUNET_DV_ServiceHandle);
587   sh->cfg = cfg;
588   sh->cls = cls;
589   sh->connect_cb = connect_cb;
590   sh->distance_cb = distance_cb;
591   sh->disconnect_cb = disconnect_cb;
592   sh->message_cb = message_cb;
593   sh->peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES);
594   reconnect (sh);
595   return sh;
596 }
597
598
599 /**
600  * Disconnect from DV service.
601  *
602  * @param sh service handle
603  */
604 void
605 GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh)
606 {
607   struct GNUNET_DV_TransmitHandle *pos;
608
609   if (NULL == sh)
610     return;
611   if (NULL != sh->th)
612   {
613     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
614     sh->th = NULL;
615   }
616   while (NULL != (pos = sh->th_head))
617   {
618     GNUNET_CONTAINER_DLL_remove (sh->th_head,
619                                  sh->th_tail,
620                                  pos);
621     GNUNET_free (pos);
622   }
623   if (NULL != sh->client)
624   {
625     GNUNET_CLIENT_disconnect (sh->client);
626     sh->client = NULL;
627   }
628   GNUNET_CONTAINER_multipeermap_iterate (sh->peers,
629                                          &cleanup_send_cb,
630                                          sh);
631   GNUNET_CONTAINER_multipeermap_destroy (sh->peers);
632   GNUNET_free (sh);
633 }
634
635
636 /**
637  * Send a message via DV service.
638  *
639  * @param sh service handle
640  * @param target intended recpient
641  * @param msg message payload
642  * @param cb function to invoke when done
643  * @param cb_cls closure for @a cb
644  * @return handle to cancel the operation
645  */
646 struct GNUNET_DV_TransmitHandle *
647 GNUNET_DV_send (struct GNUNET_DV_ServiceHandle *sh,
648                 const struct GNUNET_PeerIdentity *target,
649                 const struct GNUNET_MessageHeader *msg,
650                 GNUNET_DV_MessageSentCallback cb,
651                 void *cb_cls)
652 {
653   struct GNUNET_DV_TransmitHandle *th;
654   struct GNUNET_DV_SendMessage *sm;
655   struct ConnectedPeer *peer;
656
657   if (ntohs (msg->size) + sizeof (struct GNUNET_DV_SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
658   {
659     GNUNET_break (0);
660     return NULL;
661   }
662   LOG (GNUNET_ERROR_TYPE_DEBUG,
663        "Asked to send %u bytes of type %u to %s via %p\n",
664        (unsigned int) ntohs (msg->size),
665        (unsigned int) ntohs (msg->type),
666        GNUNET_i2s (target),
667        sh->client);
668   peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
669                                             target);
670   if (NULL == peer)
671   {
672     GNUNET_break (0);
673     return NULL;
674   }
675   th = GNUNET_malloc (sizeof (struct GNUNET_DV_TransmitHandle) +
676                       sizeof (struct GNUNET_DV_SendMessage) +
677                       ntohs (msg->size));
678   th->sh = sh;
679   th->target = peer;
680   th->cb = cb;
681   th->cb_cls = cb_cls;
682   th->msg = (const struct GNUNET_MessageHeader *) &th[1];
683   sm = (struct GNUNET_DV_SendMessage *) &th[1];
684   sm->header.type = htons (GNUNET_MESSAGE_TYPE_DV_SEND);
685   sm->header.size = htons (sizeof (struct GNUNET_DV_SendMessage) +
686                            ntohs (msg->size));
687   if (0 == sh->uid_gen)
688     sh->uid_gen = 1;
689   th->uid = sh->uid_gen;
690   sm->uid = htonl (sh->uid_gen++);
691   /* use memcpy here as 'target' may not be sufficiently aligned */
692   memcpy (&sm->target, target, sizeof (struct GNUNET_PeerIdentity));
693   memcpy (&sm[1], msg, ntohs (msg->size));
694   GNUNET_CONTAINER_DLL_insert (sh->th_head,
695                                sh->th_tail,
696                                th);
697   start_transmit (sh);
698   return th;
699 }
700
701
702 /**
703  * Abort send operation (naturally, the message may have
704  * already been transmitted; this only stops the 'cb'
705  * from being called again).
706  *
707  * @param th send operation to cancel
708  */
709 void
710 GNUNET_DV_send_cancel (struct GNUNET_DV_TransmitHandle *th)
711 {
712   struct GNUNET_DV_ServiceHandle *sh = th->sh;
713
714   if (NULL == th->msg)
715     GNUNET_CONTAINER_DLL_remove (th->target->head,
716                                  th->target->tail,
717                                  th);
718   else
719     GNUNET_CONTAINER_DLL_remove (sh->th_head,
720                                  sh->th_tail,
721                                  th);
722   GNUNET_free (th);
723 }
724
725
726 /* end of dv_api.c */