plugin API works

This commit is contained in:
Thomas Ries
2008-02-02 17:10:58 +00:00
parent af78b380d7
commit b03dc44f33
10 changed files with 204 additions and 137 deletions
+2 -1
View File
@@ -19,6 +19,7 @@
#
SUBDIRS = src doc scripts contrib
SUBDIRS = libltdl src doc scripts contrib
INCLUDES = $(LTDLINCL)
EXTRA_DIST = TODO RELNOTES siproxd.spec autogen.sh
-3
View File
@@ -75,9 +75,6 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `ltdl' library (-lltdl). */
#undef HAVE_LIBLTDL
/* Define to 1 if you have the `osip2' library (-losip2). */
#undef HAVE_LIBOSIP2
+50 -85
View File
@@ -20,7 +20,7 @@ dnl 1-Nov-2003 tries check for NETFILTER (IPTABLES)
dnl 18-Nov-2003 tries include sysconfdir to search for config
dnl 19-Nov-2003 tries changes to support Solaris & BSD/OS
dnl 22-Nov-2003 tries test for gethostbyname_r() & # of args
dnl 22-Jan-2004 tries add --enable-static option
dnl 22-Jan-2004 tries add --enable-static-exe option
dnl 06-Mar-2004 tries del daemon(), add setsid()
dnl 09-Apr-2004 tries add --with-custom-fwmodule
dnl 09-May-2004 tries fli4l-uclibc links statically agains pthreads
@@ -38,6 +38,8 @@ dnl 18-Apr-2005 tries added libresolv
dnl 9-Oct-2005 tries libresolv is now included in static build
dnl 10-Jun-2007 tries test more .h files (DNS resolver related)
dnl 18-Dec-2007 tries requires libosip2-3.x.x
dnl 02-Feb-2008 tries removed variety of fli4l and static extra
dnl build options
dnl
dnl
@@ -73,21 +75,39 @@ dnl
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AC_PROG_RANLIB
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_LIBTOOL
AC_CHECK_PROG(docbook2pdf,docbook2pdf,yes,)
AM_CONDITIONAL(have_docbook2pdf,test "$docbook2pdf" = "yes")
AC_CHECK_PROG(docbook2html,docbook2html,yes,)
AM_CONDITIONAL(have_docbook2html,test "$docbook2html" = "yes")
dnl
dnl prepare libtool
dnl
dnl We require the ltdl (libtool portable dynamic loader)
dnl for our plugins. As this would require the automake/libtool
dnl packages to be present we include that library with
dnl the siproxd package as so called "convenience library"
dnl
AC_LIBLTDL_CONVENIENCE
dnl
dnl Check for dlopen support
AC_LIBTOOL_DLOPEN
dnl Configure libtool
AC_PROG_LIBTOOL
dnl
dnl Substitute LTDLINCL and LIBLTDL in the Makefiles
AC_SUBST(LTDLINCL)
AC_SUBST(LIBLTDL)
AC_CONFIG_SUBDIRS(libltdl)
dnl allow plugins to use functions in main program
LDFLAGS="-export-dynamic"
dnl **********************************************
dnl * Location of libraries / headerfiles
dnl **********************************************
dnl
dnl add
dnl --with-extra-includes
dnl --with-extra-libs
dnl
@@ -114,16 +134,24 @@ for each in $extra_libs; do
done
dnl **********************************************
dnl * Selection of static build variants
dnl **********************************************
dnl
dnl add
dnl --enable-static-exe
AC_MSG_CHECKING(build statically linked executable)
AC_ARG_ENABLE(static-exe,
[ --enable-static-exe build statically linked executable],
LDFLAGS="$LDFLAGS -static";
dnl --enable-static-libosip2
dnl
build_static_libosip="no"
AC_MSG_CHECKING(link statically against libosip2)
AC_ARG_ENABLE(static-libosip2,
[ --enable-static-libosip2 link statically against libosip2],
build_static_libosip="yes";
AC_MSG_RESULT(yes), AC_MSG_RESULT(no))
dnl **********************************************
dnl * Debugging helps
dnl **********************************************
dnl
dnl add
dnl --enable-dmalloc
@@ -193,80 +221,15 @@ case "$target" in
esac
dnl
dnl Feature:
dnl Fli4l libc5 support (only available on Linux, yet)
dnl Fli4l uClibc support (only available on Linux, yet)
dnl
build_fli4l_libc5="no"
build_fli4l_21_uclibc="no"
build_fli4l_22_uclibc="no"
case "$target" in
*-*-linux*)
AC_MSG_CHECKING(whether build for FLI4L 2.0.x libc5 )
AC_ARG_ENABLE(fli4l-libc5,
[ --enable-fli4l-libc5 build FLI4L 2.0.x (libc5)],
build_fli4l_libc5=$enableval,)
AC_MSG_RESULT($build_fli4l_libc5)
AC_MSG_CHECKING(whether build for FLI4L 2.1.x uClibc)
AC_ARG_ENABLE(fli4l-21-uclibc,
[ --enable-fli4l-21-uclibc build FLI4L 2.1.x (uClibc, no pthreads support)],
build_fli4l_21_uclibc=$enableval,)
AC_MSG_RESULT($build_fli4l_21_uclibc)
AC_MSG_CHECKING(whether build for FLI4L 2.2.x uClibc)
AC_ARG_ENABLE(fli4l-22-uclibc,
[ --enable-fli4l-22-uclibc build FLI4L 2.2.x (uClibc, with pthreads support)],
build_fli4l_22_uclibc=$enableval,)
AC_MSG_RESULT($build_fli4l_22_uclibc)
;;
*)
AC_MSG_RESULT(building FLI4L not available on this platform)
;;
esac
build_static_libosip="no"
build_static_pthread="no"
if test "x$build_fli4l_libc5" = "xyes"; then
dnl FLI4l 2.0.x libc5: make libosip and libpthread static
build_static_libosip="yes"
build_static_pthread="yes"
elif test "x$build_fli4l_21_uclibc" = "xyes"; then
dnl FLI4l 2.1.x uClibc: build all static
LDFLAGS="$LDFLAGS -static";
elif test "x$build_fli4l_22_uclibc" = "xyes"; then
dnl FLI4l 2.2.x uClibc: make libosip
build_static_libosip="yes"
fi
dnl
dnl Check for pthreads
dnl
ACX_PTHREAD(,
echo "*** ERROR: pthreads is required!"; exit 1;)
if test "x$build_static_pthread" = "xno"; then
dnl link dynamically (default)
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"
else
dnl link statically to this lib
dnl (this probably only will work on linux yet...)
AC_MSG_CHECKING("where I can find static threads library")
libpthreads_static=`find $extra_libs /lib /usr/lib /usr/local/lib \
-name 'lib*thread*.a' 2>/dev/null| \
egrep 'lib[p]?thread[s]?.a' |head -1`
if test "x$libpthreads_static" != "x"; then
LIBS="$libpthreads_static $LIBS"
AC_MSG_RESULT($libpthreads_static)
else
echo "*** ERROR: a static threads library is required!";exit 1;
fi
CC="$PTHREAD_CC"
fi
dnl
@@ -299,6 +262,9 @@ if test x$libosip_prefix_dir != x; then
CPPFLAGS="$CPPFLAGS -I$libosip_prefix_dir/include"
fi
dnl
dnl Static or dynamic linking against libosip
dnl
if test "x$build_static_libosip" = "xno"; then
dnl link dynamically (default)
AC_CHECK_LIB(osip2, osip_init,,
@@ -330,7 +296,9 @@ else
fi
fi
dnl
dnl correct version of libosip2?
dnl
ACX_CHECK_LIBOSIP_VERSION()
@@ -383,9 +351,8 @@ dnl
dnl Checks for libraries
dnl Note: the __res_query symbol is to catch the lib when building "static"
dnl
AC_CHECK_LIB(resolv,res_query,)
AC_CHECK_LIB(resolv,__res_query,)
AC_CHECK_LIB(ltdl,lt_dlopen,)
AC_CHECK_LIB(resolv,res_query,) dnl found only in static lib
AC_CHECK_LIB(resolv,__res_query,) dnl found in static and dynamic lib
dnl
@@ -399,11 +366,9 @@ AC_CHECK_FUNCS(strerror)
AC_CHECK_FUNCS(gethostbyname)
dnl !! with FLI4L 2.0.x strange behavior with gethostbyname_r()
dnl !! has been seen, dont use it.
if test "x$build_fli4l_libc5" = "xno"; then
dnl AC_CHECK_FUNCS(gethostbyname_r)
ACX_WHICH_GETHOSTBYNAME_R()
fi
dnl !! has been seen, dont use it. the ACX_WHICH_GETHOSTBYNAME_R
dnl !! shall then be commented out.
ACX_WHICH_GETHOSTBYNAME_R()
AC_CHECK_FUNCS(getopt_long setsid syslog)
AC_CHECK_FUNCS(getuid setuid getgid setgid getpwnam chroot)
+2
View File
@@ -26,6 +26,8 @@ AM_CFLAGS = -Wall -D_GNU_SOURCE \
# Siproxd itself
#
sbin_PROGRAMS = siproxd
siproxd_LDFLAGS=-export-dynamic
siproxd_LDADD = $(LIBLTDL)
siproxd_SOURCES = siproxd.c proxy.c register.c sock.c utils.c \
sip_utils.c sip_layer.c log.c readconf.c rtpproxy.c \
rtpproxy_relay.c accessctl.c route_processing.c \
+26 -8
View File
@@ -28,14 +28,28 @@
#include <osipparser2/osip_parser.h>
#include "siproxd.h"
#include "log.h"
#include "plugins.h"
#include "log.h"
static char const ident[]="$Id$";
/* configuration storage */
/* Plug-in identification */
static char name[]="plugin_demo";
static char desc[]="This is just a demo plugin without any purpose";
/* global configuration storage - required for config file location */
extern struct siproxd_config configuration;
/* plugin configuration storage */
static struct plugin_config {
char *string;
} plugin_cfg;
/* Instructions for config parser */
static cfgopts_t plugin_cfg_opts[] = {
{ "plugin_demo_string", TYP_STRING, &plugin_cfg.string },
{0, 0, 0}
};
/*
@@ -49,13 +63,21 @@ int plugin_init(plugin_def_t *plugin_def) {
plugin_def->api_version=SIPROXD_API_VERSION;
/* Name and descriptive text of the plugin */
plugin_def->name=strdup("plugin_demo");
plugin_def->desc=strdup("This is just a demo plugin without any purpose");
plugin_def->name=name;
plugin_def->desc=desc;
/* Execution mask - during what stages of SIP processing shall
* the plugin be called. */
plugin_def->exe_mask=PLUGIN_DETERMINE_TARGET|PLUGIN_PRE_PROXY;
/* read the config file */
if (read_config(configuration.configfile,
configuration.config_search,
plugin_cfg_opts, name) == STS_FAILURE) {
ERROR("Plugin '%s': could not load config file", name);
return STS_FAILURE;
}
INFO("plugin_demo is initialized");
return STS_SUCCESS;
}
@@ -77,10 +99,6 @@ int plugin_process(int stage, sip_ticket_t *ticket){
* connections, whatever the plugin messes around with)
*/
int plugin_end(plugin_def_t *plugin_def){
/* free my allocated rescources */
if (plugin_def->name) {free(plugin_def->name); plugin_def->name=NULL;}
if (plugin_def->desc) {free(plugin_def->desc); plugin_def->desc=NULL;}
INFO("plugin_demo ends here");
return STS_SUCCESS;
}
+40 -18
View File
@@ -29,29 +29,55 @@
#include <osipparser2/osip_parser.h>
#include "siproxd.h"
#include "log.h"
#include "plugins.h"
#include "log.h"
static char const ident[]="$Id$";
/* configuration storage */
/* Plug-in identification */
static char name[]="plugin_shortdial";
static char desc[]="Handles Dial shortcuts as defined in config file";
/* global configuration storage - required for config file location */
extern struct siproxd_config configuration;
/* plugin configuration storage */
static struct plugin_config {
char *shortdial_akey;
stringa_t shortdial_entry;
} plugin_cfg;
/* Instructions for config parser */
static cfgopts_t plugin_cfg_opts[] = {
{ "plugin_shortdial_akey", TYP_STRING, &plugin_cfg.shortdial_akey },
{ "plugin_shortdial_entry", TYP_STRINGA,&plugin_cfg.shortdial_entry },
{0, 0, 0}
};
/* local prototypes */
static int plugin_shortdial_redirect(sip_ticket_t *ticket, int shortcut_no);
static int plugin_shortdial(sip_ticket_t *ticket);
/*
* Plugin API functions code
*/
/* Initialization */
int plugin_init(plugin_def_t *plugin_def) {
plugin_def->api_version=SIPROXD_API_VERSION;
plugin_def->name=strdup("plugin_shortdial");
plugin_def->desc=strdup("Handles Dial shortcuts as defined in config file");
plugin_def->name=name;
plugin_def->desc=desc;
plugin_def->exe_mask=PLUGIN_DETERMINE_TARGET;
/* read the config file */
if (read_config(configuration.configfile,
configuration.config_search,
plugin_cfg_opts, name) == STS_FAILURE) {
ERROR("Plugin '%s': could not load config file", name);
return STS_FAILURE;
}
return STS_SUCCESS;
}
@@ -64,10 +90,6 @@ int plugin_process(int stage, sip_ticket_t *ticket){
/* De-Initialization */
int plugin_end(plugin_def_t *plugin_def){
/* free my allocated rescources */
if (plugin_def->name) {free(plugin_def->name); plugin_def->name=NULL;}
if (plugin_def->desc) {free(plugin_def->desc); plugin_def->desc=NULL;}
return STS_SUCCESS;
}
@@ -85,8 +107,8 @@ static int plugin_shortdial(sip_ticket_t *ticket) {
int shortcut_no=0;
/* plugin loaded and not configured, return with success */
if (configuration.pi_shortdial_akey==NULL) return STS_SUCCESS;
if (configuration.pi_shortdial_entry.used==0) return STS_SUCCESS;
if (plugin_cfg.shortdial_akey==NULL) return STS_SUCCESS;
if (plugin_cfg.shortdial_entry.used==0) return STS_SUCCESS;
DEBUGC(DBCLASS_PLUGIN,"plugin entered");
req_url=osip_message_get_uri(ticket->sipmsg);
@@ -103,23 +125,23 @@ static int plugin_shortdial(sip_ticket_t *ticket) {
/* REQ URI with username must exist, length as defined in config,
* shortdial must be enabled and short dial key must match */
if (!req_url || !req_url->username ||
!configuration.pi_shortdial_akey ||
(strlen(req_url->username) != strlen(configuration.pi_shortdial_akey)) ||
(req_url->username[0] != configuration.pi_shortdial_akey[0]))
!plugin_cfg.shortdial_akey ||
(strlen(req_url->username) != strlen(plugin_cfg.shortdial_akey)) ||
(req_url->username[0] != plugin_cfg.shortdial_akey[0]))
return STS_SUCCESS; /* ignore */
shortcut_no = atoi(&(req_url->username[1]));
if ((shortcut_no <= 0) || (shortcut_no >= LONG_MAX)) return STS_SUCCESS; /* not a number */
/* requested number is not defined (out of range) */
if (shortcut_no > configuration.pi_shortdial_entry.used) {
if (shortcut_no > plugin_cfg.shortdial_entry.used) {
DEBUGC(DBCLASS_PLUGIN, "shortdial: shortcut %i > available shortcuts (%i)",
shortcut_no, configuration.pi_shortdial_entry.used);
shortcut_no, plugin_cfg.shortdial_entry.used);
return STS_SUCCESS;
}
/* requested number is not defined (empty) */
if (!configuration.pi_shortdial_entry.string[shortcut_no-1]) {
if (!plugin_cfg.shortdial_entry.string[shortcut_no-1]) {
DEBUGC(DBCLASS_PLUGIN, "shortdial: shortcut %i empty", shortcut_no);
return STS_SUCCESS;
}
@@ -153,7 +175,7 @@ static int plugin_shortdial_redirect(sip_ticket_t *ticket, int shortcut_no) {
size_t len;
osip_contact_t *contact = NULL;
new_to_user=configuration.pi_shortdial_entry.string[shortcut_no-1];
new_to_user=plugin_cfg.shortdial_entry.string[shortcut_no-1];
if (!new_to_user) return STS_SUCCESS;
DEBUGC(DBCLASS_PLUGIN,"redirect: redirecting [%s]->[%s]",
+14 -7
View File
@@ -104,6 +104,20 @@ int load_plugins (void) {
/* call the init function */
sts=(*plugin_init)(cur);
/* Tell the user something about the plugin we have just loaded */
INFO("Plugin '%s' [%s] loaded with %s, exemask=0x%X",
cur->name, cur->desc, (sts==STS_SUCCESS)?"success":"failure",
cur->exe_mask);
/* check return status from plugin - failure leads to unload */
if (sts != STS_SUCCESS) {
/* complain and unload the plugin */
ERROR("Plugin '%s' did fail to load.", cur->name);
sts=(*plugin_end)(cur);
free(cur);
continue;
}
/* check API version that the plugin was biuilt against */
if (cur->api_version != SIPROXD_API_VERSION) {
/* complain and unload the plugin */
@@ -120,11 +134,6 @@ int load_plugins (void) {
cur->plugin_end = plugin_end;
cur->dlhandle = handle;
/* Tell the user something about the plugin we have just loaded */
INFO("Plugin '%s' [%s] loaded with %s, exemask=0x%X",
cur->name, cur->desc, (sts==STS_SUCCESS)?"success":"failure",
cur->exe_mask);
/* store forward pointer */
if (siproxd_plugins == NULL) {
/* first in chain */
@@ -220,8 +229,6 @@ int unload_plugins(void) {
ERROR("lt_dlclose() failed, ptr=%p", cur);
}
/* NOTE: cur->name and cur->desc must be cleaned up by the plugin! */
/* deallocate plugins list item */
if (last) last->next=NULL;
free(cur);
+59 -7
View File
@@ -38,7 +38,7 @@ static char const ident[]="$Id$";
extern struct siproxd_config configuration;
/* prototypes used locally only */
static int parse_config (FILE *configfile, cfgopts_t cfgopts[]);
static int parse_config (FILE *configfile, cfgopts_t cfgopts[], char *filter);
/* try to open (whichever is found first):
@@ -48,11 +48,14 @@ static int parse_config (FILE *configfile, cfgopts_t cfgopts[]);
* /usr/etc/<name>.conf
* /usr/local/etc/<name>.conf
*
* cfgopts: control array for the parser. defined keywors and types
* filter: passed to parse_config (read there for details)
*
* RETURNS
* STS_SUCCESS on success
* STS_FAILURE on error
*/
int read_config(char *name, int search, cfgopts_t cfgopts[]) {
int read_config(char *name, int search, cfgopts_t cfgopts[], char *filter) {
int sts;
FILE *configfile=NULL;
int i;
@@ -97,7 +100,7 @@ int read_config(char *name, int search, cfgopts_t cfgopts[]) {
return STS_FAILURE;
}
sts = parse_config(configfile, cfgopts);
sts = parse_config(configfile, cfgopts, filter);
fclose(configfile);
/*
@@ -126,18 +129,31 @@ int read_config(char *name, int search, cfgopts_t cfgopts[]) {
/*
* parse configuration file
*
* configfile - file STREAM to open config file
*
* configoptions - control structure for config parser
*
* filter = NULL - no filtering done
* filter = "plugin_xxx" - only consider keywords starting
* with "plugin_xxx", skip the rest.
* PLugins set this to load their scope
* of config options
* filter = "" - read main configuration, skipp everything
* starting with "plugin_" (hardwired).
*
* RETURNS
* STS_SUCCESS on success
* STS_FAILURE on error
*/
static int parse_config (FILE *configfile, cfgopts_t configoptions[]) {
static int parse_config (FILE *configfile, cfgopts_t configoptions[],
char *filter) {
char buff[1024];
char *ptr;
int i;
int k;
int i, k;
int num;
size_t len;
char *tmpptr;
char *eqsign;
while (fgets(buff,sizeof(buff),configfile) != NULL) {
/* life insurance */
@@ -161,7 +177,43 @@ static int parse_config (FILE *configfile, cfgopts_t configoptions[]) {
}
if (i == strlen(buff)) continue;
DEBUGC(DBCLASS_CONFIG,"pc:\"%s\"",buff);
/* search for token separator '=' */
eqsign = strchr(buff, '=');
if (eqsign == NULL) {
ERROR("Syntax error in config file [%s]", buff);
continue;
}
/* keyword filtering:
* filter = NULL - no filtering done
* filter = "plugin_xxx" - only consider keywords starting
* with "plugin_xxx", skip the rest.
* PLugins set this to load their scope
* of config options
* filter = "" - read main configuration, skipp everything
* starting with "plugin_" (hardwired).
*/
if (filter) {
/* filter == "": skip all plugin config entries */
if (filter[0] == '\0') {
ptr=strstr(buff,"plugin_");
if (ptr && (ptr < eqsign)) {
DEBUGC(DBCLASS_CONFIG,"skipped: \"%s\"",buff);
continue;
}
/* filter == "something": only consider "somethingxxxxx" */
} else {
ptr=strstr(buff, filter);
if ((ptr==NULL) || (ptr > eqsign)) {
DEBUGC(DBCLASS_CONFIG,"skipped: \"%s\"",buff);
continue;
}
}
} /* filter == NULL */
DEBUGC(DBCLASS_CONFIG,"cfg line:\"%s\"",buff);
/* scan for known keyword */
for (k=0; configoptions[k].keyword != NULL; k++) {
+7 -5
View File
@@ -45,7 +45,7 @@ static char const ident[]="$Id$";
/* configuration storage */
struct siproxd_config configuration;
/* instructions for config parser */
cfgopts_t main_cfg_opts[] = {
static cfgopts_t main_cfg_opts[] = {
{ "debug_level", TYP_INT4, &configuration.debuglevel },
{ "debug_port", TYP_INT4, &configuration.debugport },
{ "sip_listen_port", TYP_INT4, &configuration.sip_listen_port },
@@ -82,12 +82,10 @@ cfgopts_t main_cfg_opts[] = {
{ "pid_file", TYP_STRING, &configuration.pid_file },
{ "default_expires", TYP_INT4, &configuration.default_expires },
{ "autosave_registrations",TYP_INT4, &configuration.autosave_registrations },
{ "pi_shortdial_akey", TYP_STRING, &configuration.pi_shortdial_akey },
{ "pi_shortdial_entry", TYP_STRINGA,&configuration.pi_shortdial_entry },
{ "ua_string", TYP_STRING, &configuration.ua_string },
{ "use_rport", TYP_INT4, &configuration.use_rport },
{ "obscure_loops", TYP_INT4, &configuration.obscure_loops },
{ "plugin_dir", TYP_STRING, &configuration.plugin_dir },
{ "plugindir", TYP_STRING, &configuration.plugin_dir },
{ "load_plugin", TYP_STRINGA,&configuration.load_plugin },
{0, 0, 0}
};
@@ -232,9 +230,13 @@ int main (int argc, char *argv[])
INFO(PACKAGE"-"VERSION"-"BUILDSTR" "UNAME" starting up");
/* read the config file */
if (read_config(configfile, config_search, main_cfg_opts) == STS_FAILURE) {
if (read_config(configfile, config_search, main_cfg_opts, "") == STS_FAILURE) {
exit(1);
}
/* remember where config file is located, so the plugins
* can load it as well - if required */
configuration.configfile = strdup(configfile);
configuration.config_search = config_search;
/* if a debug level > 0 has been given on the commandline use its
value and not what is in the config file */
+4 -3
View File
@@ -57,6 +57,9 @@ typedef struct {
* global configuration option table
*/
struct siproxd_config {
char *configfile;
int config_search;
/* everything below here will be filled according to the config file */
unsigned int debuglevel;
int debugport;
char *inbound_if;
@@ -92,8 +95,6 @@ struct siproxd_config {
char *pid_file;
int default_expires;
int autosave_registrations;
char *pi_shortdial_akey;
stringa_t pi_shortdial_entry;
char *ua_string;
int use_rport;
int obscure_loops;
@@ -209,7 +210,7 @@ int sip_fixup_asterisk(char *buff, int *buflen); /*X*/
int sip_obscure_callid(sip_ticket_t *ticket); /*X*/
/* readconf.c */
int read_config(char *name, int search, cfgopts_t cfgopts[]); /*X*/
int read_config(char *name, int search, cfgopts_t cfgopts[], char *filter); /*X*/
int make_default_config(void); /*X*/
/* rtpproxy.c */