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.
454 lines
13 KiB
454 lines
13 KiB
/*
|
|
* Copyright (C) 2008-2009 1&1 Internet AG
|
|
* Copyright (C) 2001-2003 FhG Fokus
|
|
*
|
|
* 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 Module with several utiltity functions related to SIP messages handling
|
|
* \ingroup siputils
|
|
* - Module \ref siputils
|
|
*/
|
|
|
|
/*!
|
|
* \defgroup siputils SIPUTILS :: Various SIP message handling functions
|
|
*
|
|
*
|
|
* This module implement various functions and checks related to
|
|
* SIP message handling and URI handling.
|
|
*
|
|
* It offers some functions related to handle ringing. In a
|
|
* parallel forking scenario you get several 183s with SDP. You
|
|
* don't want that your customers hear more than one ringtone or
|
|
* answer machine in parallel on the phone. So its necessary to
|
|
* drop the 183 in this cases and send a 180 instead.
|
|
*
|
|
* This module provides a function to answer OPTIONS requests
|
|
* which are directed to the server itself. This means an OPTIONS
|
|
* request which has the address of the server in the request
|
|
* URI, and no username in the URI. The request will be answered
|
|
* with a 200 OK which the capabilities of the server.
|
|
*
|
|
* To answer OPTIONS request directed to your server is the
|
|
* easiest way for is-alive-tests on the SIP (application) layer
|
|
* from remote (similar to ICMP echo requests, also known as
|
|
* "ping", on the network layer).
|
|
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "../../sr_module.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../dprint.h"
|
|
#include "../../script_cb.h"
|
|
#include "../../locking.h"
|
|
#include "../../ut.h"
|
|
#include "../../mod_fix.h"
|
|
#include "../../error.h"
|
|
#include "../../parser/parse_option_tags.h"
|
|
|
|
#include "ring.h"
|
|
#include "options.h"
|
|
|
|
#include "checks.h"
|
|
|
|
#include "rpid.h"
|
|
#include "siputils.h"
|
|
|
|
#include "utils.h"
|
|
#include "contact_ops.h"
|
|
#include "sipops.h"
|
|
#include "config.h"
|
|
#include "chargingvector.h"
|
|
|
|
MODULE_VERSION
|
|
|
|
/* rpid handling defs */
|
|
#define DEF_RPID_PREFIX ""
|
|
#define DEF_RPID_SUFFIX ";party=calling;id-type=subscriber;screen=yes"
|
|
#define DEF_RPID_AVP "$avp(s:rpid)"
|
|
|
|
/*! Default Remote-Party-ID prefix */
|
|
str rpid_prefix = {DEF_RPID_PREFIX, sizeof(DEF_RPID_PREFIX) - 1};
|
|
/*! Default Remote-Party-IDD suffix */
|
|
str rpid_suffix = {DEF_RPID_SUFFIX, sizeof(DEF_RPID_SUFFIX) - 1};
|
|
/*! Definition of AVP containing rpid value */
|
|
char* rpid_avp_param = DEF_RPID_AVP;
|
|
|
|
gen_lock_t *ring_lock = NULL;
|
|
unsigned int ring_timeout = 0;
|
|
/* for options functionality */
|
|
str opt_accept = str_init(ACPT_DEF);
|
|
str opt_accept_enc = str_init(ACPT_ENC_DEF);
|
|
str opt_accept_lang = str_init(ACPT_LAN_DEF);
|
|
str opt_supported = str_init(SUPT_DEF);
|
|
/** SL API structure */
|
|
sl_api_t opt_slb;
|
|
|
|
static int mod_init(void);
|
|
static void mod_destroy(void);
|
|
|
|
/* Fixup functions to be defined later */
|
|
static int fixup_set_uri(void** param, int param_no);
|
|
static int fixup_free_set_uri(void** param, int param_no);
|
|
static int fixup_tel2sip(void** param, int param_no);
|
|
static int fixup_get_uri_param(void** param, int param_no);
|
|
static int free_fixup_get_uri_param(void** param, int param_no);
|
|
static int fixup_option(void** param, int param_no);
|
|
|
|
|
|
char *contact_flds_separator = DEFAULT_SEPARATOR;
|
|
|
|
static cmd_export_t cmds[]={
|
|
{"ring_insert_callid", (cmd_function)ring_insert_callid, 0, ring_fixup,
|
|
0, REQUEST_ROUTE|FAILURE_ROUTE},
|
|
{"options_reply", (cmd_function)opt_reply, 0, 0,
|
|
0, REQUEST_ROUTE},
|
|
{"is_user", (cmd_function)is_user, 1, fixup_str_null,
|
|
0, REQUEST_ROUTE|LOCAL_ROUTE},
|
|
{"has_totag", (cmd_function)has_totag, 0, 0,
|
|
0, ANY_ROUTE},
|
|
{"uri_param", (cmd_function)uri_param_1, 1, fixup_str_null,
|
|
0, REQUEST_ROUTE|LOCAL_ROUTE},
|
|
{"uri_param", (cmd_function)uri_param_2, 2, fixup_str_str,
|
|
0, REQUEST_ROUTE|LOCAL_ROUTE},
|
|
{"add_uri_param", (cmd_function)add_uri_param, 1, fixup_str_null,
|
|
0, REQUEST_ROUTE},
|
|
{"get_uri_param", (cmd_function)get_uri_param, 2, fixup_get_uri_param,
|
|
free_fixup_get_uri_param, REQUEST_ROUTE|LOCAL_ROUTE},
|
|
{"tel2sip", (cmd_function)tel2sip, 3, fixup_tel2sip, 0,
|
|
REQUEST_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE},
|
|
{"is_e164", (cmd_function)is_e164, 1, fixup_pvar_null,
|
|
fixup_free_pvar_null, REQUEST_ROUTE|FAILURE_ROUTE|LOCAL_ROUTE},
|
|
{"is_uri_user_e164", (cmd_function)w_is_uri_user_e164, 1, fixup_pvar_null,
|
|
fixup_free_pvar_null, ANY_ROUTE},
|
|
{"encode_contact", (cmd_function)encode_contact, 2, 0,
|
|
0, REQUEST_ROUTE|ONREPLY_ROUTE},
|
|
{"decode_contact", (cmd_function)decode_contact, 0, 0,
|
|
0, REQUEST_ROUTE},
|
|
{"decode_contact_header", (cmd_function)decode_contact_header, 0, 0,
|
|
0,REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE},
|
|
{"cmp_uri", (cmd_function)w_cmp_uri, 2, fixup_spve_spve,
|
|
0, ANY_ROUTE},
|
|
{"cmp_aor", (cmd_function)w_cmp_aor, 2, fixup_spve_spve,
|
|
0, ANY_ROUTE},
|
|
{"is_rpid_user_e164", (cmd_function)is_rpid_user_e164, 0, 0,
|
|
0, REQUEST_ROUTE},
|
|
{"append_rpid_hf", (cmd_function)append_rpid_hf, 0, 0,
|
|
0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE},
|
|
{"append_rpid_hf", (cmd_function)append_rpid_hf_p, 2, fixup_str_str,
|
|
0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE},
|
|
{"set_uri_user", (cmd_function)set_uri_user, 2, fixup_set_uri,
|
|
fixup_free_set_uri, ANY_ROUTE},
|
|
{"set_uri_host", (cmd_function)set_uri_host, 2, fixup_set_uri,
|
|
fixup_free_set_uri, ANY_ROUTE},
|
|
{"bind_siputils", (cmd_function)bind_siputils, 1, 0,
|
|
0, 0},
|
|
{"is_request", (cmd_function)w_is_request, 0, 0,
|
|
0, ANY_ROUTE},
|
|
{"is_reply", (cmd_function)w_is_reply, 0, 0,
|
|
0, ANY_ROUTE},
|
|
{"is_gruu", (cmd_function)w_is_gruu, 0, 0,
|
|
0, ANY_ROUTE},
|
|
{"is_gruu", (cmd_function)w_is_gruu, 1, fixup_spve_null,
|
|
0, ANY_ROUTE},
|
|
{"is_supported", (cmd_function)w_is_supported, 1, fixup_option,
|
|
0, ANY_ROUTE},
|
|
{"is_first_hop", (cmd_function)w_is_first_hop, 0, 0,
|
|
0, ANY_ROUTE},
|
|
{"is_tel_number", (cmd_function)is_tel_number, 1, fixup_spve_null,
|
|
0, ANY_ROUTE},
|
|
{"is_numeric", (cmd_function)is_numeric, 1, fixup_spve_null,
|
|
0, ANY_ROUTE},
|
|
{"sip_p_charging_vector", (cmd_function)sip_handle_pcv, 1, fixup_spve_null,
|
|
fixup_free_spve_null, ANY_ROUTE},
|
|
{0,0,0,0,0,0}
|
|
};
|
|
|
|
static param_export_t params[] = {
|
|
{"ring_timeout", INT_PARAM, &default_siputils_cfg.ring_timeout},
|
|
{"options_accept", PARAM_STR, &opt_accept},
|
|
{"options_accept_encoding", PARAM_STR, &opt_accept_enc},
|
|
{"options_accept_language", PARAM_STR, &opt_accept_lang},
|
|
{"options_support", PARAM_STR, &opt_supported},
|
|
{"contact_flds_separator", PARAM_STRING, &contact_flds_separator},
|
|
{"rpid_prefix", PARAM_STR, &rpid_prefix },
|
|
{"rpid_suffix", PARAM_STR, &rpid_suffix },
|
|
{"rpid_avp", PARAM_STRING, &rpid_avp_param },
|
|
{0, 0, 0}
|
|
};
|
|
|
|
|
|
static pv_export_t mod_pvs[] = {
|
|
{ {"pcv", (sizeof("pvc")-1)}, PVT_OTHER, pv_get_charging_vector,
|
|
0, pv_parse_charging_vector_name, 0, 0, 0},
|
|
|
|
{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
|
|
};
|
|
|
|
struct module_exports exports= {
|
|
"siputils",
|
|
DEFAULT_DLFLAGS, /* dlopen flags */
|
|
cmds, /* Exported functions */
|
|
params, /* param exports */
|
|
0, /* exported statistics */
|
|
0, /* exported MI functions */
|
|
mod_pvs, /* exported pseudo-variables */
|
|
0, /* extra processes */
|
|
mod_init, /* initialization function */
|
|
0, /* Response function */
|
|
mod_destroy, /* Destroy function */
|
|
0, /* Child init function */
|
|
};
|
|
|
|
|
|
static int mod_init(void)
|
|
{
|
|
if(default_siputils_cfg.ring_timeout > 0) {
|
|
ring_init_hashtable();
|
|
|
|
ring_lock = lock_alloc();
|
|
assert(ring_lock);
|
|
if (lock_init(ring_lock) == 0) {
|
|
LM_CRIT("cannot initialize lock.\n");
|
|
return -1;
|
|
}
|
|
if (register_script_cb(ring_filter, PRE_SCRIPT_CB|ONREPLY_CB, 0) != 0) {
|
|
LM_ERR("could not insert callback");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* bind the SL API */
|
|
if (sl_load_api(&opt_slb)!=0) {
|
|
LM_ERR("cannot bind to SL API\n");
|
|
return -1;
|
|
}
|
|
|
|
if ( init_rpid_avp(rpid_avp_param)<0 ) {
|
|
LM_ERR("failed to init rpid AVP name\n");
|
|
return -1;
|
|
}
|
|
|
|
if(cfg_declare("siputils", siputils_cfg_def, &default_siputils_cfg, cfg_sizeof(siputils), &siputils_cfg)){
|
|
LM_ERR("Fail to declare the configuration\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void mod_destroy(void)
|
|
{
|
|
if (ring_lock) {
|
|
lock_destroy(ring_lock);
|
|
lock_dealloc((void *)ring_lock);
|
|
ring_lock = NULL;
|
|
}
|
|
|
|
ring_destroy_hashtable();
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief Bind function for the SIPUTILS API
|
|
* \param api binded API
|
|
* \return 0 on success, -1 on failure
|
|
*/
|
|
int bind_siputils(siputils_api_t* api)
|
|
{
|
|
if (!api) {
|
|
LM_ERR("invalid parameter value\n");
|
|
return -1;
|
|
}
|
|
|
|
get_rpid_avp( &api->rpid_avp, &api->rpid_avp_type );
|
|
api->has_totag = has_totag;
|
|
api->is_uri_user_e164 = is_uri_user_e164;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Fix set_uri_* function params: uri (writable pvar) and value (pvar)
|
|
*/
|
|
static int fixup_set_uri(void** param, int param_no)
|
|
{
|
|
if (param_no == 1) {
|
|
if (fixup_pvar_null(param, 1) != 0) {
|
|
LM_ERR("failed to fixup uri pvar\n");
|
|
return -1;
|
|
}
|
|
if (((pv_spec_t *)(*param))->setf == NULL) {
|
|
LM_ERR("uri pvar is not writeble\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (param_no == 2) {
|
|
return fixup_pvar_null(param, 1);
|
|
}
|
|
|
|
LM_ERR("invalid parameter number <%d>\n", param_no);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Free set_uri_* params.
|
|
*/
|
|
static int fixup_free_set_uri(void** param, int param_no)
|
|
{
|
|
return fixup_free_pvar_null(param, 1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Fix tel2sip function params: uri and hostpart pvars and
|
|
* result writable pvar.
|
|
*/
|
|
static int fixup_tel2sip(void** param, int param_no)
|
|
{
|
|
if ((param_no == 1) || (param_no == 2)) {
|
|
if (fixup_var_str_12(param, 1) < 0) {
|
|
LM_ERR("failed to fixup uri or hostpart pvar\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (param_no == 3) {
|
|
if (fixup_pvar_null(param, 1) != 0) {
|
|
LM_ERR("failed to fixup result pvar\n");
|
|
return -1;
|
|
}
|
|
if (((pv_spec_t *)(*param))->setf == NULL) {
|
|
LM_ERR("result pvar is not writeble\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LM_ERR("invalid parameter number <%d>\n", param_no);
|
|
return -1;
|
|
}
|
|
|
|
/* */
|
|
static int fixup_get_uri_param(void** param, int param_no) {
|
|
if (param_no == 1) {
|
|
return fixup_str_null(param, 1);
|
|
}
|
|
if (param_no == 2) {
|
|
if (fixup_pvar_null(param, 1) != 0) {
|
|
LM_ERR("failed to fixup result pvar\n");
|
|
return -1;
|
|
}
|
|
if (((pv_spec_t *)(*param))->setf == NULL) {
|
|
LM_ERR("result pvar is not writeble\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LM_ERR("invalid parameter number <%d>\n", param_no);
|
|
return -1;
|
|
}
|
|
|
|
/* */
|
|
static int free_fixup_get_uri_param(void** param, int param_no) {
|
|
if (param_no == 1) {
|
|
LM_WARN("free function has not been defined for spve\n");
|
|
return 0;
|
|
}
|
|
if (param_no == 2) {
|
|
return fixup_free_pvar_null(param, 1);
|
|
}
|
|
LM_ERR("invalid parameter number <%d>\n", param_no);
|
|
return -1;
|
|
}
|
|
|
|
/* */
|
|
static int fixup_option(void** param, int param_no) {
|
|
|
|
char *option;
|
|
unsigned int option_len, res;
|
|
|
|
option = (char *)*param;
|
|
option_len = strlen(option);
|
|
|
|
if (param_no != 1) {
|
|
LM_ERR("invalid parameter number <%d>\n", param_no);
|
|
return -1;
|
|
}
|
|
|
|
switch (option_len) {
|
|
case 4:
|
|
if (strncasecmp(option, "path", 4) == 0)
|
|
res = F_OPTION_TAG_PATH;
|
|
else if (strncasecmp(option, "gruu", 4) == 0)
|
|
res = F_OPTION_TAG_GRUU;
|
|
else {
|
|
LM_ERR("unknown option <%s>\n", option);
|
|
return -1;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (strncasecmp(option, "timer", 5) == 0)
|
|
res = F_OPTION_TAG_TIMER;
|
|
else {
|
|
LM_ERR("unknown option <%s>\n", option);
|
|
return -1;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (strncasecmp(option, "100rel", 6) == 0)
|
|
res = F_OPTION_TAG_100REL;
|
|
else {
|
|
LM_ERR("unknown option <%s>\n", option);
|
|
return -1;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (strncasecmp(option, "outbound", 8) == 0)
|
|
res = F_OPTION_TAG_OUTBOUND;
|
|
else {
|
|
LM_ERR("unknown option <%s>\n", option);
|
|
return -1;
|
|
}
|
|
break;
|
|
case 9:
|
|
if (strncasecmp(option, "eventlist", 9) == 0)
|
|
res = F_OPTION_TAG_EVENTLIST;
|
|
else {
|
|
LM_ERR("unknown option <%s>\n", option);
|
|
return -1;
|
|
}
|
|
break;
|
|
default:
|
|
LM_ERR("unknown option <%s>\n", option);
|
|
return -1;
|
|
}
|
|
|
|
*param = (void *)(long)res;
|
|
return 0;
|
|
}
|