uncrustify as demanded.
[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      SPDX-License-Identifier: AGPL3.0-or-later
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_ats.h"
30 #include "gnunet-service-transport_plugins.h"
31
32 /**
33  * Entry in doubly-linked list of all of our plugins.
34  */
35 struct TransportPlugin {
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  * 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_NetworkType
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_NT_UNSPECIFIED;
116     }
117   return GNUNET_NT_scanner_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 */