-Merge branch 'master' of ssh://gnunet.org/gnunet into gsoc2018/rest_api
[oweals/gnunet.git] / src / transport / gnunet-service-transport_plugins.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2010-2014 GNUnet e.V.
4
5   GNUnet is free software: you can redistribute it and/or modify it
6   under the terms of the GNU Affero General Public License as published
7   by the Free Software Foundation, either version 3 of the License,
8   or (at your 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   Affero General Public License for more details.
14  
15   You should have received a copy of the GNU Affero General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file transport/gnunet-service-transport_plugins.c
21  * @brief plugin management
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
25 #include "gnunet-service-transport.h"
26 #include "gnunet-service-transport_hello.h"
27 #include "gnunet-service-transport_ats.h"
28 #include "gnunet-service-transport_plugins.h"
29
30 /**
31  * Entry in doubly-linked list of all of our plugins.
32  */
33 struct TransportPlugin
34 {
35   /**
36    * This is a doubly-linked list.
37    */
38   struct TransportPlugin *next;
39
40   /**
41    * This is a doubly-linked list.
42    */
43   struct TransportPlugin *prev;
44
45   /**
46    * API of the transport as returned by the plugin's
47    * initialization function.
48    */
49   struct GNUNET_TRANSPORT_PluginFunctions *api;
50
51   /**
52    * Short name for the plugin (i.e. "tcp").
53    */
54   char *short_name;
55
56   /**
57    * Name of the library (i.e. "gnunet_plugin_transport_tcp").
58    */
59   char *lib_name;
60
61   /**
62    * Environment this transport service is using
63    * for this plugin.
64    */
65   struct GNUNET_TRANSPORT_PluginEnvironment env;
66
67 };
68
69 /**
70  * Head of DLL of all loaded plugins.
71  */
72 static struct TransportPlugin *plugins_head;
73
74 /**
75  * Head of DLL of all loaded plugins.
76  */
77 static struct TransportPlugin *plugins_tail;
78
79
80 /**
81  * Function that will be called to update metrics for an address
82  *
83  * @param cls closure
84  * @param address address to update metrics for
85  * @param session the session
86  * @param distance new distance
87  */
88 static void
89 plugin_env_update_distance (void *cls,
90                             const struct GNUNET_HELLO_Address *address,
91                             uint32_t distance)
92 {
93   GST_ats_update_distance (address,
94                            distance);
95 }
96
97
98 /**
99  * Function that will be called to figure if an address is an loopback,
100  * LAN, WAN etc. address
101  *
102  * @param cls closure
103  * @param addr binary address
104  * @param addrlen length of the @a addr
105  * @return type of the network @a addr belongs to
106  */
107 static enum GNUNET_ATS_Network_Type
108 plugin_env_address_to_type (void *cls,
109                             const struct sockaddr *addr,
110                             size_t addrlen)
111 {
112   if (NULL == GST_is)
113   {
114     GNUNET_break(0);
115     return GNUNET_ATS_NET_UNSPECIFIED;
116   }
117   return GNUNET_ATS_scanner_address_get_type (GST_is,
118                                               addr,
119                                               addrlen);
120 }
121
122
123 /**
124  * Load and initialize all plugins.  The respective functions will be
125  * invoked by the plugins when the respective events happen.  The
126  * closure will be set to a 'const char*' containing the name of the
127  * plugin that caused the call.
128  *
129  * @param recv_cb function to call when data is received
130  * @param address_cb function to call when our public addresses changed
131  * @param session_start_cb function to call when a session was created
132  * @param session_end_cb function to call when a session was terminated
133  * @param address_type_cb function to call when a address type is requested
134  */
135 void
136 GST_plugins_load (GNUNET_TRANSPORT_PluginReceiveCallback recv_cb,
137                   GNUNET_TRANSPORT_AddressNotification address_cb,
138                   GNUNET_TRANSPORT_SessionStart session_start_cb,
139                   GNUNET_TRANSPORT_SessionEnd session_end_cb)
140 {
141   struct TransportPlugin *plug;
142   struct TransportPlugin *next;
143   unsigned long long tneigh;
144   char *libname;
145   char *plugs;
146   char *pos;
147   int fail;
148
149   if (GNUNET_OK !=
150       GNUNET_CONFIGURATION_get_value_number (GST_cfg,
151                                              "TRANSPORT",
152                                              "NEIGHBOUR_LIMIT",
153                                              &tneigh))
154   {
155     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
156                 _("Transport service is lacking NEIGHBOUR_LIMIT option.\n"));
157     return;
158   }
159   if (GNUNET_OK !=
160       GNUNET_CONFIGURATION_get_value_string (GST_cfg,
161                                              "TRANSPORT",
162                                              "PLUGINS",
163                                              &plugs))
164     return;
165   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
166               _("Starting transport plugins `%s'\n"),
167               plugs);
168   for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
169   {
170     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
171                 _("Loading `%s' transport plugin\n"),
172                 pos);
173     GNUNET_asprintf (&libname,
174                      "libgnunet_plugin_transport_%s",
175                      pos);
176     plug = GNUNET_new (struct TransportPlugin);
177     plug->short_name = GNUNET_strdup (pos);
178     plug->lib_name = libname;
179     plug->env.cfg = GST_cfg;
180     plug->env.my_identity = &GST_my_identity;
181     plug->env.get_our_hello = &GST_hello_get;
182     plug->env.cls = plug->short_name;
183     plug->env.receive = recv_cb;
184     plug->env.notify_address = address_cb;
185     plug->env.session_start = session_start_cb;
186     plug->env.session_end = session_end_cb;
187     plug->env.get_address_type = &plugin_env_address_to_type;
188     plug->env.update_address_distance = &plugin_env_update_distance;
189     plug->env.max_connections = tneigh;
190     plug->env.stats = GST_stats;
191     GNUNET_CONTAINER_DLL_insert (plugins_head,
192                                  plugins_tail,
193                                  plug);
194   }
195   GNUNET_free (plugs);
196   next = plugins_head;
197   while (NULL != next)
198   {
199     plug = next;
200     next = plug->next;
201     plug->api = GNUNET_PLUGIN_load (plug->lib_name,
202                                     &plug->env);
203     if (NULL == plug->api)
204     {
205       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
206                   _("Failed to load transport plugin for `%s'\n"),
207                   plug->lib_name);
208       GNUNET_CONTAINER_DLL_remove (plugins_head,
209                                    plugins_tail,
210                                    plug);
211       GNUNET_free (plug->short_name);
212       GNUNET_free (plug->lib_name);
213       GNUNET_free (plug);
214       continue;
215     }
216     fail = GNUNET_NO;
217     if (NULL == plug->api->address_pretty_printer)
218     {
219         fail = GNUNET_YES;
220       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
221                   _("Missing function `%s' in transport plugin for `%s'\n"),
222                   "address_pretty_printer",
223                   plug->lib_name);
224     }
225     if (NULL == plug->api->address_to_string)
226     {
227         fail = GNUNET_YES;
228       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
229                   _("Missing function `%s' in transport plugin for `%s'\n"),
230                   "address_to_string",
231                   plug->lib_name);
232     }
233     if (NULL == plug->api->string_to_address)
234     {
235         fail = GNUNET_YES;
236       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
237                   _("Missing function `%s' in transport plugin for `%s'\n"),
238                   "string_to_address",
239                   plug->lib_name);
240     }
241     if (NULL == plug->api->check_address)
242     {
243       fail = GNUNET_YES;
244       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
245                   _("Missing function `%s' in transport plugin for `%s'\n"),
246                   "check_address",
247                   plug->lib_name);
248     }
249     if (NULL == plug->api->get_session)
250     {
251       fail = GNUNET_YES;
252       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
253                   _("Missing function `%s' in transport plugin for `%s'\n"),
254                   "get_session",
255                   plug->lib_name);
256     }
257     if (NULL == plug->api->get_network)
258     {
259       fail = GNUNET_YES;
260       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
261                   _("Missing function `%s' in transport plugin for `%s'\n"),
262                   "get_network",
263                   plug->lib_name);
264     }
265     if (NULL == plug->api->send)
266     {
267       fail = GNUNET_YES;
268       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
269                   _("Missing function `%s' in transport plugin for `%s'\n"),
270                   "send",
271                   plug->lib_name);
272     }
273     if (NULL == plug->api->disconnect_peer)
274     {
275         fail = GNUNET_YES;
276       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
277                   _("Missing function `%s' in transport plugin for `%s'\n"),
278                   "disconnect_peer",
279                   plug->lib_name);
280     }
281     if (NULL == plug->api->disconnect_session)
282     {
283         fail = GNUNET_YES;
284       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
285                   _("Missing function `%s' in transport plugin for `%s'\n"),
286                   "disconnect_session",
287                   plug->lib_name);
288     }
289     if (NULL == plug->api->query_keepalive_factor)
290     {
291       fail = GNUNET_YES;
292       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
293                   _("Missing function `%s' in transport plugin for `%s'\n"),
294                   "query_keepalive_factor",
295                   plug->lib_name);
296     }
297     if (NULL == plug->api->update_session_timeout)
298     {
299         fail = GNUNET_YES;
300       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
301                   _("Missing function `%s' in transport plugin for `%s'\n"),
302                   "update_session_timeout",
303                   plug->lib_name);
304     }
305     if (GNUNET_YES == fail)
306     {
307       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
308                   _("Did not load plugin `%s' due to missing functions\n"),
309                   plug->lib_name);
310       GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
311       GNUNET_CONTAINER_DLL_remove (plugins_head,
312                                    plugins_tail,
313                                    plug);
314       GNUNET_free (plug->short_name);
315       GNUNET_free (plug->lib_name);
316       GNUNET_free (plug);
317     }
318   }
319 }
320
321
322 /**
323  * Unload all plugins
324  */
325 void
326 GST_plugins_unload ()
327 {
328   struct TransportPlugin *plug;
329
330   while (NULL != (plug = plugins_head))
331   {
332     GNUNET_break (NULL == GNUNET_PLUGIN_unload (plug->lib_name, plug->api));
333     GNUNET_free (plug->lib_name);
334     GNUNET_free (plug->short_name);
335     GNUNET_CONTAINER_DLL_remove (plugins_head, plugins_tail, plug);
336     GNUNET_free (plug);
337   }
338 }
339
340
341 /**
342  * Obtain the plugin API based on a plugin name.
343  *
344  * @param name name of the plugin
345  * @return the plugin's API, NULL if the plugin is not loaded
346  */
347 struct GNUNET_TRANSPORT_PluginFunctions *
348 GST_plugins_find (const char *name)
349 {
350   struct TransportPlugin *pos;
351
352   for (pos = plugins_head; NULL != pos; pos = pos->next)
353     if (0 == strcmp (name, pos->short_name))
354       break;
355   if (NULL == pos)
356     return NULL;
357   return pos->api;
358 }
359
360
361 /**
362  * Obtain the plugin API based on a the stripped plugin name after the underscore.
363  *
364  * Example: GST_plugins_printer_find (http_client) will return all plugins
365  * starting with the prefix "http":
366  * http_client or server if loaded
367  *
368  * @param name name of the plugin
369  * @return the plugin's API, NULL if the plugin is not loaded
370  */
371 struct GNUNET_TRANSPORT_PluginFunctions *
372 GST_plugins_printer_find (const char *name)
373 {
374   struct TransportPlugin *pos;
375   char *stripped = GNUNET_strdup (name);
376   char *sep = strchr (stripped, '_');
377
378   if (NULL != sep)
379     sep[0] = '\0';
380   for (pos = plugins_head; NULL != pos; pos = pos->next)
381     if (pos->short_name == strstr (pos->short_name, stripped))
382         break;
383   GNUNET_free (stripped);
384   if (NULL == pos)
385     return NULL;
386   return pos->api;
387 }
388
389
390 /**
391  * Convert a given address to a human-readable format.  Note that the
392  * return value will be overwritten on the next call to this function.
393  *
394  * @param address the address to convert
395  * @return statically allocated (!) human-readable address
396  */
397 const char *
398 GST_plugins_a2s (const struct GNUNET_HELLO_Address *address)
399 {
400   struct GNUNET_TRANSPORT_PluginFunctions *api;
401   static char unable_to_show[1024];
402   static const char *s;
403
404   if (NULL == address)
405     return "<NULL>";
406   if (0 == address->address_length)
407     return TRANSPORT_SESSION_INBOUND_STRING; /* Addresse with length 0 are inbound, address->address itself may be NULL */
408   api = GST_plugins_printer_find (address->transport_name);
409   if (NULL == api)
410   {
411     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412                 "Failed to find transport plugin `%s'\n",
413                 address->transport_name);
414     return "<plugin unknown>";
415   }
416   if (0 == address->address_length)
417   {
418     GNUNET_snprintf (unable_to_show,
419                      sizeof (unable_to_show),
420                      "<unable to stringify %u-byte long address of %s transport>",
421                      (unsigned int) address->address_length,
422                      address->transport_name);
423     return unable_to_show;
424   }
425   return (NULL != (s = api->address_to_string (NULL,
426                                                address->address,
427                                                address->address_length))
428           ? s
429           : "<invalid>");
430 }
431
432
433 /**
434  * Register callback with all plugins to monitor their status.
435  *
436  * @param cb callback to register, NULL to unsubscribe
437  * @param cb_cls closure for @a cb
438  */
439 void
440 GST_plugins_monitor_subscribe (GNUNET_TRANSPORT_SessionInfoCallback cb,
441                                void *cb_cls)
442 {
443   struct TransportPlugin *pos;
444
445   for (pos = plugins_head; NULL != pos; pos = pos->next)
446     if (NULL == pos->api->setup_monitor)
447       GNUNET_break (0);
448     else
449       pos->api->setup_monitor (pos->api->cls,
450                                cb,
451                                cb_cls);
452 }
453
454
455 /* end of file gnunet-service-transport_plugins.c */