/* * 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 #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; }