mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
581 lines
13 KiB
581 lines
13 KiB
/*
|
|
* Copyright (C) 2009 1&1 Internet AG
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/*!
|
|
* \file
|
|
* \brief Kamailio utils ::
|
|
* \ingroup utils
|
|
* Module: \ref utils
|
|
*/
|
|
|
|
#include "conf.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../mem/shm_mem.h"
|
|
#include "../../sr_module.h"
|
|
#include "../../proxy.h"
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#define BUFSIZE 1000
|
|
|
|
/*! \brief special filter indices */
|
|
enum {
|
|
sfidx_request = 0,
|
|
sfidx_reply,
|
|
sfilter_cnt
|
|
};
|
|
|
|
/*! special filter masks */
|
|
static int sfilter_mask[sfilter_cnt] = { 1, 2 };
|
|
|
|
/*! special filter names */
|
|
static char *sfilter_str[sfilter_cnt] = {
|
|
"REQUEST",
|
|
"REPLY"
|
|
};
|
|
|
|
|
|
struct fwd_setting {
|
|
int active;
|
|
int sfilter;
|
|
char *filter_methods;
|
|
struct proxy_l* proxy;
|
|
};
|
|
|
|
|
|
static struct fwd_setting *fwd_settings = NULL;
|
|
static int fwd_max_id = 0;
|
|
|
|
|
|
/*!
|
|
* \brief Removes white spaces and new lines from s
|
|
* \todo check if we can use the functions from ut.h
|
|
* Removes white spaces and new lines from s, the contents of s are modified.
|
|
* \param s the string.
|
|
*/
|
|
static void remove_spaces(char *s)
|
|
{
|
|
char *p, *dst;
|
|
for (p = s, dst = s; *p != '\0'; ++p) {
|
|
if (!isspace(*p)) *dst++ = *p;
|
|
}
|
|
*dst = '\0';
|
|
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Converts a string to integer.
|
|
* \todo check if we can use the functions from ut.h
|
|
* params:
|
|
* s: The string to be converted to int.
|
|
* returns:
|
|
* >=0 on success
|
|
* -1 otherwise
|
|
*/
|
|
static int conf_str2int(char *s)
|
|
{
|
|
if (s == NULL) return -1;
|
|
|
|
errno = 0;
|
|
char *end = NULL;
|
|
long int i = strtol(s, &end, 10);
|
|
if ((errno != 0) || (i == LONG_MIN) || (i == LONG_MAX) || (end == s)) {
|
|
LM_ERR("invalid string '%s'.\n", s);
|
|
return -1;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Converts string to integer and checks for validity.
|
|
* \todo check if we can use the functions from ut.h
|
|
* params:
|
|
* id_str: ID as string to be converted to int.
|
|
* returns:
|
|
* >=0 on success
|
|
* -1 otherwise
|
|
*/
|
|
int conf_str2id(char *id_str)
|
|
{
|
|
int id = conf_str2int(id_str);
|
|
|
|
if ((id<0) || (id > fwd_max_id)) {
|
|
LM_ERR("id %d is out of range.\n", id);
|
|
return -1;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Updates switch configuration
|
|
* \param id Update the configuration with this ID.
|
|
* \param param_str can be either "off" or "on".
|
|
* \return 0 on success, -1 otherwise
|
|
*/
|
|
static int update_switch(int id, char* param_str)
|
|
{
|
|
if (param_str == NULL) {
|
|
LM_ERR("param_str is NULL.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (strcmp(param_str, "on") == 0) {
|
|
fwd_settings[id].active = 1;
|
|
return 0;
|
|
} else if (strcmp(param_str, "off") == 0) {
|
|
fwd_settings[id].active = 0;
|
|
return 0;
|
|
}
|
|
|
|
LM_ERR("invalid switch '%s'.\n", param_str);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Updates filter configuration.
|
|
* Updates filter configuration.
|
|
* If filter_methods is not NULL, memory is freed.
|
|
* If filter methods are found, memory for the string is allocated,
|
|
* otherwise filter_methods is set to NULL.
|
|
* \param id update the configuration with this ID.
|
|
* \param flist a list of filter names.
|
|
* \return 0 on success, -1 otherwise
|
|
*/
|
|
static int update_filter(int id, char *flist)
|
|
{
|
|
if (flist == NULL) {
|
|
LM_ERR("flist is NULL.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* reset special filter mask and filter methods*/
|
|
fwd_settings[id].sfilter = 0;
|
|
if (fwd_settings[id].filter_methods != NULL) {
|
|
shm_free(fwd_settings[id].filter_methods);
|
|
fwd_settings[id].filter_methods = NULL;
|
|
}
|
|
|
|
int i;
|
|
for (i=0; i<sfilter_cnt; i++) {
|
|
if (strstr(flist, sfilter_str[i]) != NULL) {
|
|
/* special filter name is found in flist -> add to special filter mask */
|
|
fwd_settings[id].sfilter |= sfilter_mask[i];
|
|
}
|
|
}
|
|
|
|
char buf[BUFSIZE+1], tmp[BUFSIZE+1];
|
|
buf[0] = '\0';
|
|
char *set_p = flist;
|
|
char *token = NULL;
|
|
while ((token = strsep(&set_p, ":"))) { /* iterate through list of filters */
|
|
int found = 0;
|
|
/* is it a special filter? */
|
|
for (i=0; i<sfilter_cnt; i++) {
|
|
if (strcmp(token, sfilter_str[i]) == 0) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found == 0) {
|
|
/* no special filter! */
|
|
if (buf[0]) {
|
|
strcpy(tmp, buf);
|
|
snprintf(buf, BUFSIZE, "%s:%s", tmp, token);
|
|
buf[BUFSIZE]='\0';
|
|
} else {
|
|
snprintf(buf, BUFSIZE, "%s", token);
|
|
buf[BUFSIZE]='\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
int len = strlen(buf);
|
|
if (len > 0) {
|
|
char *flc = shm_malloc(len+1);
|
|
if (flc == NULL) {
|
|
SHM_MEM_ERROR;
|
|
return -1;
|
|
}
|
|
memcpy(flc, buf, len+1);
|
|
fwd_settings[id].filter_methods = flc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* Updates proxy configuration
|
|
* \param id update the configuration with this ID.
|
|
* \param host_str the destination host.
|
|
* \param port_str the port number as string.
|
|
* \return 0 on success, -1 otherwise
|
|
*/
|
|
static int update_proxy(int id, char *host_str, char *port_str)
|
|
{
|
|
if (host_str == NULL) {
|
|
LM_ERR("host_str is NULL.\n");
|
|
return -1;
|
|
}
|
|
if (port_str == NULL) {
|
|
LM_ERR("port_str is NULL.\n");
|
|
return -1;
|
|
}
|
|
|
|
int port = conf_str2int(port_str);
|
|
if (port < 0) {
|
|
LM_ERR("invalid port '%s'.\n", port_str);
|
|
return -1;
|
|
}
|
|
|
|
/* make copy of host string since mk_proxy does not */
|
|
str host;
|
|
host.len = strlen(host_str);
|
|
host.s = shm_malloc(host.len+1);
|
|
if (host.s == NULL) {
|
|
SHM_MEM_ERROR;
|
|
return -1;
|
|
}
|
|
strcpy(host.s, host_str);
|
|
|
|
/* make proxy in shared memory */
|
|
struct proxy_l* proxy;
|
|
proxy = mk_shm_proxy(&host, port, PROTO_UDP);
|
|
if (proxy == NULL) {
|
|
LM_ERR("cannot make proxy (host='%s', port=%d).\n", host_str, port);
|
|
shm_free(host.s);
|
|
return -1;
|
|
}
|
|
|
|
if (fwd_settings[id].proxy) {
|
|
/* cleaning up old proxy */
|
|
if (fwd_settings[id].proxy->name.s) {
|
|
shm_free(fwd_settings[id].proxy->name.s);
|
|
}
|
|
free_shm_proxy(fwd_settings[id].proxy);
|
|
shm_free(fwd_settings[id].proxy);
|
|
}
|
|
fwd_settings[id].proxy = proxy; /* new proxy is now acitvated */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Parses configuration string for the switch
|
|
* Parses a configuration string for switch settings and updates
|
|
* the configuration structure.
|
|
* \param settings the configuration string in the following form:
|
|
\verbatim
|
|
* <id>=<switch>[,<id>=<switch>]...
|
|
\endverbatim
|
|
* \return 1 on success, -1 otherwise
|
|
*/
|
|
int conf_parse_switch(char *settings)
|
|
{
|
|
/* make a copy since we are modifying it */
|
|
int len = strlen(settings);
|
|
if (len==0) return 1;
|
|
char *strc = (char *)pkg_malloc(len+1);
|
|
if (strc == NULL) {
|
|
PKG_MEM_ERROR;
|
|
return -1;
|
|
}
|
|
memcpy(strc, settings, len+1);
|
|
remove_spaces(strc);
|
|
|
|
char *set_p = strc;
|
|
char *token = NULL;
|
|
while ((token = strsep(&set_p, ","))) { /* iterate through list of settings */
|
|
char *id_str = strsep(&token, "=");
|
|
int id = conf_str2id(id_str);
|
|
if (id < 0) {
|
|
LM_ERR("cannot parse id '%s'.\n", id_str);
|
|
pkg_free(strc);
|
|
return -1;
|
|
}
|
|
|
|
/* got all data for one setting -> update configuration now */
|
|
if (update_switch(id, token) < 0) {
|
|
LM_ERR("cannot update switch.\n");
|
|
pkg_free(strc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
pkg_free(strc);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Output configuration in FIFO format
|
|
* \param rpl_tree FIFO root
|
|
* \return 0 on success, -1 on failure
|
|
*/
|
|
int conf_show(struct mi_root* rpl_tree)
|
|
{
|
|
int id, sfilter;
|
|
struct mi_node * node = NULL;
|
|
|
|
node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "id switch %30s proxy\n", "filter");
|
|
if(node == NULL)
|
|
goto error;
|
|
|
|
for (id=0; id<=fwd_max_id; id++) {
|
|
char buf[BUFSIZE+1];
|
|
char tmp[BUFSIZE+1];
|
|
buf[0]='\0';
|
|
for (sfilter=0; sfilter<sfilter_cnt; sfilter++) {
|
|
if (fwd_settings[id].sfilter&sfilter_mask[sfilter]) {
|
|
if (buf[0]) {
|
|
strcpy(tmp, buf);
|
|
snprintf(buf, BUFSIZE, "%s:%s", tmp, sfilter_str[sfilter]);
|
|
buf[BUFSIZE]='\0';
|
|
} else {
|
|
snprintf(buf, BUFSIZE, "%s", sfilter_str[sfilter]);
|
|
buf[BUFSIZE]='\0';
|
|
}
|
|
}
|
|
}
|
|
if (fwd_settings[id].filter_methods) {
|
|
if (buf[0]) {
|
|
strcpy(tmp, buf);
|
|
snprintf(buf, BUFSIZE, "%s:%s", tmp, fwd_settings[id].filter_methods);
|
|
buf[BUFSIZE]='\0';
|
|
} else {
|
|
snprintf(buf, BUFSIZE, "%s", fwd_settings[id].filter_methods);
|
|
buf[BUFSIZE]='\0';
|
|
}
|
|
}
|
|
node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%2d %s %33s %s:%d\n", id,
|
|
fwd_settings[id].active ? "on " : "off", buf,
|
|
fwd_settings[id].proxy ? fwd_settings[id].proxy->name.s : "",
|
|
fwd_settings[id].proxy ? fwd_settings[id].proxy->port : 0);
|
|
if(node == NULL)
|
|
goto error;
|
|
|
|
}
|
|
return 0;
|
|
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Parses a configuration string for the filter
|
|
* Parses a configuration string for switch settings and
|
|
* updates the configuration structure.
|
|
* \param settings The configuration string in the following form:
|
|
\verbatim
|
|
* <id>=<filter>[:<filter>]...[,<id>=<filter>[:<filter>]...]...
|
|
\endverbatim
|
|
* \return 1 on success, -1 otherwise
|
|
*/
|
|
int conf_parse_filter(char *settings)
|
|
{
|
|
/* make a copy since we are modifying it */
|
|
int len = strlen(settings);
|
|
if (len==0) return 1;
|
|
char *strc = (char *)pkg_malloc(len+1);
|
|
if (strc == NULL) {
|
|
PKG_MEM_ERROR;
|
|
return -1;
|
|
}
|
|
memcpy(strc, settings, len+1);
|
|
remove_spaces(strc);
|
|
|
|
char *set_p = strc;
|
|
char *token = NULL;
|
|
while ((token = strsep(&set_p, ","))) { /* iterate through list of settings */
|
|
char *id_str = strsep(&token, "=");
|
|
int id = conf_str2id(id_str);
|
|
if (id<0) {
|
|
LM_ERR("cannot parse id '%s'.\n", id_str);
|
|
pkg_free(strc);
|
|
return -1;
|
|
}
|
|
if (update_filter(id, token) < 0) {
|
|
LM_ERR("cannot extract filters.\n");
|
|
pkg_free(strc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
pkg_free(strc);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Parses a configuration string for proxy settings
|
|
* Parses a configuration string for proxy settings and
|
|
* updates the configuration structure.
|
|
* \param settings: The configuration string in the following form:
|
|
\verbatim
|
|
* <id>=<host>:<port>[,<id>=<host>:<port>]...
|
|
\endverbatim
|
|
* \return: 1 on success, -1 otherwise
|
|
*/
|
|
int conf_parse_proxy(char *settings)
|
|
{
|
|
/* make a copy since we are modifying it */
|
|
int len = strlen(settings);
|
|
if (len==0) return 1;
|
|
char *strc = (char *)pkg_malloc(len+1);
|
|
if (strc == NULL) {
|
|
PKG_MEM_ERROR;
|
|
return -1;
|
|
}
|
|
memcpy(strc, settings, len+1);
|
|
remove_spaces(strc);
|
|
|
|
char *set_p = strc;
|
|
char *token = NULL;
|
|
while ((token = strsep(&set_p, ","))) { /* iterate through list of settings */
|
|
char *id_str = strsep(&token, "=");
|
|
int id = conf_str2id(id_str);
|
|
if (id<0) {
|
|
LM_ERR("cannot parse id '%s'.\n", id_str);
|
|
pkg_free(strc);
|
|
return -1;
|
|
}
|
|
char *host = strsep(&token, ":");
|
|
|
|
/* got all data for one setting -> update configuration now */
|
|
if (update_proxy(id, host, token) < 0) {
|
|
LM_ERR("cannot update proxy.\n");
|
|
pkg_free(strc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
pkg_free(strc);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Checks if method string is in filter_methods
|
|
* \param id use configuration with this ID when checking
|
|
* \param method method string to be searched for
|
|
* \param method_len length of method string
|
|
* \return 1 if method is found in filter_methods, 0 otherwise
|
|
*/
|
|
static int filter_methods_contains_request(int id, char *method, int method_len)
|
|
{
|
|
char *p = fwd_settings[id].filter_methods;
|
|
|
|
while (p != NULL) {
|
|
if (strncmp(p, method, method_len) == 0) {
|
|
return 1;
|
|
}
|
|
p = strchr(p, ':');
|
|
if (p != NULL) p++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Checks forwarding is needed
|
|
* \param msg the SIP message to be forwarded
|
|
* \param id use configuration with this ID when checking
|
|
* \return pointer to proxy structure of destination if forwarding is needed, NULL otherwise
|
|
*/
|
|
struct proxy_l *conf_needs_forward(struct sip_msg *msg, int id)
|
|
{
|
|
if ((msg == NULL) || (fwd_settings[id].active == 0)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (msg->first_line.type == SIP_REPLY) {
|
|
if (fwd_settings[id].sfilter&sfilter_mask[sfidx_reply]) {
|
|
return fwd_settings[id].proxy;
|
|
}
|
|
}
|
|
|
|
if (msg->first_line.type == SIP_REQUEST) {
|
|
if (fwd_settings[id].sfilter&sfilter_mask[sfidx_request]) {
|
|
return fwd_settings[id].proxy;
|
|
}
|
|
|
|
if (filter_methods_contains_request(id, msg->first_line.u.request.method.s, msg->first_line.u.request.method.len) > 0) {
|
|
return fwd_settings[id].proxy;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Initialize configuration
|
|
* \param max_id number of configuration statements
|
|
* \return 0 on success, -1 on failure
|
|
*/
|
|
int conf_init(int max_id)
|
|
{
|
|
/* allocate and initialize memory for configuration */
|
|
fwd_settings = shm_malloc(sizeof(struct fwd_setting)*(max_id+1));
|
|
if (fwd_settings == NULL) {
|
|
SHM_MEM_ERROR;
|
|
return -1;
|
|
}
|
|
memset(fwd_settings, 0, sizeof(struct fwd_setting)*(max_id+1));
|
|
fwd_max_id = max_id;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Destroy configuration
|
|
*/
|
|
void conf_destroy(void)
|
|
{
|
|
int id;
|
|
|
|
if (fwd_settings) {
|
|
for (id=0; id<=fwd_max_id; id++) {
|
|
fwd_settings[id].active = 0;
|
|
if (fwd_settings[id].proxy) {
|
|
if (fwd_settings[id].proxy->name.s) {
|
|
shm_free(fwd_settings[id].proxy->name.s);
|
|
}
|
|
free_shm_proxy(fwd_settings[id].proxy);
|
|
shm_free(fwd_settings[id].proxy);
|
|
}
|
|
}
|
|
shm_free(fwd_settings);
|
|
}
|
|
}
|