remember direct route to be released on direct disconnect
[oweals/gnunet.git] / src / dv / plugin_transport_dv.c
1 /*
2      This file is part of GNUnet
3      (C) 2002--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/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
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_dv_service.h"
34 #include "gnunet_transport_service.h"
35 #include "gnunet_transport_plugin.h"
36 #include "dv.h"
37
38
39 #define LOG(kind,...) GNUNET_log_from (kind, "transport-dv",__VA_ARGS__)
40
41
42 /**
43  * Encapsulation of all of the state of the plugin.
44  */
45 struct Plugin;
46
47
48 /**
49  * An active request for transmission via DV.
50  */
51 struct PendingRequest
52 {
53
54   /**
55    * This is a DLL.
56    */
57   struct PendingRequest *next;
58
59   /**
60    * This is a DLL.
61    */
62   struct PendingRequest *prev;
63
64   /**
65    * Continuation function to call once the transmission buffer
66    * has again space available.  NULL if there is no
67    * continuation to call.
68    */
69   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
70
71   /**
72    * Closure for transmit_cont.
73    */
74   void *transmit_cont_cls;
75
76   /**
77    * Transmission handle from DV client library.
78    */
79   struct GNUNET_DV_TransmitHandle *th;
80
81   /**
82    * Session of this request.
83    */
84   struct Session *session;
85
86 };
87
88
89 /**
90  * Session handle for connections.
91  */
92 struct Session
93 {
94
95   /**
96    * Mandatory session header.
97    */
98   struct SessionHeader header;
99
100   /**
101    * Pointer to the global plugin struct.
102    */
103   struct Plugin *plugin;
104
105   /**
106    * Head of pending requests.
107    */
108   struct PendingRequest *pr_head;
109
110   /**
111    * Tail of pending requests.
112    */
113   struct PendingRequest *pr_tail;
114
115   /**
116    * To whom are we talking to.
117    */
118   struct GNUNET_PeerIdentity sender;
119
120   /**
121    * Current distance to the given peer.
122    */
123   uint32_t distance;
124
125   /**
126    * Does the transport service know about this session (and we thus
127    * need to call 'session_end' when it is released?)
128    */
129   int active;
130
131 };
132
133
134 /**
135  * Encapsulation of all of the state of the plugin.
136  */
137 struct Plugin
138 {
139   /**
140    * Our environment.
141    */
142   struct GNUNET_TRANSPORT_PluginEnvironment *env;
143
144   /**
145    * Hash map of sessions (active and inactive).
146    */
147   struct GNUNET_CONTAINER_MultiPeerMap *sessions;
148
149   /**
150    * Copy of the handler array where the closures are
151    * set to this struct's instance.
152    */
153   struct GNUNET_SERVER_MessageHandler *handlers;
154
155   /**
156    * Handle to the DV service
157    */
158   struct GNUNET_DV_ServiceHandle *dvh;
159
160   /**
161    * Tokenizer for boxed messages.
162    */
163   struct GNUNET_SERVER_MessageStreamTokenizer *mst;
164
165 };
166
167
168 /**
169  * Notify transport service about the change in distance.
170  *
171  * @param session session where the distance changed
172  */
173 static void
174 notify_distance_change (struct Session *session)
175 {
176   struct Plugin *plugin = session->plugin;
177   struct GNUNET_ATS_Information ats;
178
179   ats.type = htonl ((uint32_t) GNUNET_ATS_QUALITY_NET_DISTANCE);
180   ats.value = htonl (session->distance);
181   plugin->env->update_address_metrics (plugin->env->cls,
182                                        &session->sender,
183                                        "", 0,
184                                        session,
185                                        &ats, 1);
186 }
187
188
189 /**
190  * Function called by MST on each message from the box.
191  *
192  * @param cls closure with the 'struct Plugin'
193  * @param client identification of the client (with the 'struct Session')
194  * @param message the actual message
195  * @return GNUNET_OK on success
196  */
197 static int
198 unbox_cb (void *cls,
199           void *client,
200           const struct GNUNET_MessageHeader *message)
201 {
202   struct Plugin *plugin = cls;
203   struct Session *session = client;
204   struct GNUNET_ATS_Information ats;
205
206   ats.type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
207   ats.value = htonl (session->distance);
208   session->active = GNUNET_YES;
209   plugin->env->receive (plugin->env->cls,
210                         &session->sender,
211                         message,
212                         session, "", 0);
213   plugin->env->update_address_metrics (plugin->env->cls,
214                 &session->sender, "", 0, session, &ats, 1);
215   return GNUNET_OK;
216 }
217
218
219 /**
220  * Handler for messages received from the DV service.
221  *
222  * @param cls closure with the plugin
223  * @param sender sender of the message
224  * @param distance how far did the message travel
225  * @param msg actual message payload
226  */
227 static void
228 handle_dv_message_received (void *cls,
229                             const struct GNUNET_PeerIdentity *sender,
230                             uint32_t distance,
231                             const struct GNUNET_MessageHeader *msg)
232 {
233   struct Plugin *plugin = cls;
234   struct GNUNET_ATS_Information ats;
235   struct Session *session;
236
237   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
238                                                sender);
239   if (NULL == session)
240   {
241     GNUNET_break (0);
242     return;
243   }
244   if (GNUNET_MESSAGE_TYPE_DV_BOX == ntohs (msg->type))
245   {
246     /* need to unbox using MST */
247     GNUNET_SERVER_mst_receive (plugin->mst,
248                                session,
249                                (const char *) &msg[1],
250                                ntohs (msg->size) - sizeof (struct GNUNET_MessageHeader),
251                                GNUNET_YES,
252                                GNUNET_NO);
253     return;
254   }
255   ats.type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
256   ats.value = htonl (distance);
257   session->active = GNUNET_YES;
258   plugin->env->receive (plugin->env->cls, sender,
259                         msg,
260                         session, "", 0);
261   plugin->env->update_address_metrics (plugin->env->cls,
262                                        sender, "", 0, session, &ats, 1);
263 }
264
265
266 /**
267  * Function called if DV starts to be able to talk to a peer.
268  *
269  * @param cls closure with 'struct Plugin'
270  * @param peer newly connected peer
271  * @param distance distance to the peer
272  */
273 static void
274 handle_dv_connect (void *cls,
275                    const struct GNUNET_PeerIdentity *peer,
276                    uint32_t distance)
277 {
278   struct Plugin *plugin = cls;
279   struct Session *session;
280
281   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
282                                                peer);
283   if (NULL != session)
284   {
285     GNUNET_break (0);
286     session->distance = distance;
287     if (GNUNET_YES == session->active)
288       notify_distance_change (session);
289     return; /* nothing to do */
290   }
291   session = GNUNET_new (struct Session);
292   session->sender = *peer;
293   session->distance = distance;
294   GNUNET_assert (GNUNET_YES ==
295                  GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
296                                                     &session->sender,
297                                                     session,
298                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
299 }
300
301
302 /**
303  * Function called if DV distance to a peer is changed.
304  *
305  * @param cls closure with 'struct Plugin'
306  * @param peer connected peer
307  * @param distance new distance to the peer
308  */
309 static void
310 handle_dv_distance_changed (void *cls,
311                             const struct GNUNET_PeerIdentity *peer,
312                             uint32_t distance)
313 {
314   struct Plugin *plugin = cls;
315   struct Session *session;
316
317   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
318                                                peer);
319   if (NULL == session)
320   {
321     GNUNET_break (0);
322     handle_dv_connect (plugin, peer, distance);
323     return;
324   }
325   session->distance = distance;
326   if (GNUNET_YES == session->active)
327     notify_distance_change (session);
328 }
329
330
331 /**
332  * Release session object and clean up associated resources.
333  *
334  * @param session session to clean up
335  */
336 static void
337 free_session (struct Session *session)
338 {
339   struct Plugin *plugin = session->plugin;
340   struct PendingRequest *pr;
341
342   GNUNET_assert (GNUNET_YES ==
343                  GNUNET_CONTAINER_multipeermap_remove (plugin->sessions,
344                                                        &session->sender,
345                                                        session));
346   if (GNUNET_YES == session->active)
347     plugin->env->session_end (plugin->env->cls,
348                               &session->sender,
349                               session);
350   while (NULL != (pr = session->pr_head))
351   {
352     GNUNET_CONTAINER_DLL_remove (session->pr_head,
353                                  session->pr_tail,
354                                  pr);
355     GNUNET_DV_send_cancel (pr->th);
356     pr->th = NULL;
357     if (NULL != pr->transmit_cont)
358       pr->transmit_cont (pr->transmit_cont_cls,
359                          &session->sender,
360                          GNUNET_SYSERR, 0, 0);
361     GNUNET_free (pr);
362   }
363   GNUNET_free (session);
364 }
365
366
367 /**
368  * Function called if DV is no longer able to talk to a peer.
369  *
370  * @param cls closure with 'struct Plugin'
371  * @param peer peer that disconnected
372  */
373 static void
374 handle_dv_disconnect (void *cls,
375                       const struct GNUNET_PeerIdentity *peer)
376 {
377   struct Plugin *plugin = cls;
378   struct Session *session;
379
380   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
381                                                peer);
382   if (NULL == session)
383     return; /* nothing to do */
384   free_session (session);
385 }
386
387
388 /**
389  * Function called once the delivery of a message has been successful.
390  * Clean up the pending request, and call continuations.
391  *
392  * @param cls closure
393  * @param ok GNUNET_OK on success, GNUNET_SYSERR on error
394  */
395 static void
396 send_finished (void *cls,
397                int ok)
398 {
399   struct PendingRequest *pr = cls;
400   struct Session *session = pr->session;
401
402   pr->th = NULL;
403   GNUNET_CONTAINER_DLL_remove (session->pr_head,
404                                session->pr_tail,
405                                pr);
406   if (NULL != pr->transmit_cont)
407     pr->transmit_cont (pr->transmit_cont_cls,
408                        &session->sender,
409                        ok, 0, 0);
410   GNUNET_free (pr);
411 }
412
413
414 /**
415  * Function that can be used by the transport service to transmit
416  * a message using the plugin.
417  *
418  * @param cls closure
419  * @param session the session used
420  * @param priority how important is the message
421  * @param msgbuf the message to transmit
422  * @param msgbuf_size number of bytes in 'msgbuf'
423  * @param timeout when should we time out
424  * @param cont continuation to call once the message has
425  *        been transmitted (or if the transport is ready
426  *        for the next transmission call; or if the
427  *        peer disconnected...)
428  * @param cont_cls closure for cont
429  * @return number of bytes used (on the physical network, with overheads);
430  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
431  *         and does NOT mean that the message was not transmitted (DV)
432  */
433 static ssize_t
434 dv_plugin_send (void *cls,
435                 struct Session *session,
436                 const char *msgbuf, size_t msgbuf_size, unsigned int priority,
437                 struct GNUNET_TIME_Relative timeout,
438                 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
439 {
440   struct PendingRequest *pr;
441   const struct GNUNET_MessageHeader *msg;
442   struct GNUNET_MessageHeader *box;
443
444   box = NULL;
445   msg = (const struct GNUNET_MessageHeader *) msgbuf;
446   if (ntohs (msg->size) != msgbuf_size)
447   {
448     /* need to box */
449     box = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + msgbuf_size);
450     box->type = htons (GNUNET_MESSAGE_TYPE_DV_BOX);
451     box->size = htons (sizeof (struct GNUNET_MessageHeader) + msgbuf_size);
452     memcpy (&box[1], msgbuf, msgbuf_size);
453     msg = box;
454   }
455   pr = GNUNET_new (struct PendingRequest);
456   pr->transmit_cont = cont;
457   pr->transmit_cont_cls = cont_cls;
458   pr->session = session;
459   GNUNET_CONTAINER_DLL_insert_tail (session->pr_head,
460                                     session->pr_tail,
461                                     pr);
462   pr->th = GNUNET_DV_send (session->plugin->dvh,
463                            &session->sender,
464                            msg,
465                            &send_finished,
466                            pr);
467   GNUNET_free_non_null (box);
468   return 0; /* DV */
469 }
470
471
472 /**
473  * Function that can be used to force the plugin to disconnect
474  * from the given peer and cancel all previous transmissions
475  * (and their continuations).
476  *
477  * @param cls closure
478  * @param target peer from which to disconnect
479  */
480 static void
481 dv_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
482 {
483   struct Plugin *plugin = cls;
484   struct Session *session;
485   struct PendingRequest *pr;
486
487   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
488                                                target);
489   if (NULL == session)
490     return; /* nothing to do */
491   while (NULL != (pr = session->pr_head))
492   {
493     GNUNET_CONTAINER_DLL_remove (session->pr_head,
494                                  session->pr_tail,
495                                  pr);
496     GNUNET_DV_send_cancel (pr->th);
497     pr->th = NULL;
498     if (NULL != pr->transmit_cont)
499       pr->transmit_cont (pr->transmit_cont_cls,
500                          &session->sender,
501                          GNUNET_SYSERR, 0, 0);
502     GNUNET_free (pr);
503   }
504   session->active = GNUNET_NO;
505 }
506
507
508 /**
509  * Convert the transports address to a nice, human-readable
510  * format.
511  *
512  * @param cls closure
513  * @param type name of the transport that generated the address
514  * @param addr one of the addresses of the host, NULL for the last address
515  *        the specific address format depends on the transport
516  * @param addrlen length of the address
517  * @param numeric should (IP) addresses be displayed in numeric form?
518  * @param timeout after how long should we give up?
519  * @param asc function to call on each string
520  * @param asc_cls closure for asc
521  */
522 static void
523 dv_plugin_address_pretty_printer (void *cls, const char *type, const void *addr,
524                                   size_t addrlen, int numeric,
525                                   struct GNUNET_TIME_Relative timeout,
526                                   GNUNET_TRANSPORT_AddressStringCallback asc,
527                                   void *asc_cls)
528 {
529   if ( (0 == addrlen) &&
530        (0 == strcmp (type, "dv")) )
531     asc (asc_cls, "dv");
532   asc (asc_cls, NULL);
533 }
534
535
536 /**
537  * Convert the DV address to a pretty string.
538  *
539  * @param cls closure
540  * @param addr the (hopefully) DV address
541  * @param addrlen the length of the address
542  *
543  * @return string representing the DV address
544  */
545 static const char *
546 dv_plugin_address_to_string (void *cls, const void *addr, size_t addrlen)
547 {
548   if (0 != addrlen)
549   {
550     GNUNET_break (0); /* malformed */
551     return NULL;
552   }
553   return "dv";
554 }
555
556
557 /**
558  * Another peer has suggested an address for this peer and transport
559  * plugin.  Check that this could be a valid address.  This function
560  * is not expected to 'validate' the address in the sense of trying to
561  * connect to it but simply to see if the binary format is technically
562  * legal for establishing a connection to this peer (and make sure that
563  * the address really corresponds to our network connection/settings
564  * and not some potential man-in-the-middle).
565  *
566  * @param cls closure
567  * @param addr pointer to the address
568  * @param addrlen length of addr
569  * @return GNUNET_OK if this is a plausible address for this peer
570  *         and transport, GNUNET_SYSERR if not
571  *
572  */
573 static int
574 dv_plugin_check_address (void *cls, const void *addr, size_t addrlen)
575 {
576   if (0 != addrlen)
577     return GNUNET_SYSERR;
578   return GNUNET_OK;
579 }
580
581
582 /**
583  * Create a new session to transmit data to the target
584  * This session will used to send data to this peer and the plugin will
585  * notify us by calling the env->session_end function
586  *
587  * @param cls the plugin
588  * @param address the address
589  * @return the session if the address is valid, NULL otherwise
590  */
591 static struct Session *
592 dv_get_session (void *cls,
593                 const struct GNUNET_HELLO_Address *address)
594 {
595   struct Plugin *plugin = cls;
596   struct Session *session;
597
598   if (0 != address->address_length)
599     return NULL;
600   session = GNUNET_CONTAINER_multipeermap_get (plugin->sessions,
601                                                &address->peer);
602   if (NULL == session)
603     return NULL; /* not valid right now */
604   session->active = GNUNET_YES;
605   return session;
606 }
607
608
609 /**
610  * Function called to convert a string address to
611  * a binary address.
612  *
613  * @param cls closure ('struct Plugin*')
614  * @param addr string address
615  * @param addrlen length of the address including \0 termination
616  * @param buf location to store the buffer
617  *        If the function returns GNUNET_SYSERR, its contents are undefined.
618  * @param added length of created address
619  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
620  */
621 static int
622 dv_plugin_string_to_address (void *cls,
623                              const char *addr,
624                              uint16_t addrlen,
625                              void **buf,
626                              size_t *added)
627 {
628   if ( (addrlen == 3) &&
629        (0 == strcmp ("dv", addr)) )
630   {
631     *added = 0;
632     return GNUNET_OK;
633   }
634   return GNUNET_SYSERR;
635 }
636
637
638
639 /**
640  * Function to obtain the network type for a session
641  * FIXME: we should probably look at the network type
642  * used by the next hop here.  Or find some other way
643  * to properly allow ATS-DV resource allocation.
644  *
645  * @param cls closure ('struct Plugin*')
646  * @param session the session
647  * @return the network type
648  */
649 static enum GNUNET_ATS_Network_Type
650 dv_get_network (void *cls,
651                 struct Session *session)
652 {
653   GNUNET_assert (NULL != session);
654   return GNUNET_ATS_NET_UNSPECIFIED;
655 }
656
657
658 /**
659  * Entry point for the plugin.
660  *
661  * @param cls closure with the plugin environment
662  * @return plugin API
663  */
664 void *
665 libgnunet_plugin_transport_dv_init (void *cls)
666 {
667   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
668   struct GNUNET_TRANSPORT_PluginFunctions *api;
669   struct Plugin *plugin;
670
671   plugin = GNUNET_new (struct Plugin);
672   plugin->env = env;
673   plugin->sessions = GNUNET_CONTAINER_multipeermap_create (1024 * 8, GNUNET_YES);
674   plugin->mst = GNUNET_SERVER_mst_create (&unbox_cb,
675                                           plugin);
676   plugin->dvh = GNUNET_DV_service_connect (env->cfg,
677                                            plugin,
678                                            &handle_dv_connect,
679                                            &handle_dv_distance_changed,
680                                            &handle_dv_disconnect,
681                                            &handle_dv_message_received);
682   if (NULL == plugin->dvh)
683   {
684     GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
685     GNUNET_SERVER_mst_destroy (plugin->mst);
686     GNUNET_free (plugin);
687     return NULL;
688   }
689   api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
690   api->cls = plugin;
691   api->send = &dv_plugin_send;
692   api->disconnect = &dv_plugin_disconnect;
693   api->address_pretty_printer = &dv_plugin_address_pretty_printer;
694   api->check_address = &dv_plugin_check_address;
695   api->address_to_string = &dv_plugin_address_to_string;
696   api->string_to_address = &dv_plugin_string_to_address;
697   api->get_session = &dv_get_session;
698   api->get_network = &dv_get_network;
699   return api;
700 }
701
702
703 /**
704  * Function called to free a session.
705  *
706  * @param cls NULL
707  * @param key unused
708  * @param value session to free
709  * @return GNUNET_OK (continue to iterate)
710  */
711 static int
712 free_session_iterator (void *cls,
713                        const struct GNUNET_PeerIdentity *key,
714                        void *value)
715 {
716   struct Session *session = value;
717
718   free_session (session);
719   return GNUNET_OK;
720 }
721
722
723 /**
724  * Exit point from the plugin.
725  *
726  * @param cls plugin API
727  * @return NULL
728  */
729 void *
730 libgnunet_plugin_transport_dv_done (void *cls)
731 {
732   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
733   struct Plugin *plugin = api->cls;
734
735   GNUNET_DV_service_disconnect (plugin->dvh);
736   GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
737                                          &free_session_iterator,
738                                          NULL);
739   GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
740   GNUNET_SERVER_mst_destroy (plugin->mst);
741   GNUNET_free (plugin);
742   GNUNET_free (api);
743   return NULL;
744 }
745
746 /* end of plugin_transport_dv.c */