-fix endianess before printing DEBUG message
[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     th->msg = NULL;
232     ret += tsize;
233     if (NULL != th->cb)
234     {
235       GNUNET_CONTAINER_DLL_insert (th->target->head,
236                                    th->target->tail,
237                                    th);
238     }
239     else
240     {
241       GNUNET_free (th);
242     }
243   }
244   return ret;
245 }
246
247
248 /**
249  * Start sending messages from our queue to the service.
250  *
251  * @param sh service handle
252  */
253 static void
254 start_transmit (struct GNUNET_DV_ServiceHandle *sh)
255 {
256   if (NULL != sh->th)
257     return;
258   if (NULL == sh->th_head)
259     return;
260   sh->th =
261     GNUNET_CLIENT_notify_transmit_ready (sh->client,
262                                          ntohs (sh->th_head->msg->size),
263                                          GNUNET_TIME_UNIT_FOREVER_REL,
264                                          GNUNET_NO,
265                                          &transmit_pending, sh);
266 }
267
268
269 /**
270  * We got disconnected from the service and thus all of the
271  * pending send callbacks will never be confirmed.  Clean up.
272  *
273  * @param cls the 'struct GNUNET_DV_ServiceHandle'
274  * @param key a peer identity
275  * @param value a `struct ConnectedPeer` to clean up
276  * @return #GNUNET_OK (continue to iterate)
277  */
278 static int
279 cleanup_send_cb (void *cls,
280                  const struct GNUNET_PeerIdentity *key,
281                  void *value)
282 {
283   struct GNUNET_DV_ServiceHandle *sh = cls;
284   struct ConnectedPeer *peer = value;
285   struct GNUNET_DV_TransmitHandle *th;
286
287   GNUNET_assert (GNUNET_YES ==
288                  GNUNET_CONTAINER_multipeermap_remove (sh->peers,
289                                                        key,
290                                                        peer));
291   sh->disconnect_cb (sh->cls,
292                      key);
293   while (NULL != (th = peer->head))
294   {
295     GNUNET_CONTAINER_DLL_remove (peer->head, peer->tail, th);
296     th->cb (th->cb_cls, GNUNET_SYSERR);
297     GNUNET_free (th);
298   }
299   GNUNET_free (peer);
300   return GNUNET_OK;
301 }
302
303
304 /**
305  * Handles a message sent from the DV service to us.
306  * Parse it out and give it to the plugin.
307  *
308  * @param cls the handle to the DV API
309  * @param msg the message that was received
310  */
311 static void
312 handle_message_receipt (void *cls,
313                         const struct GNUNET_MessageHeader *msg)
314 {
315   struct GNUNET_DV_ServiceHandle *sh = cls;
316   const struct GNUNET_DV_ConnectMessage *cm;
317   const struct GNUNET_DV_DistanceUpdateMessage *dum;
318   const struct GNUNET_DV_DisconnectMessage *dm;
319   const struct GNUNET_DV_ReceivedMessage *rm;
320   const struct GNUNET_MessageHeader *payload;
321   const struct GNUNET_DV_AckMessage *ack;
322   struct GNUNET_DV_TransmitHandle *th;
323   struct GNUNET_DV_TransmitHandle *tn;
324   struct ConnectedPeer *peer;
325
326   if (NULL == msg)
327   {
328     /* Connection closed */
329     reconnect (sh);
330     return;
331   }
332   LOG (GNUNET_ERROR_TYPE_DEBUG,
333        "Received message of type %u with %u bytes from DV service\n",
334        (unsigned int) ntohs (msg->type),
335        (unsigned int) ntohs (msg->size));
336   switch (ntohs (msg->type))
337   {
338   case GNUNET_MESSAGE_TYPE_DV_CONNECT:
339     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ConnectMessage))
340     {
341       GNUNET_break (0);
342       reconnect (sh);
343       return;
344     }
345     cm = (const struct GNUNET_DV_ConnectMessage *) msg;
346     peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
347                                               &cm->peer);
348     if (NULL != peer)
349     {
350       GNUNET_break (0);
351       reconnect (sh);
352       return;
353     }
354     peer = GNUNET_new (struct ConnectedPeer);
355     peer->pid = cm->peer;
356     GNUNET_assert (GNUNET_OK ==
357                    GNUNET_CONTAINER_multipeermap_put (sh->peers,
358                                                       &peer->pid,
359                                                       peer,
360                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
361     sh->connect_cb (sh->cls,
362                     &cm->peer,
363                     ntohl (cm->distance),
364                     (enum GNUNET_ATS_Network_Type) ntohl (cm->network));
365     break;
366   case GNUNET_MESSAGE_TYPE_DV_DISTANCE_CHANGED:
367     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DistanceUpdateMessage))
368     {
369       GNUNET_break (0);
370       reconnect (sh);
371       return;
372     }
373     dum = (const struct GNUNET_DV_DistanceUpdateMessage *) msg;
374     sh->distance_cb (sh->cls,
375                      &dum->peer,
376                      ntohl (dum->distance),
377                      (enum GNUNET_ATS_Network_Type) ntohl (dum->network));
378     break;
379   case GNUNET_MESSAGE_TYPE_DV_DISCONNECT:
380     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DisconnectMessage))
381     {
382       GNUNET_break (0);
383       reconnect (sh);
384       return;
385     }
386     dm = (const struct GNUNET_DV_DisconnectMessage *) msg;
387     peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
388                                               &dm->peer);
389     if (NULL == peer)
390     {
391       GNUNET_break (0);
392       reconnect (sh);
393       return;
394     }
395     tn = sh->th_head;
396     while (NULL != (th = tn))
397     {
398       tn = th->next;
399       if (peer == th->target)
400       {
401         GNUNET_CONTAINER_DLL_remove (sh->th_head,
402                                      sh->th_tail,
403                                      th);
404         th->cb (th->cb_cls, GNUNET_SYSERR);
405         GNUNET_free (th);
406       }
407     }
408     cleanup_send_cb (sh, &dm->peer, peer);
409     break;
410   case GNUNET_MESSAGE_TYPE_DV_RECV:
411     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_ReceivedMessage) + sizeof (struct GNUNET_MessageHeader))
412     {
413       GNUNET_break (0);
414       reconnect (sh);
415       return;
416     }
417     rm = (const struct GNUNET_DV_ReceivedMessage *) msg;
418     payload = (const struct GNUNET_MessageHeader *) &rm[1];
419     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (payload->size))
420     {
421       GNUNET_break (0);
422       reconnect (sh);
423       return;
424     }
425     sh->message_cb (sh->cls,
426                     &rm->sender,
427                     ntohl (rm->distance),
428                     payload);
429     break;
430   case GNUNET_MESSAGE_TYPE_DV_SEND_ACK:
431   case GNUNET_MESSAGE_TYPE_DV_SEND_NACK:
432     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_AckMessage))
433     {
434       GNUNET_break (0);
435       reconnect (sh);
436       return;
437     }
438     ack = (const struct GNUNET_DV_AckMessage *) msg;
439     peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
440                                               &ack->target);
441     if (NULL == peer)
442       return; /* this happens, just ignore */
443     for (th = peer->head; NULL != th; th = th->next)
444     {
445       if (th->uid != ntohl (ack->uid))
446         continue;
447       LOG (GNUNET_ERROR_TYPE_DEBUG,
448            "Matched ACK for message to peer %s\n",
449            GNUNET_i2s (&ack->target));
450       GNUNET_CONTAINER_DLL_remove (peer->head,
451                                    peer->tail,
452                                    th);
453       th->cb (th->cb_cls,
454               (ntohs (ack->header.type) == GNUNET_MESSAGE_TYPE_DV_SEND_ACK)
455               ? GNUNET_OK
456               : GNUNET_SYSERR);
457       GNUNET_free (th);
458       break;
459     }
460     break;
461   default:
462     reconnect (sh);
463     break;
464   }
465   GNUNET_CLIENT_receive (sh->client,
466                          &handle_message_receipt, sh,
467                          GNUNET_TIME_UNIT_FOREVER_REL);
468 }
469
470
471 /**
472  * Transmit the start message to the DV service.
473  *
474  * @param cls the `struct GNUNET_DV_ServiceHandle *`
475  * @param size number of bytes available in buf
476  * @param buf where to copy the message
477  * @return number of bytes written to buf
478  */
479 static size_t
480 transmit_start (void *cls,
481                 size_t size,
482                 void *buf)
483 {
484   struct GNUNET_DV_ServiceHandle *sh = cls;
485   struct GNUNET_MessageHeader start_message;
486
487   sh->th = NULL;
488   if (NULL == buf)
489   {
490     GNUNET_break (0);
491     reconnect (sh);
492     return 0;
493   }
494   GNUNET_assert (size >= sizeof (start_message));
495   start_message.size = htons (sizeof (struct GNUNET_MessageHeader));
496   start_message.type = htons (GNUNET_MESSAGE_TYPE_DV_START);
497   memcpy (buf, &start_message, sizeof (start_message));
498   GNUNET_CLIENT_receive (sh->client,
499                          &handle_message_receipt, sh,
500                          GNUNET_TIME_UNIT_FOREVER_REL);
501   start_transmit (sh);
502   return sizeof (start_message);
503 }
504
505
506 /**
507  * Disconnect and then reconnect to the DV service.
508  *
509  * @param sh service handle
510  */
511 static void
512 reconnect (struct GNUNET_DV_ServiceHandle *sh)
513 {
514   if (NULL != sh->th)
515   {
516     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
517     sh->th = NULL;
518   }
519   if (NULL != sh->client)
520   {
521     GNUNET_CLIENT_disconnect (sh->client);
522     sh->client = NULL;
523   }
524   GNUNET_CONTAINER_multipeermap_iterate (sh->peers,
525                                          &cleanup_send_cb,
526                                          sh);
527   LOG (GNUNET_ERROR_TYPE_DEBUG,
528        "Connecting to DV service\n");
529   sh->client = GNUNET_CLIENT_connect ("dv", sh->cfg);
530   if (NULL == sh->client)
531   {
532     GNUNET_break (0);
533     return;
534   }
535   sh->th = GNUNET_CLIENT_notify_transmit_ready (sh->client,
536                                                 sizeof (struct GNUNET_MessageHeader),
537                                                 GNUNET_TIME_UNIT_FOREVER_REL,
538                                                 GNUNET_YES,
539                                                 &transmit_start,
540                                                 sh);
541 }
542
543
544 /**
545  * Connect to the DV service.
546  *
547  * @param cfg configuration
548  * @param cls closure for callbacks
549  * @param connect_cb function to call on connects
550  * @param distance_cb function to call if distances change
551  * @param disconnect_cb function to call on disconnects
552  * @param message_cb function to call if we receive messages
553  * @return handle to access the service
554  */
555 struct GNUNET_DV_ServiceHandle *
556 GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
557                            void *cls,
558                            GNUNET_DV_ConnectCallback connect_cb,
559                            GNUNET_DV_DistanceChangedCallback distance_cb,
560                            GNUNET_DV_DisconnectCallback disconnect_cb,
561                            GNUNET_DV_MessageReceivedCallback message_cb)
562 {
563   struct GNUNET_DV_ServiceHandle *sh;
564
565   sh = GNUNET_new (struct GNUNET_DV_ServiceHandle);
566   sh->cfg = cfg;
567   sh->cls = cls;
568   sh->connect_cb = connect_cb;
569   sh->distance_cb = distance_cb;
570   sh->disconnect_cb = disconnect_cb;
571   sh->message_cb = message_cb;
572   sh->peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES);
573   reconnect (sh);
574   return sh;
575 }
576
577
578 /**
579  * Disconnect from DV service.
580  *
581  * @param sh service handle
582  */
583 void
584 GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh)
585 {
586   struct GNUNET_DV_TransmitHandle *pos;
587
588   if (NULL == sh)
589     return;
590   if (NULL != sh->th)
591   {
592     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
593     sh->th = NULL;
594   }
595   while (NULL != (pos = sh->th_head))
596   {
597     GNUNET_CONTAINER_DLL_remove (sh->th_head,
598                                  sh->th_tail,
599                                  pos);
600     GNUNET_free (pos);
601   }
602   if (NULL != sh->client)
603   {
604     GNUNET_CLIENT_disconnect (sh->client);
605     sh->client = NULL;
606   }
607   GNUNET_CONTAINER_multipeermap_iterate (sh->peers,
608                                          &cleanup_send_cb,
609                                          sh);
610   GNUNET_CONTAINER_multipeermap_destroy (sh->peers);
611   GNUNET_free (sh);
612 }
613
614
615 /**
616  * Send a message via DV service.
617  *
618  * @param sh service handle
619  * @param target intended recpient
620  * @param msg message payload
621  * @param cb function to invoke when done
622  * @param cb_cls closure for @a cb
623  * @return handle to cancel the operation
624  */
625 struct GNUNET_DV_TransmitHandle *
626 GNUNET_DV_send (struct GNUNET_DV_ServiceHandle *sh,
627                 const struct GNUNET_PeerIdentity *target,
628                 const struct GNUNET_MessageHeader *msg,
629                 GNUNET_DV_MessageSentCallback cb,
630                 void *cb_cls)
631 {
632   struct GNUNET_DV_TransmitHandle *th;
633   struct GNUNET_DV_SendMessage *sm;
634   struct ConnectedPeer *peer;
635
636   if (ntohs (msg->size) + sizeof (struct GNUNET_DV_SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
637   {
638     GNUNET_break (0);
639     return NULL;
640   }
641   LOG (GNUNET_ERROR_TYPE_DEBUG,
642        "Asked to send %u bytes of type %u to %s\n",
643        (unsigned int) ntohs (msg->size),
644        (unsigned int) ntohs (msg->type),
645        GNUNET_i2s (target));
646   peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
647                                             target);
648   if (NULL == peer)
649   {
650     GNUNET_break (0);
651     return NULL;
652   }
653   th = GNUNET_malloc (sizeof (struct GNUNET_DV_TransmitHandle) +
654                       sizeof (struct GNUNET_DV_SendMessage) +
655                       ntohs (msg->size));
656   th->sh = sh;
657   th->target = peer;
658   th->cb = cb;
659   th->cb_cls = cb_cls;
660   th->msg = (const struct GNUNET_MessageHeader *) &th[1];
661   sm = (struct GNUNET_DV_SendMessage *) &th[1];
662   sm->header.type = htons (GNUNET_MESSAGE_TYPE_DV_SEND);
663   sm->header.size = htons (sizeof (struct GNUNET_DV_SendMessage) +
664                            ntohs (msg->size));
665   if (0 == sh->uid_gen)
666     sh->uid_gen = 1;
667   th->uid = sh->uid_gen;
668   sm->uid = htonl (sh->uid_gen++);
669   /* use memcpy here as 'target' may not be sufficiently aligned */
670   memcpy (&sm->target, target, sizeof (struct GNUNET_PeerIdentity));
671   memcpy (&sm[1], msg, ntohs (msg->size));
672   GNUNET_CONTAINER_DLL_insert (sh->th_head,
673                                sh->th_tail,
674                                th);
675   start_transmit (sh);
676   return th;
677 }
678
679
680 /**
681  * Abort send operation (naturally, the message may have
682  * already been transmitted; this only stops the 'cb'
683  * from being called again).
684  *
685  * @param th send operation to cancel
686  */
687 void
688 GNUNET_DV_send_cancel (struct GNUNET_DV_TransmitHandle *th)
689 {
690   struct GNUNET_DV_ServiceHandle *sh = th->sh;
691
692   if (NULL == th->msg)
693     GNUNET_CONTAINER_DLL_remove (th->target->head,
694                                  th->target->tail,
695                                  th);
696   else
697     GNUNET_CONTAINER_DLL_remove (sh->th_head,
698                                  sh->th_tail,
699                                  th);
700   GNUNET_free (th);
701 }
702
703
704 /* end of dv_api.c */