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