fix #4546
[oweals/gnunet.git] / src / dv / plugin_transport_dv.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2002--2014 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file dv/plugin_transport_dv.c
23  * @brief DV transport service, takes incoming DV requests and deals with
24  * the DV service
25  * @author Nathan Evans
26  * @author Christian Grothoff
27  */
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_dv_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_transport_plugin.h"
35 #include "dv.h"
36
37
38 #define LOG(kind,...) GNUNET_log_from (kind, "transport-dv",__VA_ARGS__)
39
40
41 /**
42  * Encapsulation of all of the state of the plugin.
43  */
44 struct Plugin;
45
46
47 /**
48  * An active request for transmission via DV.
49  */
50 struct PendingRequest
51 {
52
53   /**
54    * This is a DLL.
55    */
56   struct PendingRequest *next;
57
58   /**
59    * This is a DLL.
60    */
61   struct PendingRequest *prev;
62
63   /**
64    * Continuation function to call once the transmission buffer
65    * has again space available.  NULL if there is no
66    * continuation to call.
67    */
68   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
69
70   /**
71    * Closure for @e transmit_cont.
72    */
73   void *transmit_cont_cls;
74
75   /**
76    * Transmission handle from DV client library.
77    */
78   struct GNUNET_DV_TransmitHandle *th;
79
80   /**
81    * Session of this request.
82    */
83   struct GNUNET_ATS_Session *session;
84
85   /**
86    * Number of bytes to transmit.
87    */
88   size_t size;
89 };
90
91
92 /**
93  * Session handle for connections.
94  */
95 struct GNUNET_ATS_Session
96 {
97   /**
98    * Pointer to the global plugin struct.
99    */
100   struct Plugin *plugin;
101
102   /**
103    * Head of pending requests.
104    */
105   struct PendingRequest *pr_head;
106
107   /**
108    * Tail of pending requests.
109    */
110   struct PendingRequest *pr_tail;
111
112   /**
113    * Address we use for the other peer.
114    */
115   struct GNUNET_HELLO_Address *address;
116
117   /**
118    * To whom are we talking to.
119    */
120   struct GNUNET_PeerIdentity sender;
121
122   /**
123    * Number of bytes waiting for transmission to this peer.
124    * FIXME: not set yet.
125    */
126   unsigned long long bytes_in_queue;
127
128   /**
129    * Number of messages waiting for transmission to this peer.
130    * FIXME: not set yet.
131    */
132   unsigned int msgs_in_queue;
133
134   /**
135    * Current distance to the given peer.
136    */
137   uint32_t distance;
138
139   /**
140    * Current network the next hop peer is located in
141    */
142   enum GNUNET_ATS_Network_Type network;
143
144   /**
145    * Does the transport service know about this session (and we thus
146    * need to call `session_end` when it is released?)
147    */
148   int active;
149
150 };
151
152
153 /**
154  * Encapsulation of all of the state of the plugin.
155  */
156 struct Plugin
157 {
158   /**
159    * Our environment.
160    */
161   struct GNUNET_TRANSPORT_PluginEnvironment *env;
162
163   /**
164    * Hash map of sessions (active and inactive).
165    */
166   struct GNUNET_CONTAINER_MultiPeerMap *sessions;
167
168   /**
169    * Copy of the handler array where the closures are
170    * set to this struct's instance.
171    */
172   struct GNUNET_SERVER_MessageHandler *handlers;
173
174   /**
175    * Handle to the DV service
176    */
177   struct GNUNET_DV_ServiceHandle *dvh;
178
179   /**
180    * Tokenizer for boxed messages.
181    */
182   struct GNUNET_SERVER_MessageStreamTokenizer *mst;
183
184   /**
185    * Function to call about session status changes.
186    */
187   GNUNET_TRANSPORT_SessionInfoCallback sic;
188
189   /**
190    * Closure for @e sic.
191    */
192   void *sic_cls;
193 };
194
195
196 /**
197  * If a session monitor is attached, notify it about the new
198  * session state.
199  *
200  * @param plugin our plugin
201  * @param session session that changed state
202  * @param state new state of the session
203  */
204 static void
205 notify_session_monitor (struct Plugin *plugin,
206                         struct GNUNET_ATS_Session *session,
207                         enum GNUNET_TRANSPORT_SessionState state)
208 {
209   struct GNUNET_TRANSPORT_SessionInfo info;
210
211   if (NULL == plugin->sic)
212     return;
213   memset (&info, 0, sizeof (info));
214   info.state = state;
215   info.is_inbound = GNUNET_SYSERR; /* hard to say */
216   info.num_msg_pending = session->msgs_in_queue;
217   info.num_bytes_pending = session->bytes_in_queue;
218   /* info.receive_delay remains zero as this is not supported by DV
219      (cannot selectively not receive from 'core') */
220   info.session_timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
221   info.address = session->address;
222   plugin->sic (plugin->sic_cls,
223                session,
224                &info);
225 }
226
227
228 /**
229  * Notify transport service about the change in distance.
230  *
231  * @param session session where the distance changed
232  */
233 static void
234 notify_distance_change (struct GNUNET_ATS_Session *session)
235 {
236   struct Plugin *plugin = session->plugin;
237
238   if (GNUNET_YES != session->active)
239     return;
240   plugin->env->update_address_distance (plugin->env->cls,
241                                         session->address,
242                                         session->distance);
243 }
244
245
246 /**
247  * Function called by MST on each message from the box.
248  *
249  * @param cls closure with the `struct Plugin *`
250  * @param client identification of the client (with the 'struct GNUNET_ATS_Session')
251  * @param message the actual message
252  * @return #GNUNET_OK on success
253  */
254 static int
255 unbox_cb (void *cls,
256           void *client,
257           const struct GNUNET_MessageHeader *message)
258 {
259   struct Plugin *plugin = cls;
260   struct GNUNET_ATS_Session *session = client;
261
262   session->active = GNUNET_YES;
263   LOG (GNUNET_ERROR_TYPE_DEBUG,
264        "Delivering message of type %u with %u bytes from peer `%s'\n",
265        ntohs (message->type),
266        ntohs (message->size),
267        GNUNET_i2s (&session->sender));
268   plugin->env->receive (plugin->env->cls,
269                         session->address,
270                         session,
271                         message);
272   plugin->env->update_address_distance (plugin->env->cls,
273                                         session->address,
274                                         session->distance);
275   return GNUNET_OK;
276 }
277
278
279 /**
280  * Handler for messages received from the DV service.
281  *
282  * @param cls closure with the plugin
283  * @param sender sender of the message
284  * @param distance how far did the message travel
285  * @param msg actual message payload
286  */
287 static void
288 handle_dv_message_received (void *cls,
289                             const struct GNUNET_PeerIdentity *sender,
290                             uint32_t distance,
291                             const struct GNUNET_MessageHeader *msg)
292 {
293   struct Plugin *plugin = cls;
294   struct GNUNET_ATS_Session *session;
295
296   LOG (GNUNET_ERROR_TYPE_DEBUG,
297        "Received DV_MESSAGE_RECEIVED message for peer `%s': new distance %u\n",
298        GNUNET_i2s (sender),
299        distance);
300   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
301                                                sender);
302   if (NULL == session)
303   {
304     GNUNET_break (0);
305     return;
306   }
307   if (GNUNET_MESSAGE_TYPE_DV_BOX == ntohs (msg->type))
308   {
309     /* need to unbox using MST */
310     LOG (GNUNET_ERROR_TYPE_DEBUG,
311          "Unboxing DV message using MST\n");
312     GNUNET_SERVER_mst_receive (plugin->mst,
313                                session,
314                                (const char *) &msg[1],
315                                ntohs (msg->size) - sizeof (struct GNUNET_MessageHeader),
316                                GNUNET_YES,
317                                GNUNET_NO);
318     return;
319   }
320   session->active = GNUNET_YES;
321   LOG (GNUNET_ERROR_TYPE_DEBUG,
322        "Delivering message of type %u with %u bytes from peer `%s'\n",
323        ntohs (msg->type),
324        ntohs (msg->size),
325        GNUNET_i2s (sender));
326   plugin->env->receive (plugin->env->cls,
327                         session->address,
328                         session,
329                         msg);
330   plugin->env->update_address_distance (plugin->env->cls,
331                                         session->address,
332                                         session->distance);
333 }
334
335
336 /**
337  * Function called if DV starts to be able to talk to a peer.
338  *
339  * @param cls closure with `struct Plugin *`
340  * @param peer newly connected peer
341  * @param distance distance to the peer
342  * @param network the network the next hop is located in
343  */
344 static void
345 handle_dv_connect (void *cls,
346                    const struct GNUNET_PeerIdentity *peer,
347                    uint32_t distance,
348                    enum GNUNET_ATS_Network_Type network)
349 {
350   struct Plugin *plugin = cls;
351   struct GNUNET_ATS_Session *session;
352
353   GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != network);
354   /**
355    * This requires transport plugin to be linked to libgnunetats.
356    * If you remove it, also remove libgnunetats linkage from Makefile.am
357    */
358   LOG (GNUNET_ERROR_TYPE_DEBUG,
359        "Received DV_CONNECT message for peer `%s' with next hop in network %s\n",
360        GNUNET_i2s (peer),
361        GNUNET_ATS_print_network_type (network));
362
363   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
364                                                peer);
365   if (NULL != session)
366   {
367     GNUNET_break (0);
368     session->distance = distance;
369     notify_distance_change (session);
370     return; /* nothing to do */
371   }
372
373   session = GNUNET_new (struct GNUNET_ATS_Session);
374   session->address = GNUNET_HELLO_address_allocate (peer, "dv",
375                                                     NULL, 0,
376                                                     GNUNET_HELLO_ADDRESS_INFO_NONE);
377   session->sender = *peer;
378   session->plugin = plugin;
379   session->distance = distance;
380   session->network = network;
381   GNUNET_assert(GNUNET_YES ==
382                 GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
383                                                    &session->sender, session,
384                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
385
386   LOG (GNUNET_ERROR_TYPE_DEBUG,
387        "Creating new DV session %p for peer `%s' at distance %u\n",
388        session,
389        GNUNET_i2s (peer),
390        distance);
391
392   session->active = GNUNET_YES;
393   plugin->env->session_start (plugin->env->cls,
394                               session->address,
395                               session,
396                               network);
397   plugin->env->update_address_distance (plugin->env->cls,
398                                         session->address,
399                                         session->distance);
400
401   notify_session_monitor (session->plugin,
402                           session,
403                           GNUNET_TRANSPORT_SS_UP);
404 }
405
406
407 /**
408  * Function called if DV distance to a peer is changed.
409  *
410  * @param cls closure with `struct Plugin *`
411  * @param peer connected peer
412  * @param distance new distance to the peer
413  * @param network network type used for the connection
414  */
415 static void
416 handle_dv_distance_changed (void *cls,
417                             const struct GNUNET_PeerIdentity *peer,
418                             uint32_t distance,
419                             enum GNUNET_ATS_Network_Type network)
420 {
421   struct Plugin *plugin = cls;
422   struct GNUNET_ATS_Session *session;
423
424   GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != network);
425   LOG (GNUNET_ERROR_TYPE_DEBUG,
426        "Received `%s' message for peer `%s': new distance %u\n",
427        "DV_DISTANCE_CHANGED",
428        GNUNET_i2s (peer),
429        distance);
430   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
431                                                peer);
432   if (NULL == session)
433   {
434     GNUNET_break (0);
435     handle_dv_connect (plugin, peer, distance, network);
436     return;
437   }
438   session->distance = distance;
439   notify_distance_change (session);
440 }
441
442
443 /**
444  * Release session object and clean up associated resources.
445  *
446  * @param session session to clean up
447  */
448 static void
449 free_session (struct GNUNET_ATS_Session *session)
450 {
451   struct Plugin *plugin = session->plugin;
452   struct PendingRequest *pr;
453
454   GNUNET_assert (GNUNET_YES ==
455                  GNUNET_CONTAINER_multipeermap_remove (plugin->sessions,
456                                                        &session->sender,
457                                                        session));
458
459   LOG (GNUNET_ERROR_TYPE_DEBUG,
460        "Freeing session %p for peer `%s'\n",
461        session,
462        GNUNET_i2s (&session->sender));
463   if (GNUNET_YES == session->active)
464   {
465     notify_session_monitor (session->plugin,
466                             session,
467                             GNUNET_TRANSPORT_SS_DONE);
468     plugin->env->session_end (plugin->env->cls,
469                               session->address,
470                               session);
471     session->active = GNUNET_NO;
472   }
473   while (NULL != (pr = session->pr_head))
474   {
475     GNUNET_CONTAINER_DLL_remove (session->pr_head,
476                                  session->pr_tail,
477                                  pr);
478     GNUNET_DV_send_cancel (pr->th);
479     pr->th = NULL;
480     if (NULL != pr->transmit_cont)
481       pr->transmit_cont (pr->transmit_cont_cls,
482                          &session->sender,
483                          GNUNET_SYSERR,
484                          pr->size, 0);
485     GNUNET_free (pr);
486   }
487   GNUNET_HELLO_address_free (session->address);
488   GNUNET_free (session);
489 }
490
491
492 /**
493  * Function called if DV is no longer able to talk to a peer.
494  *
495  * @param cls closure with `struct Plugin *`
496  * @param peer peer that disconnected
497  */
498 static void
499 handle_dv_disconnect (void *cls,
500                       const struct GNUNET_PeerIdentity *peer)
501 {
502   struct Plugin *plugin = cls;
503   struct GNUNET_ATS_Session *session;
504
505   LOG (GNUNET_ERROR_TYPE_DEBUG,
506        "Received `%s' message for peer `%s'\n",
507        "DV_DISCONNECT",
508        GNUNET_i2s (peer));
509   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
510                                                peer);
511   if (NULL == session)
512     return; /* nothing to do */
513   free_session (session);
514 }
515
516
517 /**
518  * Function called once the delivery of a message has been successful.
519  * Clean up the pending request, and call continuations.
520  *
521  * @param cls closure
522  * @param ok #GNUNET_OK on success, #GNUNET_SYSERR on error
523  */
524 static void
525 send_finished (void *cls,
526                int ok)
527 {
528   struct PendingRequest *pr = cls;
529   struct GNUNET_ATS_Session *session = pr->session;
530
531   pr->th = NULL;
532   GNUNET_CONTAINER_DLL_remove (session->pr_head,
533                                session->pr_tail,
534                                pr);
535   if (NULL != pr->transmit_cont)
536     pr->transmit_cont (pr->transmit_cont_cls,
537                        &session->sender,
538                        ok,
539                        pr->size, 0);
540   GNUNET_free (pr);
541 }
542
543
544 /**
545  * Function that can be used by the transport service to transmit
546  * a message using the plugin.
547  *
548  * @param cls closure
549  * @param session the session used
550  * @param priority how important is the message
551  * @param msgbuf the message to transmit
552  * @param msgbuf_size number of bytes in 'msgbuf'
553  * @param timeout when should we time out
554  * @param cont continuation to call once the message has
555  *        been transmitted (or if the transport is ready
556  *        for the next transmission call; or if the
557  *        peer disconnected...)
558  * @param cont_cls closure for @a cont
559  * @return number of bytes used (on the physical network, with overheads);
560  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
561  *         and does NOT mean that the message was not transmitted (DV)
562  */
563 static ssize_t
564 dv_plugin_send (void *cls,
565                 struct GNUNET_ATS_Session *session,
566                 const char *msgbuf,
567                 size_t msgbuf_size,
568                 unsigned int priority,
569                 struct GNUNET_TIME_Relative timeout,
570                 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
571 {
572   struct Plugin *plugin = cls;
573   struct PendingRequest *pr;
574   const struct GNUNET_MessageHeader *msg;
575   struct GNUNET_MessageHeader *box;
576
577   box = NULL;
578   msg = (const struct GNUNET_MessageHeader *) msgbuf;
579   if (ntohs (msg->size) != msgbuf_size)
580   {
581     /* need to box */
582     LOG (GNUNET_ERROR_TYPE_DEBUG,
583          "Boxing DV message\n");
584     box = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + msgbuf_size);
585     box->type = htons (GNUNET_MESSAGE_TYPE_DV_BOX);
586     box->size = htons (sizeof (struct GNUNET_MessageHeader) + msgbuf_size);
587     memcpy (&box[1], msgbuf, msgbuf_size);
588     msg = box;
589   }
590   pr = GNUNET_new (struct PendingRequest);
591   pr->transmit_cont = cont;
592   pr->transmit_cont_cls = cont_cls;
593   pr->session = session;
594   pr->size = msgbuf_size;
595   GNUNET_CONTAINER_DLL_insert_tail (session->pr_head,
596                                     session->pr_tail,
597                                     pr);
598
599   pr->th = GNUNET_DV_send (plugin->dvh,
600                            &session->sender,
601                            msg,
602                            &send_finished,
603                            pr);
604   GNUNET_free_non_null (box);
605   return 0; /* DV */
606 }
607
608
609 /**
610  * Function that can be used to force the plugin to disconnect
611  * from the given peer and cancel all previous transmissions
612  * (and their continuations).
613  *
614  * @param cls closure with the `struct Plugin *`
615  * @param target peer from which to disconnect
616  */
617 static void
618 dv_plugin_disconnect_peer (void *cls,
619                            const struct GNUNET_PeerIdentity *target)
620 {
621   struct Plugin *plugin = cls;
622   struct GNUNET_ATS_Session *session;
623   struct PendingRequest *pr;
624
625   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
626                                                target);
627   if (NULL == session)
628     return; /* nothing to do */
629   while (NULL != (pr = session->pr_head))
630   {
631     GNUNET_CONTAINER_DLL_remove (session->pr_head,
632                                  session->pr_tail,
633                                  pr);
634     GNUNET_DV_send_cancel (pr->th);
635     pr->th = NULL;
636     if (NULL != pr->transmit_cont)
637       pr->transmit_cont (pr->transmit_cont_cls,
638                          &session->sender,
639                          GNUNET_SYSERR,
640                          pr->size, 0);
641     GNUNET_free (pr);
642   }
643   session->active = GNUNET_NO;
644 }
645
646
647 /**
648  * Function that can be used to force the plugin to disconnect
649  * from the given peer and cancel all previous transmissions
650  * (and their continuations).
651  *
652  * @param cls closure with the `struct Plugin *`
653  * @param session which session to disconnect
654  * @return #GNUNET_OK
655  */
656 static int
657 dv_plugin_disconnect_session (void *cls,
658                               struct GNUNET_ATS_Session *session)
659 {
660   struct PendingRequest *pr;
661
662   while (NULL != (pr = session->pr_head))
663   {
664     GNUNET_CONTAINER_DLL_remove (session->pr_head,
665                                  session->pr_tail,
666                                  pr);
667     GNUNET_DV_send_cancel (pr->th);
668     pr->th = NULL;
669     if (NULL != pr->transmit_cont)
670       pr->transmit_cont (pr->transmit_cont_cls,
671                          &session->sender,
672                          GNUNET_SYSERR,
673                          pr->size, 0);
674     GNUNET_free (pr);
675   }
676   session->active = GNUNET_NO;
677   return GNUNET_OK;
678 }
679
680
681 /**
682  * Convert the transports address to a nice, human-readable
683  * format.
684  *
685  * @param cls closure
686  * @param type name of the transport that generated the address
687  * @param addr one of the addresses of the host, NULL for the last address
688  *        the specific address format depends on the transport
689  * @param addrlen length of the address
690  * @param numeric should (IP) addresses be displayed in numeric form?
691  * @param timeout after how long should we give up?
692  * @param asc function to call on each string
693  * @param asc_cls closure for @a asc
694  */
695 static void
696 dv_plugin_address_pretty_printer (void *cls, const char *type,
697                                   const void *addr,
698                                   size_t addrlen, int numeric,
699                                   struct GNUNET_TIME_Relative timeout,
700                                   GNUNET_TRANSPORT_AddressStringCallback asc,
701                                   void *asc_cls)
702 {
703   if ( (0 == addrlen) &&
704        (0 == strcmp (type, "dv")) )
705     asc (asc_cls,
706          "dv",
707          GNUNET_OK);
708   else
709     asc (asc_cls,
710          NULL,
711          GNUNET_SYSERR);
712   asc (asc_cls,
713        NULL,
714        GNUNET_OK);
715 }
716
717
718 /**
719  * Convert the DV address to a pretty string.
720  *
721  * @param cls closure
722  * @param addr the (hopefully) DV address
723  * @param addrlen the length of the @a addr
724  * @return string representing the DV address
725  */
726 static const char *
727 dv_plugin_address_to_string (void *cls,
728                              const void *addr,
729                              size_t addrlen)
730 {
731   if (0 != addrlen)
732   {
733     GNUNET_break (0); /* malformed */
734     return NULL;
735   }
736   return "dv";
737 }
738
739
740 /**
741  * Another peer has suggested an address for this peer and transport
742  * plugin.  Check that this could be a valid address.  This function
743  * is not expected to 'validate' the address in the sense of trying to
744  * connect to it but simply to see if the binary format is technically
745  * legal for establishing a connection to this peer (and make sure that
746  * the address really corresponds to our network connection/settings
747  * and not some potential man-in-the-middle).
748  *
749  * @param cls closure
750  * @param addr pointer to the address
751  * @param addrlen length of @a addr
752  * @return #GNUNET_OK if this is a plausible address for this peer
753  *         and transport, #GNUNET_SYSERR if not
754  *
755  */
756 static int
757 dv_plugin_check_address (void *cls,
758                          const void *addr,
759                          size_t addrlen)
760 {
761   if (0 != addrlen)
762     return GNUNET_SYSERR;
763   return GNUNET_OK;
764 }
765
766
767 /**
768  * Create a new session to transmit data to the target
769  * This session will used to send data to this peer and the plugin will
770  * notify us by calling the env->session_end function
771  *
772  * @param cls the plugin
773  * @param address the address
774  * @return the session if the address is valid, NULL otherwise
775  */
776 static struct GNUNET_ATS_Session *
777 dv_get_session (void *cls,
778                 const struct GNUNET_HELLO_Address *address)
779 {
780   struct Plugin *plugin = cls;
781   struct GNUNET_ATS_Session *session;
782
783   if (0 != address->address_length)
784     return NULL;
785   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
786                                                &address->peer);
787   if (NULL == session)
788     return NULL; /* not valid right now */
789   session->active = GNUNET_YES;
790   return session;
791 }
792
793
794 /**
795  * Function called to convert a string address to
796  * a binary address.
797  *
798  * @param cls closure ('struct Plugin*')
799  * @param addr string address
800  * @param addrlen length of the @a addr including \0 termination
801  * @param buf location to store the buffer
802  *        If the function returns #GNUNET_SYSERR, its contents are undefined.
803  * @param added length of created address
804  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
805  */
806 static int
807 dv_plugin_string_to_address (void *cls,
808                              const char *addr,
809                              uint16_t addrlen,
810                              void **buf,
811                              size_t *added)
812 {
813   if ( (addrlen == 3) &&
814        (0 == strcmp ("dv", addr)) )
815   {
816     *added = 0;
817     return GNUNET_OK;
818   }
819   return GNUNET_SYSERR;
820 }
821
822
823 /**
824  * Function that will be called whenever the transport service wants to
825  * notify the plugin that a session is still active and in use and
826  * therefore the session timeout for this session has to be updated
827  *
828  * @param cls closure (`struct Plugin *`)
829  * @param peer which peer was the session for
830  * @param session which session is being updated
831  */
832 static void
833 dv_plugin_update_session_timeout (void *cls,
834                                   const struct GNUNET_PeerIdentity *peer,
835                                   struct GNUNET_ATS_Session *session)
836 {
837   /* DV currently doesn't time out like "normal" plugins,
838      so it should be safe to do nothing, right?
839      (or should we add an internal timeout?) */
840 }
841
842
843 /**
844  * Function to obtain the network type for a session
845  * FIXME: we should probably look at the network type
846  * used by the next hop here.  Or find some other way
847  * to properly allow ATS-DV resource allocation.
848  *
849  * @param cls closure (`struct Plugin *`)
850  * @param session the session
851  * @return the network type
852  */
853 static enum GNUNET_ATS_Network_Type
854 dv_get_network (void *cls,
855                 struct GNUNET_ATS_Session *session)
856 {
857   GNUNET_assert (NULL != session);
858   return session->network;
859 }
860
861
862 /**
863  * Function obtain the network type for an address.
864  *
865  * @param cls closure (`struct Plugin *`)
866  * @param address the address
867  * @return the network type
868  */
869 static enum GNUNET_ATS_Network_Type
870 dv_plugin_get_network_for_address (void *cls,
871                                    const struct GNUNET_HELLO_Address *address)
872 {
873   return GNUNET_ATS_NET_WAN; /* FOR NOW */
874 }
875
876
877 /**
878  * Function that is called to get the keepalive factor.
879  * #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to
880  * calculate the interval between keepalive packets.
881  *
882  * @param cls closure with the `struct Plugin`
883  * @return keepalive factor
884  */
885 static unsigned int
886 dv_plugin_query_keepalive_factor (void *cls)
887 {
888   return 3;
889 }
890
891
892 /**
893  * Return information about the given session to the
894  * monitor callback.
895  *
896  * @param cls the `struct Plugin` with the monitor callback (`sic`)
897  * @param peer peer we send information about
898  * @param value our `struct GNUNET_ATS_Session` to send information about
899  * @return #GNUNET_OK (continue to iterate)
900  */
901 static int
902 send_session_info_iter (void *cls,
903                         const struct GNUNET_PeerIdentity *peer,
904                         void *value)
905 {
906   struct Plugin *plugin = cls;
907   struct GNUNET_ATS_Session *session = value;
908
909   if (GNUNET_YES != session->active)
910     return GNUNET_OK;
911   notify_session_monitor (plugin,
912                           session,
913                           GNUNET_TRANSPORT_SS_UP);
914   return GNUNET_OK;
915 }
916
917
918 /**
919  * Begin monitoring sessions of a plugin.  There can only
920  * be one active monitor per plugin (i.e. if there are
921  * multiple monitors, the transport service needs to
922  * multiplex the generated events over all of them).
923  *
924  * @param cls closure of the plugin
925  * @param sic callback to invoke, NULL to disable monitor;
926  *            plugin will being by iterating over all active
927  *            sessions immediately and then enter monitor mode
928  * @param sic_cls closure for @a sic
929  */
930 static void
931 dv_plugin_setup_monitor (void *cls,
932                           GNUNET_TRANSPORT_SessionInfoCallback sic,
933                           void *sic_cls)
934 {
935   struct Plugin *plugin = cls;
936
937   plugin->sic = sic;
938   plugin->sic_cls = sic_cls;
939   if (NULL != sic)
940   {
941     GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
942                                            &send_session_info_iter,
943                                            plugin);
944     /* signal end of first iteration */
945     sic (sic_cls, NULL, NULL);
946   }
947 }
948
949
950 /**
951  * Entry point for the plugin.
952  *
953  * @param cls closure with the plugin environment
954  * @return plugin API
955  */
956 void *
957 libgnunet_plugin_transport_dv_init (void *cls)
958 {
959   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
960   struct GNUNET_TRANSPORT_PluginFunctions *api;
961   struct Plugin *plugin;
962
963   plugin = GNUNET_new (struct Plugin);
964   plugin->env = env;
965   plugin->sessions = GNUNET_CONTAINER_multipeermap_create (1024 * 8, GNUNET_YES);
966   plugin->mst = GNUNET_SERVER_mst_create (&unbox_cb,
967                                           plugin);
968   plugin->dvh = GNUNET_DV_service_connect (env->cfg,
969                                            plugin,
970                                            &handle_dv_connect,
971                                            &handle_dv_distance_changed,
972                                            &handle_dv_disconnect,
973                                            &handle_dv_message_received);
974   if (NULL == plugin->dvh)
975   {
976     GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
977     GNUNET_SERVER_mst_destroy (plugin->mst);
978     GNUNET_free (plugin);
979     return NULL;
980   }
981   api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
982   api->cls = plugin;
983   api->send = &dv_plugin_send;
984   api->disconnect_peer = &dv_plugin_disconnect_peer;
985   api->disconnect_session = &dv_plugin_disconnect_session;
986   api->address_pretty_printer = &dv_plugin_address_pretty_printer;
987   api->check_address = &dv_plugin_check_address;
988   api->address_to_string = &dv_plugin_address_to_string;
989   api->string_to_address = &dv_plugin_string_to_address;
990   api->query_keepalive_factor = &dv_plugin_query_keepalive_factor;
991   api->get_session = &dv_get_session;
992   api->get_network = &dv_get_network;
993   api->get_network_for_address = &dv_plugin_get_network_for_address;
994   api->update_session_timeout = &dv_plugin_update_session_timeout;
995   api->setup_monitor = &dv_plugin_setup_monitor;
996   return api;
997 }
998
999
1000 /**
1001  * Function called to free a session.
1002  *
1003  * @param cls NULL
1004  * @param key unused
1005  * @param value session to free
1006  * @return #GNUNET_OK (continue to iterate)
1007  */
1008 static int
1009 free_session_iterator (void *cls,
1010                        const struct GNUNET_PeerIdentity *key,
1011                        void *value)
1012 {
1013   struct GNUNET_ATS_Session *session = value;
1014
1015   free_session (session);
1016   return GNUNET_OK;
1017 }
1018
1019
1020 /**
1021  * Exit point from the plugin.
1022  *
1023  * @param cls plugin API
1024  * @return NULL
1025  */
1026 void *
1027 libgnunet_plugin_transport_dv_done (void *cls)
1028 {
1029   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1030   struct Plugin *plugin = api->cls;
1031
1032   GNUNET_DV_service_disconnect (plugin->dvh);
1033   GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
1034                                          &free_session_iterator,
1035                                          NULL);
1036   GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
1037   GNUNET_SERVER_mst_destroy (plugin->mst);
1038   GNUNET_free (plugin);
1039   GNUNET_free (api);
1040   return NULL;
1041 }
1042
1043 /* end of plugin_transport_dv.c */