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