1 /*
2     Copyright (C) 2003-2009  Thomas Ries <tries@gmx.net>
3 
4     This file is part of Siproxd.
5 
6     Siproxd is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     Siproxd is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warrantry of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with Siproxd; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #include "config.h"
22 #include <string.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 
26 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <sys/socket.h>
30 
31 #include <osipparser2/osip_parser.h>
32 
33 #include "siproxd.h"
34 #include "log.h"
35 #include "plugins.h"
36 
37 static char const ident[]="$Id: plugins.c 499 2014-09-26 21:37:21Z hb9xar $";
38 
39 /* configuration storage */
40 extern struct siproxd_config configuration;
41 
42 /* Plugin "database" - queue header */
43 plugin_def_t *siproxd_plugins=NULL;
44 
45 /* code */
46 typedef int (*func_plugin_init_t)(plugin_def_t *plugin_def);
47 typedef int (*func_plugin_process_t)(int stage, sip_ticket_t *ticket);
48 typedef int (*func_plugin_end_t)(plugin_def_t *plugin_def);
49 
50 
51 /*
52  * Load the plugins in the order as specified in the config file.
53  * This is done once, starts with empty plugin list
54  */
load_plugins(void)55 int load_plugins (void) {
56    int sts;
57    int i;
58    char path[PATH_STRING_SIZE];
59 
60    lt_dlhandle handle=NULL;
61    plugin_def_t *cur=NULL;
62    plugin_def_t *last=siproxd_plugins;
63 
64    func_plugin_init_t plugin_init       = NULL;
65    func_plugin_process_t plugin_process = NULL;
66    func_plugin_end_t plugin_end         = NULL;
67 
68    /* initialize the libtool dynamic loader */
69    LTDL_SET_PRELOADED_SYMBOLS();
70    sts = lt_dlinit();
71    if (sts != 0) {
72       ERROR("ltdl (libtool dynamic loader) initialization failed.");
73       return STS_FAILURE;
74    }
75 
76    /* find plugins to load from config file */
77    for (i=0; i<configuration.load_plugin.used; i++) {
78       /* construct the path where the plugin is */
79       if (configuration.plugin_dir) {
80          strcpy(path, configuration.plugin_dir);
81          strcat(path, configuration.load_plugin.string[i]);
82       } else {
83          strcpy(path, configuration.load_plugin.string[i]);
84       }
85 
86       /* dlopen() the plugin */
87       DEBUGC(DBCLASS_PLUGIN, "load_plugins: opening plugin [%s]", path);
88       handle=lt_dlopen(path);
89 
90       if (handle == NULL) {
91          /* complain and next plugin */
92          ERROR("plugin %s not found - skipped", configuration.load_plugin.string[i]);
93          continue;
94       }
95 
96       /* find the plugin_process and plugin_end functions and store them */
97       plugin_init    = lt_dlsym (handle, "plugin_init");
98       plugin_process = lt_dlsym (handle, "plugin_process");
99       plugin_end     = lt_dlsym (handle, "plugin_end");
100 
101       DEBUGC(DBCLASS_PLUGIN, "load_plugins: init=%p, process=%p, end=%p",
102              plugin_init, plugin_process, plugin_end);
103 
104       /* all functions present? */
105       if (plugin_init && plugin_process && plugin_end) {
106          /* allocate an new item in plugins array */
107          cur=malloc(sizeof(plugin_def_t));
108          memset(cur,0,sizeof(plugin_def_t));
109 
110          /* call the init function */
111          sts=(*plugin_init)(cur);
112 
113          /* Tell the user something about the plugin we have just loaded */
114          INFO("Plugin '%s' [%s] loaded with %s, exemask=0x%X",
115               cur->name, cur->desc, (sts==STS_SUCCESS)?"success":"failure",
116               cur->exe_mask);
117 
118          /* check return status from plugin - failure leads to unload */
119          if (sts != STS_SUCCESS) {
120             /* complain and unload the plugin */
121             ERROR("Plugin '%s' did fail to load.", cur->name);
122             sts=(*plugin_end)(cur);
123             free(cur);
124             continue;
125          }
126 
127          /* check API version that the plugin was biuilt against */
128          if (cur->api_version != SIPROXD_API_VERSION) {
129             /* complain and unload the plugin */
130             ERROR("Plugin '%s' does not support correct API version. "
131                   "Please compile plugin with correct siproxd version.",
132                   cur->name);
133             sts=(*plugin_end)(cur);
134             free(cur);
135             continue;
136          }
137 
138          /* store the function pointers */
139          cur->plugin_process = plugin_process;
140          cur->plugin_end = plugin_end;
141          cur->dlhandle = handle;
142 
143          /* store forward pointer */
144          if (siproxd_plugins == NULL) {
145             /* first in chain */
146             siproxd_plugins = cur;
147             last=cur;
148             cur=NULL;
149          } else {
150             /* not first in chain */
151             last->next=(void*)cur;
152             last=cur;
153             cur=NULL;
154          }
155       } else {
156          /* complain and dlclose the handle...*/
157          ERROR("plugin %s does not provide correct API functions - skipped",
158                configuration.load_plugin.string[i]);
159          INFO("make sure to specify plugin_<name>.la to load and not the .so!");
160          lt_dlclose(handle);
161       }
162    }
163    return STS_SUCCESS;
164 }
165 
166 
167 /*
168  * Called at different stages of SIP processing.
169  */
call_plugins(int stage,sip_ticket_t * ticket)170 int call_plugins(int stage, sip_ticket_t *ticket) {
171    plugin_def_t *cur;
172    int sts;
173    func_plugin_process_t plugin_process;
174 
175    /* sanity check, beware plugins from crappy stuff
176     * applies when SIP message has been parsed        */
177    if ((stage > PLUGIN_PROCESS_RAW) && (!ticket || !ticket->sipmsg)) return STS_FAILURE;
178 
179    /* for each plugin in plugins, do */
180    for (cur=siproxd_plugins; cur != NULL; cur = cur->next) {
181       /* check stage bitmask, if plugin wants to be called do so */
182       if (cur->exe_mask & stage) {
183          plugin_process=cur->plugin_process;
184          sts=(*plugin_process)(stage, ticket);
185          switch (stage) {
186             /* PLUGIN_PROCESS_RAW can be prematurely ended by plugin -
187                plugin determines that the UDP message is to be discarded */
188             case (PLUGIN_PROCESS_RAW):
189                /* return with the plugins status back to the caller */
190                if (sts == STS_FAILURE) {
191                   DEBUGC(DBCLASS_PLUGIN, "call_plugins: PLUGIN_PROCESS_RAW "
192                          "prematurely ending plugin processing in module "
193                          "%s sts=STS_FAILURE", cur->name);
194                   return sts;
195                }
196                break;
197             /* PLUGIN_VALIDATE can be prematurely ended by plugin -
198                plugin determines that the UDP message is to be discarded */
199             case (PLUGIN_VALIDATE):
200                /* return with the plugins status back to the caller */
201                if (sts == STS_FAILURE) {
202                   DEBUGC(DBCLASS_PLUGIN, "call_plugins: PLUGIN_VALIDATE "
203                          "prematurely ending plugin processing in module "
204                          "%s sts=STS_FAILURE", cur->name);
205                   return sts;
206                }
207                break;
208             /* PLUGIN_DETERMINE_TARGET can be prematurely ended by plugin -
209                plugin processes and sends the final SIP message itself */
210             case (PLUGIN_DETERMINE_TARGET):
211                /* return with the plugins status back to the caller */
212                if (sts == STS_SIP_SENT) {
213                   DEBUGC(DBCLASS_PLUGIN, "call_plugins: PLUGIN_DETERMINE_TARGET "
214                          "prematurely ending plugin processing in module "
215                          "%s sts=STS_SIP_SENT", cur->name);
216                   return sts;
217                }
218                break;
219             default:
220                break;
221          } /* switch*/
222       } /* if */
223    } /* for */
224 
225    return STS_SUCCESS;
226 }
227 
228 
229 /*
230  * At termination of siproxd "unloads" the plugins.
231  * Actually gives the plugins the chance to cleanup whatever they want.
232  * The plugins are called in reversed order of loading (last loaded is
233  * unloaded first).
234  */
unload_plugins(void)235 int unload_plugins(void) {
236    plugin_def_t *cur, *last;
237    int sts;
238    func_plugin_end_t plugin_end;
239 
240    /* for each plugin in plugins do (start at the end of the plugin list) */
241    DEBUGC(DBCLASS_PLUGIN, "unloading dynamic plugins");
242 
243    /* call plugin_end function - the plugin may need to cleanup as well... */
244    while (siproxd_plugins) {
245       /* search the end */
246       last=NULL;
247       for (cur=siproxd_plugins; cur->next != NULL; cur = cur->next) {last=cur;}
248 
249       plugin_end=cur->plugin_end;
250       DEBUGC(DBCLASS_PLUGIN, "unload_plugins: '%s' unloading ptr=%p",
251              cur->name, cur);
252       sts=(*plugin_end)(cur);
253       DEBUGC(DBCLASS_PLUGIN, "unload_plugins: status=%s",
254              (sts==STS_SUCCESS)?"success":"failure");
255 
256       /* dlclose */
257       sts = lt_dlclose(cur->dlhandle);
258       if (sts != 0) {
259          ERROR("lt_dlclose() failed, ptr=%p", cur);
260       }
261 
262       /* deallocate plugins list item */
263       if (last) last->next=NULL;
264       free(cur);
265 
266       /* I don't like this */
267       if (cur == siproxd_plugins) siproxd_plugins=NULL;
268    }
269 
270    /* shutdown ltdl */
271    sts = lt_dlexit();
272    if (sts != 0) {
273       ERROR("lt_dlexit() failed");
274    }
275 
276    return STS_SUCCESS;
277 }
278