diff --git a/etc/kamailio.cfg b/etc/kamailio.cfg index 708aade7bbf..ddadaadb02b 100644 --- a/etc/kamailio.cfg +++ b/etc/kamailio.cfg @@ -851,9 +851,9 @@ route[NATMANAGE] { #!ifdef WITH_RTPENGINE if(nat_uac_test("8")) { - rtpengine_manage("replace-origin replace-session-connection"); + rtpengine_manage("SIP-source-address replace-origin replace-session-connection"); } else { - rtpengine_manage("trust-address replace-origin replace-session-connection"); + rtpengine_manage("replace-origin replace-session-connection"); } #!else if(nat_uac_test("8")) { diff --git a/misc/fuzz/README.md b/misc/fuzz/README.md new file mode 100644 index 00000000000..bc0238f56ce --- /dev/null +++ b/misc/fuzz/README.md @@ -0,0 +1,14 @@ +# Kamailio OSS Fuzz Integration # + +OSS-Fuzz is a free service run by Google that performs continuous fuzzing of +various open source projects. + + * https://github.com/google/oss-fuzz + +OSS-Fuzz pull request to integrate Kamailio: + + * https://github.com/google/oss-fuzz/pull/5279 + +Initial pull request in Kamailio, with additional details: + + * https://github.com/kamailio/kamailio/pull/2660 diff --git a/misc/fuzz/fuzz_uri.c b/misc/fuzz/fuzz_uri.c new file mode 100644 index 00000000000..9418d6eed4d --- /dev/null +++ b/misc/fuzz/fuzz_uri.c @@ -0,0 +1,8 @@ +#include "../parser/parse_uri.c" + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + struct sip_uri uri; + parse_uri(data, size, &uri); + return 0; +} diff --git a/pkg/kamailio/obs/kamailio.spec b/pkg/kamailio/obs/kamailio.spec index 0c0170d100d..29d074cac45 100644 --- a/pkg/kamailio/obs/kamailio.spec +++ b/pkg/kamailio/obs/kamailio.spec @@ -1448,6 +1448,7 @@ fi %doc %{_docdir}/kamailio/modules/README.ipops %doc %{_docdir}/kamailio/modules/README.kemix %doc %{_docdir}/kamailio/modules/README.kex +%doc %{_docdir}/kamailio/modules/README.lrkproxy %doc %{_docdir}/kamailio/modules/README.malloc_test %doc %{_docdir}/kamailio/modules/README.mangler %doc %{_docdir}/kamailio/modules/README.matrix @@ -1606,6 +1607,7 @@ fi %{_libdir}/kamailio/modules/ipops.so %{_libdir}/kamailio/modules/kemix.so %{_libdir}/kamailio/modules/kex.so +%{_libdir}/kamailio/modules/lrkproxy.so %{_libdir}/kamailio/modules/malloc_test.so %{_libdir}/kamailio/modules/mangler.so %{_libdir}/kamailio/modules/matrix.so diff --git a/src/Makefile.groups b/src/Makefile.groups index 251a191e771..34bbd986780 100644 --- a/src/Makefile.groups +++ b/src/Makefile.groups @@ -23,7 +23,7 @@ mod_list_extra=avp auth_diameter call_control call_obj dmq domainpolicy msrp \ carrierroute pdb qos sca seas sms sst timer tmrec uac_redirect \ xhttp xhttp_rpc xprint jsonrpcs nosip dmq_usrloc statsd rtjson \ log_custom keepalive ss7ops app_sqlang acc_diameter evrexec \ - sipjson xhttp_prom + sipjson lrkproxy xhttp_prom # - common modules depending on database mod_list_db=acc alias_db auth_db avpops cfg_db db_text db_flatstore \ @@ -184,6 +184,12 @@ mod_list_uuid=uuid # - modules depending on ev library mod_list_ev=evapi +# - modules depending on libjwt library +mod_list_jwt=jwt + +# - modules depending on libstirshaken library +mod_list_stirshaken=stirshaken + # - modules depending on kazoo/rabbitmq mod_list_kazoo=kazoo @@ -256,6 +262,8 @@ mod_list_all=$(sort $(mod_list_basic) $(mod_list_extra) \ $(mod_list_kafka) \ $(mod_list_mqtt) \ $(mod_list_secsipid) \ + $(mod_list_jwt) \ + $(mod_list_stirshaken) \ $(mod_list_rtp_media_server) @@ -446,6 +454,12 @@ module_group_kuuid=$(mod_list_uuid) # pkg libev modules module_group_kev=$(mod_list_ev) +# pkg jwt module +module_group_kjwt=$(mod_list_jwt) + +# pkg stirshaken module +module_group_kstirshaken=$(mod_list_stirshaken) + # pkg kazoo module module_group_kkazoo=$(mod_list_kazoo) diff --git a/src/core/cfg.lex b/src/core/cfg.lex index e3e4be7be82..afab5b68a59 100644 --- a/src/core/cfg.lex +++ b/src/core/cfg.lex @@ -467,6 +467,9 @@ VERBOSE_STARTUP "verbose_startup" SERVER_ID "server_id" ROUTE_LOCKS_SIZE "route_locks_size" +WAIT_WORKER1_MODE "wait_worker1_mode" +WAIT_WORKER1_TIME "wait_worker1_time" +WAIT_WORKER1_USLEEP "wait_worker1_usleep" KEMI "kemi" ONSEND_ROUTE_CALLBACK "onsend_route_callback" @@ -989,6 +992,9 @@ IMPORTFILE "import_file" {VERBOSE_STARTUP} { count(); yylval.strval=yytext; return VERBOSE_STARTUP; } {ROUTE_LOCKS_SIZE} { count(); yylval.strval=yytext; return ROUTE_LOCKS_SIZE; } +{WAIT_WORKER1_MODE} { count(); yylval.strval=yytext; return WAIT_WORKER1_MODE; } +{WAIT_WORKER1_TIME} { count(); yylval.strval=yytext; return WAIT_WORKER1_TIME; } +{WAIT_WORKER1_USLEEP} { count(); yylval.strval=yytext; return WAIT_WORKER1_USLEEP; } {SERVER_ID} { count(); yylval.strval=yytext; return SERVER_ID;} {KEMI} { count(); yylval.strval=yytext; return KEMI;} {REPLY_ROUTE_CALLBACK} { count(); yylval.strval=yytext; return REPLY_ROUTE_CALLBACK;} diff --git a/src/core/cfg.y b/src/core/cfg.y index f659aad087a..be0a664dcea 100644 --- a/src/core/cfg.y +++ b/src/core/cfg.y @@ -498,6 +498,9 @@ extern char *default_routename; %token VERSION_TABLE_CFG %token VERBOSE_STARTUP %token ROUTE_LOCKS_SIZE +%token WAIT_WORKER1_MODE +%token WAIT_WORKER1_TIME +%token WAIT_WORKER1_USLEEP %token CFG_DESCRIPTION %token SERVER_ID %token KEMI @@ -1702,6 +1705,12 @@ assign_stm: | VERBOSE_STARTUP EQUAL error { yyerror("boolean value expected"); } | ROUTE_LOCKS_SIZE EQUAL NUMBER { ksr_route_locks_size=$3; } | ROUTE_LOCKS_SIZE EQUAL error { yyerror("number expected"); } + | WAIT_WORKER1_MODE EQUAL NUMBER { ksr_wait_worker1_mode=$3; } + | WAIT_WORKER1_MODE EQUAL error { yyerror("number expected"); } + | WAIT_WORKER1_TIME EQUAL NUMBER { ksr_wait_worker1_time=$3; } + | WAIT_WORKER1_TIME EQUAL error { yyerror("number expected"); } + | WAIT_WORKER1_USLEEP EQUAL NUMBER { ksr_wait_worker1_usleep=$3; } + | WAIT_WORKER1_USLEEP EQUAL error { yyerror("number expected"); } | SERVER_ID EQUAL NUMBER { server_id=$3; } | SERVER_ID EQUAL error { yyerror("number expected"); } | KEMI DOT ONSEND_ROUTE_CALLBACK EQUAL STRING { diff --git a/src/core/globals.h b/src/core/globals.h index 97895af18c0..f3777d37a83 100644 --- a/src/core/globals.h +++ b/src/core/globals.h @@ -218,6 +218,11 @@ extern str _ksr_xavp_via_fields; extern int ksr_sip_parser_mode; extern int ksr_cfg_print_mode; +extern int ksr_wait_worker1_mode; +extern int ksr_wait_worker1_time; +extern int ksr_wait_worker1_usleep; +extern int *ksr_wait_worker1_done; + extern char *_sr_uri_host_extra_chars; extern unsigned char *_ksr_hname_extra_chars; diff --git a/src/core/resolve.c b/src/core/resolve.c index 193ac528af4..928f6af3c52 100644 --- a/src/core/resolve.c +++ b/src/core/resolve.c @@ -57,6 +57,20 @@ #include "dns_cache.h" #endif +#define KSR_IPADDR_LIST_SIZE 6 +static ip_addr_t _ksr_ipaddr_list[KSR_IPADDR_LIST_SIZE]; +static int _ksr_ipaddr_list_idx = 0; + +static ip_addr_t* get_next_ipaddr_buf(void) +{ + ip_addr_t *ipb; + + ipb = &_ksr_ipaddr_list[_ksr_ipaddr_list_idx]; + _ksr_ipaddr_list_idx = (_ksr_ipaddr_list_idx + 1) % KSR_IPADDR_LIST_SIZE; + + return ipb; +} + /* counters framework */ struct dns_counters_h dns_cnts_h; counter_def_t dns_cnt_defs[] = { @@ -1780,13 +1794,14 @@ int str2ipbuf(str* st, ip_addr_t* ipb) Warning: the result is a pointer to a statically allocated structure */ ip_addr_t* str2ip(str* st) { - static ip_addr_t ip; + ip_addr_t *ipb; - if(str2ipbuf(st, &ip)<0) { + ipb = get_next_ipaddr_buf(); + if(str2ipbuf(st, ipb)<0) { return NULL; } - return &ip; + return ipb; } /* converts a str to an ipv6 address struct stored in ipb @@ -1895,13 +1910,14 @@ int str2ip6buf(str* st, ip_addr_t* ipb) * the ip_addr struct is static, so subsequent calls will destroy its content*/ ip_addr_t* str2ip6(str* st) { - static ip_addr_t ip; + ip_addr_t *ipb; - if(str2ip6buf(st, &ip)<0) { + ipb = get_next_ipaddr_buf(); + if(str2ip6buf(st, ipb)<0) { return NULL; } - return &ip; + return ipb; } /* converts a str to an ipvv/6 address struct stored in ipb @@ -1922,13 +1938,14 @@ int str2ipxbuf(str* st, ip_addr_t* ipb) * the ip_addr struct is static, so subsequent calls will destroy its content*/ struct ip_addr* str2ipx(str* st) { - static ip_addr_t ip; + ip_addr_t *ipb; - if(str2ipbuf(st, &ip)<0) { - if(str2ip6buf(st, &ip)<0) { + ipb = get_next_ipaddr_buf(); + if(str2ipbuf(st, ipb)<0) { + if(str2ip6buf(st, ipb)<0) { return NULL; } } - return &ip; + return ipb; } diff --git a/src/core/str_list.h b/src/core/str_list.h index fe1e53410d9..db3fa12887e 100644 --- a/src/core/str_list.h +++ b/src/core/str_list.h @@ -20,7 +20,7 @@ */ /** - * @file + * @file * @brief Kamailio core :: Simple str type list and helper functions */ @@ -32,15 +32,15 @@ /** * @brief Simple str type list */ -struct str_list { +typedef struct str_list { str s; struct str_list *next; -}; +} str_list_t; /** * @brief Add a new allocated list element to an existing list - * + * * Add a new allocated list element to an existing list, the allocation is done * from the private memory pool * @param s input character diff --git a/src/core/tcp_init.h b/src/core/tcp_init.h index ddabbc5fce4..70a193a0e27 100644 --- a/src/core/tcp_init.h +++ b/src/core/tcp_init.h @@ -13,8 +13,8 @@ * 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 + * 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 */ @@ -54,7 +54,7 @@ struct tcp_child{ int init_tcp(void); void destroy_tcp(void); int tcp_init(struct socket_info* sock_info); -int tcp_init_children(void); +int tcp_init_children(int *woneinit); void tcp_main_loop(void); void tcp_receive_loop(int unix_sock); int tcp_fix_child_sockets(int* fd); diff --git a/src/core/tcp_main.c b/src/core/tcp_main.c index aecf3a88c15..3b460260271 100644 --- a/src/core/tcp_main.c +++ b/src/core/tcp_main.c @@ -5034,16 +5034,16 @@ int tcp_fix_child_sockets(int* fd) /* starts the tcp processes */ -int tcp_init_children() +int tcp_init_children(int *woneinit) { int r, i; int reader_fd_1; /* for comm. with the tcp children read */ pid_t pid; char si_desc[MAX_PT_DESC]; struct socket_info *si; - + /* estimate max fd. no: - * 1 tcp send unix socket/all_proc, + * 1 tcp send unix socket/all_proc, * + 1 udp sock/udp proc + 1 tcp_child sock/tcp child* * + no_listen_tcp */ for(r=0, si=tcp_listen; si; si=si->next, r++); @@ -5051,12 +5051,12 @@ int tcp_init_children() if (! tls_disable) for (si=tls_listen; si; si=si->next, r++); #endif - + register_fds(r+tcp_max_connections+get_max_procs()-1 /* tcp main */); #if 0 tcp_max_fd_no=get_max_procs()*2 +r-1 /* timer */ +3; /* stdin/out/err*/ /* max connections can be temporarily exceeded with estimated_process_count - * - tcp_main (tcpconn_connect called simultaneously in all all the + * - tcp_main (tcpconn_connect called simultaneously in all all the * processes) */ tcp_max_fd_no+=tcp_max_connections+get_max_procs()-1 /* tcp main */; #endif @@ -5095,7 +5095,7 @@ int tcp_init_children() /* create the tcp sock_info structures */ /* copy the sockets --moved to main_loop*/ - + /* fork children & create the socket pairs*/ for(r=0; r0){ - /* parent */ + /* parent - main process */ + if(*woneinit==0 && ksr_wait_worker1_mode!=0) { + int wcount=0; + while(*ksr_wait_worker1_done==0) { + sleep_us(ksr_wait_worker1_usleep); + wcount++; + if(ksr_wait_worker1_time<=wcount*ksr_wait_worker1_usleep) { + LM_ERR("waiting for child one too long - wait time: %d\n", + ksr_wait_worker1_time); + goto error; + } + } + LM_DBG("child one initialized after %d wait steps\n", + wcount); + } + *woneinit = 1; }else{ /* child */ bind_address=0; /* force a SEGFAULT if someone uses a non-init. bind address on tcp */ + if(*woneinit==0) { + if(run_child_one_init_route()<0) + goto error; + } + if(ksr_wait_worker1_mode!=0) { + *ksr_wait_worker1_done = 1; + LM_DBG("child one finished initialization\n"); + } + tcp_receive_loop(reader_fd_1); } } diff --git a/src/main.c b/src/main.c index 6f8b354d223..ee558db13f4 100644 --- a/src/main.c +++ b/src/main.c @@ -156,7 +156,6 @@ #endif - static char help_msg[]= "\ Usage: " NAME " [options]\n\ Options:\n\ @@ -541,6 +540,11 @@ static int *_sr_instance_started = NULL; int ksr_cfg_print_mode = 0; int ksr_atexit_mode = 1; +int ksr_wait_worker1_mode = 0; +int ksr_wait_worker1_time = 4000000; +int ksr_wait_worker1_usleep = 100000; +int *ksr_wait_worker1_done = NULL; + /** * return 1 if all child processes were forked * - note: they might still be in init phase (i.e., child init) @@ -1651,6 +1655,14 @@ int main_loop(void) woneinit = 0; + if(ksr_wait_worker1_mode!=0) { + ksr_wait_worker1_done=(int*)shm_malloc(sizeof(int)); + if(ksr_wait_worker1_done==0) { + SHM_MEM_ERROR; + goto error; + } + *ksr_wait_worker1_done = 0; + } /* udp processes */ for(si=udp_listen; si; si=si->next){ nrprocs = (si->workers>0)?si->workers:children_no; @@ -1689,8 +1701,27 @@ int main_loop(void) if(run_child_one_init_route()<0) goto error; } + if(ksr_wait_worker1_mode!=0) { + *ksr_wait_worker1_done = 1; + LM_DBG("child one finished initialization\n"); + } return udp_rcv_loop(); } + /* main process */ + if(woneinit==0 && ksr_wait_worker1_mode!=0) { + int wcount=0; + while(*ksr_wait_worker1_done==0) { + sleep_us(ksr_wait_worker1_usleep); + wcount++; + if(ksr_wait_worker1_time<=wcount*ksr_wait_worker1_usleep) { + LM_ERR("waiting for child one too long - wait time: %d\n", + ksr_wait_worker1_time); + goto error; + } + } + LM_DBG("child one initialized after %d wait steps\n", + wcount); + } woneinit = 1; } /*parent*/ @@ -1720,8 +1751,32 @@ int main_loop(void) /* child */ bind_address=si; /* shortcut */ + if(woneinit==0) { + if(run_child_one_init_route()<0) + goto error; + } + if(ksr_wait_worker1_mode!=0) { + *ksr_wait_worker1_done = 1; + LM_DBG("child one finished initialization\n"); + } return sctp_core_rcv_loop(); } + /* main process */ + if(woneinit==0 && ksr_wait_worker1_mode!=0) { + int wcount=0; + while(*ksr_wait_worker1_done==0) { + sleep_us(ksr_wait_worker1_usleep); + wcount++; + if(ksr_wait_worker1_time<=wcount*ksr_wait_worker1_usleep) { + LM_ERR("waiting for child one too long - wait time: %d\n", + ksr_wait_worker1_time); + goto error; + } + } + LM_DBG("child one initialized after %d wait steps\n", + wcount); + } + woneinit = 1; } /*parent*/ /*close(sctp_sock)*/; /*if closed=>sendto invalid fd errors?*/ @@ -1777,7 +1832,7 @@ int main_loop(void) #ifdef USE_TCP if (!tcp_disable){ /* start tcp & tls receivers */ - if (tcp_init_children()<0) goto error; + if (tcp_init_children(&woneinit)<0) goto error; /* start tcp+tls main attendant proc */ pid = fork_process(PROC_TCP_MAIN, "tcp main process", 0); if (pid<0){ diff --git a/src/modules/carrierroute/cr_data.c b/src/modules/carrierroute/cr_data.c index 98ad8520629..56610c2cd07 100644 --- a/src/modules/carrierroute/cr_data.c +++ b/src/modules/carrierroute/cr_data.c @@ -464,6 +464,7 @@ int add_route(struct route_data_t * rd, int carrier_id, LM_ERR("could not retrieve domain data\n"); return -1; } + domain_data->sum_prob = domain_data->sum_prob + prob; LM_INFO("found carrier and domain, now adding route\n"); return add_route_to_tree(domain_data->tree, scan_prefix, flags, mask, scan_prefix, max_targets, prob, rewrite_hostpart, diff --git a/src/modules/carrierroute/cr_db.c b/src/modules/carrierroute/cr_db.c index ce9667b38a1..04e3659dfe8 100644 --- a/src/modules/carrierroute/cr_db.c +++ b/src/modules/carrierroute/cr_db.c @@ -31,6 +31,7 @@ #include "carrierroute.h" #include "cr_db.h" #include "cr_carrier.h" +#include "cr_domain.h" #include "config.h" #include #include @@ -265,9 +266,8 @@ int load_user_carrier(str * user, str * domain) { */ int load_route_data_db(struct route_data_t * rd) { db1_res_t * res = NULL; - db1_res_t * prob_res = NULL; db_row_t * row = NULL; - int i, ret; + int i, j, ret; struct carrier_data_t * tmp_carrier_data; static str query_str; str tmp_scan_prefix, tmp_rewrite_host, tmp_rewrite_prefix, @@ -353,7 +353,6 @@ int load_route_data_db(struct route_data_t * rd) { } } int n = 0; - crboolean query_done = crfalse; do { LM_DBG("loading, cycle %d", n++); for (i = 0; i < RES_ROW_N(res); ++i) { @@ -379,6 +378,7 @@ int load_route_data_db(struct route_data_t * rd) { p_tmp_comment = &tmp_comment; } + if (add_route(rd, row->values[COL_CARRIER].val.int_val, row->values[COL_DOMAIN].val.int_val, @@ -398,34 +398,6 @@ int load_route_data_db(struct route_data_t * rd) { p_tmp_comment) == -1) { goto errout; } - if (row->values[COL_PROB].val.double_val == 0 && !query_done) { - int ret_tmp; - char query_tmp[QUERY_LEN]; - str query_tmp_str; - - memset(query_tmp, 0, QUERY_LEN); - ret_tmp = snprintf(query_tmp, QUERY_LEN, "SELECT * FROM %.*s WHERE %.*s=%d and %.*s=%d and %.*s>%d", - carrierroute_table.len, carrierroute_table.s, columns[COL_CARRIER]->len, columns[COL_CARRIER]->s, row->values[COL_CARRIER].val.int_val, - columns[COL_DOMAIN]->len, columns[COL_DOMAIN]->s, row->values[COL_DOMAIN].val.int_val, columns[COL_PROB]->len, columns[COL_PROB]->s, 0); - - if (ret_tmp < 0) { - LM_ERR("error in snprintf while querying prob column"); - goto errout; - } - query_tmp_str.s = query_tmp; - query_tmp_str.len = ret_tmp; - - if (carrierroute_dbf.raw_query(carrierroute_dbh, &query_tmp_str, &prob_res) < 0) { - LM_ERR("Failed to query carrierroute db table based on prob column.\n"); - goto errout; - } - if(RES_ROW_N(prob_res) == 0) { - LM_ERR("Carrierroute db table contains route(s) with only 0 probability.\n"); - query_done = crtrue; - } - carrierroute_dbf.free_result(carrierroute_dbh, prob_res); - prob_res = NULL; - } } if (DB_CAPABILITY(carrierroute_dbf, DB_CAP_FETCH)) { @@ -439,6 +411,16 @@ int load_route_data_db(struct route_data_t * rd) { } } while(RES_ROW_N(res) > 0); + for (i = 0; i < rd->carrier_num; ++i) { + for (j = 0; j < rd->carriers[i]->domain_num; ++j) { + if (rd->carriers[i]->domains[j]->sum_prob == 0.0) { + LM_ERR("All routes with carrier id %d (%.*s) and domain id %d (%.*s) have probability 0.\n", + rd->carriers[i]->id, rd->carriers[i]->name->len, rd->carriers[i]->name->s, + rd->carriers[i]->domains[j]->id, rd->carriers[i]->domains[j]->name->len, rd->carriers[i]->domains[j]->name->s); + } + } + } + carrierroute_dbf.free_result(carrierroute_dbh, res); res = NULL; @@ -493,8 +475,5 @@ int load_route_data_db(struct route_data_t * rd) { if (res) { carrierroute_dbf.free_result(carrierroute_dbh, res); } - if (prob_res) { - carrierroute_dbf.free_result(carrierroute_dbh, prob_res); - } return -1; } diff --git a/src/modules/carrierroute/cr_db.h b/src/modules/carrierroute/cr_db.h index 255b627c543..1f53c9307bd 100644 --- a/src/modules/carrierroute/cr_db.h +++ b/src/modules/carrierroute/cr_db.h @@ -88,9 +88,4 @@ int load_route_data_db (struct route_data_t * rd); int load_user_carrier(str * user, str * domain); -typedef enum { - crfalse = 0, - crtrue = 1 -} crboolean; - #endif diff --git a/src/modules/carrierroute/cr_domain.h b/src/modules/carrierroute/cr_domain.h index 0b3757cbc17..1ef526bce5e 100644 --- a/src/modules/carrierroute/cr_domain.h +++ b/src/modules/carrierroute/cr_domain.h @@ -40,6 +40,7 @@ struct domain_data_t { int id; /*!< the numerical id of the routing tree */ str * name; /*!< the name of the routing tree. This points to the name in domain_map to avoid duplication. */ + double sum_prob; /*!< sums the probabilities of all entries in the normal tree. Used to warn that (carrier, domain) has only routes with probability 0. */ struct dtrie_node_t * tree; /*!< the root node of the routing tree. Payload is of type (struct route_flags *) */ struct dtrie_node_t * failure_tree; /*!< the root node of the failure routing tree. Payload is of type (struct failure_route_rule *) */ }; diff --git a/src/modules/corex/corex_mod.c b/src/modules/corex/corex_mod.c index ee1ddd37f4b..b4e0b67a872 100644 --- a/src/modules/corex/corex_mod.c +++ b/src/modules/corex/corex_mod.c @@ -31,6 +31,7 @@ #include "../../core/pvar.h" #include "../../core/fmsg.h" #include "../../core/kemi.h" +#include "../../core/str_list.h" #include "../../core/events.h" #include "../../core/onsend.h" #include "../../core/dns_cache.h" @@ -83,6 +84,10 @@ static int mod_init(void); static int child_init(int); static void mod_destroy(void); +static str_list_t *corex_dns_cache_list = NULL; + +static int corex_dns_cache_param_add(str *pval); + static int corex_sip_reply_out(sr_event_param_t *evp); static pv_export_t mod_pvs[] = { @@ -186,6 +191,8 @@ struct module_exports exports = { */ static int mod_init(void) { + str_list_t *sit; + if(corex_init_rpc()<0) { LM_ERR("failed to register RPC commands\n"); @@ -204,6 +211,13 @@ static int mod_init(void) return -1; } + for(sit = corex_dns_cache_list; sit!=NULL; sit=sit->next) { + if(corex_dns_cache_param_add(&sit->s)<0) { + LM_ERR("failed to add record: %.*s\n", sit->s.len, sit->s.s); + return -1; + } + } + if((nio_intercept > 0) && (nio_intercept_init() < 0)) { LM_ERR("failed to register network io intercept callback\n"); @@ -336,6 +350,29 @@ int corex_alias_subdomains_param(modparam_t type, void *val) } int corex_dns_cache_param(modparam_t type, void *val) +{ + str_list_t *sit; + + if(val==NULL || ((str*)val)->s==NULL || ((str*)val)->len==0) { + LM_ERR("invalid parameter\n"); + return -1; + } + + sit = (str_list_t*)pkg_mallocxz(sizeof(str_list_t)); + if(sit==NULL) { + PKG_MEM_ERROR; + return -1; + } + sit->s = *((str*)val); + if(corex_dns_cache_list!=NULL) { + sit->next = corex_dns_cache_list; + } + corex_dns_cache_list = sit; + + return 0; +} + +static int corex_dns_cache_param_add(str *pval) { str sval; param_t* params_list = NULL; @@ -347,11 +384,11 @@ int corex_dns_cache_param(modparam_t type, void *val) int dns_ttl = 0; int dns_flags = 0; - if(val==NULL) { + if(pval==NULL) { LM_ERR("invalid parameter\n"); goto error; } - sval = *((str*)val); + sval = *pval; if(sval.s==NULL || sval.len<=0) { LM_ERR("invalid parameter value\n"); goto error; diff --git a/src/modules/cplc/cpl_run.c b/src/modules/cplc/cpl_run.c index 768f6b3d4ae..437e5126672 100644 --- a/src/modules/cplc/cpl_run.c +++ b/src/modules/cplc/cpl_run.c @@ -283,7 +283,7 @@ static inline char *run_lookup( struct cpl_interpreter *intr ) } else { contact = r->contacts; /* skip expired contacts */ - while ((contact) && (contact->expires <= tc)) + while ((contact) && (contact->expires > 0) && (contact->expires <= tc)) contact = contact->next; /* any contacts left? */ if (contact) { diff --git a/src/modules/ctl/binrpc_run.c b/src/modules/ctl/binrpc_run.c index 1ce9800553c..8c786a680ad 100644 --- a/src/modules/ctl/binrpc_run.c +++ b/src/modules/ctl/binrpc_run.c @@ -961,10 +961,11 @@ static int rpc_add(struct binrpc_ctx* ctx, char* fmt, ...) { va_list ap; int err; - char* s; - str* st; + str st; + str* sp; struct rpc_struct_l* rs; - + str null_value = str_init(""); + va_start(ap, fmt); for (;*fmt; fmt++){ switch(*fmt){ @@ -976,15 +977,24 @@ static int rpc_add(struct binrpc_ctx* ctx, char* fmt, ...) if (err<0) goto error_add; break; case 's': /* asciiz */ - s=va_arg(ap, char*); - if (s==0) /* fix null strings */ - s=""; - err=binrpc_addstr(&ctx->out.pkt, s, strlen(s)); + st.s=va_arg(ap, char*); + if (st.s==0) { + /* fix null strings */ + st=null_value; + } else { + st.len=strlen(st.s); + } + err=binrpc_addstr(&ctx->out.pkt, st.s, st.len); if (err<0) goto error_add; break; case 'S': /* str */ - st=va_arg(ap, str*); - err=binrpc_addstr(&ctx->out.pkt, st->s, st->len); + sp=va_arg(ap, str*); + if(sp!=NULL && sp->s!=NULL) { + st=*sp; + } else { + st=null_value; + } + err=binrpc_addstr(&ctx->out.pkt, st.s, st.len); if (err<0) goto error_add; break; case '{': @@ -999,11 +1009,11 @@ static int rpc_add(struct binrpc_ctx* ctx, char* fmt, ...) clist_append(&ctx->out.structs, rs, next, prev); *(va_arg(ap, void**))=rs; break; - case 'f': + case 'f': err=binrpc_adddouble(&ctx->out.pkt, va_arg(ap, double)); if (err<0) goto error_add; break; - default: + default: rpc_fault(ctx, 500, "Internal server error: " "invalid formatting character \'%c\'", *fmt); LOG(L_CRIT, "BUG: binrpc: rpc_add: formatting char \'%c\'" @@ -1067,6 +1077,8 @@ static int rpc_struct_add(struct rpc_struct_l* s, char* fmt, ...) int err; struct binrpc_val avp; struct rpc_struct_l* rs; + str *sp; + str null_value = str_init(""); va_start(ap, fmt); for (;*fmt; fmt++){ @@ -1085,13 +1097,21 @@ static int rpc_struct_add(struct rpc_struct_l* s, char* fmt, ...) case 's': /* asciiz */ avp.type=BINRPC_T_STR; avp.u.strval.s=va_arg(ap, char*); - if (avp.u.strval.s==0) /* fix null strings */ - avp.u.strval.s=""; - avp.u.strval.len=strlen(avp.u.strval.s); + if (avp.u.strval.s==NULL) { + /* fix null strings */ + avp.u.strval=null_value; + } else { + avp.u.strval.len=strlen(avp.u.strval.s); + } break; case 'S': /* str */ avp.type=BINRPC_T_STR; - avp.u.strval=*(va_arg(ap, str*)); + sp = va_arg(ap, str*); + if(sp!=NULL && sp->s!=NULL) { + avp.u.strval=*sp; + } else { + avp.u.strval=null_value; + } break; case '{': case '[': @@ -1144,9 +1164,10 @@ static int rpc_array_add(struct rpc_struct_l* s, char* fmt, ...) { va_list ap; int err; - char* sv; - str* st; + str st; + str *sp; struct rpc_struct_l* rs; + str null_value = str_init(""); va_start(ap, fmt); for (;*fmt; fmt++){ @@ -1159,15 +1180,24 @@ static int rpc_array_add(struct rpc_struct_l* s, char* fmt, ...) if (err<0) goto error_add; break; case 's': /* asciiz */ - sv=va_arg(ap, char*); - if (sv==0) /* fix null strings */ - sv=""; - err=binrpc_addstr(&s->pkt, sv, strlen(sv)); + st.s=va_arg(ap, char*); + if (st.s==0) { + /* fix null strings */ + st=null_value; + } else { + st.len = strlen(st.s); + } + err=binrpc_addstr(&s->pkt, st.s, st.len); if (err<0) goto error_add; break; case 'S': /* str */ - st=va_arg(ap, str*); - err=binrpc_addstr(&s->pkt, st->s, st->len); + sp=va_arg(ap, str*); + if(sp!=NULL && sp->s!=NULL) { + st=*sp; + } else { + st=null_value; + } + err=binrpc_addstr(&s->pkt, st.s, st.len); if (err<0) goto error_add; break; case '{': diff --git a/src/modules/ctl/fifo_server.c b/src/modules/ctl/fifo_server.c index e057148059a..b40b079a1d5 100644 --- a/src/modules/ctl/fifo_server.c +++ b/src/modules/ctl/fifo_server.c @@ -1092,6 +1092,7 @@ static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap) str str_val; str* sp; char buf[256]; + str null_value = str_init(""); switch(fmt) { case 'd': @@ -1104,7 +1105,7 @@ static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap) goto err; } break; - + case 'f': str_val.s = buf; str_val.len = snprintf(buf, 256, "%f", va_arg(*ap, double)); @@ -1120,39 +1121,48 @@ static int print_value(rpc_ctx_t* ctx, char fmt, va_list* ap) goto err; } break; - + case 'b': str_val.len = 1; str_val.s = ((va_arg(*ap, int) == 0) ? "0" : "1"); l = new_chunk(&str_val); if (!l) { - rpc_fault(ctx, 500, "Internal Server Error, line %d", + rpc_fault(ctx, 500, "Internal Server Error, line %d", ctx->line_no); goto err; } break; - + case 's': str_val.s = va_arg(*ap, char*); - str_val.len = strlen(str_val.s); + if(str_val.s!=NULL) { + str_val.len = strlen(str_val.s); + } else { + str_val = null_value; + } l = new_chunk_escape(&str_val, 0); if (!l) { - rpc_fault(ctx, 500, "Internal Server Error, line %d", + rpc_fault(ctx, 500, "Internal Server Error, line %d", ctx->line_no); goto err; } break; - + case 'S': sp = va_arg(*ap, str*); - l = new_chunk_escape(sp, 0); + if(sp!=NULL && sp->s!=NULL) { + str_val = *sp; + } else { + str_val = null_value; + } + l = new_chunk_escape(&str_val, 0); if (!l) { - rpc_fault(ctx, 500, "Internal Server Error, line %d", + rpc_fault(ctx, 500, "Internal Server Error, line %d", ctx->line_no); goto err; } break; - + default: rpc_fault(ctx, 500, "Bug In SER (Invalid formatting character %c)", fmt); ERR("Invalid formatting character\n"); @@ -1368,7 +1378,7 @@ static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...) case 'd': /* Integer */ if (!line.len) { if(nofault==0) - rpc_fault(ctx, 400, "Invalid parameter value on line %d", + rpc_fault(ctx, 400, "Invalid parameter value on line %d", ctx->line_no); goto error; } @@ -1379,14 +1389,14 @@ static int rpc_scan(rpc_ctx_t* ctx, char* fmt, ...) case 'f': /* double */ if (!line.len) { if(nofault==0) - rpc_fault(ctx, 400, "Invalid parameter value on line %d", + rpc_fault(ctx, 400, "Invalid parameter value on line %d", ctx->line_no); goto error; } double_ptr = va_arg(ap, double*); *double_ptr = strtod(line.s, 0); break; - + case 's': /* zero terminated string */ case 'S': /* str structure */ l = new_chunk_unescape(&line); @@ -1444,6 +1454,7 @@ static int rpc_struct_add(struct text_chunk* s, char* fmt, ...) va_list ap; struct text_chunk* m, *c; rpc_ctx_t* ctx; + str null_value = str_init(""); ctx=(rpc_ctx_t*)s->ctx; va_start(ap, fmt); @@ -1457,7 +1468,7 @@ static int rpc_struct_add(struct text_chunk* s, char* fmt, ...) goto err; } m->flags |= CHUNK_MEMBER_NAME; - + if(*fmt=='{' || *fmt=='[') { void_ptr = va_arg(ap, void**); m->ctx=ctx; @@ -1490,13 +1501,22 @@ static int rpc_struct_add(struct text_chunk* s, char* fmt, ...) case 's': st.s = va_arg(ap, char*); - st.len = strlen(st.s); + if(st.s==NULL) { + st = null_value; + } else { + st.len = strlen(st.s); + } c = new_chunk_escape(&st, 1); break; case 'S': sp = va_arg(ap, str*); - c = new_chunk_escape(sp, 1); + if(sp!=NULL && sp->s!=NULL) { + st = *sp; + } else { + st = null_value; + } + c = new_chunk_escape(&st, 1); break; default: @@ -1576,7 +1596,7 @@ static int rpc_struct_scan(struct rpc_struct* s, char* fmt, ...) va_end(ap); return read; } - + switch(*fmt) { case 'b': /* Bool */ case 't': /* Date and time */ @@ -1599,7 +1619,7 @@ static int rpc_struct_scan(struct rpc_struct* s, char* fmt, ...) /* String in text_chunk is always zero terminated */ *double_ptr = strtod(val->s.s, 0); break; - + case 's': /* zero terminated string */ char_ptr = va_arg(ap, char**); /* String in text_chunk is always zero terminated */ @@ -1608,7 +1628,6 @@ static int rpc_struct_scan(struct rpc_struct* s, char* fmt, ...) case 'S': /* str structure */ str_ptr = va_arg(ap, str*); - str_ptr->len = strlen(str_ptr->s); *str_ptr = val->s; break; default: diff --git a/src/modules/ctl/io_listener.c b/src/modules/ctl/io_listener.c index 93ce89c9483..ac8f64fc90a 100644 --- a/src/modules/ctl/io_listener.c +++ b/src/modules/ctl/io_listener.c @@ -495,7 +495,7 @@ static int handle_stream_read(struct stream_connection* s_c, int idx) DBG("handle_stream read: eof on %s\n", s_c->parent->name); goto close_connection; } - LM_INFO("bytes read: %d\n", bytes_read); + LM_DBG("bytes read: %d\n", bytes_read); r->end+=bytes_read; if (bytes_read && (bytes_readbytes_to_go)){ r->bytes_to_go-=bytes_read; @@ -515,7 +515,7 @@ static int handle_stream_read(struct stream_connection* s_c, int idx) /* error while processing the packet => close the connection */ goto close_connection; } - LM_INFO("bytes processed: %d\n", bytes_processed); + LM_DBG("bytes processed: %d\n", bytes_processed); r->proc+=bytes_processed; r->bytes_to_go=bytes_needed; if (bytes_needed>0){ diff --git a/src/modules/dialplan/dp_db.c b/src/modules/dialplan/dp_db.c index f50c0d0df0f..4275f0260d7 100644 --- a/src/modules/dialplan/dp_db.c +++ b/src/modules/dialplan/dp_db.c @@ -13,8 +13,8 @@ * 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 + * 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 * */ @@ -46,7 +46,7 @@ str match_exp_column= str_init(MATCH_EXP_COL); str match_len_column= str_init(MATCH_LEN_COL); str subst_exp_column= str_init(SUBST_EXP_COL); str repl_exp_column = str_init(REPL_EXP_COL); -str attrs_column = str_init(ATTRS_COL); +str attrs_column = str_init(ATTRS_COL); extern int dp_fetch_rows; extern int dp_match_dynamic; @@ -75,8 +75,9 @@ void list_rule(dpl_node_t * ); void list_hash(int h_index); -dpl_id_p* rules_hash = NULL; -int * crt_idx, *next_idx; +static dpl_id_p* dp_rules_hash = NULL; +static int *dp_crt_idx = NULL; +static int *dp_next_idx = NULL; /** @@ -197,21 +198,21 @@ int init_data(void) { int *p; - rules_hash = (dpl_id_p *)shm_malloc(2*sizeof(dpl_id_p)); - if(!rules_hash) { + dp_rules_hash = (dpl_id_p *)shm_malloc(2*sizeof(dpl_id_p)); + if(!dp_rules_hash) { LM_ERR("out of shm memory\n"); return -1; } - rules_hash[0] = rules_hash[1] = 0; + dp_rules_hash[0] = dp_rules_hash[1] = 0; p = (int *)shm_malloc(2*sizeof(int)); if(!p){ LM_ERR("out of shm memory\n"); return -1; } - crt_idx = p; - next_idx = p+1; - *crt_idx = *next_idx = 0; + dp_crt_idx = p; + dp_next_idx = p+1; + *dp_crt_idx = *dp_next_idx = 0; LM_DBG("trying to initialize data from db\n"); if(init_db_data() != 0) @@ -223,15 +224,15 @@ int init_data(void) void destroy_data(void) { - if(rules_hash){ + if(dp_rules_hash){ destroy_hash(0); destroy_hash(1); - shm_free(rules_hash); - rules_hash = 0; + shm_free(dp_rules_hash); + dp_rules_hash = 0; } - if(crt_idx) - shm_free(crt_idx); + if(dp_crt_idx) + shm_free(dp_crt_idx); } @@ -252,7 +253,7 @@ int dp_load_db(void) dpl_node_t *rule; LM_DBG("init\n"); - if( (*crt_idx) != (*next_idx)){ + if( (*dp_crt_idx) != (*dp_next_idx)){ LM_WARN("a load command already generated, aborting reload...\n"); return 0; } @@ -263,7 +264,7 @@ int dp_load_db(void) } if (DB_CAPABILITY(dp_dbf, DB_CAP_FETCH)) { - if(dp_dbf.query(dp_db_handle,0,0,0,query_cols, 0, + if(dp_dbf.query(dp_db_handle,0,0,0,query_cols, 0, DP_TABLE_COL_NO, order, 0) < 0){ LM_ERR("failed to query database!\n"); return -1; @@ -285,8 +286,8 @@ int dp_load_db(void) nr_rows = RES_ROW_N(res); - *next_idx = ((*crt_idx) == 0)? 1:0; - destroy_hash(*next_idx); + *dp_next_idx = ((*dp_crt_idx) == 0)? 1:0; + destroy_hash(*dp_next_idx); if(nr_rows == 0){ LM_WARN("no data in the db\n"); @@ -302,7 +303,7 @@ int dp_load_db(void) if((rule = build_rule(values)) ==0 ) goto err2; - if(add_rule2hash(rule , *next_idx) != 0) + if(add_rule2hash(rule, *dp_next_idx) != 0) goto err2; } @@ -321,16 +322,16 @@ int dp_load_db(void) end: /*update data*/ - *crt_idx = *next_idx; - list_hash(*crt_idx); + *dp_crt_idx = *dp_next_idx; + list_hash(*dp_crt_idx); dp_dbf.free_result(dp_db_handle, res); return 0; err2: if(rule) destroy_rule(rule); - destroy_hash(*next_idx); + destroy_hash(*dp_next_idx); dp_dbf.free_result(dp_db_handle, res); - *next_idx = *crt_idx; + *dp_next_idx = *dp_crt_idx; return -1; } @@ -550,7 +551,7 @@ int add_rule2hash(dpl_node_t * rule, int h_index) dpl_index_p indexp, last_indexp, new_indexp; int new_id; - if(!rules_hash){ + if(!dp_rules_hash){ LM_ERR("data not allocated\n"); return -1; } @@ -558,7 +559,7 @@ int add_rule2hash(dpl_node_t * rule, int h_index) new_id = 0; /*search for the corresponding dpl_id*/ - for(crt_idp = last_idp =rules_hash[h_index]; crt_idp!= NULL; + for(crt_idp = last_idp =dp_rules_hash[h_index]; crt_idp!= NULL; last_idp = crt_idp, crt_idp = crt_idp->next) if(crt_idp->dp_id == rule->dpid) break; @@ -577,7 +578,7 @@ int add_rule2hash(dpl_node_t * rule, int h_index) } /*search for the corresponding dpl_index*/ - for(indexp = last_indexp =crt_idp->first_index; indexp!=NULL; + for(indexp = last_indexp =crt_idp->first_index; indexp!=NULL; last_indexp = indexp, indexp = indexp->next){ if(indexp->len == rule->matchlen) goto add_rule; @@ -617,8 +618,8 @@ int add_rule2hash(dpl_node_t * rule, int h_index) indexp->last_rule = rule; if(new_id){ - crt_idp->next = rules_hash[h_index]; - rules_hash[h_index] = crt_idp; + crt_idp->next = dp_rules_hash[h_index]; + dp_rules_hash[h_index] = crt_idp; } LM_DBG("added the rule id %i index %i pr %i next %p to the " "index with %i len\n", rule->dpid, rule->matchlen, @@ -639,10 +640,10 @@ void destroy_hash(int index) dpl_index_p indexp; dpl_node_p rulep; - if(!rules_hash[index]) + if(!dp_rules_hash[index]) return; - for(crt_idp = rules_hash[index]; crt_idp != NULL;){ + for(crt_idp = dp_rules_hash[index]; crt_idp != NULL;){ for(indexp = crt_idp->first_index; indexp != NULL;){ @@ -662,13 +663,13 @@ void destroy_hash(int index) } - rules_hash[index] = crt_idp->next; + dp_rules_hash[index] = crt_idp->next; shm_free(crt_idp); crt_idp = 0; - crt_idp = rules_hash[index]; + crt_idp = dp_rules_hash[index]; } - rules_hash[index] = 0; + dp_rules_hash[index] = 0; } @@ -677,7 +678,7 @@ void destroy_rule(dpl_node_t * rule){ if(!rule) return; - LM_DBG("destroying rule with priority %i\n", + LM_DBG("destroying rule with priority %i\n", rule->pr); if(rule->match_comp) @@ -708,10 +709,10 @@ dpl_id_p select_dpid(int id) { dpl_id_p idp; - if(!rules_hash || !crt_idx) + if(!dp_rules_hash || !dp_crt_idx) return NULL; - for(idp = rules_hash[*crt_idx]; idp!=NULL; idp = idp->next) + for(idp = dp_rules_hash[*dp_crt_idx]; idp!=NULL; idp = idp->next) if(idp->dp_id == id) return idp; @@ -727,10 +728,10 @@ void list_hash(int h_index) dpl_node_p rulep; - if(!rules_hash[h_index]) + if(!dp_rules_hash[h_index]) return; - for(crt_idp=rules_hash[h_index]; crt_idp!=NULL; crt_idp = crt_idp->next){ + for(crt_idp=dp_rules_hash[h_index]; crt_idp!=NULL; crt_idp = crt_idp->next){ LM_DBG("DPID: %i, pointer %p\n", crt_idp->dp_id, crt_idp); for(indexp=crt_idp->first_index; indexp!=NULL;indexp= indexp->next){ LM_DBG("INDEX LEN: %i\n", indexp->len); diff --git a/src/modules/dispatcher/dispatch.c b/src/modules/dispatcher/dispatch.c index 62d416620ea..80944e4b053 100644 --- a/src/modules/dispatcher/dispatch.c +++ b/src/modules/dispatcher/dispatch.c @@ -113,13 +113,13 @@ extern int ds_load_mode; static db_func_t ds_dbf; static db1_con_t *ds_db_handle = NULL; -ds_set_t **ds_lists = NULL; +static ds_set_t **ds_lists = NULL; -int *ds_list_nr = NULL; -int *crt_idx = NULL; -int *next_idx = NULL; +static int *ds_list_nr = NULL; +static int *ds_crt_idx = NULL; +static int *ds_next_idx = NULL; -#define _ds_list (ds_lists[*crt_idx]) +#define _ds_list (ds_lists[*ds_crt_idx]) #define _ds_list_nr (*ds_list_nr) static void ds_run_route(struct sip_msg *msg, str *uri, char *route, @@ -267,10 +267,10 @@ int ds_init_data(void) } memset(p, 0, 3 * sizeof(int)); - crt_idx = p; - next_idx = p + 1; + ds_crt_idx = p; + ds_next_idx = p + 1; ds_list_nr = p + 2; - *crt_idx = *next_idx = 0; + *ds_crt_idx = *ds_next_idx = 0; return 0; } @@ -784,7 +784,7 @@ int ds_load_list(char *lfile) str uri; str attrs; - if((*crt_idx) != (*next_idx)) { + if((*ds_crt_idx) != (*ds_next_idx)) { LM_WARN("load command already generated, aborting reload...\n"); return 0; } @@ -802,8 +802,8 @@ int ds_load_list(char *lfile) id = setn = flags = priority = 0; - *next_idx = (*crt_idx + 1) % 2; - ds_avl_destroy(&ds_lists[*next_idx]); + *ds_next_idx = (*ds_crt_idx + 1) % 2; + ds_avl_destroy(&ds_lists[*ds_next_idx]); p = fgets(line, 1024, f); while(p) { @@ -878,7 +878,7 @@ int ds_load_list(char *lfile) attrs.len = p - attrs.s; add_destination: - if(add_dest2list(id, uri, flags, priority, &attrs, *next_idx, &setn, 0) + if(add_dest2list(id, uri, flags, priority, &attrs, *ds_next_idx, &setn, 0) != 0) { LM_WARN("unable to add destination %.*s to set %d -- skipping\n", uri.len, uri.s, id); @@ -890,7 +890,7 @@ int ds_load_list(char *lfile) p = fgets(line, 1024, f); } - if(reindex_dests(ds_lists[*next_idx]) != 0) { + if(reindex_dests(ds_lists[*ds_next_idx]) != 0) { LM_ERR("error on reindex\n"); goto error; } @@ -899,7 +899,7 @@ int ds_load_list(char *lfile) f = NULL; /* Update list - should it be sync'ed? */ _ds_list_nr = setn; - *crt_idx = *next_idx; + *ds_crt_idx = *ds_next_idx; LM_DBG("found [%d] dest sets\n", _ds_list_nr); @@ -909,8 +909,8 @@ int ds_load_list(char *lfile) error: if(f != NULL) fclose(f); - ds_avl_destroy(&ds_lists[*next_idx]); - *next_idx = *crt_idx; + ds_avl_destroy(&ds_lists[*ds_next_idx]); + *ds_next_idx = *ds_crt_idx; return -1; } @@ -1048,7 +1048,7 @@ int ds_load_db(void) } } - if((*crt_idx) != (*next_idx)) { + if((*ds_crt_idx) != (*ds_next_idx)) { LM_WARN("load command already generated, aborting reload...\n"); return 0; } @@ -1079,8 +1079,8 @@ int ds_load_db(void) } setn = 0; - *next_idx = (*crt_idx + 1) % 2; - ds_avl_destroy(&ds_lists[*next_idx]); + *ds_next_idx = (*ds_crt_idx + 1) % 2; + ds_avl_destroy(&ds_lists[*ds_next_idx]); for(i = 0; i < nr_rows; i++) { values = ROW_VALUES(rows + i); @@ -1128,7 +1128,7 @@ int ds_load_db(void) } } LM_DBG("attributes string: [%.*s]\n", attrs.len, (attrs.s)?attrs.s:""); - if(add_dest2list(id, uri, flags, priority, &attrs, *next_idx, &setn, 0) + if(add_dest2list(id, uri, flags, priority, &attrs, *ds_next_idx, &setn, 0) != 0) { dest_errs++; LM_WARN("unable to add destination %.*s to set %d -- skipping\n", @@ -1138,7 +1138,7 @@ int ds_load_db(void) } } } - if(reindex_dests(ds_lists[*next_idx]) != 0) { + if(reindex_dests(ds_lists[*ds_next_idx]) != 0) { LM_ERR("error on reindex\n"); goto err2; } @@ -1147,7 +1147,7 @@ int ds_load_db(void) /* update data - should it be sync'ed? */ _ds_list_nr = setn; - *crt_idx = *next_idx; + *ds_crt_idx = *ds_next_idx; LM_DBG("found [%d] dest sets\n", _ds_list_nr); @@ -1158,9 +1158,9 @@ int ds_load_db(void) return 0; err2: - ds_avl_destroy(&ds_lists[*next_idx]); + ds_avl_destroy(&ds_lists[*ds_next_idx]); ds_dbf.free_result(ds_db_handle, res); - *next_idx = *crt_idx; + *ds_next_idx = *ds_crt_idx; return -1; } @@ -1174,8 +1174,8 @@ int ds_destroy_list(void) shm_free(ds_lists); } - if(crt_idx) - shm_free(crt_idx); + if(ds_crt_idx) + shm_free(ds_crt_idx); return 0; } @@ -1615,7 +1615,7 @@ int ds_load_replace(struct sip_msg *msg, str *duid) } set = it->dset; /* get the index of the set */ - if(ds_get_index(set, *crt_idx, &idx) != 0) { + if(ds_get_index(set, *ds_crt_idx, &idx) != 0) { ds_unlock_cell(_dsht_load, &msg->callid->body); LM_ERR("destination set [%d] not found\n", set); return -1; @@ -1676,7 +1676,7 @@ int ds_load_remove_byid(int set, str *duid) int i; /* get the index of the set */ - if(ds_get_index(set, *crt_idx, &idx) != 0) { + if(ds_get_index(set, *ds_crt_idx, &idx) != 0) { LM_ERR("destination set [%d] not found\n", set); return -1; } @@ -2276,7 +2276,7 @@ int ds_manage_routes(sip_msg_t *msg, ds_select_state_t *rstate) /* get the index of the set */ - if(ds_get_index(rstate->setid, *crt_idx, &idx) != 0) { + if(ds_get_index(rstate->setid, *ds_crt_idx, &idx) != 0) { LM_ERR("destination set [%d] not found\n", rstate->setid); return -1; } @@ -2579,7 +2579,7 @@ void ds_add_dest_cb(ds_set_t *node, int i, void *arg) int setn; if(add_dest2list(node->id, node->dlist[i].uri, node->dlist[i].flags, - node->dlist[i].priority, &node->dlist[i].attrs.body, *next_idx, + node->dlist[i].priority, &node->dlist[i].attrs.body, *ds_next_idx, &setn, node->dlist[i].dload) != 0) { LM_WARN("failed to add destination in group %d - %.*s\n", node->id, node->dlist[i].uri.len, node->dlist[i].uri.s); @@ -2595,35 +2595,35 @@ int ds_add_dst(int group, str *address, int flags, str *attrs) setn = _ds_list_nr; priority = 0; - *next_idx = (*crt_idx + 1) % 2; - ds_avl_destroy(&ds_lists[*next_idx]); + *ds_next_idx = (*ds_crt_idx + 1) % 2; + ds_avl_destroy(&ds_lists[*ds_next_idx]); // add all existing destinations ds_iter_set(_ds_list, &ds_add_dest_cb, NULL); // add new destination if(add_dest2list(group, *address, flags, priority, attrs, - *next_idx, &setn, 0) != 0) { + *ds_next_idx, &setn, 0) != 0) { LM_WARN("unable to add destination %.*s to set %d", address->len, address->s, group); if(ds_load_mode==1) { goto error; } } - if(reindex_dests(ds_lists[*next_idx]) != 0) { + if(reindex_dests(ds_lists[*ds_next_idx]) != 0) { LM_ERR("error on reindex\n"); goto error; } _ds_list_nr = setn; - *crt_idx = *next_idx; + *ds_crt_idx = *ds_next_idx; ds_log_sets(); return 0; error: - ds_avl_destroy(&ds_lists[*next_idx]); - *next_idx = *crt_idx; + ds_avl_destroy(&ds_lists[*ds_next_idx]); + *ds_next_idx = *ds_crt_idx; return -1; } @@ -2637,7 +2637,7 @@ void ds_filter_dest_cb(ds_set_t *node, int i, void *arg) return; if(add_dest2list(node->id, node->dlist[i].uri, node->dlist[i].flags, - node->dlist[i].priority, &node->dlist[i].attrs.body, *next_idx, + node->dlist[i].priority, &node->dlist[i].attrs.body, *ds_next_idx, filter_arg->setn, node->dlist[i].dload) != 0) { LM_WARN("failed to add destination in group %d - %.*s\n", node->id, node->dlist[i].uri.len, node->dlist[i].uri.s); @@ -2659,26 +2659,26 @@ int ds_remove_dst(int group, str *address) filter_arg.dest = dp; filter_arg.setn = &setn; - *next_idx = (*crt_idx + 1) % 2; - ds_avl_destroy(&ds_lists[*next_idx]); + *ds_next_idx = (*ds_crt_idx + 1) % 2; + ds_avl_destroy(&ds_lists[*ds_next_idx]); // add existing destinations except destination that matches group & address ds_iter_set(_ds_list, &ds_filter_dest_cb, &filter_arg); - if(reindex_dests(ds_lists[*next_idx]) != 0) { + if(reindex_dests(ds_lists[*ds_next_idx]) != 0) { LM_ERR("error on reindex\n"); goto error; } _ds_list_nr = setn; - *crt_idx = *next_idx; + *ds_crt_idx = *ds_next_idx; ds_log_sets(); return 0; error: - ds_avl_destroy(&ds_lists[*next_idx]); - *next_idx = *crt_idx; + ds_avl_destroy(&ds_lists[*ds_next_idx]); + *ds_next_idx = *ds_crt_idx; return -1; } @@ -2833,7 +2833,7 @@ int ds_update_latency(int group, str *address, int code) } /* get the index of the set */ - if(ds_get_index(group, *crt_idx, &idx) != 0) { + if(ds_get_index(group, *ds_crt_idx, &idx) != 0) { LM_ERR("destination set [%d] not found\n", group); return -1; } @@ -2927,7 +2927,7 @@ int ds_get_state(int group, str *address) } /* get the index of the set */ - if(ds_get_index(group, *crt_idx, &idx) != 0) { + if(ds_get_index(group, *ds_crt_idx, &idx) != 0) { LM_ERR("destination set [%d] not found\n", group); return -1; } @@ -2961,7 +2961,7 @@ int ds_update_state(sip_msg_t *msg, int group, str *address, int state, } /* get the index of the set */ - if(ds_get_index(group, *crt_idx, &idx) != 0) { + if(ds_get_index(group, *ds_crt_idx, &idx) != 0) { LM_ERR("destination set [%d] not found\n", group); return -1; } @@ -3162,7 +3162,7 @@ int ds_reinit_state(int group, str *address, int state) } /* get the index of the set */ - if(ds_get_index(group, *crt_idx, &idx) != 0) { + if(ds_get_index(group, *ds_crt_idx, &idx) != 0) { LM_ERR("destination set [%d] not found\n", group); return -1; } @@ -3203,7 +3203,7 @@ int ds_reinit_duid_state(int group, str *vduid, int state) } /* get the index of the set */ - if(ds_get_index(group, *crt_idx, &idx) != 0) { + if(ds_get_index(group, *ds_crt_idx, &idx) != 0) { LM_ERR("destination set [%d] not found\n", group); return -1; } @@ -3244,7 +3244,7 @@ int ds_reinit_state_all(int group, int state) } /* get the index of the set */ - if(ds_get_index(group, *crt_idx, &idx) != 0) { + if(ds_get_index(group, *ds_crt_idx, &idx) != 0) { LM_ERR("destination set [%d] not found\n", group); return -1; } diff --git a/src/modules/dispatcher/dispatcher.c b/src/modules/dispatcher/dispatcher.c index 18ece721f37..07b2e72bfa8 100644 --- a/src/modules/dispatcher/dispatcher.c +++ b/src/modules/dispatcher/dispatcher.c @@ -1635,10 +1635,10 @@ static void dispatcher_rpc_list(rpc_t *rpc, void *ctx) void *th; void *ih; - ds_set_t *ds_list = ds_get_list(); - int ds_list_nr = ds_get_list_nr(); + ds_set_t *dslist = ds_get_list(); + int dslistnr = ds_get_list_nr(); - if(ds_list == NULL || ds_list_nr <= 0) { + if(dslist == NULL || dslistnr <= 0) { LM_DBG("no destination sets\n"); rpc->fault(ctx, 500, "No Destination Sets"); return; @@ -1649,12 +1649,12 @@ static void dispatcher_rpc_list(rpc_t *rpc, void *ctx) rpc->fault(ctx, 500, "Internal error root reply"); return; } - if(rpc->struct_add(th, "d[", "NRSETS", ds_list_nr, "RECORDS", &ih) < 0) { + if(rpc->struct_add(th, "d[", "NRSETS", dslistnr, "RECORDS", &ih) < 0) { rpc->fault(ctx, 500, "Internal error sets structure"); return; } - ds_rpc_print_set(ds_list, rpc, ctx, ih); + ds_rpc_print_set(dslist, rpc, ctx, ih); return; } diff --git a/src/modules/dmq/README b/src/modules/dmq/README index 2ea40a26423..085fe3c9dfd 100644 --- a/src/modules/dmq/README +++ b/src/modules/dmq/README @@ -216,6 +216,8 @@ sip:192.168.40.17:5060;status=active ... modparam("dmq", "server_address", "sip:10.0.0.20:5060") ... +modparam("dmq", "server_address", "sip:10.0.0.20:5061;transport=tls") +... 3.2. notification_address(str) @@ -228,6 +230,8 @@ modparam("dmq", "server_address", "sip:10.0.0.20:5060") ... modparam("dmq", "notification_address", "sip:10.0.0.21:5060") ... +modparam("dmq", "notification_address", "sip:10.0.0.21:5061;transport=tls") +... 3.3. notification_channel(str) diff --git a/src/modules/dmq/dmq.c b/src/modules/dmq/dmq.c index 337d75c1b9a..06e091038a1 100644 --- a/src/modules/dmq/dmq.c +++ b/src/modules/dmq/dmq.c @@ -37,6 +37,7 @@ #include "../../core/pt.h" #include "../../core/hashes.h" #include "../../core/mod_fix.h" +#include "../../core/cfg/cfg_struct.h" #include "../../core/rpc_lookup.h" #include "../../core/kemi.h" @@ -136,19 +137,29 @@ struct module_exports exports = { static int make_socket_str_from_uri(struct sip_uri *uri, str *socket) { + str sproto = STR_NULL; + if(!uri->host.s || !uri->host.len) { LM_ERR("no host in uri\n"); return -1; } - socket->len = uri->host.len + uri->port.len + 6; + socket->len = uri->host.len + uri->port.len + 7 /*sctp + : + : \0*/; socket->s = pkg_malloc(socket->len); if(socket->s == NULL) { LM_ERR("no more pkg\n"); return -1; } - memcpy(socket->s, "udp:", 4); - socket->len = 4; + + if(get_valid_proto_string(uri->proto, 0, 0, &sproto)<0) { + LM_WARN("unknown transport protocol - fall back to udp\n"); + sproto.s = "udp"; + sproto.len = 3; + } + + memcpy(socket->s, sproto.s, sproto.len); + socket->s[sproto.len] = ':'; + socket->len = sproto.len + 1; memcpy(socket->s + socket->len, uri->host.s, uri->host.len); socket->len += uri->host.len; @@ -275,6 +286,11 @@ static int child_init(int rank) { int i, newpid; + if(rank == PROC_TCP_MAIN) { + /* do nothing for the tcp main process */ + return 0; + } + if(rank == PROC_INIT) { for(i = 0; i < dmq_num_workers; i++) { if (init_worker(&dmq_workers[i]) < 0) { @@ -289,17 +305,22 @@ static int child_init(int rank) /* fork worker processes */ for(i = 0; i < dmq_num_workers; i++) { LM_DBG("starting worker process %d\n", i); - newpid = fork_process(PROC_RPC, "DMQ WORKER", 0); + newpid = fork_process(PROC_RPC, "DMQ WORKER", 1); if(newpid < 0) { LM_ERR("failed to fork worker process %d\n", i); return -1; } else if(newpid == 0) { + if (cfg_child_init()) return -1; /* child - this will loop forever */ worker_loop(i); } else { dmq_workers[i].pid = newpid; } } + return 0; + } + + if(rank == PROC_SIPINIT) { /* notification_node - the node from which the Kamailio instance * gets the server list on startup. * the address is given as a module parameter in dmq_notification_address @@ -314,11 +335,6 @@ static int child_init(int rank) STR_FMT(&dmq_notification_address)); } } - return 0; - } - if(rank == PROC_TCP_MAIN) { - /* do nothing for the main process */ - return 0; } dmq_pid = my_pid(); @@ -355,10 +371,11 @@ static void dmq_rpc_list_nodes(rpc_t *rpc, void *c) ip_addr2sbuf(&cur->ip_address, ip, IP6_MAX_STR_SIZE); if(rpc->add(c, "{", &h) < 0) goto error; - if(rpc->struct_add(h, "SSsSdd", "host", &cur->uri.host, "port", - &cur->uri.port, "resolved_ip", ip, "status", - dmq_get_status_str(cur->status), "last_notification", - cur->last_notification, "local", cur->local) + if(rpc->struct_add(h, "SSssSdd", "host", &cur->uri.host, "port", + &cur->uri.port, "proto", get_proto_name(cur->uri.proto), + "resolved_ip", ip, "status", dmq_get_status_str(cur->status), + "last_notification", cur->last_notification, + "local", cur->local) < 0) goto error; cur = cur->next; diff --git a/src/modules/dmq/dmq_funcs.c b/src/modules/dmq/dmq_funcs.c index f9fb5c78ea0..af3db27d6ec 100644 --- a/src/modules/dmq/dmq_funcs.c +++ b/src/modules/dmq/dmq_funcs.c @@ -74,8 +74,9 @@ void dmq_tm_callback(struct cell *t, int type, struct tmcb_params *ps) int build_uri_str(str *username, struct sip_uri *uri, str *from) { - /* sip:user@host:port */ + /* sip:user@host:port;transport=abcd */ int from_len; + str sproto = STR_NULL; if(!uri->host.s || !uri->host.len) { LM_ERR("no host in uri\n"); @@ -86,7 +87,8 @@ int build_uri_str(str *username, struct sip_uri *uri, str *from) return -1; } - from_len = username->len + uri->host.len + uri->port.len + 10; + from_len = username->len + uri->host.len + uri->port.len + 12 + + TRANSPORT_PARAM_LEN; from->s = pkg_malloc(from_len); if(from->s == NULL) { LM_ERR("no more pkg\n"); @@ -112,6 +114,21 @@ int build_uri_str(str *username, struct sip_uri *uri, str *from) memcpy(from->s + from->len, uri->port.s, uri->port.len); from->len += uri->port.len; } + + if(uri->proto!=PROTO_NONE && uri->proto!=PROTO_UDP + && uri->proto!=PROTO_OTHER) { + if(get_valid_proto_string(uri->proto, 1, 0, &sproto)<0) { + LM_WARN("unknown transport protocol - fall back to udp\n"); + sproto.s = "udp"; + sproto.len = 3; + } + memcpy(from->s + from->len, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); + from->len += TRANSPORT_PARAM_LEN; + memcpy(from->s + from->len, sproto.s, sproto.len); + from->len += sproto.len; + } + from->s[from->len] = '\0'; + return 0; } diff --git a/src/modules/dmq/dmqnode.c b/src/modules/dmq/dmqnode.c index 2eb3b1c3739..7e6ef93bdf9 100644 --- a/src/modules/dmq/dmqnode.c +++ b/src/modules/dmq/dmqnode.c @@ -87,7 +87,8 @@ int cmp_dmq_node(dmq_node_t *node, dmq_node_t *cmpnode) return -1; } return STR_EQ(node->uri.host, cmpnode->uri.host) - && STR_EQ(node->uri.port, cmpnode->uri.port); + && STR_EQ(node->uri.port, cmpnode->uri.port) + && (node->uri.proto == cmpnode->uri.proto); } /** @@ -397,8 +398,10 @@ int update_dmq_node_status(dmq_node_list_t *list, dmq_node_t *node, int status) */ int build_node_str(dmq_node_t *node, char *buf, int buflen) { - /* sip:host:port;status=[status] */ + /* sip:host:port;protocol=abcd;status=[status] */ int len = 0; + str sproto = STR_NULL; + if(buflen < node->orig_uri.len + 32) { LM_ERR("no more space left for node string\n"); return -1; @@ -411,6 +414,18 @@ int build_node_str(dmq_node_t *node, char *buf, int buflen) len += 1; memcpy(buf + len, node->uri.port.s, node->uri.port.len); len += node->uri.port.len; + if(node->uri.proto!=PROTO_NONE && node->uri.proto!=PROTO_UDP + && node->uri.proto!=PROTO_OTHER) { + if(get_valid_proto_string(node->uri.proto, 1, 0, &sproto)<0) { + LM_WARN("unknown transport protocol - fall back to udp\n"); + sproto.s = "udp"; + sproto.len = 3; + } + memcpy(buf + len, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); + len += TRANSPORT_PARAM_LEN; + memcpy(buf + len, sproto.s, sproto.len); + len += sproto.len; + } memcpy(buf + len, ";", 1); len += 1; memcpy(buf + len, "status=", 7); diff --git a/src/modules/dmq/doc/dmq_admin.xml b/src/modules/dmq/doc/dmq_admin.xml index 3b4121fdb3b..d8b94614e2e 100644 --- a/src/modules/dmq/doc/dmq_admin.xml +++ b/src/modules/dmq/doc/dmq_admin.xml @@ -132,6 +132,8 @@ sip:192.168.40.17:5060;status=active ... modparam("dmq", "server_address", "sip:10.0.0.20:5060") ... +modparam("dmq", "server_address", "sip:10.0.0.20:5061;transport=tls") +... @@ -149,6 +151,8 @@ modparam("dmq", "server_address", "sip:10.0.0.20:5060") ... modparam("dmq", "notification_address", "sip:10.0.0.21:5060") ... +modparam("dmq", "notification_address", "sip:10.0.0.21:5061;transport=tls") +... diff --git a/src/modules/dmq/notification_peer.c b/src/modules/dmq/notification_peer.c index 83da389269e..c991acbf5f3 100644 --- a/src/modules/dmq/notification_peer.c +++ b/src/modules/dmq/notification_peer.c @@ -600,13 +600,14 @@ int notification_resp_callback_f( } } else if(code == 408) { if(STR_EQ(node->orig_uri, dmq_notification_address)) { - LM_ERR("not deleting notification_peer\n"); - update_dmq_node_status(dmq_node_list, node, DMQ_NODE_PENDING); + LM_ERR("not deleting notification peer [%.*s]\n", + STR_FMT(&dmq_notification_address)); + update_dmq_node_status(dmq_node_list, node, DMQ_NODE_PENDING); return 0; } if (node->status == DMQ_NODE_DISABLED) { /* deleting node - the server did not respond */ - LM_ERR("deleting server %.*s because of failed request\n", + LM_ERR("deleting server node %.*s because of failed request\n", STR_FMT(&node->orig_uri)); ret = del_dmq_node(dmq_node_list, node); LM_DBG("del_dmq_node returned %d\n", ret); diff --git a/src/modules/dmq/worker.c b/src/modules/dmq/worker.c index 43d76da3099..fa4025fff27 100644 --- a/src/modules/dmq/worker.c +++ b/src/modules/dmq/worker.c @@ -15,8 +15,8 @@ * 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 + * 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 * */ @@ -29,6 +29,7 @@ #include "../../core/sip_msg_clone.h" #include "../../core/parser/parse_from.h" #include "../../core/parser/parse_to.h" +#include "../../core/cfg/cfg_struct.h" /** * @brief set the body of a response @@ -88,6 +89,7 @@ void worker_loop(int id) } else { sleep_us(dmq_worker_usleep); } + cfg_update(); /* remove from queue until empty */ while(job_queue_size(worker->queue) > 0) { diff --git a/src/modules/htable/htable.c b/src/modules/htable/htable.c index d05cf5227d1..1bb36657def 100644 --- a/src/modules/htable/htable.c +++ b/src/modules/htable/htable.c @@ -484,7 +484,7 @@ static int ht_rm_items(sip_msg_t* msg, str* hname, str* op, str *val, case 2: if(strncmp(op->s, "re", 2)==0) { isval.s = *val; - if (ht_dmq_replicate_action(HT_DMQ_RM_CELL_RE, &ht->name, NULL, + if ((ht->dmqreplicate > 0) && ht_dmq_replicate_action(HT_DMQ_RM_CELL_RE, &ht->name, NULL, AVP_VAL_STR, &isval, mkey)!=0) { LM_ERR("dmq relication failed (op %d)\n", mkey); } @@ -494,7 +494,7 @@ static int ht_rm_items(sip_msg_t* msg, str* hname, str* op, str *val, return 1; } else if(strncmp(op->s, "sw", 2)==0) { isval.s = *val; - if (ht_dmq_replicate_action(HT_DMQ_RM_CELL_SW, &ht->name, NULL, + if ((ht->dmqreplicate > 0) &&ht_dmq_replicate_action(HT_DMQ_RM_CELL_SW, &ht->name, NULL, AVP_VAL_STR, &isval, mkey)!=0) { LM_ERR("dmq relication failed (op %d)\n", mkey); } diff --git a/src/modules/ims_charging/Ro_data.h b/src/modules/ims_charging/Ro_data.h index 6ad045c1910..ba0aba34201 100644 --- a/src/modules/ims_charging/Ro_data.h +++ b/src/modules/ims_charging/Ro_data.h @@ -131,9 +131,9 @@ typedef struct _str_list_t_slot { struct _str_list_t_slot *prev, *next; } str_list_slot_t; -typedef struct { +typedef struct str_htlist { str_list_slot_t *head, *tail; -} str_list_t; +} str_htlist_t; #define str_list_t_free(x,mem) \ do{\ @@ -163,7 +163,7 @@ typedef struct { typedef struct { str *application_server; - str_list_t application_provided_called_party_address; + str_htlist_t application_provided_called_party_address; } as_info_t; typedef struct _as_info_list_t_slot { @@ -194,7 +194,7 @@ do{\ #define as_info_list_t_copy(dst,src,mem) \ do {\ str_dup_ptr_ptr((dst)->info.application_server,(src)->info.application_server,mem);\ - WL_DUP_ALL(&((dst)->info.application_provided_called_party_address),&((src)->info.application_provided_called_party_address),str_list_t,mem);\ + WL_DUP_ALL(&((dst)->info.application_provided_called_party_address),&((src)->info.application_provided_called_party_address),str_htlist_t,mem);\ } while(0) typedef struct { @@ -268,9 +268,9 @@ typedef struct { str *user_session_id; str *outgoing_session_id; - str_list_t calling_party_address; + str_htlist_t calling_party_address; str *called_party_address; - str_list_t called_asserted_identity; + str_htlist_t called_asserted_identity; str *requested_party_address; str *access_network_info; str *app_provided_party; diff --git a/src/modules/ims_diameter_server/ims_diameter_server.c b/src/modules/ims_diameter_server/ims_diameter_server.c index c7fe941b001..fae5f58ce9a 100644 --- a/src/modules/ims_diameter_server/ims_diameter_server.c +++ b/src/modules/ims_diameter_server/ims_diameter_server.c @@ -280,6 +280,10 @@ int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* comman } LM_DBG("Peer %.*s\n", s_peer.len, s_peer.s); } + if (get_str_fparam(&s_message, msg, (fparam_t*)message) < 0) { + LM_ERR("failed to get Message\n"); + return -1; + } if (get_str_fparam(&s_appid, msg, (fparam_t*)appid) < 0) { LM_ERR("failed to get App-ID\n"); return -1; @@ -306,9 +310,14 @@ int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* comman session = cdpb.AAACreateSession(0); req = cdpb.AAACreateRequest(i_appid, i_commandcode, Flag_Proxyable, session); + if (session) { + cdpb.AAADropSession(session); + session = 0; + } + if (!req) goto error1; - if (addAVPsfromJSON(req, &s_message)) { + if (!addAVPsfromJSON(req, &s_message)) { LM_ERR("Failed to parse JSON Request\n"); return -1; } @@ -322,7 +331,7 @@ int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* comman } else { resp = cdpb.AAASendRecvMessageToPeer(req, &s_peer); LM_DBG("Successfully sent diameter\n"); - if (AAAmsg2json(resp, &responsejson) == 1) { + if (resp && AAAmsg2json(resp, &responsejson) == 1) { return 1; } else { LM_ERR("Failed to convert response to JSON\n"); @@ -337,7 +346,7 @@ int diameter_request(struct sip_msg * msg, char* peer, char* appid, char* comman } else { resp = cdpb.AAASendRecvMessage(req); LM_DBG("Successfully sent diameter\n"); - if (AAAmsg2json(resp, &responsejson) == 1) { + if (resp && AAAmsg2json(resp, &responsejson) == 1) { return 1; } else { LM_ERR("Failed to convert response to JSON\n"); diff --git a/src/modules/jwt/Makefile b/src/modules/jwt/Makefile new file mode 100644 index 00000000000..0d4f553cd81 --- /dev/null +++ b/src/modules/jwt/Makefile @@ -0,0 +1,25 @@ +# +# +# WARNING: do not run this directly, it should be run by the main Makefile + +include ../../Makefile.defs +auto_gen= +NAME=jwt.so + +ifeq ($(CROSS_COMPILE),) +JWT_BUILDER=$(shell \ + if pkg-config --exists libjwt; then \ + echo 'pkg-config libjwt'; \ + fi) +endif + +ifneq ($(JWT_BUILDER),) + DEFS += $(shell $(JWT_BUILDER) --cflags) + LIBS += $(shell $(JWT_BUILDER) --libs) +else + DEFS += -I$(LOCALBASE)/include + LIBS += -L$(LOCALBASE)/lib -ljwt +endif + +include ../../Makefile.modules + diff --git a/src/modules/jwt/README b/src/modules/jwt/README new file mode 100644 index 00000000000..7a7400a4b15 --- /dev/null +++ b/src/modules/jwt/README @@ -0,0 +1,176 @@ +JWT Module + +Daniel-Constantin Mierla + + + +Edited by + +Daniel-Constantin Mierla + + + + Copyright © 2021 asipto.com + __________________________________________________________________ + + Table of Contents + + 1. Admin Guide + + 1. Overview + 2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + + 3. Parameters + + 3.1. key_mode (int) + + 4. Functions + + 4.1. jwt_generate(prvkey, alg, claims) + 4.2. jwt_verify(pubkey, alg, claims, jwtval) + + 5. Variables + + 5.1. $jwt(key) + + List of Examples + + 1.1. Set key_mode parameter + 1.2. jwt_generate usage + 1.3. jwt_verify usage + 1.4. $jwt(name) usage + +Chapter 1. Admin Guide + + Table of Contents + + 1. Overview + 2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + + 3. Parameters + + 3.1. key_mode (int) + + 4. Functions + + 4.1. jwt_generate(prvkey, alg, claims) + 4.2. jwt_verify(pubkey, alg, claims, jwtval) + + 5. Variables + + 5.1. $jwt(key) + +1. Overview + + This module provides JWT (JSON Web Token) functions to be used in + Kamailio configuration file. + + It relies on libjwt (at least v1.12.0) library + (https://github.com/benmcollins/libjwt). + +2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + +2.1. Kamailio Modules + + The following modules must be loaded before this module: + * none. + +2.2. External Libraries or Applications + + The following libraries or applications must be installed before + running Kamailio with this module loaded: + * libjwt - minimum version 1.12.0. + +3. Parameters + + 3.1. key_mode (int) + +3.1. key_mode (int) + + Mode to use the private and public keys. If set to 0, they are read + always from the disk. If set to 1, they are cached in memory with the + first use (no reload support yet). + + Default value is 0. + + Example 1.1. Set key_mode parameter +... +modparam("jwt", "key_mode", 1) +... + +4. Functions + + 4.1. jwt_generate(prvkey, alg, claims) + 4.2. jwt_verify(pubkey, alg, claims, jwtval) + +4.1. jwt_generate(prvkey, alg, claims) + + Generate the JWT, its value can be retrieved in the variable $jwt(val). + + The parameters are: + * prvkey - path to private key + * alg - the algoritm to build the signature, as supported by the + libjwt (e.g., RS256, HS256, ES256, ...) + * claims - the list of claims to be added to JWT, in the format + "name1=value1;name2=value2;..." (same as the SIP parameters + format). + + This function can be used from ANY_ROUTE. + + Example 1.2. jwt_generate usage +... + jwt_generate("/path/to/prvkey.pem", "RS256", + "caller=$fU;callee=$tU;callid=$ci"); +... + +4.2. jwt_verify(pubkey, alg, claims, jwtval) + + Verify the JWT. + + The parameters are: + * pubkey - path to public key + * alg - the algoritm to build the signature, as supported by the + libjwt (e.g., RS256, HS256, ES256, ...) + * claims - the list of claims to be checked they are in the JWT, in + the format "name1=value1;name2=value2;..." (same as the SIP + parameters format). + * jwtval - the value of the JWT to verify + + This function can be used from ANY_ROUTE. + + Example 1.3. jwt_verify usage +... + if(!jwt_verify("/path/to/pubkey.pem", "RS256", + "caller=$fU;callee=$tU;callid=$ci", + "$var(jwt)") { + xwarn("failed to verify jwt\n"); + } +... + +5. Variables + + 5.1. $jwt(key) + +5.1. $jwt(key) + + Get the values and attributes after using JWT functions. + + The key can be: + * val - the value of JWT after a successful jwt_generate(). + * status - the status of verification after a failed jwt_verify(). + + Example 1.4. $jwt(name) usage +... + jwt_generate("/path/to/prvkey.pem", "RS256", + "caller=$fU;callee=$tU;callid=$ci"); + xinfo("jwt is: $jwt(val)"); +... diff --git a/src/modules/jwt/doc/Makefile b/src/modules/jwt/doc/Makefile new file mode 100644 index 00000000000..4b4bd6abff9 --- /dev/null +++ b/src/modules/jwt/doc/Makefile @@ -0,0 +1,4 @@ +docs = jwt.xml + +docbook_dir = ../../../../doc/docbook +include $(docbook_dir)/Makefile.module diff --git a/src/modules/jwt/doc/jwt.xml b/src/modules/jwt/doc/jwt.xml new file mode 100644 index 00000000000..62225bb855f --- /dev/null +++ b/src/modules/jwt/doc/jwt.xml @@ -0,0 +1,37 @@ + + + +%docentities; + +]> + + + + JWT Module + kamailio.org + + + Daniel-Constantin + Mierla + miconda@gmail.com + + + Daniel-Constantin + Mierla + miconda@gmail.com + + + + 2021 + asipto.com + + + + + + + + diff --git a/src/modules/jwt/doc/jwt_admin.xml b/src/modules/jwt/doc/jwt_admin.xml new file mode 100644 index 00000000000..dba928b5237 --- /dev/null +++ b/src/modules/jwt/doc/jwt_admin.xml @@ -0,0 +1,217 @@ + + + +%docentities; + +]> + + + + + &adminguide; + +
+ Overview + + This module provides JWT (JSON Web Token) functions to be used + in &kamailio; configuration file. + + + It relies on libjwt (at least v1.12.0) library (https://github.com/benmcollins/libjwt). + +
+ +
+ Dependencies +
+ &kamailio; Modules + + The following modules must be loaded before this module: + + + + none. + + + + +
+
+ External Libraries or Applications + + The following libraries or applications must be installed before running + &kamailio; with this module loaded: + + + + libjwt - minimum version 1.12.0. + + + + +
+
+ +
+ Parameters +
+ <varname>key_mode</varname> (int) + + Mode to use the private and public keys. If set to 0, they are read + always from the disk. If set to 1, they are cached in memory with + the first use (no reload support yet). + + + + Default value is 0. + + + + Set <varname>key_mode</varname> parameter + +... +modparam("jwt", "key_mode", 1) +... + + +
+ +
+ +
+ Functions +
+ + <function moreinfo="none">jwt_generate(prvkey, alg, claims)</function> + + + Generate the JWT, its value can be retrieved in the variable $jwt(val). + + + The parameters are: + + + + + prvkey - path to private key + + + + + alg - the algoritm to build the signature, as supported by the + libjwt (e.g., RS256, HS256, ES256, ...) + + + + + claims - the list of claims to be added to JWT, in the format + "name1=value1;name2=value2;..." (same as the SIP parameters format). + + + + + This function can be used from ANY_ROUTE. + + + <function>jwt_generate</function> usage + +... + jwt_generate("/path/to/prvkey.pem", "RS256", + "caller=$fU;callee=$tU;callid=$ci"); +... + + +
+ +
+ + <function moreinfo="none">jwt_verify(pubkey, alg, claims, jwtval)</function> + + + Verify the JWT. + + + The parameters are: + + + + + pubkey - path to public key + + + + + alg - the algoritm to build the signature, as supported by the + libjwt (e.g., RS256, HS256, ES256, ...) + + + + + claims - the list of claims to be checked they are in the JWT, in the format + "name1=value1;name2=value2;..." (same as the SIP parameters format). + + + + + jwtval - the value of the JWT to verify + + + + + This function can be used from ANY_ROUTE. + + + <function>jwt_verify</function> usage + +... + if(!jwt_verify("/path/to/pubkey.pem", "RS256", + "caller=$fU;callee=$tU;callid=$ci", + "$var(jwt)") { + xwarn("failed to verify jwt\n"); + } +... + + +
+
+
+ Variables +
+ + <function moreinfo="none">$jwt(key)</function> + + + Get the values and attributes after using JWT functions. + + + The key can be: + + + + + val - the value of JWT after a successful jwt_generate(). + + + + + status - the status of verification after a failed jwt_verify(). + + + + + <function>$jwt(name)</function> usage + +... + jwt_generate("/path/to/prvkey.pem", "RS256", + "caller=$fU;callee=$tU;callid=$ci"); + xinfo("jwt is: $jwt(val)"); +... + + +
+
+ +
diff --git a/src/modules/jwt/jwt_mod.c b/src/modules/jwt/jwt_mod.c new file mode 100644 index 00000000000..6426381efbd --- /dev/null +++ b/src/modules/jwt/jwt_mod.c @@ -0,0 +1,514 @@ +/** + * Copyright (C) 2021 Daniel-Constantin Mierla (asipto.com) + * + * This file is part of Kamailio, a free SIP server. + * + * This file 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 + * + * + * This file 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 + * + */ + +#include +#include +#include +#include + +#include + +#include "../../core/sr_module.h" +#include "../../core/dprint.h" +#include "../../core/mod_fix.h" +#include "../../core/lvalue.h" +#include "../../core/kemi.h" +#include "../../core/parser/parse_param.h" + + +MODULE_VERSION + +static int mod_init(void); +static int child_init(int); +static void mod_destroy(void); + +static int w_jwt_generate(sip_msg_t* msg, char* pkey, char* palg, char* pclaims); +static int w_jwt_verify(sip_msg_t* msg, char* pkey, char* palg, char* pclaims, + char *pjwtval); + +static int _jwt_key_mode = 0; + +static str _jwt_result = STR_NULL; +static unsigned int _jwt_verify_status = 0; + +typedef struct jwt_fcache { + str fname; + str fdata; + struct jwt_fcache *next; +} jwt_fcache_t; + +static jwt_fcache_t *_jwt_fcache_list = NULL; + +static cmd_export_t cmds[]={ + {"jwt_generate", (cmd_function)w_jwt_generate, 3, + fixup_spve_all, 0, ANY_ROUTE}, + {"jwt_verify", (cmd_function)w_jwt_verify, 4, + fixup_spve_all, 0, ANY_ROUTE}, + {0, 0, 0, 0, 0, 0} +}; + +static param_export_t params[]={ + { "key_mode", PARAM_INT, &_jwt_key_mode }, + + { 0, 0, 0 } +}; + +static int jwt_pv_get(sip_msg_t *msg, pv_param_t *param, pv_value_t *res); +static int jwt_pv_parse_name(pv_spec_t *sp, str *in); +static pv_export_t mod_pvs[] = { + { {"jwt", sizeof("jwt")-1}, PVT_OTHER, jwt_pv_get, 0, + jwt_pv_parse_name, 0, 0, 0 }, + { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } +}; + +struct module_exports exports = { + "jwt", /* module name */ + DEFAULT_DLFLAGS, /* dlopen flags */ + cmds, /* cmd (cfg function) exports */ + params, /* param exports */ + 0, /* RPC method exports */ + mod_pvs, /* pseudo-variables exports */ + 0, /* response handling function */ + mod_init, /* module init function */ + child_init, /* per-child init function */ + mod_destroy /* module destroy function */ +}; + + +/** + * @brief Initialize crypto module function + */ +static int mod_init(void) +{ + return 0; +} + +/** + * @brief Initialize crypto module children + */ +static int child_init(int rank) +{ + return 0; +} + +/** + * destroy module function + */ +static void mod_destroy(void) +{ + return; +} + +/** + * + */ +static int jwt_fcache_get(str *key, str *kdata) +{ + jwt_fcache_t *fc = NULL; + + if(_jwt_key_mode!=1) { + return -1; + } + for(fc=_jwt_fcache_list; fc!=NULL; fc=fc->next) { + if(fc->fname.len==key->len + && strncmp(fc->fname.s, key->s, key->len)==0) { + LM_DBG("file found in cache: %.*s\n", key->len, key->s); + *kdata = fc->fdata; + break; + } + } + return 0; +} + +/** + * + */ +static int jwt_fcache_add(str *key, str *kdata) +{ + jwt_fcache_t *fc = NULL; + + if(_jwt_key_mode!=1) { + return -1; + } + fc = (jwt_fcache_t*)pkg_malloc(sizeof(jwt_fcache_t) + key->len + + kdata->len + 2); + if(fc==NULL) { + PKG_MEM_ERROR; + return -1; + } + memset(fc, 0, sizeof(jwt_fcache_t) + key->len + kdata->len + 2); + fc->fname.s = (char*)fc + sizeof(jwt_fcache_t); + fc->fname.len = key->len; + memcpy(fc->fname.s, key->s, key->len); + fc->fdata.s = fc->fname.s + fc->fname.len + 1; + fc->fdata.len = kdata->len; + memcpy(fc->fdata.s, kdata->s, kdata->len); + fc->next = _jwt_fcache_list; + _jwt_fcache_list = fc; + + return 0; +} + +/** + * + */ +static int ki_jwt_generate(sip_msg_t* msg, str *key, str *alg, str *claims) +{ + str dupclaims = STR_NULL; + str sparams = STR_NULL; + str kdata = STR_NULL; + jwt_alg_t valg = JWT_ALG_NONE; + time_t iat; + FILE *fpk = NULL; + unsigned char keybuf[10240]; + size_t keybuf_len = 0; + param_t* params_list = NULL; + param_hooks_t phooks; + param_t *pit = NULL; + int ret = 0; + jwt_t *jwt = NULL; + + if(key==NULL || key->s==NULL || alg==NULL || alg->s==NULL + || claims==NULL || claims->s==NULL || claims->len<=0) { + LM_ERR("invalid parameters\n"); + return -1; + } + if(_jwt_result.s != NULL) { + jwt_free_str(_jwt_result.s); + _jwt_result.s = NULL; + _jwt_result.len = 0; + } + valg = jwt_str_alg(alg->s); + if (valg == JWT_ALG_INVAL) { + LM_ERR("not supported algorithm: %s\n", alg->s); + return -1; + } + if(pkg_str_dup(&dupclaims, claims)<0) { + LM_ERR("failed to duplicate claims\n"); + return -1; + } + jwt_fcache_get(key, &kdata); + if(kdata.s==NULL) { + fpk= fopen(key->s, "r"); + if(fpk==NULL) { + LM_ERR("failed to read key file: %s\n", key->s); + goto error; + } + keybuf_len = fread(keybuf, 1, sizeof(keybuf), fpk); + fclose(fpk); + if(keybuf_len==0) { + LM_ERR("unable to read key file content: %s\n", key->s); + goto error; + } + keybuf[keybuf_len] = '\0'; + kdata.s = (char*)keybuf; + kdata.len = (int)keybuf_len; + jwt_fcache_add(key, &kdata); + } + sparams = dupclaims; + if(sparams.s[sparams.len-1]==';') { + sparams.len--; + } + if (parse_params(&sparams, CLASS_ANY, &phooks, ¶ms_list)<0) { + LM_ERR("failed to parse claims\n"); + goto error; + } + + ret = jwt_new(&jwt); + if (ret != 0 || jwt == NULL) { + LM_ERR("failed to initialize jwt\n"); + goto error; + } + + iat = time(NULL); + + ret = jwt_add_grant_int(jwt, "iat", iat); + for (pit = params_list; pit; pit=pit->next) { + if(pit->name.len>0 && pit->body.len>0) { + pit->name.s[pit->name.len] = '\0'; + pit->body.s[pit->body.len] = '\0'; + jwt_add_grant(jwt, pit->name.s, pit->body.s); + } + } + + ret = jwt_set_alg(jwt, valg, (unsigned char*)kdata.s, (size_t)kdata.len); + if (ret < 0) { + LM_ERR("failed to set algorithm and key\n"); + goto error; + } + + _jwt_result.s = jwt_encode_str(jwt); + _jwt_result.len = strlen(_jwt_result.s); + + free_params(params_list); + pkg_free(dupclaims.s); + jwt_free(jwt); + + return 1; + +error: + if(params_list!=NULL) { + free_params(params_list); + } + if(dupclaims.s!=NULL) { + pkg_free(dupclaims.s); + } + if(jwt!=NULL) { + jwt_free(jwt); + } + return -1; +} + +/** + * + */ +static int w_jwt_generate(sip_msg_t* msg, char* pkey, char* palg, char* pclaims) +{ + str skey = STR_NULL; + str salg = STR_NULL; + str sclaims = STR_NULL; + + if (fixup_get_svalue(msg, (gparam_t*)pkey, &skey) != 0) { + LM_ERR("cannot get path to the key file\n"); + return -1; + } + if (fixup_get_svalue(msg, (gparam_t*)palg, &salg) != 0) { + LM_ERR("cannot get algorithm value\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)pclaims, &sclaims) != 0) { + LM_ERR("cannot get claims value\n"); + return -1; + } + + return ki_jwt_generate(msg, &skey, &salg, &sclaims); +} + +/** + * + */ +static int ki_jwt_verify(sip_msg_t* msg, str *key, str *alg, str *claims, + str *jwtval) +{ + str dupclaims = STR_NULL; + jwt_alg_t valg = JWT_ALG_NONE; + str kdata = STR_NULL; + time_t iat; + FILE *fpk = NULL; + unsigned char keybuf[10240]; + size_t keybuf_len = 0; + param_t* params_list = NULL; + param_hooks_t phooks; + param_t *pit = NULL; + int ret = 0; + jwt_t *jwt = NULL; + jwt_valid_t *jwt_valid = NULL; + str sparams = STR_NULL; + + if(key==NULL || key->s==NULL || alg==NULL || alg->s==NULL + || claims==NULL || claims->s==NULL || claims->len<=0 + || jwtval==NULL || jwtval->s==NULL || jwtval->len<=0) { + LM_ERR("invalid parameters\n"); + return -1; + } + + _jwt_verify_status = 0; + + valg = jwt_str_alg(alg->s); + if (valg == JWT_ALG_INVAL) { + LM_ERR("not supported algorithm: %s\n", alg->s); + return -1; + } + if(pkg_str_dup(&dupclaims, claims)<0) { + LM_ERR("failed to duplicate claims\n"); + return -1; + } + jwt_fcache_get(key, &kdata); + if(kdata.s==NULL) { + fpk= fopen(key->s, "r"); + if(fpk==NULL) { + LM_ERR("failed to read key file: %s\n", key->s); + goto error; + } + keybuf_len = fread(keybuf, 1, sizeof(keybuf), fpk); + fclose(fpk); + if(keybuf_len==0) { + LM_ERR("unable to read key file content: %s\n", key->s); + goto error; + } + keybuf[keybuf_len] = '\0'; + kdata.s = (char*)keybuf; + kdata.len = (int)keybuf_len; + jwt_fcache_add(key, &kdata); + } + sparams = dupclaims; + if(sparams.s[sparams.len-1]==';') { + sparams.len--; + } + if (parse_params(&sparams, CLASS_ANY, &phooks, ¶ms_list)<0) { + LM_ERR("failed to parse claims\n"); + goto error; + } + + ret = jwt_valid_new(&jwt_valid, valg); + if (ret != 0 || jwt_valid == NULL) { + LM_ERR("failed to initialize jwt valid\n"); + goto error; + } + + iat = time(NULL); + jwt_valid_set_headers(jwt_valid, 1); + jwt_valid_set_now(jwt_valid, iat); + + for (pit = params_list; pit; pit=pit->next) { + if(pit->name.len>0 && pit->body.len>0) { + pit->name.s[pit->name.len] = '\0'; + pit->body.s[pit->body.len] = '\0'; + jwt_valid_add_grant(jwt_valid, pit->name.s, pit->body.s); + } + } + + ret = jwt_decode(&jwt, jwtval->s, (unsigned char*)kdata.s, (size_t)kdata.len); + if (ret!=0 || jwt==NULL) { + LM_ERR("failed to decode jwt value\n"); + goto error; + } + if (jwt_validate(jwt, jwt_valid) != 0) { + _jwt_verify_status = jwt_valid_get_status(jwt_valid); + LM_ERR("failed to validate jwt: %08x\n", _jwt_verify_status); + goto error; + } + + free_params(params_list); + pkg_free(dupclaims.s); + jwt_free(jwt); + jwt_valid_free(jwt_valid); + + return 1; + +error: + if(params_list!=NULL) { + free_params(params_list); + } + if(dupclaims.s!=NULL) { + pkg_free(dupclaims.s); + } + if(jwt!=NULL) { + jwt_free(jwt); + } + if(jwt_valid!=NULL) { + jwt_valid_free(jwt_valid); + } + return -1; +} + +/** + * + */ +static int w_jwt_verify(sip_msg_t* msg, char* pkey, char* palg, char* pclaims, + char *pjwtval) +{ + str skey = STR_NULL; + str salg = STR_NULL; + str sclaims = STR_NULL; + str sjwtval = STR_NULL; + + if (fixup_get_svalue(msg, (gparam_t*)pkey, &skey) != 0) { + LM_ERR("cannot get path to the key file\n"); + return -1; + } + if (fixup_get_svalue(msg, (gparam_t*)palg, &salg) != 0) { + LM_ERR("cannot get algorithm value\n"); + return -1; + } + if (fixup_get_svalue(msg, (gparam_t*)pclaims, &sclaims) != 0) { + LM_ERR("cannot get claims value\n"); + return -1; + } + if (fixup_get_svalue(msg, (gparam_t*)pjwtval, &sjwtval) != 0) { + LM_ERR("cannot get jwt value\n"); + return -1; + } + + return ki_jwt_verify(msg, &skey, &salg, &sclaims, &sjwtval); +} + +/** + * + */ +static int jwt_pv_get(sip_msg_t *msg, pv_param_t *param, pv_value_t *res) +{ + switch(param->pvn.u.isname.name.n) + { + case 0: + if(_jwt_result.s==NULL) + return pv_get_null(msg, param, res); + return pv_get_strval(msg, param, res, &_jwt_result); + case 1: + return pv_get_uintval(msg, param, res, _jwt_verify_status); + default: + return pv_get_null(msg, param, res); + } +} + +/** + * + */ +static int jwt_pv_parse_name(pv_spec_t *sp, str *in) +{ + if(in->len==3 && strncmp(in->s, "val", 3)==0) { + sp->pvp.pvn.u.isname.name.n = 0; + } else if(in->len==6 && strncmp(in->s, "status", 6)==0) { + sp->pvp.pvn.u.isname.name.n = 1; + } else { + LM_ERR("unknown inner name [%.*s]\n", in->len, in->s); + return -1; + } + return 0; +} + +/** + * + */ +/* clang-format off */ +static sr_kemi_t sr_kemi_jwt_exports[] = { + { str_init("jwt"), str_init("jwt_generate"), + SR_KEMIP_INT, ki_jwt_generate, + { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR, + SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, + { str_init("jwt"), str_init("jwt_verify"), + SR_KEMIP_INT, ki_jwt_verify, + { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR, + SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, + + { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } } +}; +/* clang-format on */ + +int mod_register(char *path, int *dlflags, void *p1, void *p2) +{ + sr_kemi_modules_add(sr_kemi_jwt_exports); + return 0; +} diff --git a/src/modules/lrkproxy/Makefile b/src/modules/lrkproxy/Makefile new file mode 100644 index 00000000000..716fbbc0d00 --- /dev/null +++ b/src/modules/lrkproxy/Makefile @@ -0,0 +1,14 @@ +# +# lrkproxy module makefile +# +# +# WARNING: do not run this directly, it should be run by the main Makefile + +include ../../Makefile.defs +auto_gen= +NAME=lrkproxy.so +LIBS= + +SERLIBPATH=../../lib +include ../../Makefile.modules + diff --git a/src/modules/lrkproxy/README b/src/modules/lrkproxy/README new file mode 100644 index 00000000000..b43dd8b16a8 --- /dev/null +++ b/src/modules/lrkproxy/README @@ -0,0 +1,318 @@ +lrkproxy Module + +Mojtaba Esfandiari.S + + Nasim Telecom + + Copyright © 2020 Nasim Telecom Inc. + __________________________________________________________________ + + Table of Contents + + 1. Admin Guide + + 1. Overview + 2. LRKProxy Architecture + + 2.1. LRKP_Controlling Layer (LRKP_CL) + 2.2. LRKP_Transport Stateful Layer (LRKP_TSL) + + 3. Multiple LRKProxy usage + 4. Dependencies + + 4.1. Kamailio Modules + 4.2. External Libraries or Applications + 4.3. Parameters + + 4.3.1. lrkproxy_sock (string) + 4.3.2. lrkproxy_disable_tout (integer) + 4.3.3. lrkproxy_tout (integer) + 4.3.4. lrkproxy_retr (integer) + 4.3.5. lrkp_alg (integer) + 4.3.6. hash_table_tout (integer) + 4.3.7. hash_table_size (integer) + + 4.4. Functions + + 4.4.1. set_lrkproxy_set(setid) + 4.4.2. lrkproxy_manage([flags [, ip_address]]) + + List of Examples + + 1.1. Set lrkproxy_sock parameter + 1.2. Set lrkproxy_disable_tout parameter + 1.3. Set lrkproxy_tout parameter + 1.4. Set lrkproxy_retr parameter + 1.5. Set lrkp_alg parameter + 1.6. Set hash_table_tout parameter + 1.7. Set hash_table_size parameter + 1.8. set_lrkproxy_set usage + 1.9. lrkproxy_manage usage + +Chapter 1. Admin Guide + + Table of Contents + + 1. Overview + 2. LRKProxy Architecture + + 2.1. LRKP_Controlling Layer (LRKP_CL) + 2.2. LRKP_Transport Stateful Layer (LRKP_TSL) + + 3. Multiple LRKProxy usage + 4. Dependencies + + 4.1. Kamailio Modules + 4.2. External Libraries or Applications + 4.3. Parameters + + 4.3.1. lrkproxy_sock (string) + 4.3.2. lrkproxy_disable_tout (integer) + 4.3.3. lrkproxy_tout (integer) + 4.3.4. lrkproxy_retr (integer) + 4.3.5. lrkp_alg (integer) + 4.3.6. hash_table_tout (integer) + 4.3.7. hash_table_size (integer) + + 4.4. Functions + + 4.4.1. set_lrkproxy_set(setid) + 4.4.2. lrkproxy_manage([flags [, ip_address]]) + +1. Overview + + This is a module that enables media streams to be relayed via + pylrkproxy engine that exist in: + https://github.com/mojtabaesfandiari/pylrkproxy It does relaying audio + streams between peers in PREROUTING netfilter-hooking section in + kernel-space linux. The LRKProxy architecture is composed of two + different layers. These layers are independent of each other. For more + information about LRKProxy architecture, please visit our paper in + ieeexplore: https://ieeexplore.ieee.org/document/9303608 + +2. LRKProxy Architecture + + 2.1. LRKP_Controlling Layer (LRKP_CL) + 2.2. LRKP_Transport Stateful Layer (LRKP_TSL) + +2.1. LRKP_Controlling Layer (LRKP_CL) + + The first layer is developed as User-Space application that allows + User-Space to directly access and manipulate cache data buffer and + packet buffer in Kernel-Space. This layer gets all information about + creating new sessions, active sessions and teardown sessions which is + gotten from SDP body during signaling plan and relay them to the + LRKP-Transport Stateful Layer (LRKP- TSL). + +2.2. LRKP_Transport Stateful Layer (LRKP_TSL) + + The second layer is developed in Kernel-Space as a main decision point + for RTP admission controller and Quickpath selector to where a received + packet should be forwarded with power of packet mangling framework in + the network stack. + + The LRKP_CL and LRKP-TSL could be run as independence functions on + different machines. We could have one LRKP_CL with multiple LRKP-TSL on + different machines. The LRKP_CL could works with all LRKP-TSL with + different strategies(lrkp_alg parameter). + +3. Multiple LRKProxy usage + + The LRKP_CL Layer can support multiple LRKP_TSL Layer for + balancing/distribution and control/selection purposes. + + The module allows definition of several sets of LRKP_TSL. + Load-balancing will be performed over predefine algorithm by setting + lrkp_alg parameter. + + IMPORTANT: This module does not support balancing inside a set like as + is done RTPProxy module based on the weight of each rtpproxy from the + set. The balancing would be run on different machine + +4. Dependencies + + 4.1. Kamailio Modules + 4.2. External Libraries or Applications + 4.3. Parameters + + 4.3.1. lrkproxy_sock (string) + 4.3.2. lrkproxy_disable_tout (integer) + 4.3.3. lrkproxy_tout (integer) + 4.3.4. lrkproxy_retr (integer) + 4.3.5. lrkp_alg (integer) + 4.3.6. hash_table_tout (integer) + 4.3.7. hash_table_size (integer) + + 4.4. Functions + + 4.4.1. set_lrkproxy_set(setid) + 4.4.2. lrkproxy_manage([flags [, ip_address]]) + +4.1. Kamailio Modules + + The following modules must be loaded before this module: + * tm module - (optional) if you want to have lrkproxy_manage() fully + functional + +4.2. External Libraries or Applications + + The following libraries or applications must be installed before + running Kamailio with this module loaded: + * None. + +4.3. Parameters + +4.3.1. lrkproxy_sock (string) + + Used to define the list of LRKP_TSL instances to connect to. These can + be UNIX sockets or IPv4/IPv6 UDP sockets. Each modparam entry will + insert sockets into a single set with default value set ID '0'. To + define multiple LRKP_TSL, just add the instances in each modparam. + + Default value is “NONE” (disabled). + + Example 1.1. Set lrkproxy_sock parameter +... +# single lrkproxy +modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080") + +# multiple lrkproxies for LB in diffenrent machine +modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080") +modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.109:8080") + +... + +4.3.2. lrkproxy_disable_tout (integer) + + Once LRKP_TSL was found unreachable and marked as disabled, the LRKP_CL + module will not attempt to establish communication to LRKP_TSL for + lrkproxy_disable_tout seconds. + + Default value is “60”. + + Example 1.2. Set lrkproxy_disable_tout parameter +... +modparam("lrkproxy", "lrkproxy_disable_tout", 20) +... + +4.3.3. lrkproxy_tout (integer) + + Timeout value in waiting for reply from LRKP_TSL. + + Default value is “1”. + + Example 1.3. Set lrkproxy_tout parameter +... +modparam("lrkproxy", "lrkproxy_tout", 2) +... + +4.3.4. lrkproxy_retr (integer) + + How many times the LRKP_CL should retry to send and receive after + timeout was generated. + + Default value is “5”. + + Example 1.4. Set lrkproxy_retr parameter +... +modparam("lrkproxy", "lrkproxy_retr", 2) +... + +4.3.5. lrkp_alg (integer) + + This parameter set the algorithm of LRKP_TSL selection. lrk_LINER=0, + lrk_RR=1 + + Default value is “0”. + + Example 1.5. Set lrkp_alg parameter +... +modparam("lrkproxy", "lrkp_alg", 1) +... + +4.3.6. hash_table_tout (integer) + + Number of seconds after an lrkproxy hash table entry is marked for + deletion. By default, this parameter is set to 3600 (seconds). + + To maintain information about a selected rtp machine node, for a given + call, entries are added in a hashtable of (callid, viabranch) pairs. + When command comes, lookup callid, viabranch pairs. If found, return + chosen node. If not found, choose a new node, insert it in the hastable + and return the chosen node. + + NOTE: In the current implementation, the actual deletion happens on the + fly, while insert/remove/lookup the hastable, only for the entries in + the insert/remove/lookup path. + + NOTE: When configuring this parameter, one should consider maximum call + time VS share memory for unfinished calls. + + Default value is “3600”. + + Example 1.6. Set hash_table_tout parameter +... +modparam("lrkproxy", "hash_table_tout", "3600") +... + +4.3.7. hash_table_size (integer) + + Size of the hash table. Default value is 128. + + Default value is “128”. + + Example 1.7. Set hash_table_size parameter +... +modparam("lrkproxy", "hash_table_size", 256) +... + +4.4. Functions + +4.4.1. set_lrkproxy_set(setid) + + Sets the Id of the lrkproxy set to be used for the next + lrkproxy_manage() command. The parameter can be an integer or a config + variable holding an integer. + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, + BRANCH_ROUTE. + + Example 1.8. set_lrkproxy_set usage +... +set_lrkproxy_set("0"); +lrkproxy_manage(); +... + +4.4.2. lrkproxy_manage([flags [, ip_address]]) + + Manage the LRKProxy session - it combines the functionality of + lrkproxy_offer(), lrkproxy_answer() and unforce_lrkproxy(), detecting + internally based on message type and method which one to execute. + + IMPORTANT:The LRKProxy just has one function relating rtp packets. It + does not support combination of functionality of lrkproxy_offer(), + lrkproxy_answer() and unforce_lrkproxy() and other etc. So you have to + just use lrkproxy_manage. + + Meaning of the parameters is as follows: + * flags - flags to turn on some features. + + internal,external - The shorthand of this flag is "ie". This + can be used to relay media sessions between two different NIC + from internal to external path. + + external,internal - The shorthand of this flag is "ei". This + can be used to relay media sessions between two different NIC + from external to internal path. + * ip_address - new SDP IP address.This optional parameter is under + development. + + This function can be used from ANY_ROUTE. + + Example 1.9. lrkproxy_manage usage +... +lrkproxy_manage(); +//or +lrkproxy_manage("ie"); +//or +lrkproxy_manage("ei"); + +... diff --git a/src/modules/lrkproxy/doc/Makefile b/src/modules/lrkproxy/doc/Makefile new file mode 100644 index 00000000000..c8d8cd5de43 --- /dev/null +++ b/src/modules/lrkproxy/doc/Makefile @@ -0,0 +1,4 @@ +docs = lrkproxy.xml + +docbook_dir = ../../../../doc/docbook +include $(docbook_dir)/Makefile.module diff --git a/src/modules/lrkproxy/doc/lrkproxy.xml b/src/modules/lrkproxy/doc/lrkproxy.xml new file mode 100644 index 00000000000..bfc71580c77 --- /dev/null +++ b/src/modules/lrkproxy/doc/lrkproxy.xml @@ -0,0 +1,33 @@ + + + +%docentities; + +]> + + + + lrkproxy Module + &kamailioname; + + + Mojtaba + Esfandiari.S + Nasim Telecom +
+ esfandiari@nasimtelecom.com +
+
+
+ + 2020 + Nasim Telecom Inc. + +
+ + + +
diff --git a/src/modules/lrkproxy/doc/lrkproxy_admin.xml b/src/modules/lrkproxy/doc/lrkproxy_admin.xml new file mode 100644 index 00000000000..c826047040a --- /dev/null +++ b/src/modules/lrkproxy/doc/lrkproxy_admin.xml @@ -0,0 +1,366 @@ + + + +%docentities; + +]> + + + + + + &adminguide; + +
+ Overview + + This is a module that enables media streams to be relayed via + pylrkproxy engine that exist in: + https://github.com/mojtabaesfandiari/pylrkproxy + It does relaying audio streams between peers in + PREROUTING netfilter-hooking section in kernel-space linux. + The LRKProxy architecture is composed of two + different layers. These layers are independent of each + other. + For more information about LRKProxy architecture, please visit our paper in ieeexplore: + https://ieeexplore.ieee.org/document/9303608 + +
+ +
+ LRKProxy Architecture +
+ LRKP_Controlling Layer (LRKP_CL) + + The first layer is developed as User-Space + application that allows User-Space to directly + access and manipulate cache data + buffer and packet buffer in Kernel-Space. This layer + gets all information about creating new sessions, + active sessions and teardown sessions which is + gotten from SDP body during signaling plan and relay + them to the LRKP-Transport Stateful Layer (LRKP- + TSL). + +
+
+ LRKP_Transport Stateful Layer (LRKP_TSL) + + The second layer is developed in Kernel-Space as + a main decision point for RTP admission controller + and Quickpath selector to where a received packet + should be forwarded with power of packet mangling + framework in the network stack. + +
+ + The LRKP_CL and LRKP-TSL could be run as + independence functions on different machines. We + could have one LRKP_CL with multiple LRKP-TSL + on different machines. The LRKP_CL could works + with all LRKP-TSL with different strategies(lrkp_alg parameter). + +
+
+ Multiple LRKProxy usage + + The LRKP_CL Layer can support multiple LRKP_TSL Layer + for balancing/distribution and control/selection purposes. + + + The module allows definition of several sets of LRKP_TSL. + Load-balancing will be performed over predefine algorithm by setting lrkp_alg parameter. + + + + IMPORTANT: This module does not support balancing inside a set like as is done RTPProxy module based on + the weight of each rtpproxy from the set. The balancing would be run on different machine + +
+ +
+ Dependencies +
+ &kamailio; Modules + + The following modules must be loaded before this module: + + + + tm module - (optional) if you want to + have lrkproxy_manage() fully functional + + + + +
+
+ External Libraries or Applications + + The following libraries or applications must be installed before + running &kamailio; with this module loaded: + + + + None. + + + + +
+
+ Parameters +
+ <varname>lrkproxy_sock</varname> (string) + + Used to define the list of LRKP_TSL instances to connect to. These can + be UNIX sockets or IPv4/IPv6 UDP sockets. Each modparam entry will + insert sockets into a single set with default value set ID '0'. + To define multiple LRKP_TSL, just add the instances in each modparam. + + + + Default value is NONE (disabled). + + + + Set <varname>lrkproxy_sock</varname> parameter + +... +# single lrkproxy +modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080") + +# multiple lrkproxies for LB in diffenrent machine +modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080") +modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.109:8080") + +... + + +
+
+ <varname>lrkproxy_disable_tout</varname> (integer) + + Once LRKP_TSL was found unreachable and marked as disabled, the + LRKP_CL module will not attempt to establish communication to LRKP_TSL + for lrkproxy_disable_tout seconds. + + + + Default value is 60. + + + + Set <varname>lrkproxy_disable_tout</varname> parameter + +... +modparam("lrkproxy", "lrkproxy_disable_tout", 20) +... + + +
+
+ <varname>lrkproxy_tout</varname> (integer) + + Timeout value in waiting for reply from LRKP_TSL. + + + + Default value is 1. + + + + Set <varname>lrkproxy_tout</varname> parameter + +... +modparam("lrkproxy", "lrkproxy_tout", 2) +... + + +
+
+ <varname>lrkproxy_retr</varname> (integer) + + How many times the LRKP_CL should retry to send and receive after + timeout was generated. + + + + Default value is 5. + + + + Set <varname>lrkproxy_retr</varname> parameter + +... +modparam("lrkproxy", "lrkproxy_retr", 2) +... + + +
+
+ <varname>lrkp_alg</varname> (integer) + + This parameter set the algorithm of LRKP_TSL selection. + lrk_LINER=0, + lrk_RR=1 + + + + Default value is 0. + + + + Set <varname>lrkp_alg</varname> parameter + +... +modparam("lrkproxy", "lrkp_alg", 1) +... + + +
+ +
+ <varname>hash_table_tout</varname> (integer) + + Number of seconds after an lrkproxy hash table entry is marked for + deletion. By default, this parameter is set to 3600 (seconds). + + + To maintain information about a selected rtp machine node, for a given + call, entries are added in a hashtable of (callid, viabranch) pairs. When + command comes, lookup callid, viabranch pairs. If found, return chosen node. If not + found, choose a new node, insert it in the hastable and return the + chosen node. + + + NOTE: In the current implementation, the actual deletion happens on the + fly, while insert/remove/lookup the hastable, only for the entries in + the insert/remove/lookup path. + + + NOTE: When configuring this parameter, one should consider maximum call + time VS share memory for unfinished calls. + + + + Default value is 3600. + + + + Set <varname>hash_table_tout</varname> parameter + +... +modparam("lrkproxy", "hash_table_tout", "3600") +... + + +
+
+ <varname>hash_table_size</varname> (integer) + + Size of the hash table. Default value is 128. + + + + Default value is 128. + + + + Set <varname>hash_table_size</varname> parameter + +... +modparam("lrkproxy", "hash_table_size", 256) +... + + +
+
+
+ Functions +
+ + <function moreinfo="none">set_lrkproxy_set(setid)</function> + + + Sets the Id of the lrkproxy set to be used for the next + lrkproxy_manage() command. The parameter can be an integer or a config + variable holding an integer. + + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, + BRANCH_ROUTE. + + + <function>set_lrkproxy_set</function> usage + +... +set_lrkproxy_set("0"); +lrkproxy_manage(); +... + + +
+
+ + <function moreinfo="none">lrkproxy_manage([flags [, ip_address]])</function> + + + Manage the LRKProxy session - it combines the functionality of + lrkproxy_offer(), lrkproxy_answer() and unforce_lrkproxy(), detecting + internally based on message type and method which one to execute. + + + IMPORTANT:The LRKProxy just has one function relating rtp packets. + It does not support combination of functionality of lrkproxy_offer(), + lrkproxy_answer() and unforce_lrkproxy() and other etc. + So you have to just use lrkproxy_manage. + + Meaning of the parameters is as follows: + + + + flags - flags to turn on some features. + + + + internal,external - The shorthand of this flag is "ie". + This can be used to relay media sessions between two different NIC from internal to external path. + + + + + external,internal - The shorthand of this flag is "ei". + This can be used to relay media sessions between two different NIC from external to internal path. + + + + + ip_address - new SDP IP address.This optional parameter is under development. + + + + This function can be used from ANY_ROUTE. + + + <function>lrkproxy_manage</function> usage + +... +lrkproxy_manage(); +//or +lrkproxy_manage("ie"); +//or +lrkproxy_manage("ei"); + +... + + +
+ + +
+
+
diff --git a/src/modules/lrkproxy/lrkproxy.c b/src/modules/lrkproxy/lrkproxy.c new file mode 100644 index 00000000000..a5b93bbc230 --- /dev/null +++ b/src/modules/lrkproxy/lrkproxy.c @@ -0,0 +1,1733 @@ +/* + * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com + * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#ifndef __USE_BSD +#define __USE_BSD +#endif +#include +#ifndef __FAVOR_BSD +#define __FAVOR_BSD +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../core/flags.h" +#include "../../core/sr_module.h" +#include "../../core/dprint.h" +#include "../../core/data_lump.h" +#include "../../core/data_lump_rpl.h" +#include "../../core/error.h" +#include "../../core/forward.h" +#include "../../core/mem/mem.h" +#include "../../core/parser/parse_from.h" +#include "../../core/parser/parse_to.h" +#include "../../core/parser/parse_uri.h" +#include "../../core/parser/parser_f.h" +#include "../../core/parser/sdp/sdp.h" +#include "../../core/resolve.h" +#include "../../core/timer.h" +#include "../../core/trim.h" +#include "../../core/ut.h" +#include "../../core/pt.h" +#include "../../core/timer_proc.h" +#include "../../core/rpc.h" +#include "../../core/rpc_lookup.h" +#include "../../core/pvar.h" +#include "../../core/lvalue.h" +#include "../../core/msg_translator.h" +#include "../../core/usr_avp.h" +#include "../../core/socket_info.h" +#include "../../core/mod_fix.h" +#include "../../core/dset.h" +#include "../../core/route.h" +#include "../../core/kemi.h" +#include "../../modules/tm/tm_load.h" +#include "lrkproxy.h" +#include "lrkproxy_hash.h" +#include "lrkproxy_funcs.h" + +MODULE_VERSION + + +#if !defined(AF_LOCAL) +#define AF_LOCAL AF_UNIX +#endif +#if !defined(PF_LOCAL) +#define PF_LOCAL PF_UNIX +#endif + +///* NAT UAC test constants */ +//#define NAT_UAC_TEST_C_1918 0x01 +//#define NAT_UAC_TEST_RCVD 0x02 +//#define NAT_UAC_TEST_V_1918 0x04 +//#define NAT_UAC_TEST_S_1918 0x08 +//#define NAT_UAC_TEST_RPORT 0x10 + +#define DEFAULT_LRKP_SET_ID 0 +static str DEFAULT_LRKP_SET_ID_STR = str_init("0"); + +//#define RPC_DEFAULT_NATPING_STATE 1 + +#define RPC_MIN_RECHECK_TICKS 0 +#define RPC_MAX_RECHECK_TICKS (unsigned int)-1 + + +/* Supported version of the LRK proxy command protocol */ +#define SUP_CPROTOVER "20190708" +/* Required additional version of the LRK proxy command protocol */ +#define REQ_CPROTOVER "20190709" +/* Additional version necessary for re-packetization support */ +#define REP_CPROTOVER "20190708" +#define PTL_CPROTOVER "20190708" + +#define CPORT "22333" +#define HASH_SIZE 128 + +static char *gencookie(); +static int lrkp_test(struct lrkp_node*); +static int lrkp_get_config(struct lrkp_node *node); +static int lrkp_set_conntrack_rule(struct lrkproxy_hash_entry *e); + + +static int lrkproxy_force(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more); +static int lrkproxy_unforce(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more); + +static int lrkproxy_manage0(struct sip_msg *msg, char *flags, char *ip); +static int lrkproxy_manage1(struct sip_msg *msg, char *flags, char *ip); +static int lrkproxy_manage2(struct sip_msg *msg, char *flags, char *ip); + +static int change_media_sdp(sip_msg_t *msg, struct lrkproxy_hash_entry *e, const char *flags, enum lrk_operation op); + +static int add_lrkproxy_socks(struct lrkp_set * lrkp_list, char * lrkproxy); +static int fixup_set_id(void ** param, int param_no); +static int set_lrkproxy_set_f(struct sip_msg * msg, char * str1, char * str2); + +static struct lrkp_set * select_lrkp_set(int id_set); + +static int lrkproxy_set_store(modparam_t type, void * val); +static int lrkproxy_add_lrkproxy_set( char * lrk_proxies); + +static int mod_init(void); +static int child_init(int); +static void mod_destroy(void); + +/* Pseudo-Variables */ +//static int pv_get_lrkstat_f(struct sip_msg *, pv_param_t *, pv_value_t *); + +static int lrkproxy_disable_tout = 60; +static int lrkproxy_retr = 5; +static int lrkproxy_tout = 1; +static pid_t mypid; +static unsigned int myseqn = 0; +//static str nolrkproxy_str = str_init("a=nolrkproxy:yes"); +//static str extra_id_pv_param = {NULL, 0}; + +static char ** lrkp_strings=0; +static int lrkp_sets=0; /*used in lrkproxy_set_store()*/ +static int lrkp_set_count = 0; +static unsigned int current_msg_id = (unsigned int)-1; +/* LRK proxy balancing list */ +struct lrkp_set_head * lrkp_set_list =0; +struct lrkp_set * selected_lrkp_set =0; +struct lrkp_set * default_lrkp_set=0; +struct lrkp_node *selected_lrkp_node = 0; +int lrkp_algorithm = LRK_LINER; +static int hash_table_size = 0; +static int hash_table_tout = 3600; + + + +//static char *ice_candidate_priority_avp_param = NULL; +//static int ice_candidate_priority_avp_type; +//static int_str ice_candidate_priority_avp; +//static str lrk_inst_pv_param = {NULL, 0}; +//static pv_spec_t *lrk_inst_pvar = NULL; + +/* array with the sockets used by lrkproxy (per process)*/ +static unsigned int lrkp_no = 0; +static int *lrkp_socks = 0; + + +typedef struct lrkp_set_link { + struct lrkp_set *rset; + pv_spec_t *rpv; +} lrkp_set_link_t; + +/* tm */ +static struct tm_binds tmb; + +/*0-> disabled, 1 ->enabled*/ +//unsigned int *natping_state=0; + +static cmd_export_t cmds[] = { + + {"set_lrkproxy_set", (cmd_function)set_lrkproxy_set_f, 1, + fixup_set_id, 0, + ANY_ROUTE}, + {"lrkproxy_manage", (cmd_function)lrkproxy_manage0, 0, + 0, 0, + ANY_ROUTE}, + {"lrkproxy_manage", (cmd_function)lrkproxy_manage1, 1, + fixup_spve_null, fixup_free_spve_null, + ANY_ROUTE}, + {"lrkproxy_manage", (cmd_function)lrkproxy_manage2, 2, + fixup_spve_spve, fixup_free_spve_spve, + ANY_ROUTE}, + + {0, 0, 0, 0, 0, 0} +}; + +static param_export_t params[] = { + {"lrkproxy_sock", PARAM_STRING|USE_FUNC_PARAM, + (void*)lrkproxy_set_store }, + {"lrkproxy_disable_tout", INT_PARAM, &lrkproxy_disable_tout }, + {"lrkproxy_retr", INT_PARAM, &lrkproxy_retr }, + {"lrkproxy_tout", INT_PARAM, &lrkproxy_tout }, + {"lrkp_alg", INT_PARAM, &lrkp_algorithm }, + {"hash_table_tout", INT_PARAM, &hash_table_tout }, + {"hash_table_size", INT_PARAM, &hash_table_size }, + + {0, 0, 0} +}; + +/** module exports */ +struct module_exports exports= { + "lrkproxy", /* module name */ + DEFAULT_DLFLAGS, /* dlopen flags */ + cmds, /* cmd exports */ + params, /* param exports */ + 0, /* RPC method exports */ + 0, /* exported pseudo-variables */ + 0, /* response handling function */ + mod_init, /* module initialization function */ + child_init, /* per-child init function */ + mod_destroy /* module destroy function */ +}; + + +static int lrkproxy_set_store(modparam_t type, void * val){ + + char * p; + int len; + + p = (char* )val; + + if(p==0 || *p=='\0'){ + return 0; + } + + if(lrkp_sets==0){ + lrkp_strings = (char**)pkg_malloc(sizeof(char*)); + if(!lrkp_strings){ + LM_ERR("no pkg memory left\n"); + return -1; + } + } else {/*realloc to make room for the current set*/ + lrkp_strings = (char**)pkg_reallocxf(lrkp_strings, + (lrkp_sets+1)* sizeof(char*)); + if(!lrkp_strings){ + LM_ERR("no pkg memory left\n"); + return -1; + } + } + + /*allocate for the current set of urls*/ + len = strlen(p); + lrkp_strings[lrkp_sets] = (char*)pkg_malloc((len+1)*sizeof(char)); + + if(!lrkp_strings[lrkp_sets]){ + LM_ERR("no pkg memory left\n"); + return -1; + } + + memcpy(lrkp_strings[lrkp_sets], p, len); + lrkp_strings[lrkp_sets][len] = '\0'; + lrkp_sets++; + + return 0; +} + +struct lrkp_set *get_lrkp_set(str *const set_name) +{ + unsigned int this_set_id; + struct lrkp_set *lrkp_list; + if (lrkp_set_list == NULL) + { + LM_ERR("lrkp set list not configured\n"); + return NULL; + } + /* Only integer set_names are valid at the moment */ + if ((set_name->s == NULL) || (set_name->len == 0)) + { + LM_ERR("Invalid set name '%.*s'\n", set_name->len, set_name->s); + return NULL; + } + if (str2int(set_name, &this_set_id) < 0) + { + LM_ERR("Invalid set name '%.*s' - must be integer\n", set_name->len, set_name->s); + return NULL; + } + + lrkp_list = select_lrkp_set(this_set_id); + + if(lrkp_list==NULL){ /*if a new id_set : add a new set of lrkp*/ + lrkp_list = shm_malloc(sizeof(struct lrkp_set)); + if(!lrkp_list){ + LM_ERR("no shm memory left\n"); + return NULL; + } + memset(lrkp_list, 0, sizeof(struct lrkp_set)); + lrkp_list->id_set = this_set_id; + if (lrkp_set_list->lset_first == NULL) + { + lrkp_set_list->lset_first = lrkp_list; + } else { + lrkp_set_list->lset_last->lset_next = lrkp_list; + } + lrkp_set_list->lset_last = lrkp_list; + lrkp_set_count++; + + if (this_set_id == DEFAULT_LRKP_SET_ID) + { + default_lrkp_set = lrkp_list; + } + } + return lrkp_list; +} + +int insert_lrkp_node(struct lrkp_set *const lrkp_list, const str *const url, const int weight, const int enable) +{ + struct lrkp_node *pnode; + + if ((pnode = shm_malloc(sizeof(struct lrkp_node) + url->len + 1)) == NULL) + { + LM_ERR("out of shm memory\n"); + return -1; + } + + memset(pnode, 0, sizeof(struct lrkp_node) + url->len + 1); + + + struct lrkp_node_conf *node_conf; + node_conf = shm_malloc(sizeof(struct lrkp_node_conf)); + if (!node_conf) + { + LM_ERR("out of shm memory\n"); + return -1; + } + + memset(node_conf, 0, sizeof(struct lrkp_node_conf)); + pnode->lrkp_n_c = node_conf; + + pnode->idx = lrkp_no++; + pnode->ln_weight = weight; + pnode->ln_umode = 0; + pnode->ln_enable = enable; + /* Permanently disable if marked as disabled */ +// pnode->ln_recheck_ticks = disabled ? RPC_MAX_RECHECK_TICKS : 0; + pnode->ln_url.s = (char*)(pnode + 1); + memcpy(pnode->ln_url.s, url->s, url->len); + pnode->ln_url.len = url->len; + + LM_DBG("url is '%.*s'\n", pnode->ln_url.len, pnode->ln_url.s); + + /* Find protocol and store address */ + pnode->ln_address = pnode->ln_url.s; + if (strncasecmp(pnode->ln_address, "udp:", 4) == 0) { + pnode->ln_umode = 1; + pnode->ln_address += 4; + } else if (strncasecmp(pnode->ln_address, "udp6:", 5) == 0) { + pnode->ln_umode = 6; + pnode->ln_address += 5; + } else if (strncasecmp(pnode->ln_address, "unix:", 5) == 0) { + pnode->ln_umode = 0; + pnode->ln_address += 5; + } + + if (lrkp_list->ln_first == NULL) + { + lrkp_list->ln_first = pnode; + } else { + lrkp_list->ln_last->ln_next = pnode; + } + lrkp_list->ln_last = pnode; + lrkp_list->lrkp_node_count++; + + return 0; +} + +static int add_lrkproxy_socks(struct lrkp_set * lrkp_list, + char * lrkproxy){ + /* Make lrk proxies list. */ + char *p, *p1, *p2, *plim; + int weight; + str url; + + p = lrkproxy; + plim = p + strlen(p); + + for(;;) { + weight = 1; + while (*p && isspace((int)*p)) + ++p; + if (p >= plim) + break; + p1 = p; + while (*p && !isspace((int)*p)) + ++p; + if (p <= p1) + break; /* may happen??? */ + /* Have weight specified? If yes, scan it */ + p2 = memchr(p1, '=', p - p1); + if (p2 != NULL) { + weight = strtoul(p2 + 1, NULL, 10); + } else { + p2 = p; + } + + url.s = p1; + url.len = (p2-p1); + insert_lrkp_node(lrkp_list, &url, weight, 0); + } + return 0; +} + +/* 0-succes + * -1 - erorr + * */ +static int lrkproxy_add_lrkproxy_set( char * lrk_proxies) +{ + char *p,*p2; + struct lrkp_set * lrkp_list; + str id_set; + + /* empty definition? */ + p= lrk_proxies; + if(!p || *p=='\0'){ + return 0; + } + + for(;*p && isspace(*p);p++); + if(*p=='\0'){ + return 0; + } + + lrk_proxies = strstr(p, "=="); + if(lrk_proxies){ + if(*(lrk_proxies +2)=='\0'){ + LM_ERR("script error -invalid lrk proxy list!\n"); + return -1; + } + + *lrk_proxies = '\0'; + p2 = lrk_proxies-1; + for(;isspace(*p2); *p2 = '\0',p2--); + id_set.s = p; id_set.len = p2 - p+1; + + if(id_set.len <= 0){ + LM_ERR("script error -invalid set_id value!\n"); + return -1; + } + + lrk_proxies+=2; + }else{ + lrk_proxies = p; + id_set = DEFAULT_LRKP_SET_ID_STR; + } + + for(;*lrk_proxies && isspace(*lrk_proxies);lrk_proxies++); + + if(!(*lrk_proxies)){ + LM_ERR("script error -empty lrkproxy list\n"); + return -1;; + } + + lrkp_list = get_lrkp_set(&id_set); + if (lrkp_list == NULL) + { + LM_ERR("Failed to get or create lrkp_list for '%.*s'\n", id_set.len, id_set.s); + return -1; + } + + if(add_lrkproxy_socks(lrkp_list, lrk_proxies)!= 0){ + return -1; + } + + return 0; +} + + +static int fixup_set_id(void ** param, int param_no) +{ + int int_val, err; + struct lrkp_set* lrkp_list; + lrkp_set_link_t *lrkl = NULL; + str s; + + lrkl = (lrkp_set_link_t*)pkg_malloc(sizeof(lrkp_set_link_t)); + if(lrkl==NULL) { + LM_ERR("no more pkg memory\n"); + return -1; + } + memset(lrkl, 0, sizeof(lrkp_set_link_t)); + s.s = (char*)*param; + s.len = strlen(s.s); + + if(s.s[0] == PV_MARKER) { + int_val = pv_locate_name(&s); + if(int_val<0 || int_val!=s.len) { + LM_ERR("invalid parameter %s\n", s.s); + pkg_free(lrkl); + return -1; + } + lrkl->rpv = pv_cache_get(&s); + if(lrkl->rpv == NULL) { + LM_ERR("invalid pv parameter %s\n", s.s); + pkg_free(lrkl); + return -1; + } + } else { + int_val = str2s(*param, strlen(*param), &err); + if (err == 0) { + pkg_free(*param); + if((lrkp_list = select_lrkp_set(int_val)) ==0){ + LM_ERR("lrkp_proxy set %i not configured\n", int_val); + pkg_free(lrkl); + return E_CFG; + } + lrkl->rset = lrkp_list; + } else { + LM_ERR("bad number <%s>\n", (char *)(*param)); + pkg_free(lrkl); + return E_CFG; + } + } + *param = (void*)lrkl; + return 0; +} + + +static int +mod_init(void) +{ + int i; +// pv_spec_t avp_spec; +// str s; +// unsigned short avp_flags; + +// if(lrkproxy_rpc_init()<0) +// { +// LM_ERR("failed to register RPC commands\n"); +// return -1; +// } + + /* Configure the head of the lrkp_set_list */ + lrkp_set_list = shm_malloc(sizeof(struct lrkp_set_head)); + if (lrkp_set_list == NULL) + { + LM_ERR("no shm memory for lrkp_set_list\n"); + return -1; + } + memset(lrkp_set_list, 0, sizeof(struct lrkp_set_head)); + + + /* storing the list of lrk proxy sets in shared memory*/ + for(i=0;i callid& via-branch relation */ + if (hash_table_size < 1){ + hash_table_size = HASH_SIZE; //the default size 128 entry. + } + + if (!lrkproxy_hash_table_init(hash_table_size)) { + LM_ERR("lrkproxy_hash_table_init(%d) failed!\n", hash_table_size); + return -1; + } else { +// LM_DBG("lrkproxy_hash_table_init(%d) success!\n", hash_table_size); + LM_INFO(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table_init(%d) success!\n", hash_table_size); + } + + + + /* load tm module*/ + if (load_tm_api( &tmb ) < 0) + { + LM_DBG("could not load the TM-functions - answer-offer model" + " auto-detection is disabled\n"); + memset(&tmb, 0, sizeof(struct tm_binds)); + } + + return 0; +} + + +static int +child_init(int rank) +{ + int n; + char *cp; + struct addrinfo hints, *res; + struct lrkp_set *lrkp_list; + struct lrkp_node *pnode; + + if(lrkp_set_list==NULL ) + return 0; + + /* do not init sockets for PROC_INIT and main process when fork=yes */ + if(rank==PROC_INIT || (rank==PROC_MAIN && dont_fork==0)) { + return 0; + } + + /* Iterate known LRK proxies - create sockets */ + mypid = getpid(); + + lrkp_socks = (int*)pkg_malloc( sizeof(int)*lrkp_no ); + if (lrkp_socks==NULL) { + LM_ERR("no more pkg memory\n"); + return -1; + } + memset(lrkp_socks, -1, sizeof(int)*lrkp_no); + + for(lrkp_list = lrkp_set_list->lset_first; lrkp_list != 0; + lrkp_list = lrkp_list->lset_next){ + + for (pnode=lrkp_list->ln_first; pnode!=0; pnode = pnode->ln_next){ + char *hostname; + + if (pnode->ln_umode == 0) { + lrkp_socks[pnode->idx] = -1; + goto rptest; + } + + /* + * This is UDP or UDP6. Detect host and port; lookup host; + * do connect() in order to specify peer address + */ + hostname = (char*)pkg_malloc(sizeof(char) * (strlen(pnode->ln_address) + 1)); + if (hostname==NULL) { + LM_ERR("no more pkg memory\n"); + return -1; + } + strcpy(hostname, pnode->ln_address); + + cp = strrchr(hostname, ':'); + if (cp != NULL) { + *cp = '\0'; + cp++; + } + if (cp == NULL || *cp == '\0') + cp = CPORT; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = 0; + hints.ai_family = (pnode->ln_umode == 6) ? AF_INET6 : AF_INET; + hints.ai_socktype = SOCK_DGRAM; + if ((n = getaddrinfo(hostname, cp, &hints, &res)) != 0) { + LM_ERR("%s\n", gai_strerror(n)); + pkg_free(hostname); + return -1; + } + pkg_free(hostname); + + lrkp_socks[pnode->idx] = socket((pnode->ln_umode == 6) + ? AF_INET6 : AF_INET, SOCK_DGRAM, 0); + if ( lrkp_socks[pnode->idx] == -1) { + LM_ERR("can't create socket\n"); + freeaddrinfo(res); + return -1; + } + + if (connect( lrkp_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) { + LM_ERR("can't connect to a LRK proxy\n"); + close( lrkp_socks[pnode->idx] ); + lrkp_socks[pnode->idx] = -1; + freeaddrinfo(res); + return -1; + } + freeaddrinfo(res); +rptest: + pnode->ln_enable = lrkp_test(pnode); + if (pnode->ln_enable) { //get lrk proxy config if it is enable. +// LM_INFO("lrkp_test test is calling here\n"); //enable next line. + lrkp_get_config(pnode); + } + } + } + + return 0; +} + + +static void mod_destroy(void) +{ + struct lrkp_set * crt_list, * last_list; + struct lrkp_node * crt_lrkp, *last_lrkp; + + /*free the shared memory*/ +// if (natping_state) +// shm_free(natping_state); + + if(lrkp_set_list == NULL) + return; + + for(crt_list = lrkp_set_list->lset_first; crt_list != NULL; ){ + + for(crt_lrkp = crt_list->ln_first; crt_lrkp != NULL; ){ + + last_lrkp = crt_lrkp; + crt_lrkp = last_lrkp->ln_next; + shm_free(last_lrkp); + } + + last_list = crt_list; + crt_list = last_list->lset_next; + shm_free(last_list); + } + + shm_free(lrkp_set_list); + + /* destroy the hash table */ + if (!lrkproxy_hash_table_destroy()) { + LM_ERR("lrkproxy_hash_table_destroy() failed!\n"); + } else { + LM_DBG("lrkproxy_hash_table_destroy() success!\n"); + } + +} + + +static char * gencookie(void) +{ + static char cook[34]; + + sprintf(cook, "%d_%u ", (int)mypid, myseqn); + myseqn++; + return cook; +} + +static int lrkp_test(struct lrkp_node *node) +{ + int buflen = 256; + char buf[buflen]; + struct iovec v[2] = {{NULL, 0}, {"P", 1}}; + + memset(buf, 0, buflen); + memcpy(buf, send_lrkp_command(node, v, 2, 0), buflen); + +// if (buf == NULL) { + if (!buf[0]) { + LM_ERR("can't ping the lrk proxy %s, Disable it right now.\n", node->ln_url.s); + return 0; + } + + char *resp = buf + v[0].iov_len + v[1].iov_len + 1; + if (memcmp(resp, "PONG", 4) == 0) +// LM_DBG("Recieve PONG response from lrk proxy server %s, Enable it right now.\n", node->ln_url.s); + LM_INFO("Recieve PONG response from lrk proxy server %s, Enable it right now.\n", node->ln_url.s); + + return 1; + +} + +static int lrkp_get_config(struct lrkp_node *node){ + + int buflen = 256; + char buf[buflen]; + struct iovec v[2] = {{NULL, 0}, {"G", 1}}; + struct lrkp_node_conf *lnconf = NULL; + + memset(buf, 0, buflen); + memcpy(buf, send_lrkp_command(node, v, 2, 0), buflen); + +// if (buf == NULL) { + if (!buf[0]) { + LM_ERR("can't get config of the lrk proxy %s, Disable it right now.\n", node->ln_url.s); + return 0; + } + + lnconf = (struct lrkp_node_conf *)(buf + v[0].iov_len + v[1].iov_len + 1); + + if (lnconf == NULL){ + LM_ERR("can't get config of the lrk proxy %s, Disable it right now.\n", node->ln_url.s); + return 0; + } + + + memcpy(node->lrkp_n_c, lnconf, sizeof(struct lrkp_node_conf)); + +// node->lrkp_n_c = lnconf; + LM_INFO("the lrk proxy %s is configured successfully right now.\n", node->ln_url.s); + LM_INFO("buffer internal:%s\n", node->lrkp_n_c->internal_ip); + LM_INFO("buffer external:%s\n", node->lrkp_n_c->external_ip); + LM_INFO("buffer start_port:%d\n", node->lrkp_n_c->start_port); + LM_INFO("buffer end_port:%d\n", node->lrkp_n_c->end_port); + LM_INFO("buffer current_port:%d\n", node->lrkp_n_c->current_port); + + return 1; + + +} + +static int lrkp_set_conntrack_rule(struct lrkproxy_hash_entry *e) { + int buflen = 254; + char buf[buflen]; + int v_len = 0; + + char src_ipv4[20]; + char src_port[20]; + char dst_ipv4[20]; + char dst_port[20]; + char snat_ipv4[20]; + char snat_port[20]; + char dnat_ipv4[20]; + char dnat_port[20]; + char timeout[20]; + char callid[50]; + + struct iovec v[] = { + {NULL, 0}, /* reserved (cookie) */ + {"S", 1}, /* command & common options */ + {NULL, 0}, /* src_ipv4 */ + {NULL, 0}, /* dst_ipnv4 */ + {NULL, 0}, /* snat_ipv4 */ + {NULL, 0}, /* dnat_ipv4 */ + {NULL, 0}, /* src_port */ + {NULL, 0}, /* dst_port*/ + {NULL, 0}, /* snat_port */ + {NULL, 0}, /* dnat_port*/ + {NULL, 0}, /* timeout to clear conntrack entry*/ + {NULL, 0}, /* callid of session */ + }; + + v_len += v[1].iov_len; + + //set src_ipv4 to buffer. + sprintf(src_ipv4, " %.*s ", e->src_ipv4.len, e->src_ipv4.s); + v[2].iov_base = src_ipv4; + v[2].iov_len = strlen(v[2].iov_base); + v_len += v[2].iov_len; + + //set dst_ipv4 to buffer. + sprintf(dst_ipv4, "%.*s ", e->dst_ipv4.len, e->dst_ipv4.s); + v[3].iov_base = dst_ipv4; + v[3].iov_len = strlen(v[3].iov_base); + v_len += v[3].iov_len; + + //set snat_ipv4 to buffer. + sprintf(snat_ipv4, "%.*s ", e->snat_ipv4.len, e->snat_ipv4.s); + v[4].iov_base = snat_ipv4; + v[4].iov_len = strlen(v[4].iov_base); + v_len += v[4].iov_len; + + //set dnat_ipv4 to buffer. + sprintf(dnat_ipv4, "%.*s ", e->dnat_ipv4.len, e->dnat_ipv4.s); + v[5].iov_base = dnat_ipv4; + v[5].iov_len = strlen(v[5].iov_base); + v_len += v[5].iov_len; + + //set src_port to buffer. + sprintf(src_port, "%.*s ", e->src_port.len, e->src_port.s); + v[6].iov_base = src_port; + v[6].iov_len = strlen(v[6].iov_base); + v_len += v[6].iov_len; + + //set dst_port to buffer. + sprintf(dst_port, "%.*s ", e->dst_port.len, e->dst_port.s); + v[7].iov_base = dst_port; + v[7].iov_len = strlen(v[7].iov_base); + v_len += v[7].iov_len; + + //set snat_port to buffer. + sprintf(snat_port, "%.*s ", e->snat_port.len, e->snat_port.s); + v[8].iov_base = snat_port; + v[8].iov_len = strlen(v[8].iov_base); + v_len += v[8].iov_len; + + //set dnat_port to buffer. + sprintf(dnat_port, "%.*s ", e->dnat_port.len, e->dnat_port.s); + v[9].iov_base = dnat_port; + v[9].iov_len = strlen(v[9].iov_base); + v_len += v[9].iov_len; + + //set timeout to buffer. Set to 60 sec for default. + sprintf(timeout, "%d ", 60); + v[10].iov_base = timeout; + v[10].iov_len = strlen(v[10].iov_base); + v_len += v[10].iov_len; + + //set callid to buffer. + sprintf(callid, "%.*s ", e->callid.len, e->callid.s); + v[11].iov_base = callid; + v[11].iov_len = strlen(v[11].iov_base); + v_len += v[11].iov_len; +// LM_ERR("e->callid.len is:%d right now.\n\n", e->callid.len); + + memset(buf, 0, buflen); + memcpy(buf, send_lrkp_command(e->node, v, 12, v_len), buflen); +// + +// if (buf == NULL) { + if (!buf[0]) { + LM_ERR("can't ping the lrk proxy %s, Disable it right now.\n", e->node->ln_url.s); + return 0; + } + + v_len += v[0].iov_len; + + +// char *resp = buf + v[0].iov_len + v[1].iov_len + v[2].iov_len; + char *resp = buf + v_len; + if (memcmp(resp, "OK", 2) == 0) { + LM_INFO("Recieve OK response from lrk proxy server %s, Rule set successfully.\n", e->node->ln_url.s); + LM_DBG("Recieve OK response from lrk proxy server %s, Rule set successfully.\n", e->node->ln_url.s); + } + return 1; + +} + + +char *send_lrkp_command(struct lrkp_node *node, struct iovec *v, int vcnt, int more) +{ + struct sockaddr_un addr; + int fd, len, i; + int ret; +// char *cp; + static char buf[256]; + struct pollfd fds[1]; + + memset(buf, 0, 256); + len = 0; +// cp = buf; + if (node->ln_umode == 0) { + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + strncpy(addr.sun_path, node->ln_address, + sizeof(addr.sun_path) - 1); +#ifdef HAVE_SOCKADDR_SA_LEN + addr.sun_len = strlen(addr.sun_path); +#endif + + fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (fd < 0) { + LM_ERR("can't create socket\n"); + goto badproxy; + } + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(fd); + LM_ERR("can't connect to lrk proxy\n"); + goto badproxy; + } + + do { + len = writev(fd, v + 1, vcnt - 1); + } while (len == -1 && errno == EINTR); + if (len <= 0) { + close(fd); + LM_ERR("can't send command to a lrk proxy %s\n", node->ln_url.s); + goto badproxy; + } + do { + len = read(fd, buf, sizeof(buf) - 1); + } while (len == -1 && errno == EINTR); + close(fd); + if (len <= 0) { + LM_ERR("can't read reply from a lrk proxy %s\n", node->ln_url.s); + goto badproxy; + } + } else { + fds[0].fd = lrkp_socks[node->idx]; + fds[0].events = POLLIN; + fds[0].revents = 0; + /* Drain input buffer */ + ret = -1; + while ((poll(fds, 1, 0) == 1) && + ((fds[0].revents & POLLIN) != 0) && ret != 0) { + ret = recv(lrkp_socks[node->idx], buf, sizeof(buf) - 1, 0); + fds[0].revents = 0; + } + v[0].iov_base = gencookie(); + v[0].iov_len = strlen(v[0].iov_base); + for (i = 0; i < lrkproxy_retr; i++) { + do { + len = writev(lrkp_socks[node->idx], v, vcnt); + } while (len == -1 && (errno == EINTR || errno == ENOBUFS)); + if (len <= 0) { + LM_ERR("can't send command to a lrk proxy %s\n", node->ln_url.s); + goto badproxy; + } + while ((poll(fds, 1, lrkproxy_tout * 1000) == 1) && + (fds[0].revents & POLLIN) != 0) { + do { + len = recv(lrkp_socks[node->idx], buf, sizeof(buf) - 1, 0); + } while (len == -1 && errno == EINTR); + if (len <= 0) { + LM_ERR("can't read reply from a lrk proxy %s\n", node->ln_url.s); + goto badproxy; + } + if (len >= (v[0].iov_len - 1) && + memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) { //check coocke validation. + char *command = buf + v[0].iov_len; + switch (*command) { + case 'P': + if (len == v[0].iov_len + v[1].iov_len + 4 + 1) + goto out; +// break; + case 'G': + if (len == v[0].iov_len + v[1].iov_len + sizeof(struct lrkp_node_conf) + 1) + goto out; +// break; + case 'S': + if (len == more + v[0].iov_len + 2) + goto out; +// break; + } + + } + fds[0].revents = 0; + } + } + if (i == lrkproxy_tout) { + LM_ERR("timeout waiting reply from a lrk proxy server %s\n", node->ln_url.s); + goto badproxy; + + } + } + out: + return buf; + badproxy: + LM_ERR("lrk proxy <%s> does not respond, disable it\n", node->ln_url.s); + node->ln_enable = 0; +// node->ln_recheck_ticks = get_ticks() + lrkproxy_disable_tout; + return buf; +} + +/* + * select the set with the id_set id + */ +static struct lrkp_set * select_lrkp_set(int id_set ){ + + struct lrkp_set * lrkp_list; + /*is it a valid set_id?*/ + + if(!lrkp_set_list) + { + LM_ERR("lrkproxy set list not initialised\n"); + return NULL; + } + + for(lrkp_list=lrkp_set_list->lset_first; lrkp_list!=NULL && + lrkp_list->id_set!=id_set; lrkp_list=lrkp_list->lset_next); + + return lrkp_list; +} + + +struct lrkp_node *select_lrkp_node(int do_test) +{ + struct lrkp_node* node; + int was_forced; + int was_forced2; + + if(!selected_lrkp_set){ + LM_ERR("script error -no valid set selected\n"); + return NULL; + } + /* Most popular case: 1 proxy, nothing to calculate */ + if (selected_lrkp_set->lrkp_node_count == 1) { + node = selected_lrkp_set->ln_first; +// if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()) + if (!node->ln_enable) { + node->ln_enable = lrkp_test(node); + if (node->ln_enable) { //get lrk proxy config if it is enable. + lrkp_get_config(node); + return node; + } + } + return node->ln_enable ? node : NULL; +// return NULL; + } + + + /* Check node is enable and test it again*/ +retry: + for (node=selected_lrkp_set->ln_first; node!=NULL; node=node->ln_next) { + + if (!node->ln_enable) { + /* Try to enable if it's time to try. */ + node->ln_enable = lrkp_test(node); + if (node->ln_enable) //get lrk proxy config if it is enable. + lrkp_get_config(node); + } + } + + if (lrkp_algorithm == LRK_LINER) { + was_forced = 0; + retry2: + for (node = selected_lrkp_set->ln_first; node != NULL; node = node->ln_next) + if (node->ln_enable) + goto found; + if (was_forced) + return NULL; + + was_forced = 1; + //trying to enable all lrkproxy and check again. + for (node = selected_lrkp_set->ln_first; node != NULL; node = node->ln_next) { + /* Try to enable if it's time to try. */ + node->ln_enable = lrkp_test(node); + if (node->ln_enable) //get lrk proxy config if it is enable. + lrkp_get_config(node); + } + + + goto retry2; + } + else if(lrkp_algorithm == LRK_RR) { + was_forced2 = 0; +retry3: + if (!selected_lrkp_node) { + selected_lrkp_node = selected_lrkp_set->ln_first; + node = selected_lrkp_set->ln_first; + if(node->ln_enable) + goto found; +// was_forced2 = 1; + } + for (node = selected_lrkp_node->ln_next; node != NULL; node = node->ln_next) +// for (node = selected_lrkp_node; node != NULL; node = node->ln_next) { + if (node->ln_enable) { + selected_lrkp_node = node; + goto found; + } +// selected_lrkp_node = node->ln_next; +// if (sumcut < node->ln_weight) +// sumcut -= node->ln_weight; + + if (was_forced2) + return NULL; + + was_forced2 = 1; + selected_lrkp_node = NULL; + + goto retry3; + } + + found: + if (do_test) { +// //todo... + node->ln_enable = lrkp_test(node); + if (!node->ln_enable) + goto retry; + } + return node; +} + +//static int change_media_sdp(sip_msg_t *msg, struct lrkp_node *n, const char *flags, int type) { +static int change_media_sdp(sip_msg_t *msg, struct lrkproxy_hash_entry *e, const char *flags, enum lrk_operation op) { + struct lump *l; + str body; + str newbody; + + int len; + char *start_sdp_o = NULL; //"o="; + char *start_sdp_s = NULL; //"s="; + char *start_sdp_c = NULL; //"c=IN IP4"; + char *start_sdp_m = NULL; //"m=audio"; + char *ip_selected = NULL; + char *sdp_param_start = NULL; + char *sdp_param_end = NULL; + char *off=NULL; + char sdp_new_o[128]; + char sdp_new_s[128]; + char sdp_new_c[128]; + char sdp_new_m[128]; + + body.s = get_body(msg); + if (body.s == 0) { + LM_ERR("failed to get the message body\n"); + return -1; + } + + body.len = msg->len - (int) (body.s - msg->buf); + if (body.len == 0) { + LM_DBG("message body has zero length\n"); + return -1; + } +// LM_INFO("body:<%.*s>\n", body.len, body.s); + + //allocate new buffer to new sdp buffer. + newbody.len = 1024; + newbody.s = pkg_malloc(newbody.len); + if (newbody.s == NULL) { + LM_ERR("out of pkg memory\n"); + return -1; + } + memset(newbody.s, 0, 1024); + + off = body.s; + start_sdp_o = strstr(off, "o="); + start_sdp_s = strstr(off, "s="); + start_sdp_c = strstr(off, "c=IN IP4"); + start_sdp_m = strstr(off, "m=audio"); + + //The external_ip should be set in config file for relaying RTP media between NIC. +// if (e->node->lrkp_n_c->external_ip && flags) { + if(flags) { + if (strstr(flags, "ei")) { + ip_selected = e->node->lrkp_n_c->internal_ip;// lrk_node->internal_ip; + } else if (strstr(flags, "ie")) { + ip_selected = e->node->lrkp_n_c->external_ip; //lrk_node->external_ip; + } else { + LM_INFO("unknown flags, use internal_ip\n"); + ip_selected = e->node->lrkp_n_c->internal_ip; + } + } + else { + LM_INFO("no flags set, use internal_ip\n"); + ip_selected = e->node->lrkp_n_c->internal_ip; + } + + if (op == OP_OFFER) { + e->dst_ipv4.s = ip_selected; + e->dst_ipv4.len = strlen(e->dst_ipv4.s); + + str current_port; + current_port.s = int2str(e->node->lrkp_n_c->current_port, ¤t_port.len); + + if (shm_str_dup(&e->dst_port, ¤t_port) < 0) { + LM_ERR("lrkproxy fail to insert dst_port, calllen=%d dst_port=%.*s\n", + e->callid.len, current_port.len, current_port.s); + lrkproxy_hash_table_free_entry(e); + return 0; + } + +// e->dst_port = e->node->lrkp_n_c->current_port; + } + else if (op == OP_ANSWER){ + e->snat_ipv4.s = ip_selected; + e->snat_ipv4.len = strlen(e->snat_ipv4.s); + + str current_port; + unsigned int snat_port; + + str2int(&e->dst_port, &snat_port); + snat_port += 2; + + current_port.s = int2str(snat_port, ¤t_port.len); + + if (shm_str_dup(&e->snat_port, ¤t_port) < 0) { + LM_ERR("lrkproxy fail to insert snat_port, calllen=%d snat_port=%.*s\n", + e->callid.len, current_port.len, current_port.s); + lrkproxy_hash_table_free_entry(e); + return 0; + } + +// e->snat_port = e->dst_port + 2; + } + + + while (*off != EOB) //while end of body. + { + sdp_param_start = off; + sdp_param_end = sdp_param_start; + while (*sdp_param_end != CR && *sdp_param_end != LF && *sdp_param_end != EOB) sdp_param_end++; + len = (int) (sdp_param_end - sdp_param_start); + if ((int) (start_sdp_o - off) == 0) { + memset(sdp_new_o, 0, 128); + snprintf(sdp_new_o, 128, "o=lrkproxy %s %s IN IP4 %s\r", SUP_CPROTOVER, REQ_CPROTOVER, ip_selected); + strncat(newbody.s, sdp_new_o, strlen(sdp_new_o)); + off += len + 1; + continue; + } + if ((int) (start_sdp_s - off) == 0) { + memset(sdp_new_s, 0, 128); + snprintf(sdp_new_s, 128, "s=lrkproxy Support only Audio Call\r"); + strncat(newbody.s, sdp_new_s, strlen(sdp_new_s)); + off += len + 1; + continue; + } + if ((int) (start_sdp_c - off) == 0) { + memset(sdp_new_c, 0, 128); + snprintf(sdp_new_c, 128, "c=IN IP4 %s\r", ip_selected); + strncat(newbody.s, sdp_new_c, strlen(sdp_new_c)); + off += len + 1; + continue; + } + if ((int)(start_sdp_m - off) == 0){ + memset(sdp_new_m, 0, 128); + char *avp_flags = off; +// int occure = 0; + for (;*avp_flags && !isspace(*avp_flags); avp_flags++); + for (avp_flags++;*avp_flags && !isspace(*avp_flags); avp_flags++); + avp_flags++; + if (op == OP_OFFER) + snprintf(sdp_new_m, 128, "m=audio %.*s %.*s\r",e->dst_port.len, e->dst_port.s, (int)(len - (avp_flags-off)), avp_flags); +// snprintf(sdp_new_m, 128, "m=audio %d %.*s\r",e->node->lrkp_n_c->current_port, (int)(len - (avp_flags-off)), avp_flags); + else if (op == OP_ANSWER) + snprintf(sdp_new_m, 128, "m=audio %.*s %.*s\r",e->snat_port.len, e->snat_port.s, (int)(len - (avp_flags-off)), avp_flags); +// snprintf(sdp_new_m, 128, "m=audio %d %.*s\r",e->node->lrkp_n_c->current_port, (int)(len - (avp_flags-off)), avp_flags); +// printf("%.*s\n\n", len - (avp_flags-off), avp_flags); + strncat(newbody.s,sdp_new_m, strlen(sdp_new_m)); + off += len+1; + continue; + } + + strncat(newbody.s, off, len + 1); + off += len + 1; + } + + +// LM_INFO("%.*s", (int)strlen(newbody.s), newbody.s); + l = del_lump(msg, body.s - msg->buf, body.len, 0); + if (!l) { + LM_ERR("del_lump failed\n"); + return -1; + } + + + if (insert_new_lump_after(l, newbody.s, strlen(newbody.s), 0) == 0) { + LM_ERR("could not insert new lump\n"); + pkg_free(newbody.s); + return -1; + } + + LM_BUG("Insert_new_lump successfully\n"); + + return 1; +} + +/* This function assumes p points to a line of requested type. */ + + static int +set_lrkproxy_set_f(struct sip_msg * msg, char * str1, char * str2) +{ + lrkp_set_link_t *lrkl; + pv_value_t val; + + lrkl = (lrkp_set_link_t*)str1; + + current_msg_id = 0; + selected_lrkp_set = 0; + + if(lrkl->rset != NULL) { + current_msg_id = msg->id; + selected_lrkp_set = lrkl->rset; + } else { + if(pv_get_spec_value(msg, lrkl->rpv, &val)<0) { + LM_ERR("cannot evaluate pv param\n"); + return -1; + } + if(!(val.flags & PV_VAL_INT)) { + LM_ERR("pv param must hold an integer value\n"); + return -1; + } + selected_lrkp_set = select_lrkp_set(val.ri); + if(selected_lrkp_set==NULL) { + LM_ERR("could not locate lrkproxy set %d\n", val.ri); + return -1; + } + current_msg_id = msg->id; + } + return 1; +} + +static int +lrkproxy_manage(struct sip_msg *msg, char *flags, char *ip) +{ + int method; + int nosdp; + tm_cell_t *t = NULL; + + if (msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1) || + (msg->cseq==NULL))) + { + LM_ERR("no CSEQ header\n"); + return -1; + } + + method = get_cseq(msg)->method_id; + + if (!(method==METHOD_INVITE || method==METHOD_ACK || method==METHOD_CANCEL + || method==METHOD_BYE || method==METHOD_UPDATE)) + return -1; + + if (method==METHOD_CANCEL || method==METHOD_BYE) + return lrkproxy_unforce(msg, flags, OP_DELETE, 1); + + if (msg->msg_flags & FL_SDP_BODY) + nosdp = 0; + else + nosdp = parse_sdp(msg); + + if (msg->first_line.type == SIP_REQUEST) { + if(method==METHOD_ACK && nosdp==0) + return lrkproxy_force(msg, flags, OP_ANSWER, 1); + if(method==METHOD_UPDATE && nosdp==0) + return lrkproxy_force(msg, flags, OP_OFFER, 1); + if(method==METHOD_INVITE && nosdp==0) { + msg->msg_flags |= FL_SDP_BODY; + if(tmb.t_gett!=NULL) { + t = tmb.t_gett(); + if(t!=NULL && t!=T_UNDEFINED && t->uas.request!=NULL) { + t->uas.request->msg_flags |= FL_SDP_BODY; + } + } + if(route_type==FAILURE_ROUTE) + return lrkproxy_unforce(msg, flags, OP_DELETE, 1); + return lrkproxy_force(msg, flags, OP_OFFER, 1); + } + } else if (msg->first_line.type == SIP_REPLY) { + if (msg->first_line.u.reply.statuscode>=300) + return lrkproxy_unforce(msg, flags, OP_DELETE, 2); + if (nosdp==0) { + if (method==METHOD_UPDATE) + return lrkproxy_force(msg, flags, OP_ANSWER, 2); + if (tmb.t_gett==NULL || tmb.t_gett()==NULL + || tmb.t_gett()==T_UNDEFINED) + return lrkproxy_force(msg, flags, OP_ANSWER, 2); + if (tmb.t_gett()->uas.request->msg_flags & FL_SDP_BODY) + return lrkproxy_force(msg, flags, OP_ANSWER, 2); + return lrkproxy_force(msg, flags, OP_OFFER, 2); + } + } + + return -1; +} + +static int +lrkproxy_manage0(struct sip_msg *msg, char *flags, char *ip) +{ + return lrkproxy_manage(msg, 0, 0); +} + +static int +lrkproxy_manage1(struct sip_msg *msg, char *flags, char *ip) +{ + str flag_str; + if(fixup_get_svalue(msg, (gparam_p)flags, &flag_str)<0) + { + LM_ERR("invalid flags parameter\n"); + return -1; + } + return lrkproxy_manage(msg, flag_str.s, 0); +} + +static int +lrkproxy_manage2(struct sip_msg *msg, char *flags, char *ip) +{ + str flag_str; + str ip_str; + if(fixup_get_svalue(msg, (gparam_p)flags, &flag_str)<0) + { + LM_ERR("invalid flags parameter\n"); + return -1; + } + if(fixup_get_svalue(msg, (gparam_p)ip, &ip_str)<0) + { + LM_ERR("invalid IP parameter\n"); + return -1; + } + return lrkproxy_manage(msg, flag_str.s, ip_str.s); +} + + +static int lrkproxy_force(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more) { + +// lrk_sdp_info_t lrk_sdp_info; + struct lrkproxy_hash_entry *entry = NULL; + str viabranch = STR_NULL; + str call_id; + int via_id; + + if (get_callid(msg, &call_id) == -1) { + LM_ERR("can't get Call-Id field\n"); + return -1; + } + + /*We have to choice VIA id, + * for SIP_REQUEST we use VIA1 and for SIP_REPLY we use VIA2 */ + via_id = more; + + if (get_via_branch(msg, via_id, &viabranch) == -1) { + LM_ERR("can't get Call-Id field\n"); + return -1; + } + + if (op == OP_OFFER) { + LM_INFO ("Here is SIP_REQUEST & METHOD_INVITE\n"); + int more_test = 1; + + //select new node based on lrkp_algorithm param. + struct lrkp_node *node = select_lrkp_node(more_test); + if (!node) { + LM_ERR("can't ping any lrk proxy right now.\n"); + return -1; + } + + LM_DBG("selected lrk proxy node: %s\n", node->ln_url.s); + + //check if entry not exist. + entry = lrkproxy_hash_table_lookup(call_id, viabranch); + if (entry) + return -1; + +// lrk_get_sdp_info(msg, &lrk_sdp_info); + + //build new entry for hash table. +// struct lrkproxy_hash_entry *entry = shm_malloc(sizeof(struct lrkproxy_hash_entry)); + entry = shm_malloc(sizeof(struct lrkproxy_hash_entry)); + if (!entry) { + LM_ERR("lrkproxy hash table fail to create entry for calllen=%d callid=%.*s viabranch=%.*s\n", + call_id.len, call_id.len, call_id.s, + viabranch.len, viabranch.s); + return 0; + } + memset(entry, 0, sizeof(struct lrkproxy_hash_entry)); + + // fill the entry + if (call_id.s && call_id.len > 0) { + if (shm_str_dup(&entry->callid, &call_id) < 0) { + LM_ERR("lrkproxy hash table fail to instert call_id, calllen=%d callid=%.*s\n", + call_id.len, call_id.len, call_id.s); + lrkproxy_hash_table_free_entry(entry); + return 0; + } + } + + if (viabranch.s && viabranch.len > 0) { + if (shm_str_dup(&entry->viabranch, &viabranch) < 0) { + LM_ERR("lrkproxy hash table fail to insert viabranch, calllen=%d viabranch=%.*s\n", + call_id.len, viabranch.len, viabranch.s); + lrkproxy_hash_table_free_entry(entry); + return 0; + } + } + + //fill src_ipv4 and src_port for entry. + str src_ipv4; + if (get_sdp_ipaddr_media(msg, &src_ipv4) == -1) { + LM_ERR("can't get media src_ipv4 from sdp field\n"); + return -1; + } + + if (src_ipv4.s && src_ipv4.len > 0) { + LM_DBG("src_ipv4 from sdp:%.*s\n", src_ipv4.len, src_ipv4.s); + if (shm_str_dup(&entry->src_ipv4, &src_ipv4) < 0) { + LM_ERR("lrkproxy hash table fail to insert src_ipv4, calllen=%d src_ipv4=%.*s\n", + call_id.len, src_ipv4.len, src_ipv4.s); + lrkproxy_hash_table_free_entry(entry); + return 0; + } + } + + str src_port; + if (get_sdp_port_media(msg, &src_port) == -1) { + LM_ERR("can't get media src_port from sdp field\n"); + return -1; + } + + + if (src_port.s && src_port.len > 0) { + LM_DBG("src_port from sdp:%.*s\n", src_port.len, src_port.s); + if (shm_str_dup(&entry->src_port, &src_port) < 0) { + LM_ERR("lrkproxy hash table fail to insert src_port, calllen=%d src_port=%.*s\n", + call_id.len, src_port.len, src_port.s); + lrkproxy_hash_table_free_entry(entry); + return 0; + } + } + +// entry-> + entry->node = node; + entry->next = NULL; + entry->tout = get_ticks() + hash_table_tout; + + // insert the key<->entry from the hashtable + if (!lrkproxy_hash_table_insert(call_id, viabranch, entry)) { + LM_ERR( + "lrkproxy hash table fail to insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n", + node->ln_url.len, node->ln_url.s, call_id.len, + call_id.len, call_id.s, viabranch.len, viabranch.s); + lrkproxy_hash_table_free_entry(entry); + return 0; + } else { + LM_INFO("lrkproxy hash table insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n", + node->ln_url.len, node->ln_url.s, call_id.len, + call_id.len, call_id.s, viabranch.len, viabranch.s); + + LM_DBG("lrkproxy hash table insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n", + node->ln_url.len, node->ln_url.s, call_id.len, + call_id.len, call_id.s, viabranch.len, viabranch.s); + } + + + if (flags) + change_media_sdp(msg, entry, flags, op); + else + change_media_sdp(msg, entry, NULL, op); + + if (node->lrkp_n_c->current_port >= node->lrkp_n_c->end_port) + node->lrkp_n_c->current_port = node->lrkp_n_c->start_port; + else + node->lrkp_n_c->current_port += 4; + + } else if (op == OP_ANSWER) { + LM_INFO ("Here is SIP_REPLY of METHOD_INVITE\n"); + + + entry = lrkproxy_hash_table_lookup(call_id, viabranch); + if (!entry){ + LM_ERR("No found entry in hash table\n"); + //todo... + return 0; + } + + //fill other data for entry + str dnat_ipv4; + if (get_sdp_ipaddr_media(msg, &dnat_ipv4) == -1) { + LM_ERR("can't get media dnat_ipv4 from sdp field\n"); + return -1; + } + + if(dnat_ipv4.s && dnat_ipv4.len > 0) { + LM_DBG("dnat_ipv4 from sdp:%.*s\n", dnat_ipv4.len, dnat_ipv4.s); + if (shm_str_dup(&entry->dnat_ipv4, &dnat_ipv4) < 0) { + LM_ERR("lrkproxy hash table fail to insert dnat_ipv4, calllen=%d dnat_ipv4=%.*s\n", + call_id.len, dnat_ipv4.len, dnat_ipv4.s); + lrkproxy_hash_table_free_entry(entry); + return 0; + } + } + + str dnat_port; + if (get_sdp_port_media(msg, &dnat_port) == -1) { + LM_ERR("can't get media port from sdp field\n"); + return -1; + } + + + if(dnat_port.s && dnat_port.len > 0) { + LM_DBG("port from sdp:%.*s\n", dnat_port.len, dnat_port.s); + if (shm_str_dup(&entry->dnat_port, &dnat_port) < 0) { + LM_ERR("lrkproxy hash table fail to insert dnat_port, calllen=%d dnat_port=%.*s\n", + call_id.len, dnat_port.len, dnat_port.s); + lrkproxy_hash_table_free_entry(entry); + return 0; + } + } + + + if (flags) + change_media_sdp(msg, entry, flags, op); + else + change_media_sdp(msg, entry, NULL, op); + + + LM_INFO("selected node: %s\n",entry->node->ln_url.s); + LM_INFO("call_is: %.*s\n",entry->callid.len, entry->callid.s); + LM_INFO("viabranch: %.*s\n",entry->viabranch.len, entry->viabranch.s); + LM_INFO("src_ipv4: %.*s\n",entry->src_ipv4.len, entry->src_ipv4.s); + LM_INFO("src_port: %.*s\n",entry->src_port.len, entry->src_port.s); + LM_INFO("dst_ipv4: %.*s\n",entry->dst_ipv4.len, entry->dst_ipv4.s); + LM_INFO("dst_port: %.*s\n",entry->dst_port.len, entry->dst_port.s); + + LM_INFO("dnat_ipv4: %.*s\n",entry->dnat_ipv4.len, entry->dnat_ipv4.s); + LM_INFO("dnat_port: %.*s\n",entry->dnat_port.len, entry->dnat_port.s); + LM_INFO("snat_ipv4: %.*s\n",entry->snat_ipv4.len, entry->snat_ipv4.s); + LM_INFO("snat_port: %.*s\n",entry->snat_port.len, entry->snat_port.s); + + + lrkp_set_conntrack_rule(entry); + + } + return 1; +} + +static int lrkproxy_unforce(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more){ +// LM_INFO ("Here is lrkproxy_unforce\n"); +// struct lrkproxy_hash_entry *entry = NULL; + str viabranch = STR_NULL; + str call_id; + int via_id; + + if (get_callid(msg, &call_id) == -1) { + LM_ERR("can't get Call-Id field\n"); + return -1; + } + + /*We have to choice VIA id, + * for SIP_REQUEST we use VIA1 and for SIP_REPLY we use VIA2 */ + via_id = more; + + if (get_via_branch(msg, via_id, &viabranch) == -1) { + LM_ERR("can't get Call-Id field\n"); + return -1; + } + + if (op == OP_DELETE) { + /* Delete the key<->value from the hashtable */ + if (!lrkproxy_hash_table_remove(call_id, viabranch, op)) { + LM_ERR("lrkproxy hash table failed to remove entry for callen=%d callid=%.*s viabranch=%.*s\n", + call_id.len, call_id.len, call_id.s, + viabranch.len, viabranch.s); + } else { + LM_DBG("lrkproxy hash table remove entry for callen=%d callid=%.*s viabranch=%.*s\n", + call_id.len, call_id.len, call_id.s, + viabranch.len, viabranch.s); + } + } + LM_INFO("lrkproxy hash table remove entry for callen=%d callid=%.*s viabranch=%.*s successfully\n", + call_id.len, call_id.len, call_id.s, + viabranch.len, viabranch.s); + return 1; +} diff --git a/src/modules/lrkproxy/lrkproxy.h b/src/modules/lrkproxy/lrkproxy.h new file mode 100644 index 00000000000..2cf855e419c --- /dev/null +++ b/src/modules/lrkproxy/lrkproxy.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com + * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom + * + * 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 + * + */ + + +#ifndef _LRKPROXY_H +#define _LRKPROXY_H + +#include +#include "../../core/str.h" + +/* Handy macros */ +#define STR2IOVEC(sx, ix) do {(ix).iov_base = (sx).s; (ix).iov_len = (sx).len;} while(0) +#define SZ2IOVEC(sx, ix) do {(ix).iov_base = (sx); (ix).iov_len = strlen(sx);} while(0) + +#define CR '\r' +#define LF '\n' +#define EOB '\0' + +enum lrk_operation { + OP_OFFER = 1, + OP_ANSWER, + OP_DELETE, + OP_PING, + OP_GETINFO, + OP_SETCONNT, + + OP_ANY, +}; + + +enum lrk_alg{ + LRK_LINER=0, + LRK_RR +}; + +struct lrkp_node_conf +{ + int start_port; + int end_port; + int current_port; + char internal_ip[20]; + char external_ip[20]; +}; + +struct lrkp_node { + unsigned int idx; /* overall index */ + str ln_url; /* unparsed, deletable */ + int ln_umode; + char *ln_address; /* substring of rn_url */ + int ln_enable; /* found unaccessible? */ + unsigned ln_weight; /* for load balancing */ +// unsigned int ln_recheck_ticks; +// int ln_rep_supported; +// int ln_ptl_supported; + struct lrkp_node_conf *lrkp_n_c; + struct lrkp_node *ln_next; +}; + + + +struct lrkp_set{ + unsigned int id_set; + unsigned weight_sum; + unsigned int lrkp_node_count; + int set_disabled; + unsigned int set_recheck_ticks; + struct lrkp_node *ln_first; + struct lrkp_node *ln_last; + struct lrkp_set *lset_next; +}; + + + +struct lrkp_set_head{ + struct lrkp_set *lset_first; + struct lrkp_set *lset_last; +}; +/* Functions from nathelper */ +//struct lrkp_node *lrkp_node(str, int); +struct lrkp_node *select_lrkp_node(int); +char *send_lrkp_command(struct lrkp_node *, struct iovec *, int, int); + +struct lrkp_set *get_lrkp_set(str *set_name); +int insert_lrkp_node(struct lrkp_set *const lrkp_list, const str *const url, + const int weight, const int enable); + + +#endif //_LRKPROXY_H diff --git a/src/modules/lrkproxy/lrkproxy_funcs.c b/src/modules/lrkproxy/lrkproxy_funcs.c new file mode 100644 index 00000000000..08a41f27cfe --- /dev/null +++ b/src/modules/lrkproxy/lrkproxy_funcs.c @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2001-2003 FhG Fokus + * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com + * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom + * + * 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 + */ + +#include +#include +#include +#include +#include "lrkproxy_funcs.h" +#include "../../core/dprint.h" +#include "../../core/config.h" +#include "../../core/ut.h" +#include "../../core/forward.h" +#include "../../core/resolve.h" +#include "../../core/globals.h" +#include "../../core/udp_server.h" +#include "../../core/pt.h" +#include "../../core/parser/msg_parser.h" +#include "../../core/trim.h" +#include "../../core/parser/parse_from.h" +#include "../../core/parser/contact/parse_contact.h" +#include "../../core/parser/parse_uri.h" +#include "../../core/parser/parse_content.h" +#include "../../core/parser/parser_f.h" +#include "../../core/parser/sdp/sdp_helpr_funcs.h" + +#define READ(val) \ + (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24)) +#define advance(_ptr,_n,_str,_error) \ + do{\ + if ((_ptr)+(_n)>(_str).s+(_str).len)\ + goto _error;\ + (_ptr) = (_ptr) + (_n);\ + }while(0); +#define one_of_16( _x , _t ) \ + (_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ + ||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\ + ||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14]) +#define one_of_8( _x , _t ) \ + (_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\ + ||_x==_t[5]||_x==_t[6]) + + + +/** + * return: + * -1: error + * 1: text or sdp + * 2: multipart + * 3: trickle ice sdp fragment + */ +int check_content_type(struct sip_msg *msg) +{ + static unsigned int appl[16] = { + 0x6c707061/*appl*/,0x6c707041/*Appl*/,0x6c705061/*aPpl*/, + 0x6c705041/*APpl*/,0x6c507061/*apPl*/,0x6c507041/*ApPl*/, + 0x6c505061/*aPPl*/,0x6c505041/*APPl*/,0x4c707061/*appL*/, + 0x4c707041/*AppL*/,0x4c705061/*aPpL*/,0x4c705041/*APpL*/, + 0x4c507061/*apPL*/,0x4c507041/*ApPL*/,0x4c505061/*aPPL*/, + 0x4c505041/*APPL*/}; + static unsigned int icat[16] = { + 0x74616369/*icat*/,0x74616349/*Icat*/,0x74614369/*iCat*/, + 0x74614349/*ICat*/,0x74416369/*icAt*/,0x74416349/*IcAt*/, + 0x74414369/*iCAt*/,0x74414349/*ICAt*/,0x54616369/*icaT*/, + 0x54616349/*IcaT*/,0x54614369/*iCaT*/,0x54614349/*ICaT*/, + 0x54416369/*icAT*/,0x54416349/*IcAT*/,0x54414369/*iCAT*/, + 0x54414349/*ICAT*/}; + static unsigned int ion_[8] = { + 0x006e6f69/*ion_*/,0x006e6f49/*Ion_*/,0x006e4f69/*iOn_*/, + 0x006e4f49/*IOn_*/,0x004e6f69/*ioN_*/,0x004e6f49/*IoN_*/, + 0x004e4f69/*iON_*/,0x004e4f49/*ION_*/}; + static unsigned int sdp_[8] = { + 0x00706473/*sdp_*/,0x00706453/*Sdp_*/,0x00704473/*sDp_*/, + 0x00704453/*SDp_*/,0x00506473/*sdP_*/,0x00506453/*SdP_*/, + 0x00504473/*sDP_*/,0x00504453/*SDP_*/}; + str str_type; + unsigned int x; + char *p; + + if (!msg->content_type) + { + LM_WARN("the header Content-TYPE is absent!" + "let's assume the content is text/plain ;-)\n"); + return 1; + } + + trim_len(str_type.len,str_type.s,msg->content_type->body); + if (str_type.len>=15 && (*str_type.s=='m' || *str_type.s=='M') + && strncasecmp(str_type.s, "multipart/mixed", 15) == 0) { + return 2; + } + p = str_type.s; + advance(p,4,str_type,error_1); + x = READ(p-4); + if (!one_of_16(x,appl)) + goto other; + advance(p,4,str_type,error_1); + x = READ(p-4); + if (!one_of_16(x,icat)) + goto other; + advance(p,3,str_type,error_1); + x = READ(p-3) & 0x00ffffff; + if (!one_of_8(x,ion_)) + goto other; + + /* skip spaces and tabs if any */ + while (*p==' ' || *p=='\t') + advance(p,1,str_type,error_1); + if (*p!='/') + { + LM_ERR("no / found after primary type\n"); + goto error; + } + advance(p,1,str_type,error_1); + while ((*p==' ' || *p=='\t') && p+1 found valid\n", (int)(p-str_type.s), str_type.s); + return 1; + } else { + LM_ERR("bad end for type!\n"); + return -1; + } + + error_1: + LM_ERR("body ended :-(!\n"); + error: + return -1; + other: + LM_ERR("invalid type for a message\n"); + return -1; +} + + +/* + * Get message body and check Content-Type header field + */ +int extract_body(struct sip_msg *msg, str *body ) +{ + char c; + int ret; + str mpdel; + char *rest, *p1, *p2; + struct hdr_field hf; + unsigned int mime; + + body->s = get_body(msg); + if (body->s==0) { + LM_ERR("failed to get the message body\n"); + goto error; + } + + /* + * Better use the content-len value - no need of any explicit + * parcing as get_body() parsed all headers and Conten-Length + * body header is automaticaly parsed when found. + */ + if (msg->content_length==0) { + LM_ERR("failed to get the content length in message\n"); + goto error; + } + + body->len = get_content_length(msg); + if (body->len==0) { + LM_ERR("message body has length zero\n"); + goto error; + } + + if (body->len + body->s > msg->buf + msg->len) { + LM_ERR("content-length exceeds packet-length by %d\n", + (int)((body->len + body->s) - (msg->buf + msg->len))); + goto error; + } + + /* no need for parse_headers(msg, EOH), get_body will + * parse everything */ + /*is the content type correct?*/ + if((ret = check_content_type(msg))==-1) + { + LM_ERR("content type mismatching\n"); + goto error; + } + + if(ret!=2) + goto done; + + /* multipart body */ + if(get_mixed_part_delimiter(&msg->content_type->body,&mpdel) < 0) { + goto error; + } + p1 = find_sdp_line_delimiter(body->s, body->s+body->len, mpdel); + if (p1 == NULL) { + LM_ERR("empty multipart content\n"); + return -1; + } + p2=p1; + c = 0; + for(;;) + { + p1 = p2; + if (p1 == NULL || p1 >= body->s+body->len) + break; /* No parts left */ + p2 = find_next_sdp_line_delimiter(p1, body->s+body->len, + mpdel, body->s+body->len); + /* p2 is text limit for application parsing */ + rest = eat_line(p1 + mpdel.len + 2, p2 - p1 - mpdel.len - 2); + if ( rest > p2 ) { + LM_ERR("Unparsable <%.*s>\n", (int)(p1-p1), p1); + return -1; + } + while( rest>16) == TYPE_APPLICATION) + && ((mime&0x00ff) == SUBTYPE_SDP)) { + c = 1; + } + } + } /* end of while */ + if(c==1) + { + if (rest < p2 && *rest == '\r') rest++; + if (rest < p2 && *rest == '\n') rest++; + if (rest < p2 && p2[-1] == '\n') p2--; + if (rest < p2 && p2[-1] == '\r') p2--; + body->s = rest; + body->len = p2-rest; + goto done; + } + } + + error: + return -1; + + done: + /*LM_DBG("DEBUG:extract_body:=|%.*s|\n",body->len,body->s);*/ + return ret; /* mirrors return type of check_content_type */ +} + +/* + * Some helper functions taken verbatim from tm module. + */ + +/* + * Extract Call-ID value + * assumes the callid header is already parsed + * (so make sure it is, before calling this function or + * it might fail even if the message _has_ a callid) + */ +int +get_callid(struct sip_msg* _m, str* _cid) +{ + + if ((parse_headers(_m, HDR_CALLID_F, 0) == -1)) { + LM_ERR("failed to parse call-id header\n"); + return -1; + } + + if (_m->callid == NULL) { + LM_ERR("call-id not found\n"); + return -1; + } + + _cid->s = _m->callid->body.s; + _cid->len = _m->callid->body.len; + trim(_cid); + return 0; +} + +/* + * Extract tag from To header field of a response + */ +int +get_to_tag(struct sip_msg* _m, str* _tag) +{ + + if (parse_to_header(_m) < 0) { + LM_ERR("To header field missing\n"); + return -1; + } + + if (get_to(_m)->tag_value.len) { + _tag->s = get_to(_m)->tag_value.s; + _tag->len = get_to(_m)->tag_value.len; + } else { + _tag->s = NULL; /* fixes gcc 4.0 warnings */ + _tag->len = 0; + } + + return 0; +} + +/* + * Extract tag from From header field of a request + */ +int +get_from_tag(struct sip_msg* _m, str* _tag) +{ + + if (parse_from_header(_m)<0) { + LM_ERR("failed to parse From header\n"); + return -1; + } + + if (get_from(_m)->tag_value.len) { + _tag->s = get_from(_m)->tag_value.s; + _tag->len = get_from(_m)->tag_value.len; + } else { + _tag->s = NULL; /* fixes gcc 4.0 warnings */ + _tag->len = 0; + } + + return 0; +} + +/* + * Extract URI from the Contact header field + */ +int +get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c) +{ + + if ((parse_headers(_m, HDR_CONTACT_F, 0) == -1) || !_m->contact) + return -1; + if (!_m->contact->parsed && parse_contact(_m->contact) < 0) { + LM_ERR("failed to parse Contact body\n"); + return -1; + } + *_c = ((contact_body_t*)_m->contact->parsed)->contacts; + if (*_c == NULL) + /* no contacts found */ + return -1; + + if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) { + LM_ERR("failed to parse Contact URI [%.*s]\n", + (*_c)->uri.len, ((*_c)->uri.s)?(*_c)->uri.s:""); + return -1; + } + return 0; +} + +/* + * Extract branch from Via header + */ +int +get_via_branch(struct sip_msg* msg, int vianum, str* _branch) +{ + struct via_body *via; + struct via_param *p; + + if (parse_via_header(msg, vianum, &via) < 0) + return -1; + + for (p = via->param_lst; p; p = p->next) + { + if (p->name.len == strlen("branch") + && strncasecmp(p->name.s, "branch", strlen("branch")) == 0) { + _branch->s = p->value.s; + _branch->len = p->value.len; + return 0; + } + } + return -1; +} + +int get_sdp_ipaddr_media(struct sip_msg *msg, str *ip_addr) { + sdp_session_cell_t *sdp_session; + sdp_stream_cell_t *sdp_stream; + sdp_info_t *sdp = (sdp_info_t *) msg->body; + if (!sdp) { + LM_INFO("sdp null\n"); + return -1; + } + + + int sdp_session_num = 0; + sdp_session = get_sdp_session(msg, sdp_session_num); + + if (!sdp_session) { + LM_INFO("can not get the sdp session\n"); + return 0; + } + + if (sdp_session->ip_addr.s && sdp_session->ip_addr.len > 0) { + LM_INFO("sdp_session->ip_addr:%.*s\n", sdp_session->ip_addr.len, sdp_session->ip_addr.s); + ip_addr->s = sdp_session->ip_addr.s; + ip_addr->len = sdp_session->ip_addr.len; + trim(ip_addr); + } + else { + int sdp_stream_num = 0; + sdp_stream = get_sdp_stream(msg, sdp_session_num, sdp_stream_num); + if (!sdp_stream) { + LM_INFO("can not get the sdp stream\n"); + return 0; + } + if (sdp_stream->ip_addr.s && sdp_stream->ip_addr.len > 0) { + LM_INFO("sdp_stream->ip_addr:%.*s\n", sdp_stream->ip_addr.len, sdp_stream->ip_addr.s); + ip_addr->s = sdp_stream->ip_addr.s; + ip_addr->len = sdp_stream->ip_addr.len; + trim(ip_addr); + } + } + + + return 0; +} + + +int get_sdp_port_media(struct sip_msg *msg, str *port){ +// sdp_session_cell_t *sdp_session; + sdp_stream_cell_t *sdp_stream; + int sdp_session_num = 0; + + sdp_info_t *sdp = (sdp_info_t *)msg->body; + if(!sdp) { + LM_INFO("sdp null\n"); + return -1; + } + +// sdp_session = get_sdp_session(msg, sdp_session_num); +// if(!sdp_session) { +// LM_INFO("can not get the sdp session\n"); +// return 0; +// } else { +// LM_INFO("NEW_IP_ADDRESS:>%.*s>\n", sdp_session->ip_addr.len, sdp_session->ip_addr.s); +// lrk_sdp_info->ip_addr.s = sdp_session->ip_addr; + int sdp_stream_num = 0; + sdp_stream = get_sdp_stream(msg, sdp_session_num, sdp_stream_num); + if (!sdp_stream) { + LM_INFO("can not get the sdp stream\n"); + return -1; + } else { +// LM_INFO ("PORT:<%.*s>\n", sdp_stream->port.len, sdp_stream->port.s); +// str2int(&sdp_stream->port, lrk_sdp_info->port) + port->s = sdp_stream->port.s; + port->len = sdp_stream->port.len; + trim(port); + } +// } + return 0; + +} + diff --git a/src/modules/lrkproxy/lrkproxy_funcs.h b/src/modules/lrkproxy/lrkproxy_funcs.h new file mode 100644 index 00000000000..d644235226f --- /dev/null +++ b/src/modules/lrkproxy/lrkproxy_funcs.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2001-2003 FhG Fokus + * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com + * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom + * + * 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 + */ +#ifndef _LRKPROXY_FUNCS_H +#define _LRKPROXY_FUNCS_H + + +#include "../../core/str.h" +#include "../../core/parser/msg_parser.h" +#include "../../core/parser/contact/contact.h" + +int extract_body(struct sip_msg * , str *); +int check_content_type(struct sip_msg * ); +int get_callid(struct sip_msg *, str *); +int get_to_tag(struct sip_msg *, str *); +int get_from_tag(struct sip_msg *, str *); +int get_contact_uri(struct sip_msg *, struct sip_uri *, contact_t **); +int get_via_branch(struct sip_msg *, int, str *); +int get_sdp_ipaddr_media(struct sip_msg *msg, str *ip_addr); +int get_sdp_port_media(struct sip_msg *msg, str *port); + + +#endif //_LRKPROXY_FUNCS_H diff --git a/src/modules/lrkproxy/lrkproxy_hash.c b/src/modules/lrkproxy/lrkproxy_hash.c new file mode 100644 index 00000000000..460db1d56e9 --- /dev/null +++ b/src/modules/lrkproxy/lrkproxy_hash.c @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com + * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com + * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom + * + * 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 + * + */ + + +#include "lrkproxy.h" +#include "lrkproxy_hash.h" + +#include "../../core/str.h" +#include "../../core/dprint.h" +#include "../../core/mem/shm_mem.h" +#include "../../core/timer.h" + +static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock); + +static struct lrkproxy_hash_table *lrkproxy_hash_table; + +/* get from sipwise rtpengine */ +static int str_cmp_str(const str a, const str b) { + if (a.len < b.len) + return -1; + if (a.len > b.len) + return 1; + if (a.len == 0 && b.len == 0) + return 0; + return memcmp(a.s, b.s, a.len); +} + +/* get from sipwise rtpengine */ +static int str_equal(str a, str b) { + return (str_cmp_str(a, b) == 0); +} + +/* get from sipwise rtpengine */ +static unsigned int str_hash(str s) { + unsigned int ret = 5381; + str it = s; + + while (it.len > 0) { + ret = (ret << 5) + ret + *it.s; + it.s++; + it.len--; + } + + return ret % lrkproxy_hash_table->size; +} + +/* lrkproxy hash API */ +int lrkproxy_hash_table_init(int size) { + int i; + int hash_table_size; + + + hash_table_size = size; + + +// LM_DBG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size); + LM_INFO(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size); + + // init hashtable + lrkproxy_hash_table = shm_malloc(sizeof(struct lrkproxy_hash_table)); + if (!lrkproxy_hash_table) { + LM_ERR("no shm left to create lrkproxy_hash_table\n"); + return 0; + } + memset(lrkproxy_hash_table, 0, sizeof(struct lrkproxy_hash_table)); + lrkproxy_hash_table->size = hash_table_size; + + // init hashtable row_locks + lrkproxy_hash_table->row_locks = shm_malloc(hash_table_size * sizeof(gen_lock_t*)); + if (!lrkproxy_hash_table->row_locks) { + LM_ERR("no shm left to create lrkproxy_hash_table->row_locks\n"); + lrkproxy_hash_table_destroy(); + return 0; + } + memset(lrkproxy_hash_table->row_locks, 0, hash_table_size * sizeof(gen_lock_t*)); + + // init hashtable row_entry_list + lrkproxy_hash_table->row_entry_list = shm_malloc(lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*)); + if (!lrkproxy_hash_table->row_entry_list) { + LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list\n"); + lrkproxy_hash_table_destroy(); + return 0; + } + memset(lrkproxy_hash_table->row_entry_list, 0, lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*)); + + // init hashtable row_totals + lrkproxy_hash_table->row_totals = shm_malloc(hash_table_size * sizeof(unsigned int)); + if (!lrkproxy_hash_table->row_totals) { + LM_ERR("no shm left to create lrkproxy_hash_table->row_totals\n"); + lrkproxy_hash_table_destroy(); + return 0; + } + memset(lrkproxy_hash_table->row_totals, 0, hash_table_size * sizeof(unsigned int)); + + // init hashtable row_locks[i], row_entry_list[i] and row_totals[i] + for (i = 0; i < hash_table_size; i++) { + // alloc hashtable row_locks[i] + lrkproxy_hash_table->row_locks[i] = lock_alloc(); + if (!lrkproxy_hash_table->row_locks[i]) { + LM_ERR("no shm left to create lrkproxy_hash_table->row_locks[%d]\n", i); + lrkproxy_hash_table_destroy(); + return 0; + } + + // init hashtable row_locks[i] + if (!lock_init(lrkproxy_hash_table->row_locks[i])) { + LM_ERR("fail to init lrkproxy_hash_table->row_locks[%d]\n", i); + lrkproxy_hash_table_destroy(); + return 0; + } + + // init hashtable row_entry_list[i] + lrkproxy_hash_table->row_entry_list[i] = shm_malloc(sizeof(struct lrkproxy_hash_entry)); + if (!lrkproxy_hash_table->row_entry_list[i]) { + LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list[%d]\n", i); + lrkproxy_hash_table_destroy(); + return 0; + } + memset(lrkproxy_hash_table->row_entry_list[i], 0, sizeof(struct lrkproxy_hash_entry)); + + lrkproxy_hash_table->row_entry_list[i]->tout = -1; + lrkproxy_hash_table->row_entry_list[i]->next = NULL; + + // init hashtable row_totals[i] + lrkproxy_hash_table->row_totals[i] = 0; + } + + return 1; +} + +int lrkproxy_hash_table_destroy() { + int i; + + // check lrkproxy hashtable + if (!lrkproxy_hash_table) { + LM_ERR("NULL lrkproxy_hash_table\n"); + return 1; + } + + // check lrkproxy hashtable->row_locks + if (!lrkproxy_hash_table->row_locks) { + LM_ERR("NULL lrkproxy_hash_table->row_locks\n"); + shm_free(lrkproxy_hash_table); + lrkproxy_hash_table = NULL; + return 1; + } + + // destroy hashtable content + for (i = 0; i < lrkproxy_hash_table->size; i++) { + // lock + if (!lrkproxy_hash_table->row_locks[i]) { + LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", i); + continue; + } else { + lock_get(lrkproxy_hash_table->row_locks[i]); + } + + // check lrkproxy hashtable->row_entry_list + if (!lrkproxy_hash_table->row_entry_list) { + LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n"); + } else { + // destroy hashtable row_entry_list[i] + lrkproxy_hash_table_free_row_entry_list(lrkproxy_hash_table->row_entry_list[i]); + lrkproxy_hash_table->row_entry_list[i] = NULL; + } + + // unlock + lock_release(lrkproxy_hash_table->row_locks[i]); + + // destroy hashtable row_locks[i] + lrkproxy_hash_table_free_row_lock(lrkproxy_hash_table->row_locks[i]); + lrkproxy_hash_table->row_locks[i] = NULL; + } + + // destroy hashtable row_entry_list + if (!lrkproxy_hash_table->row_entry_list) { + LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n"); + } else { + shm_free(lrkproxy_hash_table->row_entry_list); + lrkproxy_hash_table->row_entry_list = NULL; + } + + // destroy hashtable row_totals + if (!lrkproxy_hash_table->row_totals) { + LM_ERR("NULL lrkproxy_hash_table->row_totals\n"); + } else { + shm_free(lrkproxy_hash_table->row_totals); + lrkproxy_hash_table->row_totals = NULL; + } + + // destroy hashtable row_locks + if (!lrkproxy_hash_table->row_locks) { + // should not be the case; just for code symmetry + LM_ERR("NULL lrkproxy_hash_table->row_locks\n"); + } else { + shm_free(lrkproxy_hash_table->row_locks); + lrkproxy_hash_table->row_locks = NULL; + } + + // destroy hashtable + if (!lrkproxy_hash_table) { + // should not be the case; just for code symmetry + LM_ERR("NULL lrkproxy_hash_table\n"); + } else { + shm_free(lrkproxy_hash_table); + lrkproxy_hash_table = NULL; + } + + return 1; +} + + +void lrkproxy_hash_table_free_entry(struct lrkproxy_hash_entry *entry) { + if (!entry) { + LM_ERR("try to free a NULL entry\n"); + return ; + } + + // free callid + if (entry->callid.s) { + shm_free(entry->callid.s); + } + + // free viabranch + if (entry->viabranch.s) { + shm_free(entry->viabranch.s); + } + + // free entry + shm_free(entry); + + return ; +} + +void lrkproxy_hash_table_free_row_entry_list(struct lrkproxy_hash_entry *row_entry_list) { + struct lrkproxy_hash_entry *entry, *last_entry; + + if (!row_entry_list) { + LM_ERR("try to free a NULL row_entry_list\n"); + return ; + } + + entry = row_entry_list; + while (entry) { + last_entry = entry; + entry = entry->next; + lrkproxy_hash_table_free_entry(last_entry); + last_entry = NULL; + } + + return ; +} + +int lrkproxy_hash_table_insert(str callid, str viabranch, struct lrkproxy_hash_entry *value) { + struct lrkproxy_hash_entry *entry, *last_entry; + struct lrkproxy_hash_entry *new_entry = (struct lrkproxy_hash_entry *) value; + unsigned int hash_index; + + // sanity checks + if (!lrkproxy_hash_table_sanity_checks()) { + LM_ERR("sanity checks failed\n"); + return 0; + } + + // get entry list + hash_index = str_hash(callid); + entry = lrkproxy_hash_table->row_entry_list[hash_index]; + last_entry = entry; + + // lock + if (lrkproxy_hash_table->row_locks[hash_index]) { + lock_get(lrkproxy_hash_table->row_locks[hash_index]); + } else { + LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index); + return 0; + } + + while (entry) { + // if found, don't add new entry + if (str_equal(entry->callid, new_entry->callid) && + str_equal(entry->viabranch, new_entry->viabranch)) { + // unlock + lock_release(lrkproxy_hash_table->row_locks[hash_index]); + LM_NOTICE("callid=%.*s, viabranch=%.*s already in hashtable, ignore new value\n", + entry->callid.len, entry->callid.s, + entry->viabranch.len, entry->viabranch.s); + return 0; + } + + // if expired entry discovered, delete it + if (entry->tout < get_ticks()) { + // set pointers; exclude entry + last_entry->next = entry->next; + + // free current entry; entry points to unknown + lrkproxy_hash_table_free_entry(entry); + + // set pointers + entry = last_entry; + + // update total + lrkproxy_hash_table->row_totals[hash_index]--; + } + + // next entry in the list + last_entry = entry; + entry = entry->next; + } + + last_entry->next = new_entry; + + // update total + lrkproxy_hash_table->row_totals[hash_index]++; + + // unlock + lock_release(lrkproxy_hash_table->row_locks[hash_index]); + + return 1; +} + +int lrkproxy_hash_table_remove(str callid, str viabranch, enum lrk_operation op) { + struct lrkproxy_hash_entry *entry, *last_entry; + unsigned int hash_index; + int found = 0; + + // sanity checks + if (!lrkproxy_hash_table_sanity_checks()) { + LM_ERR("sanity checks failed\n"); + return 0; + } + + // get first entry from entry list; jump over unused list head + hash_index = str_hash(callid); + entry = lrkproxy_hash_table->row_entry_list[hash_index]; + last_entry = entry; + + // lock + if (lrkproxy_hash_table->row_locks[hash_index]) { + lock_get(lrkproxy_hash_table->row_locks[hash_index]); + } else { + LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index); + return 0; + } + + while (entry) { + // if callid found, delete entry + if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) || + (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) { + // set pointers; exclude entry + last_entry->next = entry->next; + + // free current entry; entry points to unknown + lrkproxy_hash_table_free_entry(entry); + + // set pointers + entry = last_entry; + + // update total + lrkproxy_hash_table->row_totals[hash_index]--; + + found = 1; + + if (!(viabranch.len == 0 && op == OP_DELETE)) { + // unlock + lock_release(lrkproxy_hash_table->row_locks[hash_index]); + return found; + } + + // try to also delete other viabranch entries for callid + last_entry = entry; + entry = entry->next; + continue; + } + + // if expired entry discovered, delete it + if (entry->tout < get_ticks()) { + // set pointers; exclude entry + last_entry->next = entry->next; + + // free current entry; entry points to unknown + lrkproxy_hash_table_free_entry(entry); + + // set pointers + entry = last_entry; + + // update total + lrkproxy_hash_table->row_totals[hash_index]--; + } + + last_entry = entry; + entry = entry->next; + } + + // unlock + lock_release(lrkproxy_hash_table->row_locks[hash_index]); + + return found; +} +//struct lrkp_node *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) { +//struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) { +struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch) { + struct lrkproxy_hash_entry *entry, *last_entry; + unsigned int hash_index; +// struct lrkp_node *node; + + // sanity checks + if (!lrkproxy_hash_table_sanity_checks()) { + LM_ERR("sanity checks failed\n"); + return 0; + } + + // get first entry from entry list; jump over unused list head + hash_index = str_hash(callid); + entry = lrkproxy_hash_table->row_entry_list[hash_index]; + last_entry = entry; + + // lock + if (lrkproxy_hash_table->row_locks[hash_index]) { + lock_get(lrkproxy_hash_table->row_locks[hash_index]); + } else { + LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index); + return 0; + } + + while (entry) { + // if callid found, return entry +// if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) || +// (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) { + if (str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) { +// node = entry->node; + // unlock + lock_release(lrkproxy_hash_table->row_locks[hash_index]); + return entry; +// return node; + } + + // if expired entry discovered, delete it + if (entry->tout < get_ticks()) { + // set pointers; exclude entry + last_entry->next = entry->next; + + // free current entry; entry points to unknown + lrkproxy_hash_table_free_entry(entry); + + // set pointers + entry = last_entry; + + // update total + lrkproxy_hash_table->row_totals[hash_index]--; + } + + last_entry = entry; + entry = entry->next; + } + + // unlock + lock_release(lrkproxy_hash_table->row_locks[hash_index]); + + return NULL; +} + + +static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock) { + if (!row_lock) { + LM_ERR("try to free a NULL lock\n"); + return ; + } + + lock_destroy(row_lock); + lock_dealloc(row_lock); + + return ; +} + +int lrkproxy_hash_table_sanity_checks() { + // check lrkproxy hashtable + if (!lrkproxy_hash_table) { + LM_ERR("NULL lrkproxy_hash_table\n"); + return 0; + } + + // check lrkproxy hashtable->row_locks + if (!lrkproxy_hash_table->row_locks) { + LM_ERR("NULL lrkproxy_hash_table->row_locks\n"); + return 0; + } + + // check lrkproxy hashtable->row_entry_list + if (!lrkproxy_hash_table->row_entry_list) { + LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n"); + return 0; + } + + // check lrkproxy hashtable->row_totals + if (!lrkproxy_hash_table->row_totals) { + LM_ERR("NULL lrkproxy_hash_table->row_totals\n"); + return 0; + } + + return 1; +} + diff --git a/src/modules/lrkproxy/lrkproxy_hash.h b/src/modules/lrkproxy/lrkproxy_hash.h new file mode 100644 index 00000000000..0943021b526 --- /dev/null +++ b/src/modules/lrkproxy/lrkproxy_hash.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com + * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com + * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom + * + * 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 + * + */ + +#ifndef LRKPROXY_HASH_H +#define LRKPROXY_HASH_H + +#include "../../core/str.h" +#include "../../core/locking.h" + + +/* table entry */ +struct lrkproxy_hash_entry { + str src_ipv4; //media ip address of initiator call in INVITE SIP message. + str dst_ipv4; //media ip address of selected node in 200Ok SIP message. + str snat_ipv4; //change media ip address to selected node. + str dnat_ipv4; //change media ip address to orgin destination party. + str src_port; //media port of initiator call in INVITE SIP message + str dst_port; //media port of selected node in 200Ok SIP message. + str snat_port; //change media port to selected node. + str dnat_port; //change media port to orgin destination party. + + str callid; // call callid + str viabranch; // call viabranch + struct lrkp_node *node; // call selected node + + unsigned int tout; // call timeout + struct lrkproxy_hash_entry *next; // call next +}; + +/* table */ +struct lrkproxy_hash_table { + struct lrkproxy_hash_entry **row_entry_list; // vector of size pointers to entry + gen_lock_t **row_locks; // vector of size pointers to locks + unsigned int *row_totals; // vector of size numbers of entries in the hashtable rows + unsigned int size; // hash table size +}; + + + +int lrkproxy_hash_table_init(int hsize); +int lrkproxy_hash_table_destroy(); +int lrkproxy_hash_table_insert(str callid, str viabranch, struct lrkproxy_hash_entry *value); +int lrkproxy_hash_table_remove(str callid, str viabranch, enum lrk_operation); +struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch); +//struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation); +//struct lrkp_node *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation); +//void lrkproxy_hash_table_print(); +//unsigned int lrkproxy_hash_table_total(); + +void lrkproxy_hash_table_free_entry(struct lrkproxy_hash_entry *entry); +void lrkproxy_hash_table_free_row_entry_list(struct lrkproxy_hash_entry *row_entry_list); + +int lrkproxy_hash_table_sanity_checks(); + +#endif //LRKPROXY_HASH_H diff --git a/src/modules/mangler/contact_ops.c b/src/modules/mangler/contact_ops.c index de94edb5342..c3e1a2ff2ea 100644 --- a/src/modules/mangler/contact_ops.c +++ b/src/modules/mangler/contact_ops.c @@ -610,28 +610,28 @@ decode_uri (str* uri, char separator, str * result, str* dst_uri) { LOG(L_ERR,"ERROR: decode_uri: Unable to decode host address \n"); return -2;/* should I quit or ignore ? */ - } + } if ((format.password.len > 0) && (format.username.len <= 0)) { LOG(L_ERR,"ERROR: decode_uri: Password decoded but no username available\n"); return -3; } - + /* a complete uri would be sip:username:password@ip:port;transport=protocol goes to * sip:enc_pref#username#password#ip#port#protocol@public_ip */ result->len = format.first + (uri->len - format.second); /* not NULL terminated */ if (format.username.len > 0) result->len += format.username.len + 1; //: or @ if (format.password.len > 0) result->len += format.password.len + 1; //@ - + /* if (format.ip.len > 0) */ result->len += format.ip.len; - + if (format.port.len > 0) result->len += 1 + format.port.len; //: if (format.protocol.len > 0) result->len += 1 + 10 + format.protocol.len; //;transport= - + /* adding one comes from * */ - result->s = pkg_malloc (result->len); + result->s = pkg_malloc (result->len + 1); /* NULL termination */ if (result->s == NULL) { LOG(L_ERR,"ERROR: decode_contact: Unable to allocate memory\n"); @@ -640,7 +640,7 @@ decode_uri (str* uri, char separator, str * result, str* dst_uri) pos = result->s; memcpy (pos, uri->s, format.first); /* till sip: */ pos = pos + format.first; - + if (format.username.len > 0) { memcpy (pos, format.username.s, format.username.len); @@ -662,7 +662,7 @@ decode_uri (str* uri, char separator, str * result, str* dst_uri) memcpy (pos, format.ip.s, format.ip.len); pos = pos + format.ip.len; - + if (format.port.len > 0) { memcpy (pos, ":", 1); @@ -677,9 +677,11 @@ decode_uri (str* uri, char separator, str * result, str* dst_uri) memcpy (pos, format.protocol.s, format.protocol.len); pos = pos + format.protocol.len; } - + memcpy (pos, uri->s + format.second, uri->len - format.second); /* till end: */ - + + result->s[result->len] = '\0'; + /* dst_uri */ if (dst_uri && format.rcv_ip.s){ dst_uri->len=4 /* sip: */ + format.rcv_ip.len; diff --git a/src/modules/ndb_redis/redis_client.c b/src/modules/ndb_redis/redis_client.c index 42880310f29..befb69ab79a 100644 --- a/src/modules/ndb_redis/redis_client.c +++ b/src/modules/ndb_redis/redis_client.c @@ -1059,6 +1059,11 @@ int redisc_exec(str *srv, str *res, str *cmd, ...) if(redisc_reconnect_server(rsrv)==0) { rpl->rplRedis = redisvCommand(rsrv->ctxRedis, cmd->s, ap4); + if(rpl->rplRedis == NULL) + { + redis_count_err_and_disable(rsrv); + goto error_exec; + } } else { LM_ERR("unable to reconnect to redis server: %.*s\n", srv->len, srv->s); diff --git a/src/modules/pipelimit/README b/src/modules/pipelimit/README index ebe43e1103d..716d0a07e7c 100644 --- a/src/modules/pipelimit/README +++ b/src/modules/pipelimit/README @@ -46,7 +46,8 @@ Daniel-Constantin Mierla 5. Functions 5.1. pl_check(name [, algorithm, limit]) - 5.2. pl_drop([ [min ], max ]) + 5.2. pl_active(name) + 5.3. pl_drop([ [min ], max ]) 6. RPC Commands @@ -76,7 +77,8 @@ Daniel-Constantin Mierla 1.14. Set clean_unused parameter 1.15. Set clean_unused parameter at runtime 1.16. pl_check usage - 1.17. pl_drop usage + 1.17. pl_active usage + 1.18. pl_drop usage Chapter 1. Admin Guide @@ -107,7 +109,8 @@ Chapter 1. Admin Guide 5. Functions 5.1. pl_check(name [, algorithm, limit]) - 5.2. pl_drop([ [min ], max ]) + 5.2. pl_active(name) + 5.3. pl_drop([ [min ], max ]) 6. RPC Commands @@ -379,7 +382,8 @@ kamcmd cfg.set_now_int pipelimit clean_unused 10 5. Functions 5.1. pl_check(name [, algorithm, limit]) - 5.2. pl_drop([ [min ], max ]) + 5.2. pl_active(name) + 5.3. pl_drop([ [min ], max ]) 5.1. pl_check(name [, algorithm, limit]) @@ -460,7 +464,22 @@ with unexpected retcode=$var(check_result)\n"); } ... -5.2. pl_drop([ [min ], max ]) +5.2. pl_active(name) + + Check the pipe 'name' was already created. Return 1 (true) if the pipe + is found, -1 (false) if the pipe is not found. + + This function can be used from ANY_ROUTE. + + Example 1.17. pl_active usage +... + if (!pl_active("one")) { + # pipe does not exist + exit; + } +... + +5.3. pl_drop([ [min ], max ]) For the current request, a "503 - Server Unavailable" reply is sent back. The reply may or may not have a "Retry-After" header. If no @@ -477,7 +496,7 @@ with unexpected retcode=$var(check_result)\n"); This function can be used from REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE|ONSEND_ROUTE. - Example 1.17. pl_drop usage + Example 1.18. pl_drop usage ... if (!pl_check("one")) { # send back a "503 - Server Unavailable" diff --git a/src/modules/pipelimit/doc/pipelimit_admin.xml b/src/modules/pipelimit/doc/pipelimit_admin.xml index ce470f80eb9..688b19957ff 100644 --- a/src/modules/pipelimit/doc/pipelimit_admin.xml +++ b/src/modules/pipelimit/doc/pipelimit_admin.xml @@ -511,6 +511,30 @@ with unexpected retcode=$var(check_result)\n"); +
+ + <function moreinfo="none">pl_active(name)</function> + + + Check the pipe 'name' was already created. Return 1 (true) if the pipe + is found, -1 (false) if the pipe is not found. + + + This function can be used from ANY_ROUTE. + + + <function>pl_active</function> usage + +... + if (!pl_active("one")) { + # pipe does not exist + exit; + } +... + + +
+
<function moreinfo="none">pl_drop([ [min ], max ])</function> diff --git a/src/modules/pipelimit/pipelimit.c b/src/modules/pipelimit/pipelimit.c index faacd086df4..d7b4f129c80 100644 --- a/src/modules/pipelimit/pipelimit.c +++ b/src/modules/pipelimit/pipelimit.c @@ -122,6 +122,7 @@ static ticks_t pl_timer_handle(ticks_t, struct timer_ln*, void*); static void pl_timer_exec(unsigned int ticks, void *param); static int w_pl_check(struct sip_msg*, char *, char *); static int w_pl_check3(struct sip_msg*, char *, char *, char *); +static int w_pl_active(sip_msg_t *, char *, char *); static int w_pl_drop_default(struct sip_msg*, char *, char *); static int w_pl_drop_forced(struct sip_msg*, char *, char *); static int w_pl_drop(struct sip_msg*, char *, char *); @@ -133,6 +134,8 @@ static cmd_export_t cmds[]={ 0, ANY_ROUTE}, {"pl_check", (cmd_function)w_pl_check3, 3, fixup_pl_check3, 0, ANY_ROUTE}, + {"pl_active", (cmd_function)w_pl_active, 1, fixup_spve_null, + 0, ANY_ROUTE}, {"pl_drop", (cmd_function)w_pl_drop_default, 0, 0, 0, REQUEST_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE|ONSEND_ROUTE}, {"pl_drop", (cmd_function)w_pl_drop_forced, 1, fixup_uint_null, @@ -729,6 +732,36 @@ static int fixup_pl_check3(void** param, int param_no) return 0; } +static int pl_active(sip_msg_t *msg, str *pipeid) +{ + pl_pipe_t *pipe = NULL; + + pipe = pl_pipe_get(pipeid, 0); + if(pipe==NULL) { + LM_ERR("pipe does not exist [%.*s]\n", pipeid->len, pipeid->s); + return -1; + } + + return 1; +} + +/** + * checking if pipe is active + */ +static int w_pl_active(sip_msg_t* msg, char *p1, char *p2) +{ + str pipeid = {0, 0}; + + if(fixup_get_svalue(msg, (gparam_p)p1, &pipeid)!=0 + || pipeid.s == 0) + { + LM_ERR("invalid pipeid parameter"); + return -1; + } + + return pl_active(msg, &pipeid); +} + static void pl_timer_refresh(void) { if(pl_load_fetch!=0) { @@ -873,6 +906,11 @@ static sr_kemi_t sr_kemi_pipelimit_exports[] = { { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_INT, SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } }, + { str_init("pipelimit"), str_init("pl_active"), + SR_KEMIP_INT, pl_active, + { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, { str_init("pipelimit"), str_init("pl_drop"), SR_KEMIP_INT, ki_pl_drop, { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, diff --git a/src/modules/pipelimit/pl_ht.c b/src/modules/pipelimit/pl_ht.c index 1f666b89f34..38434068b47 100644 --- a/src/modules/pipelimit/pl_ht.c +++ b/src/modules/pipelimit/pl_ht.c @@ -221,7 +221,7 @@ pl_pipe_t* pl_pipe_get(str *pipeid, int mode) unsigned int cellid; unsigned int idx; pl_pipe_t *it; - + if(_pl_pipes_ht==NULL) return NULL; @@ -236,7 +236,7 @@ pl_pipe_t* pl_pipe_get(str *pipeid, int mode) } while(it!=NULL && it->cellid == cellid) { - if(pipeid->len==it->name.len + if(pipeid->len==it->name.len && strncmp(pipeid->s, it->name.s, pipeid->len)==0) { if(mode==0) lock_release(&_pl_pipes_ht->slots[idx].lock); diff --git a/src/modules/presence/presence_dmq.c b/src/modules/presence/presence_dmq.c index 287f2fd67d9..8dd7c99fe89 100644 --- a/src/modules/presence/presence_dmq.c +++ b/src/modules/presence/presence_dmq.c @@ -102,11 +102,15 @@ static int pres_dmq_init_proc() } } + if(publ_cache_mode==PS_PCACHE_RECORD && pres_subs_dbmode==NO_DB) { + goto finish; + } + if(!pa_db) { LM_DBG("Initializing presence DB connection for pid (%d)\n", my_pid()); if(pa_dbf.init == 0) { - LM_ERR("dmq_worker_init: database not bound\n"); + LM_ERR("database not bound\n"); return -1; } @@ -123,6 +127,7 @@ static int pres_dmq_init_proc() } } +finish: *pres_dmq_proc_init = 1; LM_DBG("process initialization complete\n"); diff --git a/src/modules/pv/pv_branch.c b/src/modules/pv/pv_branch.c index 51fb30287f5..d3e34c681a7 100644 --- a/src/modules/pv/pv_branch.c +++ b/src/modules/pv/pv_branch.c @@ -93,6 +93,12 @@ int pv_get_branchx_helper(sip_msg_t *msg, pv_param_t *param, if(br->location_ua_len==0) return pv_get_null(msg, param, res); return pv_get_strlval(msg, param, res, br->location_ua, br->location_ua_len); + case 9: /* otcpid */ + return pv_get_uintval(msg, param, res, br->otcpid); + case 10: /* instance */ + if(br->instance_len==0) + return pv_get_null(msg, param, res); + return pv_get_strlval(msg, param, res, br->instance, br->instance_len); default: /* 0 - uri */ return pv_get_strlval(msg, param, res, br->uri, br->len); @@ -283,6 +289,22 @@ int pv_set_branchx_helper(sip_msg_t *msg, pv_param_t *param, case 8: /* location_ua */ /* do nothing - cannot set the location_ua */ break; + case 9: /* otcpid */ + if(val==NULL || (val->flags&PV_VAL_NULL)) + { + br->otcpid = 0; + break; + } + if(!(val->flags&PV_VAL_INT)) + { + LM_ERR("int value required to set branch flags\n"); + return -1; + } + br->otcpid = val->ri; + break; + case 10: /* instance */ + /* do nothing - cannot set the instance */ + break; default: /* 0 - uri */ if(val==NULL || (val->flags&PV_VAL_NULL)) @@ -335,42 +357,53 @@ int pv_parse_branchx_name(pv_spec_p sp, str *in) switch(in->len) { - case 3: - if(strncmp(in->s, "uri", 3)==0) - sp->pvp.pvn.u.isname.name.n = 0; + case 1: + if(*in->s=='q' || *in->s=='Q') + sp->pvp.pvn.u.isname.name.n = 3; else goto error; break; - case 7: - if(strncmp(in->s, "dst_uri", 7)==0) - sp->pvp.pvn.u.isname.name.n = 1; + case 3: + if(strncmp(in->s, "uri", 3)==0) + sp->pvp.pvn.u.isname.name.n = 0; else goto error; break; - case 4: + case 4: if(strncmp(in->s, "path", 4)==0) sp->pvp.pvn.u.isname.name.n = 2; else if (strncmp(in->s, "ruid", 4)==0) sp->pvp.pvn.u.isname.name.n = 7; else goto error; break; - case 1: - if(*in->s=='q' || *in->s=='Q') - sp->pvp.pvn.u.isname.name.n = 3; + case 5: + if(strncmp(in->s, "count", 5)==0) + sp->pvp.pvn.u.isname.name.n = 5; + else if(strncmp(in->s, "flags", 5)==0) + sp->pvp.pvn.u.isname.name.n = 6; + else goto error; + break; + case 6: + if(strncmp(in->s, "otcpid", 6)==0) + sp->pvp.pvn.u.isname.name.n = 9; + else goto error; + break; + case 7: + if(strncmp(in->s, "dst_uri", 7)==0) + sp->pvp.pvn.u.isname.name.n = 1; + else goto error; + break; + case 8: + if(strncmp(in->s, "instance", 8)==0) + sp->pvp.pvn.u.isname.name.n = 10; else goto error; break; - case 11: + case 11: if(strncmp(in->s, "send_socket", 11)==0) sp->pvp.pvn.u.isname.name.n = 4; else if(strncmp(in->s, "location_ua", 11)==0) sp->pvp.pvn.u.isname.name.n = 8; else goto error; break; - case 5: - if(strncmp(in->s, "count", 5)==0) - sp->pvp.pvn.u.isname.name.n = 5; - else if(strncmp(in->s, "flags", 5)==0) - sp->pvp.pvn.u.isname.name.n = 6; - else goto error; - break; + default: goto error; } @@ -804,6 +837,7 @@ int sbranch_set_ruri(sip_msg_t *msg) set_force_socket(msg, br->force_send_socket); msg->reg_id = br->reg_id; + msg->otcpid = br->otcpid; set_ruri_q(br->q); old_bflags = 0; getbflagsval(0, &old_bflags); @@ -824,7 +858,9 @@ int sbranch_append(sip_msg_t *msg) str path = {0}; str ruid = {0}; str location_ua = {0}; + str instance = {0}; branch_t *br; + branch_t *newbr; br = &_pv_sbranch; if(br->len==0) @@ -849,14 +885,19 @@ int sbranch_append(sip_msg_t *msg) location_ua.s = br->location_ua; location_ua.len = br->location_ua_len; } + if(br->instance_len) { + instance.s = br->instance; + instance.len = br->instance_len; + } - if (append_branch(msg, &uri, &duri, &path, br->q, br->flags, - br->force_send_socket, 0 /*instance*/, br->reg_id, - &ruid, &location_ua) - == -1) { + newbr = ksr_push_branch(msg, &uri, &duri, &path, br->q, br->flags, + br->force_send_socket, &instance, br->reg_id, + &ruid, &location_ua); + if(newbr==NULL) { LM_ERR("failed to append static branch\n"); return -1; } + newbr->otcpid = br->otcpid; return 0; } diff --git a/src/modules/pv_headers/pvh_func.c b/src/modules/pv_headers/pvh_func.c index 749275e8d79..90feddeefde 100644 --- a/src/modules/pv_headers/pvh_func.c +++ b/src/modules/pv_headers/pvh_func.c @@ -62,6 +62,7 @@ int pvh_collect_headers(struct sip_msg *msg) char hvals[header_name_size][header_value_size]; int idx = 0, d_size = 0; str val_part = STR_NULL; + char *marker = NULL; if(pvh_hdrs_collected(msg)) { LM_ERR("headers are already collected\n"); @@ -95,10 +96,10 @@ int pvh_collect_headers(struct sip_msg *msg) val.len = hf->body.len; val.s = hf->body.s; - if(strchr(val.s, ',') != NULL + if(( marker = pvh_detect_split_char(val.s)) != NULL && str_hash_case_get(&split_headers, name.s, name.len)) { - if(pvh_split_values(&val, hvals, &d_size, 1) < 0) { + if(pvh_split_values(&val, hvals, &d_size, 1, marker) < 0) { LM_ERR("could not parse %.*s header comma separated " "value", name.len, name.s); diff --git a/src/modules/pv_headers/pvh_hash.c b/src/modules/pv_headers/pvh_hash.c index 80297bcaafe..545fcd0578a 100644 --- a/src/modules/pv_headers/pvh_hash.c +++ b/src/modules/pv_headers/pvh_hash.c @@ -37,7 +37,7 @@ int pvh_str_hash_init(struct str_hash_table *ht, str *keys, char *desc) int idx = 0, d_size = 0; str val = STR_NULL; - if(pvh_split_values(keys, split, &d_size, 0) < 0) { + if(pvh_split_values(keys, split, &d_size, 0, NULL) < 0) { LM_ERR("could not parse %s param\n", desc); return -1; } diff --git a/src/modules/pv_headers/pvh_str.c b/src/modules/pv_headers/pvh_str.c index 6ede032ca1c..60c1a2fe455 100644 --- a/src/modules/pv_headers/pvh_str.c +++ b/src/modules/pv_headers/pvh_str.c @@ -103,10 +103,39 @@ int pvh_extract_display_uri(char *suri, str *display, str *duri) return 1; } -int pvh_split_values( - str *s, char d[][header_value_size], int *d_size, int keep_spaces) +char *pvh_detect_split_char(char *val) { - char p; + char *quote_a = NULL, *quote_b = NULL; + char *split = NULL; + + if(val == NULL) + return NULL; + + split = strchr(val, ','); + if(split == NULL) { + LM_DBG("no split marker detected\n"); + return NULL; + } + + quote_a = strchr(val, '"'); + if(quote_a == NULL || split < quote_a) { + LM_DBG("split marker detected[%ld], not between quotes\n", split - val); + return split; + } + + quote_b = strchr(val + (split - quote_a + 1), '"'); + if(quote_b == NULL) { + LM_DBG("split marker detected[%ld], quote occurrence unbalanced[%ld]\n", + split - val, quote_b - val); + return split; + } + return pvh_detect_split_char(val + (quote_b - val + 1)); +} + +int pvh_split_values(str *s, char d[][header_value_size], int *d_size, + int keep_spaces, char *marker) +{ + char *p = NULL; int idx = 0, c_idx = 0; *d_size = -1; @@ -115,12 +144,17 @@ int pvh_split_values( *d_size = 0; return 1; } - + if(!marker) + marker = pvh_detect_split_char(s->s); while(idx < s->len) { - strncpy(&p, s->s + idx++, 1); - if(keep_spaces == 0 && strncmp(&p, " ", 1) == 0) + p = s->s + idx++; + if(keep_spaces == 0 && strncmp(p, " ", 1) == 0) continue; - if(strncmp(&p, ",", 1) == 0) { + if(p == marker) { + if(marker && idx < s->len) { + LM_DBG("search next split marker[%d]\n", idx); + marker = pvh_detect_split_char(p + 1); + } if(c_idx == 0) continue; if(c_idx + 1 < header_value_size) @@ -131,7 +165,7 @@ int pvh_split_values( } if(c_idx == 0) (*d_size)++; - strncpy(&d[*d_size][c_idx++], &p, 1); + strncpy(&d[*d_size][c_idx++], p, 1); } if(c_idx > 0) { diff --git a/src/modules/pv_headers/pvh_str.h b/src/modules/pv_headers/pvh_str.h index 10aa216aca2..24feb330c3c 100644 --- a/src/modules/pv_headers/pvh_str.h +++ b/src/modules/pv_headers/pvh_str.h @@ -34,7 +34,8 @@ int pvh_str_new(str *s, int size); int pvh_str_free(str *s); int pvh_str_copy(str *dst, str *src, unsigned int max_size); int pvh_extract_display_uri(char *suri, str *display, str *duri); -int pvh_split_values( - str *s, char d[][header_value_size], int *d_size, int keep_spaces); +char *pvh_detect_split_char(char *s); +int pvh_split_values(str *s, char d[][header_value_size], int *d_size, + int keep_spaces, char *marker); #endif /* PV_STR_H */ \ No newline at end of file diff --git a/src/modules/registrar/README b/src/modules/registrar/README index 8100557bd55..e3876fa7c04 100644 --- a/src/modules/registrar/README +++ b/src/modules/registrar/README @@ -85,15 +85,16 @@ Bogdan-Andre Iancu 4.1. save(domain, [, flags [, uri]]) 4.2. lookup(domain [, uri]) - 4.3. lookup_branches(domain) - 4.4. registered(domain [, uri [, match_option [, + 4.3. lookup_to_dset(domain [, uri]) + 4.4. lookup_branches(domain) + 4.5. registered(domain [, uri [, match_option [, match_action]]]) - 4.5. add_sock_hdr(hdr_name) - 4.6. unregister(domain, uri[, ruid]) - 4.7. reg_fetch_contacts(domain, uri, profile) - 4.8. reg_free_contacts(profile) - 4.9. reg_send_reply() + 4.6. add_sock_hdr(hdr_name) + 4.7. unregister(domain, uri[, ruid]) + 4.8. reg_fetch_contacts(domain, uri, profile) + 4.9. reg_free_contacts(profile) + 4.10. reg_send_reply() 5. Event Routes @@ -151,15 +152,16 @@ Bogdan-Andre Iancu 1.34. Set use_expired_contacts parameter 1.35. save usage 1.36. lookup usage - 1.37. lookup_branches usage - 1.38. registered usage - 1.39. add_sock_hdr usage - 1.40. unregister usage - 1.41. reg_fetch_contacts usage - 1.42. reg_free_contacts usage - 1.43. reg_send_reply usage - 1.44. event_route[usrloc:contact-expired] usage - 1.45. $ulc(name) usage + 1.37. lookup_to_dset usage + 1.38. lookup_branches usage + 1.39. registered usage + 1.40. add_sock_hdr usage + 1.41. unregister usage + 1.42. reg_fetch_contacts usage + 1.43. reg_free_contacts usage + 1.44. reg_send_reply usage + 1.45. event_route[usrloc:contact-expired] usage + 1.46. $ulc(name) usage Chapter 1. Admin Guide @@ -217,13 +219,14 @@ Chapter 1. Admin Guide 4.1. save(domain, [, flags [, uri]]) 4.2. lookup(domain [, uri]) - 4.3. lookup_branches(domain) - 4.4. registered(domain [, uri [, match_option [, match_action]]]) - 4.5. add_sock_hdr(hdr_name) - 4.6. unregister(domain, uri[, ruid]) - 4.7. reg_fetch_contacts(domain, uri, profile) - 4.8. reg_free_contacts(profile) - 4.9. reg_send_reply() + 4.3. lookup_to_dset(domain [, uri]) + 4.4. lookup_branches(domain) + 4.5. registered(domain [, uri [, match_option [, match_action]]]) + 4.6. add_sock_hdr(hdr_name) + 4.7. unregister(domain, uri[, ruid]) + 4.8. reg_fetch_contacts(domain, uri, profile) + 4.9. reg_free_contacts(profile) + 4.10. reg_send_reply() 5. Event Routes @@ -968,13 +971,14 @@ kamcmd cfg.set_now_int registrar use_expired_contacts 0 4.1. save(domain, [, flags [, uri]]) 4.2. lookup(domain [, uri]) - 4.3. lookup_branches(domain) - 4.4. registered(domain [, uri [, match_option [, match_action]]]) - 4.5. add_sock_hdr(hdr_name) - 4.6. unregister(domain, uri[, ruid]) - 4.7. reg_fetch_contacts(domain, uri, profile) - 4.8. reg_free_contacts(profile) - 4.9. reg_send_reply() + 4.3. lookup_to_dset(domain [, uri]) + 4.4. lookup_branches(domain) + 4.5. registered(domain [, uri [, match_option [, match_action]]]) + 4.6. add_sock_hdr(hdr_name) + 4.7. unregister(domain, uri[, ruid]) + 4.8. reg_fetch_contacts(domain, uri, profile) + 4.9. reg_free_contacts(profile) + 4.10. reg_send_reply() 4.1. save(domain, [, flags [, uri]]) @@ -1055,7 +1059,8 @@ save("location", "0x00", "sip:test@kamailio.org"); Example 1.36. lookup usage ... lookup("location"); -switch ($retcode) { + switch ($retcode) {:1 + case -1: case -3: sl_send_reply("404", "Not Found"); @@ -1066,7 +1071,21 @@ switch ($retcode) { }; ... -4.3. lookup_branches(domain) +4.3. lookup_to_dset(domain [, uri]) + + Similar to lookup(...), but push the location contacts to destination + set, without changing the R-URI (first branch not changed, it creates + additional branches). For the meaning of the parameters and the return + codes, see the documentation for lookup(...) function. + + This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. + + Example 1.37. lookup_to_dset usage +... +lookup_to_dset("location"); +... + +4.4. lookup_branches(domain) The function performs lookup(domain) on r-uri and additional branches (only branches that have no other attributes set than uri). @@ -1078,12 +1097,12 @@ switch ($retcode) { This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. - Example 1.37. lookup_branches usage + Example 1.38. lookup_branches usage ... lookup_branches("location"); ... -4.4. registered(domain [, uri [, match_option [, match_action]]]) +4.5. registered(domain [, uri [, match_option [, match_action]]]) The function returns true if the AOR in the URI is registered, false otherwise. The function does not modify the message being process, it @@ -1110,7 +1129,7 @@ lookup_branches("location"); This function can be used from ANY_ROUTE. - Example 1.38. registered usage + Example 1.39. registered usage ... if (registered("location")) { sl_send_reply("100", "Trying"); @@ -1124,7 +1143,7 @@ if (registered("location","$rz:$Au", 2)) { }; ... -4.5. add_sock_hdr(hdr_name) +4.6. add_sock_hdr(hdr_name) Adds a new header to the current REGISTER request with “hdr_name” which contains the description of the received socket (proto:ip:port) @@ -1137,12 +1156,12 @@ if (registered("location","$rz:$Au", 2)) { This function can be used from REQUEST_ROUTE. - Example 1.39. add_sock_hdr usage + Example 1.40. add_sock_hdr usage ... add_sock_hdr("Sock-Info"); ... -4.6. unregister(domain, uri[, ruid]) +4.7. unregister(domain, uri[, ruid]) The function removes contacts associated with 'uri' from the location database. If 'ruid' is provided a specific contact is removed, if @@ -1167,7 +1186,7 @@ add_sock_hdr("Sock-Info"); * -2 - Error in unregistering user * -3 - Contacts for AOR not found - Example 1.40. unregister usage + Example 1.41. unregister usage ... unregister("location", "$ru"); unregister("location", "sip:user@kamailio.org"); @@ -1175,7 +1194,7 @@ unregister("location", "$ru", "$ulc(caller=>ruid)"); unregister("location", "", "$ruid"); ... -4.7. reg_fetch_contacts(domain, uri, profile) +4.8. reg_fetch_contacts(domain, uri, profile) The function fetches the contacts for 'uri' from table 'domain' to pseudo-variable $ulc(profile). @@ -1191,13 +1210,13 @@ unregister("location", "", "$ruid"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. - Example 1.41. reg_fetch_contacts usage + Example 1.42. reg_fetch_contacts usage ... reg_fetch_contacts("location", "$ru", "callee"); reg_fetch_contacts("location", "sip:user@kamailio.org", "caller"); ... -4.8. reg_free_contacts(profile) +4.9. reg_free_contacts(profile) The function frees the contacts from pseudo-variable $ulc(profile). Should be called to release the content of a profile. Anyhow, fetching @@ -1210,12 +1229,12 @@ reg_fetch_contacts("location", "sip:user@kamailio.org", "caller"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. - Example 1.42. reg_free_contacts usage + Example 1.43. reg_free_contacts usage ... reg_free_contacts("callee"); ... -4.9. reg_send_reply() +4.10. reg_send_reply() The function sends the SIP reply that is normally sent by save(...), but that was skipped due to flag 0x2. It must be used after save(..., @@ -1224,7 +1243,7 @@ reg_free_contacts("callee"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. - Example 1.43. reg_send_reply usage + Example 1.44. reg_send_reply usage ... save("location", "0x2"); ... @@ -1240,7 +1259,7 @@ reg_send_reply(); Executed when a contact in location table has expired. The variable $ulc(exp=>...) is filled with the attributes of the expired contact. - Example 1.44. event_route[usrloc:contact-expired] usage + Example 1.45. event_route[usrloc:contact-expired] usage ... event_route[usrloc:contact-expired] { xlog("expired contact for $ulc(exp=>aor)\n"); @@ -1314,7 +1333,7 @@ event_route[usrloc:contact-expired] { The pseudo-variable accepts positive index value to access a specific contact record. - Example 1.45. $ulc(name) usage + Example 1.46. $ulc(name) usage ... if(reg_fetch_contacts("location", "$fu", "caller")) { diff --git a/src/modules/registrar/doc/registrar_admin.xml b/src/modules/registrar/doc/registrar_admin.xml index f4bb213b141..e310636f01d 100644 --- a/src/modules/registrar/doc/registrar_admin.xml +++ b/src/modules/registrar/doc/registrar_admin.xml @@ -1338,7 +1338,8 @@ save("location", "0x00", "sip:test@kamailio.org"); <programlisting format="linespecific"> ... lookup("location"); -switch ($retcode) { + switch ($retcode) {:1 + case -1: case -3: sl_send_reply("404", "Not Found"); @@ -1352,6 +1353,29 @@ switch ($retcode) { </example> </section> + <section id="registrar.f.lookup_to_dset"> + <title> + <function moreinfo="none">lookup_to_dset(domain [, uri])</function> + + + Similar to lookup(...), but push the location contacts to destination + set, without changing the R-URI (first branch not changed, it creates + additional branches). For the meaning of the parameters and the return + codes, see the documentation for lookup(...) function. + + + This function can be used from REQUEST_ROUTE, FAILURE_ROUTE. + + + <function>lookup_to_dset</function> usage + +... +lookup_to_dset("location"); +... + + +
+
<function moreinfo="none">lookup_branches(domain)</function> diff --git a/src/modules/rtp_media_server/Makefile b/src/modules/rtp_media_server/Makefile index 490b7590dc9..5386a3065a2 100644 --- a/src/modules/rtp_media_server/Makefile +++ b/src/modules/rtp_media_server/Makefile @@ -9,5 +9,4 @@ BCUNITLIBS=-lbcunit MS2LIBS=-lmediastreamer LIBS=$(ORTPLIBS) $(BCUNITLIBS) $(MS2LIBS) -DEFS+=-DKAMAILIO_MOD_INTERFACE include ../../Makefile.modules diff --git a/src/modules/siputils/contact_ops.c b/src/modules/siputils/contact_ops.c index f21d7705b3e..1964ef59eef 100644 --- a/src/modules/siputils/contact_ops.c +++ b/src/modules/siputils/contact_ops.c @@ -165,12 +165,14 @@ int ki_decode_contact(sip_msg_t *msg) return res; } else { /* we do not modify the original first line */ - if((msg->new_uri.s == NULL) || (msg->new_uri.len == 0)) + if((msg->new_uri.s == NULL) || (msg->new_uri.len == 0)) { msg->new_uri = newUri; - else { + } else { pkg_free(msg->new_uri.s); msg->new_uri = newUri; } + msg->parsed_uri_ok=0; + ruri_mark_new(); } return 1; } @@ -577,7 +579,7 @@ int decode_uri(str uri, char separator, str *result) uri.len); /* adding one comes from * */ - result->s = pkg_malloc(result->len); + result->s = pkg_malloc(result->len + 1); /* NULL termination */ if(result->s == NULL) { LM_ERR("unable to allocate pkg memory\n"); return -4; @@ -626,6 +628,7 @@ int decode_uri(str uri, char separator, str *result) memcpy(pos, uri.s + format.second, uri.len - format.second); /* till end: */ + result->s[result->len] = '\0'; LM_DBG("New decoded uri [%.*s]\n", result->len, result->s); return 0; diff --git a/src/modules/smsops/smsops_impl.c b/src/modules/smsops/smsops_impl.c index e956656d1a5..6205c899280 100644 --- a/src/modules/smsops/smsops_impl.c +++ b/src/modules/smsops/smsops_impl.c @@ -173,6 +173,10 @@ void freeRP_DATA(sms_rp_data_t * rpdata) { #define BITMASK_HIGH_4BITS 0xF0 #define BITMASK_LOW_4BITS 0x0F #define BITMASK_TP_UDHI 0x40 +#define BITMASK_TP_VPF 0x18 +#define BITMASK_TP_VPF_RELATIVE 0x10 +#define BITMASK_TP_VPF_ENHANCED 0x08 +#define BITMASK_TP_VPF_ABSOLUTE 0x18 // Encode SMS-Message by merging 7 bit ASCII characters into 8 bit octets. static int ascii_to_gsm(str sms, char * output_buffer, int buffer_size) { @@ -525,7 +529,27 @@ int decode_3gpp_sms(struct sip_msg *msg) { } rp_data->pdu.pid = (unsigned char)body.s[p++]; rp_data->pdu.coding = (unsigned char)body.s[p++]; - rp_data->pdu.validity = (unsigned char)body.s[p++]; + + // 3GPP TS 03.40 9.2.2.2 SMS SUBMIT type + // https://en.wikipedia.org/wiki/GSM_03.40 + if(rp_data->pdu.msg_type == SUBMIT){ + // 3GPP TS 03.40 9.2.3.3 TP Validity Period Format (TP VPF) + switch (rp_data->pdu.flags & BITMASK_TP_VPF){ + case BITMASK_TP_VPF_RELATIVE: // 3GPP TS 03.40 9.2.3.12.1 TP-VP (Relative format) + rp_data->pdu.validity = (unsigned char)body.s[p++]; + break; + case BITMASK_TP_VPF_ENHANCED: // 3GPP TS 03.40 9.2.3.12.2 TP-VP (Absolute format) + p += 7; + LM_WARN("3GPP TS 03.40 9.2.3.12.2 TP-VP (Absolute format) is not supported\n"); + break; + case BITMASK_TP_VPF_ABSOLUTE: // 3GPP TS 03.40 9.2.3.12.3 TP-VP (Enhanced format) + p += 7; + LM_WARN("3GPP TS 03.40 9.2.3.12.3 TP-VP (Enhanced format) is not supported\n"); + break; + default: + break; + } + } //TP-User-Data-Length and TP-User-Data len = (unsigned char)body.s[p++]; diff --git a/src/modules/stirshaken/Makefile b/src/modules/stirshaken/Makefile new file mode 100644 index 00000000000..7383665fcdc --- /dev/null +++ b/src/modules/stirshaken/Makefile @@ -0,0 +1,28 @@ +# +# stirshaken module makefile +# +# WARNING: do not run this directly, it should be run by the main Makefile + +include ../../Makefile.defs +auto_gen= +NAME=stirshaken.so + +ifeq ($(CROSS_COMPILE),) + BUILDER = $(shell which pkg-config) +ifneq ($(BUILDER),) + PKGLIBSECSIPID = $(shell $(BUILDER) --exists stirshaken > /dev/null 2>&1 ; echo $$? ) +ifneq ($(PKGLIBSECSIPID),0) + BUILDER = +endif +endif +endif + +ifeq ($(BUILDER),) + DEFS+= -I. + LIBS = -L. -lstirshaken +else + DEFS+= $(shell pkg-config --cflags stirshaken) + LIBS = $(shell pkg-config --libs stirshaken) +endif + +include ../../Makefile.modules diff --git a/src/modules/stirshaken/README b/src/modules/stirshaken/README new file mode 100644 index 00000000000..911d6d3231c --- /dev/null +++ b/src/modules/stirshaken/README @@ -0,0 +1,489 @@ +Stirshaken Module + +Piotr Gregor + + signalwire.com + <piotr@signalwire.com> + +Edited by + +Piotr Gregor + + <piotr@signalwire.com> + + Copyright © 2021 https://www.signalwire.com + __________________________________________________________________ + + Table of Contents + + 1. Admin Guide + + 1. Overview + 2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + + 3. Parameters + + 3.1. as_default_key (str) + 3.2. vs_verify_x509_cert_path (int) + 3.3. vs_ca_dir (str) + 3.4. vs_crl_dir (str) + 3.5. vs_identity_expire_s (int) + 3.6. vs_connect_timeout_s (int) + 3.7. vs_cache_certificates (int) + 3.8. vs_cache_dir (str) + 3.9. vs_cache_expire_s (int) + + 4. Functions + + 4.1. stirshaken_check_identity() + 4.2. stirshaken_check_identity_with_key(keyPath) + 4.3. stirshaken_check_identity_with_cert(certPath) + 4.4. stirshaken_add_identity(x5u, attest, origtn_val, + desttn_val, origid) + + 4.5. stirshaken_add_identity_with_key(x5u, attest, + origtn_val, desttn_val, origid, keyPath) + + 5. Installation + + List of Examples + + 1.1. Set as_default_key parameter + 1.2. Set vs_verify_x509_cert_path parameter + 1.3. Set vs_ca_dir parameter + 1.4. Set vs_crl_dir parameter + 1.5. Set vs_identity_expire_s parameter + 1.6. Set vs_connect_timeout_s parameter + 1.7. Set vs_cache_certificates parameter + 1.8. Set vs_cache_dir parameter + 1.9. Set vs_cache_expire_s parameter + 1.10. stirshaken_check_identity usage + 1.11. stirshaken_check_identity_with_key usage + 1.12. stirshaken_check_identity_with_cert usage + 1.13. stirshaken_add_identity with origid usage + 1.14. stirshaken_add_identity with auto generated uuid as origid usage + 1.15. stirshaken_add_identity_with_key usage + 1.16. libstirshaken installation + +Chapter 1. Admin Guide + + Table of Contents + + 1. Overview + 2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + + 3. Parameters + + 3.1. as_default_key (str) + 3.2. vs_verify_x509_cert_path (int) + 3.3. vs_ca_dir (str) + 3.4. vs_crl_dir (str) + 3.5. vs_identity_expire_s (int) + 3.6. vs_connect_timeout_s (int) + 3.7. vs_cache_certificates (int) + 3.8. vs_cache_dir (str) + 3.9. vs_cache_expire_s (int) + + 4. Functions + + 4.1. stirshaken_check_identity() + 4.2. stirshaken_check_identity_with_key(keyPath) + 4.3. stirshaken_check_identity_with_cert(certPath) + 4.4. stirshaken_add_identity(x5u, attest, origtn_val, desttn_val, + origid) + + 4.5. stirshaken_add_identity_with_key(x5u, attest, origtn_val, + desttn_val, origid, keyPath) + + 5. Installation + +1. Overview + + The module implements secure SIP identity specifications - STIR (Secure + Telephony Identity Revisited) and SHAKEN (Signature-based Handling of + Asserted information using toKENs) IETF extensions for SIP (RFC8224, + RFC8588). + + stirshaken module exports the functions to check and to generate + PASSporT, wrapped into SIP Identity header. For call authentication two + functions are available: stirshaken_add_identity(...) and + stirshaken_add_identity_with_key(key). stirshaken_add_identity() uses + default key (through Authentication Service), + stirshaken_add_identity_with_key(..., key) uses specifoed key. For call + verification three methods are available: stirshaken_check_identity() + (through Verification Service), stirshaken_check_identity_with_key(key) + and stirshaken_check_identity_with_cert(cert). + +2. Dependencies + + 2.1. Kamailio Modules + 2.2. External Libraries or Applications + +2.1. Kamailio Modules + + The following modules must be loaded before this module: + * No dependencies on other Kamailio modules. + +2.2. External Libraries or Applications + + The following libraries or applications must be installed before + running Kamailio with this module loaded: + * libstirshaken - https://github.com/signalwire/libstirshaken. + +3. Parameters + + 3.1. as_default_key (str) + 3.2. vs_verify_x509_cert_path (int) + 3.3. vs_ca_dir (str) + 3.4. vs_crl_dir (str) + 3.5. vs_identity_expire_s (int) + 3.6. vs_connect_timeout_s (int) + 3.7. vs_cache_certificates (int) + 3.8. vs_cache_dir (str) + 3.9. vs_cache_expire_s (int) + +3.1. as_default_key (str) + + SSL private key to be used as default. Default key must be set if calls + to stirshaken_add_identity() are executed. When set, module starts + Authentication Service which makes each call to + stirshaken_add_identity() using this key. Default key doesn't need to + be set (Authentication Service doesn't need to be running) for the + stirshaken_add_identity_with_key(..., key) to be available. This param + has no meaning for calls to stirshaken_add_identity_with_key(..., key). + + Default value is "" (not set). + + Example 1.1. Set as_default_key parameter +... +modparam("stirshaken", "as_default_key", "/path/to/key") +... + +3.2. vs_verify_x509_cert_path (int) + + If set, then stirshaken_check_identity() will execute X509 certificate + path check on certificate referenced in PASSporT. This param has no + meaning for calls to stirshaken_check_identity_with_key(key) and + stirshaken_check_identity_with_cert(cert). + + Default value is 0, (turned off). + + Example 1.2. Set vs_verify_x509_cert_path parameter +... +modparam("stirshaken", "vs_verify_x509_cert_path", 1) +... + +3.3. vs_ca_dir (str) + + The path to folder containing CA root certificates with names hashed. + If set then must point to existing directory. This must be set when + enabled X509 certificate path check, otherwise no end entity + certificate will pass that check. This param has no meaning for calls + to stirshaken_check_identity_with_key(key) and + stirshaken_check_identity_with_cert(cert). + + Default value is "" (not set). + + Example 1.3. Set vs_ca_dir parameter +... +modparam("stirshaken", "vs_ca_dir", "/path/to/ca_dir") +... + +3.4. vs_crl_dir (str) + + The path to folder containing CRLs. If set, then must point to existing + directory. This is optional when X509 certificate path check is + enabled, only vs_ca_dir is mandatory. If X509 certificate path check is + enabled, and vs_crl_dir is set, then CRLs are loaded from this + directory, which renders revoked certificates invalid (not trusted). + This param has no meaning for calls to + stirshaken_check_identity_with_key(key) and + stirshaken_check_identity_with_cert(cert). + + Default value is "" (not set). + + Example 1.4. Set vs_crl_dir parameter +... +modparam("stirshaken", "vs_crl_dir", "/path/to/crl_dir") +... + +3.5. vs_identity_expire_s (int) + + This parameter defines a maximum time in seconds for which PASSporT is + considered valid. + + Default value is 60 seconds. + + Example 1.5. Set vs_identity_expire_s parameter +... +modparam("stirshaken", "vs_identity_expire_s", 20) +... + +3.6. vs_connect_timeout_s (int) + + During a call verification with stirshaken_check_identity() a blocking + HTTP(s) call is executed to download certificate referneced in PASSporT + (unless certificate caching is turned on and a valid cert is found in + cache). This parameter defines a maximum time in seconds for this + blocking HTTP(s) connection to be established. After this time had + passed and connection did not succeed (could not resolve host, address + unreachable or other network errors) a call to + stirshaken_check_identity() will return with error. This param has no + meaning for calls to stirshaken_check_identity_with_key(key) and + stirshaken_check_identity_with_cert(cert). + + Default value is 5 seconds. + + Example 1.6. Set vs_connect_timeout_s parameter +... +modparam("stirshaken", "vs_connect_timeout_s", 10) +... + +3.7. vs_cache_certificates (int) + + If set, then certificates caching is turned on. This means that + certificates downloaded during call verification with + stirshaken_check_identity() are cached inside vs_cache_dir, and will be + loaded from that cache as long as they are not there for more than + vs_cache_expire_s seconds (see vs_cache_expire_s). If + vs_cache_certificates is set then vs_cache_dir must be set too and + pointing to existing directory. This param has no meaning for calls to + stirshaken_check_identity_with_key(key) and + stirshaken_check_identity_with_cert(cert). + + Default value is 0 (turned off). + + Example 1.7. Set vs_cache_certificates parameter +... +modparam("stirshaken", "vs_cache_certificates", 1) +... + +3.8. vs_cache_dir (str) + + If vs_cache_certificates is set then vs_cache_dir must be set too and + pointing to existing directory. Cached certificates are saved in this + directory and loaded from there when needed during a call verification + executed with stirshaken_check_identity(), as long as they are not + there for more than vs_cache_expire_s seconds. This param has no + meaning for calls to stirshaken_check_identity_with_key(key) and + stirshaken_check_identity_with_cert(cert). + + Default value is "" (not set). + + Example 1.8. Set vs_cache_dir parameter +... +modparam("stirshaken", "vs_cache_dir", "/tmp/cert_cache") +... + +3.9. vs_cache_expire_s (int) + + If vs_cache_certificates is set then cached certificates are saved in + vs_cache_dir directory and loaded from there when needed during a call + verification executed with stirshaken_check_identity(), as long as they + are not there for more than vs_cache_expire_s seconds. If they are in + cache for more than vs_cache_expire_s seconds, then a blocking HTTP(s) + call is executed to download a new version of (expired) certificate. If + this is successful then old version is removed and new version is saved + in cache. This param has no meaning for calls to + stirshaken_check_identity_with_key(key) and + stirshaken_check_identity_with_cert(cert). + + Default value is 120 seconds. + + Example 1.9. Set vs_cache_expire_s parameter +... +modparam("stirshaken", "vs_cache_expire_s", 15) +... + +4. Functions + + 4.1. stirshaken_check_identity() + 4.2. stirshaken_check_identity_with_key(keyPath) + 4.3. stirshaken_check_identity_with_cert(certPath) + 4.4. stirshaken_add_identity(x5u, attest, origtn_val, desttn_val, + origid) + + 4.5. stirshaken_add_identity_with_key(x5u, attest, origtn_val, + desttn_val, origid, keyPath) + +4.1. stirshaken_check_identity() + + Check the validity of the Identity header by decoding PASSporT's + signature with a certificate referenced in it's x5u header and + (optionally) checking that certificate for being trusted by X509 + certificate check with CA root certificates in vs_ca_dir (and + optionally CRLs in vs_crl_dir). PASSporT's iat grant is also checked + for being too fresh or expired against vs_identity_expire_s seconds. + This function executes a call to a callback which may supply + certificates from cache (see vs_cache_certificates param). If + certificate needs to be downloaded this call will block for a maximum + of vs_connect_timeout_s seconds (see vs_connect_timeout_s param); + + This function takes no parameters (only SIP message is passed + implicitly). + + This function can be used from ANY_ROUTE. + + Example 1.10. stirshaken_check_identity usage +... +modparam("stirshaken", "vs_verify_x509_cert_path", 1) +modparam("stirshaken", "vs_ca_dir", "/path/to/ca") +modparam("stirshaken", "vs_cache_certificates", 1) +modparam("stirshaken", "vs_cache_dir", "/tmp/cert_cache") +modparam("stirshaken", "vs_cache_expire_s", 100) + +request_route { + ... + if(stirshaken_check_identity()) { // bad identity } + ... +} +... + +4.2. stirshaken_check_identity_with_key(keyPath) + + Check the validity of the Identity header by decoding PASSporT's + signature with a key read from the location provided. PASSporT's iat + grant is also checked for being too fresh or expired against + vs_identity_expire_s seconds. This method does not involve HTTP(s) + transcations. This method does not execute a call to a callback + (vs_cache_certificates param has no meaning for this method). WARNING: + This method only checks if SIP Identity Header was signed by a key + corresponding to specified public key. This method doesn't attempt to + obtain certificate referenced in PASSporT (but PASSporT should be + checked with key corresponding to that certificate). Therefore it is + possible that this check will be successful, while PASSporT is not + valid (could be signed with key that doesn't match certificate + referenced in x5u header). If you want a complete Shaken check or if + you are not sure what you're doing, then you should execute + w_stirshaken_check_identity() instead (and configure Verification + Service to perform X509 certificate path verification with + stirshaken_vs_verify_x509_cert_path param set to 1). + + The parameters can contain pseudo-variables. + + This function can be used from ANY_ROUTE. + + Example 1.11. stirshaken_check_identity_with_key usage +... +request_route { + ... + if(stirshaken_check_identity_with_key("/path/to/key")) { // bad identity + } + ... +} +... + +4.3. stirshaken_check_identity_with_cert(certPath) + + Same as stirshaken_check_identity_with_key(keyPath) but the key is read + from the certificate which is read from the location provided. + + The parameters can contain pseudo-variables. + + This function can be used from ANY_ROUTE. + + Example 1.12. stirshaken_check_identity_with_cert usage +... +request_route { + ... + if(stirshaken_check_identity_with_cert("/path/to/cert")) { // bad identi +ty } + ... +} +... + +4.4. stirshaken_add_identity(x5u, attest, origtn_val, desttn_val, origid) + + Add SIP Identity Header to the call using default private key (see + as_default_key param). Authenticate call with STIR-Shaken. If origID is + empty, a UUID string is generated to fill the field. The origtn_val + represents the origination telephone number; desttn_val, represents the + destination telephone number; x5u is the HTTP(s) URL referencing to the + public key that should be used to verify the signature; attest + represents the attestation level (should be "A", "B" or "C"). + + The parameters can contain pseudo-variables. If origid is empty, an + unique identifier will be generated wih libuuid, e.g. + "3f31bd2b-9fc4-4084-b0b0-566506c46292". + + This function can be used from ANY_ROUTE. + + Example 1.13. stirshaken_add_identity with origid usage +... +request_route { + ... + stirshaken_add_identity("https://sp.com/sp.pem", "B", "+44100", "+44200" +, "origid"); + ... +} +... + + Example 1.14. stirshaken_add_identity with auto generated uuid as + origid usage + + If origid is empty, an unique identifier will be generated with + libuuid, e.g. "3f31bd2b-9fc4-4084-b0b0-566506c46292". +... +request_route { + ... + stirshaken_add_identity("https://sp.com/sp.pem", "B", "+44100", "+44200" +, ""); + ... +} +... + +4.5. stirshaken_add_identity_with_key(x5u, attest, origtn_val, desttn_val, +origid, keyPath) + + Same as stirshaken_add_identity() but using the key read from the + location provided as a last parameter. + + The parameters can contain pseudo-variables. If origid is empty, an + unique identifier will be generated with libuuid, e.g. + "3f31bd2b-9fc4-4084-b0b0-566506c46292". + + This function can be used from ANY_ROUTE. + + Example 1.15. stirshaken_add_identity_with_key usage +... +request_route { + ... + stirshaken_add_identity_with_key("https://sp.com/sp.pem", "B", "+44100", + "+44200", uuid, "/path/to/key"); + ... +} +... + +5. Installation + + The module depends on "libstirshaken", which is an open source C + library from SignalWire. It can be downloaded from + https://github.com/signalwire/libstirshaken. Until the libstirshaken is + packaged in OS distributions, libstirshaken must be compiled and + installed before the stirshaken module can be compiled. + + Installling libstirshaken is easy: + + Example 1.16. libstirshaken installation +... + git clone git@github.com:signalwire/libstirshaken.git + cd libstirshaken + ./bootstrap.sh + ./configure + make + make check + sudo make install +... + + After libstirshaken had been installed, Kamailio's stirshaken module + can then be built with +... + cd /path/to/kamailio/ + make modules modules=src/modules/stirshaken/ +... diff --git a/src/modules/stirshaken/doc/Makefile b/src/modules/stirshaken/doc/Makefile new file mode 100644 index 00000000000..7af386310ec --- /dev/null +++ b/src/modules/stirshaken/doc/Makefile @@ -0,0 +1,4 @@ +docs = stirshaken.xml + +docbook_dir = ../../../../doc/docbook +include $(docbook_dir)/Makefile.module diff --git a/src/modules/stirshaken/doc/stirshaken.xml b/src/modules/stirshaken/doc/stirshaken.xml new file mode 100644 index 00000000000..10965b952e2 --- /dev/null +++ b/src/modules/stirshaken/doc/stirshaken.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding='ISO-8859-1'?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" +"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [ + +<!-- Include general documentation entities --> +<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml"> +%docentities; + +]> + +<book xmlns:xi="http://www.w3.org/2001/XInclude"> + <bookinfo> + <title>Stirshaken Module + &kamailioname; + + + Piotr + Gregor + signalwire.com + piotr@signalwire.com +
+ + https://www.signalwire.com + +
+
+ + Piotr + Gregor + piotr@signalwire.com + +
+ + 2021 + https://www.signalwire.com + + + + + + + diff --git a/src/modules/stirshaken/doc/stirshaken_admin.xml b/src/modules/stirshaken/doc/stirshaken_admin.xml new file mode 100644 index 00000000000..55ef50a8370 --- /dev/null +++ b/src/modules/stirshaken/doc/stirshaken_admin.xml @@ -0,0 +1,471 @@ + + + +%docentities; + +]> + + + + + &adminguide; + +
+ Overview + + The module implements secure SIP identity specifications - STIR + (Secure Telephony Identity Revisited) and SHAKEN + (Signature-based Handling of Asserted information using toKENs) + IETF extensions for SIP (RFC8224, RFC8588). + + + stirshaken module exports the functions to check and to generate PASSporT, wrapped into SIP Identity header. + For call authentication two functions are available: stirshaken_add_identity(...) and stirshaken_add_identity_with_key(key). + stirshaken_add_identity() uses default key (through Authentication Service), stirshaken_add_identity_with_key(..., key) uses specifoed key. + For call verification three methods are available: stirshaken_check_identity() (through Verification Service), + stirshaken_check_identity_with_key(key) and stirshaken_check_identity_with_cert(cert). + +
+
+ Dependencies +
+ &kamailio; Modules + + The following modules must be loaded before this module: + + + + No dependencies on other &kamailio; modules. + + + + +
+
+ External Libraries or Applications + + The following libraries or applications must be installed before running + &kamailio; with this module loaded: + + + + libstirshaken - https://github.com/signalwire/libstirshaken. + + + + +
+
+
+ Parameters +
+ <varname>as_default_key</varname> (str) + + SSL private key to be used as default. Default key must be set if calls to stirshaken_add_identity() are executed. + When set, module starts Authentication Service which makes each call to stirshaken_add_identity() using this key. + Default key doesn't need to be set (Authentication Service doesn't need to be running) for the stirshaken_add_identity_with_key(..., key) to be available. + This param has no meaning for calls to stirshaken_add_identity_with_key(..., key). + + + + Default value is "" (not set). + + + + Set <varname>as_default_key</varname> parameter + +... +modparam("stirshaken", "as_default_key", "/path/to/key") +... + + +
+
+ <varname>vs_verify_x509_cert_path</varname> (int) + + If set, then stirshaken_check_identity() will execute X509 certificate path check on certificate referenced in PASSporT. + This param has no meaning for calls to stirshaken_check_identity_with_key(key) and stirshaken_check_identity_with_cert(cert). + + + + Default value is 0, (turned off). + + + + Set <varname>vs_verify_x509_cert_path</varname> parameter + +... +modparam("stirshaken", "vs_verify_x509_cert_path", 1) +... + + +
+
+ <varname>vs_ca_dir</varname> (str) + + The path to folder containing CA root certificates with names hashed. If set then must point to existing directory. + This must be set when enabled X509 certificate path check, otherwise no end entity certificate will pass that check. + This param has no meaning for calls to stirshaken_check_identity_with_key(key) and stirshaken_check_identity_with_cert(cert). + + + + Default value is "" (not set). + + + + Set <varname>vs_ca_dir</varname> parameter + +... +modparam("stirshaken", "vs_ca_dir", "/path/to/ca_dir") +... + + +
+
+ <varname>vs_crl_dir</varname> (str) + + The path to folder containing CRLs. If set, then must point to existing directory. + This is optional when X509 certificate path check is enabled, only vs_ca_dir is mandatory. + If X509 certificate path check is enabled, and vs_crl_dir is set, then CRLs are loaded from this directory, + which renders revoked certificates invalid (not trusted). + This param has no meaning for calls to stirshaken_check_identity_with_key(key) and stirshaken_check_identity_with_cert(cert). + + + + Default value is "" (not set). + + + + Set <varname>vs_crl_dir</varname> parameter + +... +modparam("stirshaken", "vs_crl_dir", "/path/to/crl_dir") +... + + +
+
+ <varname>vs_identity_expire_s</varname> (int) + + This parameter defines a maximum time in seconds for which PASSporT is considered valid. + + + + Default value is 60 seconds. + + + + Set <varname>vs_identity_expire_s</varname> parameter + +... +modparam("stirshaken", "vs_identity_expire_s", 20) +... + + +
+
+ <varname>vs_connect_timeout_s </varname> (int) + + During a call verification with stirshaken_check_identity() a blocking HTTP(s) call is executed to download certificate + referneced in PASSporT (unless certificate caching is turned on and a valid cert is found in cache). + This parameter defines a maximum time in seconds for this blocking HTTP(s) connection to be established. + After this time had passed and connection did not succeed (could not resolve host, address unreachable or other network errors) + a call to stirshaken_check_identity() will return with error. + This param has no meaning for calls to stirshaken_check_identity_with_key(key) and stirshaken_check_identity_with_cert(cert). + + + + Default value is 5 seconds. + + + + Set <varname>vs_connect_timeout_s</varname> parameter + +... +modparam("stirshaken", "vs_connect_timeout_s", 10) +... + + +
+
+ <varname>vs_cache_certificates</varname> (int) + + If set, then certificates caching is turned on. This means that certificates downloaded during call verification with stirshaken_check_identity() + are cached inside vs_cache_dir, and will be loaded from that cache as long as they are not there for more than vs_cache_expire_s seconds (see vs_cache_expire_s). + If vs_cache_certificates is set then vs_cache_dir must be set too and pointing to existing directory. + This param has no meaning for calls to stirshaken_check_identity_with_key(key) and stirshaken_check_identity_with_cert(cert). + + + + Default value is 0 (turned off). + + + + Set <varname>vs_cache_certificates</varname> parameter + +... +modparam("stirshaken", "vs_cache_certificates", 1) +... + + +
+
+ <varname>vs_cache_dir</varname> (str) + + If vs_cache_certificates is set then vs_cache_dir must be set too and pointing to existing directory. + Cached certificates are saved in this directory and loaded from there when needed during a call verification executed with stirshaken_check_identity(), + as long as they are not there for more than vs_cache_expire_s seconds. + This param has no meaning for calls to stirshaken_check_identity_with_key(key) and stirshaken_check_identity_with_cert(cert). + + + + Default value is "" (not set). + + + + Set <varname>vs_cache_dir</varname> parameter + +... +modparam("stirshaken", "vs_cache_dir", "/tmp/cert_cache") +... + + +
+
+ <varname>vs_cache_expire_s</varname> (int) + + If vs_cache_certificates is set then cached certificates are saved in vs_cache_dir directory and loaded from there + when needed during a call verification executed with stirshaken_check_identity(), as long as they are not there for more than vs_cache_expire_s seconds. + If they are in cache for more than vs_cache_expire_s seconds, then a blocking HTTP(s) call is executed to download a new version of (expired) certificate. + If this is successful then old version is removed and new version is saved in cache. + This param has no meaning for calls to stirshaken_check_identity_with_key(key) and stirshaken_check_identity_with_cert(cert). + + + + Default value is 120 seconds. + + + + Set <varname>vs_cache_expire_s</varname> parameter + +... +modparam("stirshaken", "vs_cache_expire_s", 15) +... + + +
+ +
+ +
+ Functions +
+ + <function moreinfo="none">stirshaken_check_identity()</function> + + + Check the validity of the Identity header by decoding PASSporT's signature with a certificate referenced in it's x5u header + and (optionally) checking that certificate for being trusted by X509 certificate check with CA root certificates in vs_ca_dir + (and optionally CRLs in vs_crl_dir). PASSporT's iat grant is also checked for being too fresh or expired against vs_identity_expire_s seconds. + This function executes a call to a callback which may supply certificates from cache (see vs_cache_certificates param). + If certificate needs to be downloaded this call will block for a maximum of vs_connect_timeout_s seconds (see vs_connect_timeout_s param); + + + This function takes no parameters (only SIP message is passed implicitly). + + + This function can be used from ANY_ROUTE. + + + <function>stirshaken_check_identity</function> usage + +... +modparam("stirshaken", "vs_verify_x509_cert_path", 1) +modparam("stirshaken", "vs_ca_dir", "/path/to/ca") +modparam("stirshaken", "vs_cache_certificates", 1) +modparam("stirshaken", "vs_cache_dir", "/tmp/cert_cache") +modparam("stirshaken", "vs_cache_expire_s", 100) + +request_route { + ... + if(stirshaken_check_identity()) { // bad identity } + ... +} +... + + +
+
+ + <function moreinfo="none">stirshaken_check_identity_with_key(keyPath)</function> + + + Check the validity of the Identity header by decoding PASSporT's signature with a key read from the location provided. + PASSporT's iat grant is also checked for being too fresh or expired against vs_identity_expire_s seconds. + This method does not involve HTTP(s) transcations. + This method does not execute a call to a callback (vs_cache_certificates param has no meaning for this method). + WARNING: + This method only checks if SIP Identity Header was signed by a key corresponding to specified public key. + This method doesn't attempt to obtain certificate referenced in PASSporT (but PASSporT should be checked with key corresponding to that certificate). + Therefore it is possible that this check will be successful, while PASSporT is not valid (could be signed with key that doesn't match certificate referenced in x5u header). + If you want a complete Shaken check or if you are not sure what you're doing, then you should execute w_stirshaken_check_identity() instead + (and configure Verification Service to perform X509 certificate path verification with stirshaken_vs_verify_x509_cert_path param set to 1). + + + The parameters can contain pseudo-variables. + + + This function can be used from ANY_ROUTE. + + + <function>stirshaken_check_identity_with_key</function> usage + +... +request_route { + ... + if(stirshaken_check_identity_with_key("/path/to/key")) { // bad identity } + ... +} +... + + +
+
+ + <function moreinfo="none">stirshaken_check_identity_with_cert(certPath)</function> + + + Same as stirshaken_check_identity_with_key(keyPath) but the key is read from the certificate which is read from the location provided. + + + The parameters can contain pseudo-variables. + + + This function can be used from ANY_ROUTE. + + + <function>stirshaken_check_identity_with_cert</function> usage + +... +request_route { + ... + if(stirshaken_check_identity_with_cert("/path/to/cert")) { // bad identity } + ... +} +... + + +
+
+ + <function moreinfo="none">stirshaken_add_identity(x5u, attest, origtn_val, desttn_val, origid)</function> + + + Add SIP Identity Header to the call using default private key (see as_default_key param). Authenticate call with STIR-Shaken. + If origID is empty, a UUID string is generated to fill the field. The origtn_val represents the origination telephone number; + desttn_val, represents the destination telephone number; x5u is the HTTP(s) URL referencing to the public key that should be used + to verify the signature; attest represents the attestation level (should be "A", "B" or "C"). + + + The parameters can contain pseudo-variables. + If origid is empty, an unique identifier will be generated wih libuuid, e.g. "3f31bd2b-9fc4-4084-b0b0-566506c46292". + + + This function can be used from ANY_ROUTE. + + + <function>stirshaken_add_identity</function> with origid usage + +... +request_route { + ... + stirshaken_add_identity("https://sp.com/sp.pem", "B", "+44100", "+44200", "origid"); + ... +} +... + + + + <function>stirshaken_add_identity</function> with auto generated uuid as origid usage + + If origid is empty, an unique identifier will be generated with libuuid, e.g. "3f31bd2b-9fc4-4084-b0b0-566506c46292". + + +... +request_route { + ... + stirshaken_add_identity("https://sp.com/sp.pem", "B", "+44100", "+44200", ""); + ... +} +... + + +
+
+ + <function moreinfo="none">stirshaken_add_identity_with_key(x5u, attest, origtn_val, desttn_val, origid, keyPath)</function> + + + Same as stirshaken_add_identity() but using the key read from the location provided as a last parameter. + + + The parameters can contain pseudo-variables. + If origid is empty, an unique identifier will be generated with libuuid, e.g. "3f31bd2b-9fc4-4084-b0b0-566506c46292". + + + This function can be used from ANY_ROUTE. + + + <function>stirshaken_add_identity_with_key</function> usage + +... +request_route { + ... + stirshaken_add_identity_with_key("https://sp.com/sp.pem", "B", "+44100", "+44200", uuid, "/path/to/key"); + ... +} +... + + +
+
+
+ Installation + + The module depends on "libstirshaken", which is an open source C library from SignalWire. It can be downloaded from https://github.com/signalwire/libstirshaken. + Until the libstirshaken is packaged in OS distributions, libstirshaken must be compiled and installed before the stirshaken module can be compiled. + + + Installling libstirshaken is easy: + + + libstirshaken installation + +... + git clone git@github.com:signalwire/libstirshaken.git + cd libstirshaken + ./bootstrap.sh + ./configure + make + make check + sudo make install +... + + Building Kamailio's stirshaken module + + After libstirshaken had been installed, Kamailio's stirshaken module can then be built with + + +... + cd /path/to/kamailio/ + make modules modules=src/modules/stirshaken/ +... + + +
+ +
+ diff --git a/src/modules/stirshaken/stirshaken_mod.c b/src/modules/stirshaken/stirshaken_mod.c new file mode 100644 index 00000000000..a98cb3a4e31 --- /dev/null +++ b/src/modules/stirshaken/stirshaken_mod.c @@ -0,0 +1,981 @@ +/** + * Copyright (C) 2021 kamailio.org + * + * This file is part of Kamailio, a free SIP server. + * + * This file 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 + * + * + * This file 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 + * + */ + +#include +#include +#include +#include + +#include + +#include "../../core/sr_module.h" +#include "../../core/dprint.h" +#include "../../core/mod_fix.h" +#include "../../core/data_lump.h" +#include "../../core/lvalue.h" +#include "../../core/kemi.h" + +MODULE_VERSION + +// Authentication service +static str stirshaken_as_default_key = str_init(""); + +// Verification service +static int stirshaken_vs_verify_x509_cert_path = 0; +static str stirshaken_vs_ca_dir = str_init(""); +static str stirshaken_vs_crl_dir = str_init(""); +static int stirshaken_vs_identity_expire_s = 60; +static int stirshaken_vs_connect_timeout_s = 5; + +static int stirshaken_vs_cache_certificates = 0; +static size_t stirshaken_vs_cache_expire_s = 120; +static str stirshaken_vs_cache_dir = str_init(""); + +static int mod_init(void); +static int child_init(int); +static void mod_destroy(void); + +static int w_stirshaken_check_identity(sip_msg_t *msg, char *str1, char *str2); +static int w_stirshaken_check_identity_with_cert(sip_msg_t *msg, char *cert_path, char *str2); +static int w_stirshaken_check_identity_with_key(sip_msg_t *msg, char *pkey_path, char *str2); +static int w_stirshaken_add_identity(sip_msg_t *msg, str *px5u, str *pattest, str *porigtn_val, str *pdesttn_val, str *porigid); +static int w_stirshaken_add_identity_with_key(sip_msg_t *msg, str *px5u, str *pattest, str *porigtn_val, str *pdesttn_val, str *porigid, str *pkeypath); + + +/* clang-format off */ +static cmd_export_t cmds[]={ + {"stirshaken_check_identity", (cmd_function)w_stirshaken_check_identity, 0, + 0, 0, ANY_ROUTE}, + {"stirshaken_check_identity_with_cert", (cmd_function)w_stirshaken_check_identity_with_cert, 1, + fixup_spve_null, fixup_free_spve_null, ANY_ROUTE}, + {"stirshaken_check_identity_with_key", (cmd_function)w_stirshaken_check_identity_with_key, 1, + fixup_spve_null, fixup_free_spve_null, ANY_ROUTE}, + {"stirshaken_add_identity", (cmd_function)w_stirshaken_add_identity, 5, + fixup_spve_all, fixup_free_spve_all, ANY_ROUTE}, + {"stirshaken_add_identity_with_key", (cmd_function)w_stirshaken_add_identity_with_key, 6, + fixup_spve_all, fixup_free_spve_all, ANY_ROUTE}, + {0, 0, 0, 0, 0, 0} +}; + +static param_export_t params[] = { + + // Authentication service + {"as_default_key", PARAM_STR, &stirshaken_as_default_key}, + + // Verification service + {"vs_verify_x509_cert_path",PARAM_INT, &stirshaken_vs_verify_x509_cert_path}, + {"vs_ca_dir", PARAM_STR, &stirshaken_vs_ca_dir}, + {"vs_crl_dir", PARAM_STR, &stirshaken_vs_crl_dir}, + {"vs_identity_expire_s", PARAM_INT, &stirshaken_vs_identity_expire_s}, + {"vs_connect_timeout_s", PARAM_INT, &stirshaken_vs_connect_timeout_s}, + {"vs_cache_certificates", PARAM_INT, &stirshaken_vs_cache_certificates}, + {"vs_cache_expire_s", PARAM_INT, &stirshaken_vs_cache_expire_s}, + {"vs_cache_dir", PARAM_STR, &stirshaken_vs_cache_dir}, + {0, 0, 0} +}; + +struct module_exports exports = { + "stirshaken", + DEFAULT_DLFLAGS, /* dlopen flags */ + cmds, + params, + 0, /* exported RPC methods */ + 0, /* exported pseudo-variables */ + 0, /* response function */ + mod_init, /* module initialization function */ + child_init, /* per child init function */ + mod_destroy /* destroy function */ +}; +/* clang-format on */ + +static void stirshaken_print_error_details(void *context) +{ + const char *error_description = NULL; + stir_shaken_error_t error_code = STIR_SHAKEN_ERROR_GENERAL; + + if (stir_shaken_is_error_set(context)) { + error_description = stir_shaken_get_error(context, &error_code); + LM_DBG("failure details:\n"); + LM_DBG("failure reason is: %s\n", error_description); + LM_DBG("failure error code is: %d\n", error_code); + } +} + +static unsigned long hash_to_long(const char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + hash = ((hash << 5) + hash) + c; + + return hash; +} + +static void hash_to_string(const char *url, char *buf, int buf_len) +{ + unsigned long hash_long = hash_to_long(url); + snprintf(buf, buf_len, "%lu.pem", hash_long); +} + +static int get_cert_name_hashed(const char *name, char *buf, int buf_len) +{ + char cert_hash[64] = { 0 }; + + hash_to_string(name, cert_hash, sizeof(cert_hash)); + + if (!stir_shaken_make_complete_path(buf, buf_len, stirshaken_vs_cache_dir.s, cert_hash, "/")) { + LM_ERR("Cannot create cert name hashed\n"); + return -1; + } + + return 0; +} + +static stir_shaken_status_t shaken_callback(stir_shaken_callback_arg_t *arg) +{ + stir_shaken_context_t ss = { 0 }; + stir_shaken_cert_t cache_copy = { 0 }; + char cert_full_path[STIR_SHAKEN_BUFLEN] = { 0 }; + + switch (arg->action) { + + case STIR_SHAKEN_CALLBACK_ACTION_CERT_FETCH_ENQUIRY: + + // Default behaviour for certificate fetch enquiry is to request downloading, but in some cases it would be useful to avoid that and use pre-cached certificate. + // Here, we supply libstirshaken with certificate we cached earlier, avoiding HTTP(S) download. + // We must return STIR_SHAKEN_STATUS_HANDLED to signal this to the library, otherwise it would execute HTTP(S) download + + if (!stirshaken_vs_cache_certificates) { + LM_DBG("Certificate caching is turned off - requesting certificate %s to be downloaded...\n", arg->cert.public_url); + return STIR_SHAKEN_STATUS_NOT_HANDLED; + } + + if (-1 == get_cert_name_hashed(arg->cert.public_url, cert_full_path, STIR_SHAKEN_BUFLEN)) { + LM_ERR("Cannot get cert name hashed\n"); + goto exit; + } + + LM_DBG("Checking for certificate %s in cache (looking for name: %s)\n", arg->cert.public_url, cert_full_path); + + if (STIR_SHAKEN_STATUS_OK == stir_shaken_file_exists(cert_full_path)) { + + LM_DBG("Certificate %s found in cache\n", arg->cert.public_url); + + if (stirshaken_vs_cache_expire_s) { + + struct stat attr = { 0 }; + time_t now_s = time(NULL), diff = 0; + + LM_DBG("Checking cached certificate against expiration setting of %zus\n", stirshaken_vs_cache_expire_s); + + if (-1 == stat(cert_full_path, &attr)) { + LM_ERR("Cannot get modification timestamp on certificate %s. Error code is %d (%s)\n", cert_full_path, errno, strerror(errno)); + goto exit; + } + + if (now_s < attr.st_mtime) { + LM_ERR("Modification timestamp on certificate %s is invalid\n", cert_full_path); + goto exit; + } + + diff = now_s - attr.st_mtime; + + LM_DBG("Checking cached certificate against expiration setting of %zus (now is: %zu, file modification timestamp is: %zu, difference is: %zu)\n", + stirshaken_vs_cache_expire_s, now_s, attr.st_mtime, diff); + + if (diff > stirshaken_vs_cache_expire_s) { + LM_WARN("Cached certificate %s is behind expiration threshold (%zu > %zu). Need to download new certificate...\n", cert_full_path, diff, stirshaken_vs_cache_expire_s); + goto exit; + } else { + LM_WARN("Cached certificate %s is valid for next %zus\n", cert_full_path, stirshaken_vs_cache_expire_s - diff); + } + } + + if (!(cache_copy.x = stir_shaken_load_x509_from_file(&ss, cert_full_path))) { + LM_ERR("Cannot load X509 from file %s\n", cert_full_path); + goto exit; + } + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_cert_copy(&ss, &arg->cert, &cache_copy)) { + LM_ERR("Cannot copy certificate %s\n", cert_full_path); + stir_shaken_cert_deinit(&cache_copy); + goto exit; + } + + stir_shaken_cert_deinit(&cache_copy); + + return STIR_SHAKEN_STATUS_HANDLED; + } + + default: + LM_DBG("Certificate %s not found in cache\n", arg->cert.public_url); + return STIR_SHAKEN_STATUS_NOT_HANDLED; + } + +exit: + + return STIR_SHAKEN_STATUS_NOT_HANDLED; +} + +stir_shaken_as_t *as = NULL; +stir_shaken_vs_t *vs = NULL; + +static int start_as(stir_shaken_context_t *ss) +{ + as = stir_shaken_as_create(ss); + if (!as) { + LM_ERR("Cannot create Authentication Service\n"); + stirshaken_print_error_details(ss); + return -1; + } + + if (stirshaken_as_default_key.len > 0) { + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_as_load_private_key(ss, as, stirshaken_as_default_key.s)) { + LM_ERR("Failed to load private key (%s). Please check @as_default_key param\n", stirshaken_as_default_key.s); + stirshaken_print_error_details(ss); + return -1; + } + } + + return 0; +} + +static int start_vs(stir_shaken_context_t *ss) +{ + vs = stir_shaken_vs_create(ss); + if (!vs) { + LM_ERR("Cannot create Verification Service\n"); + stirshaken_print_error_details(ss); + return -1; + } + + // Handle settings + + stir_shaken_vs_set_connect_timeout(ss, vs, stirshaken_vs_connect_timeout_s); + stir_shaken_vs_set_callback(ss, vs, shaken_callback); + + if (stirshaken_vs_cache_certificates) { + + if (!stirshaken_vs_cache_dir.len) { + LM_ERR("Certificate caching is turned on but cache dir is not set. Please set cache dir\n"); + return -1; + } + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_dir_exists(stirshaken_vs_cache_dir.s)) { + LM_ERR("Certificate caching is turned on but cache dir %s does not exist. Please check cache dir name\n", stirshaken_vs_cache_dir.s); + return -1; + } + } + + if (stirshaken_vs_verify_x509_cert_path) { + + stir_shaken_vs_set_x509_cert_path_check(ss, vs, 1); + + if (stirshaken_vs_ca_dir.len > 0) { + if (STIR_SHAKEN_STATUS_OK != stir_shaken_vs_load_ca_dir(ss, vs, stirshaken_vs_ca_dir.s)) { + LM_ERR("Failed to init X509 cert store with CA dir\n"); + stirshaken_print_error_details(ss); + return -1; + } + } else { + LM_WARN("Cert path check is turned on, but CA dir is not set. No end entity certificate will ever pass this check. Did you forget to set CA dir?\n"); + } + + if (stirshaken_vs_crl_dir.len > 0) { + if (STIR_SHAKEN_STATUS_OK != stir_shaken_vs_load_crl_dir(ss, vs, stirshaken_vs_crl_dir.s)) { + LM_ERR("Failed to init X509 cert store with CRL dir\n"); + stirshaken_print_error_details(ss); + return -1; + } + } else { + LM_WARN("Cert path check is turned on, but CRL dir is not set (it's not mandatory). Did you forget to set CRL dir?\n"); + } + } + + return 0; +} + +static int mod_init(void) +{ + stir_shaken_context_t ss = { 0 }; + + LM_INFO("Initialising STIR-Shaken\n"); + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_init(&ss, STIR_SHAKEN_LOGLEVEL_NOTHING)) { + LM_ERR("Cannot init libstirshaken\n"); + stirshaken_print_error_details(&ss); + return -1; + } + + if (0 == start_as(&ss)) { + if (stirshaken_as_default_key.len > 0) { + LM_INFO("Authentication Service ready (with default key %s)\n", stirshaken_as_default_key.s); + } else { + LM_INFO("Authentication Service ready (without default key)\n"); + } + } else { + LM_WARN("Cannot start Authentication Service (%s)\n", stirshaken_as_default_key.len > 0 ? "with default key" : "without default key"); + } + + if (stirshaken_as_default_key.len == 0) { + LM_WARN("Authentication Service using default key will not be available, because 'as_default_key' is not set (only authentication through w_stirshaken_add_identity_with_key is possible using a specific key)\n"); + } + + if (0 == start_vs(&ss)) { + LM_INFO("Verification Service ready (%s)\n", stirshaken_vs_verify_x509_cert_path ? "with X509 cert path check" : "without X509 cert path check"); + } else { + LM_WARN("Cannot start Verification Service (%s)\n", stirshaken_vs_verify_x509_cert_path ? "with X509 cert path check" : "without X509 cert path check"); + stir_shaken_vs_destroy(&vs); + } + + if ((vs && !stirshaken_vs_verify_x509_cert_path) || !vs) { + LM_WARN("A complete Shaken check with X509 certificate path verification will not be available (stirshaken_check_identity). " + "Only checks of PASSporT decoding will be performed via stirshaken_check_identity_with_key() " + "and stirshaken_check_identity_with_cert(). If you want complete check with downloaded certificate and/or X509 cert path verification " + "then please set @vs_verify_x509_cert_path param to 1 and configure @vs_ca_dir (and optionally @vs_crl_dir)\n"); + } + + return 0; +} + +/** + * @brief Initialize async module children + */ +static int child_init(int rank) +{ + LM_INFO("mod stirshaken child init\n"); + return 0; +} + +static void mod_destroy(void) +{ + LM_INFO("mod stirshaken destroy\n"); + stir_shaken_as_destroy(&as); + stir_shaken_vs_destroy(&vs); + stir_shaken_deinit(); + return; +} + +static int stirshaken_handle_cache(stir_shaken_context_t *ss, stir_shaken_passport_t *passport, stir_shaken_cert_t *cert) +{ + if (!passport || !cert) + return -1; + + LM_DBG("Handling certificate cache...\n"); + + if (!stirshaken_vs_cache_dir.len) { + LM_ERR("Cache dir not set\n"); + return -1; + } + + if (!ss->cert_fetched_from_cache) { + + // save certificate to cache with url as a key + char cert_full_path[STIR_SHAKEN_BUFLEN] = { 0 }; + const char *x5u = stir_shaken_passport_get_header(ss, passport, "x5u"); + + if (stir_shaken_zstr(x5u)) { + + // This should never happen as stir_shaken_sih_verify returns error in such case + + LM_ERR("PASSporT has no x5u\n"); + return -1; + } + + if (-1 == get_cert_name_hashed(x5u, cert_full_path, STIR_SHAKEN_BUFLEN)) { + LM_ERR("Cannot get cert name hashed\n"); + return -1; + } + + LM_DBG("Checking for presence of expired version of freshly downloaded certificate %s in cache (looking for name: %s)\n", x5u, cert_full_path); + + if (STIR_SHAKEN_STATUS_OK == stir_shaken_file_exists(cert_full_path)) { + + LM_DBG("Expired version of certificate %s found in cache (with name: %s). Removing it...\n", x5u, cert_full_path); + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_file_remove(cert_full_path)) { + LM_ERR("Couldn't remove certificate %s from cache\n", cert_full_path); + return -1; + } + } + + LM_DBG("Saving fresh certificate %s in cache (with name: %s)...\n", x5u, cert_full_path); + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_x509_to_disk(ss, cert->x, cert_full_path)) { + LM_ERR("Failed to write cert %s to disk (as: %s)", x5u, cert_full_path); + } + + } else { + LM_DBG("Certificate was fetched from cache, so skipping saving it\n"); + } + + return 0; +} + +#define STIRSHAKEN_HDR_IDENTITY "Identity" +#define STIRSHAKEN_HDR_IDENTITY_LEN (sizeof(STIRSHAKEN_HDR_IDENTITY) - 1) + +static int ki_stirshaken_check_identity(sip_msg_t *msg) +{ + str ibody = STR_NULL; + hdr_field_t *hf = NULL; + + stir_shaken_context_t ss = { 0 }; + stir_shaken_passport_t *passport_out = NULL; + stir_shaken_cert_t *cert_out = NULL; + + for (hf = msg->headers; hf; hf = hf->next) { + if (hf->name.len == STIRSHAKEN_HDR_IDENTITY_LEN + && strncasecmp(hf->name.s, STIRSHAKEN_HDR_IDENTITY, + STIRSHAKEN_HDR_IDENTITY_LEN) == 0) + break; + } + + if (hf == NULL) { + LM_DBG("no identity header\n"); + goto fail; + } + + ibody = hf->body; + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_vs_sih_verify(&ss, vs, ibody.s, &cert_out, &passport_out)) { + LM_ERR("SIP Identity Header did not pass verification\n"); + stirshaken_print_error_details(&ss); + goto fail; + } + + if (stirshaken_vs_cache_certificates) { + stirshaken_handle_cache(&ss, passport_out, cert_out); + } + + // Check that PASSporT applies to the current moment in time + if (STIR_SHAKEN_STATUS_OK != stir_shaken_passport_validate_iat_against_freshness(&ss, passport_out, stirshaken_vs_identity_expire_s)) { + + stir_shaken_error_t error_code = 0; + stir_shaken_get_error(&ss, &error_code); + + if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_FUTURE) { + LM_ERR("PASSporT not valid yet\n"); + } else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_EXPIRED) { + LM_ERR("PASSporT expired\n"); + } else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT) { + LM_ERR("PASSporT is missing @iat grant\n"); + } + + LM_ERR("PASSporT doesn't apply to the current moment in time\n"); + goto fail; + } + + if (stirshaken_vs_verify_x509_cert_path) { + + LM_DBG("Running X509 certificate path verification\n"); + + if (!vs) { + LM_ERR("Verification Service not started\n"); + goto fail; + } + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_verify_cert_path(&ss, cert_out, vs->store)) { + LM_ERR("Cert did not pass X509 path validation\n"); + stirshaken_print_error_details(&ss); + goto fail; + } + } + + LM_DBG("identity check: ok (%s)\n", ss.x509_cert_path_checked ? "with X509 cert path check" : "without X509 cert path check"); + stir_shaken_passport_destroy(&passport_out); + stir_shaken_cert_destroy(&cert_out); + return 1; + +fail: + stir_shaken_passport_destroy(&passport_out); + stir_shaken_cert_destroy(&cert_out); + LM_ERR("identity check: fail\n"); + return -1; +} + +/** + * Verify SIP Identity Header (involves call from libstirshaken to cache_callback, + * wich will supply requested certificate from cache [if configured to do so] + * or will let libstirshaken to perform HTTP(s) GET request to download certificate). + * Verify a call with STIR-Shaken. + * + * This method checks if SIP Identity Header covers semantically valid PASSporT. + * This method consults cache_callback in an attempt to obtain certificate that is referenced in PASSporT's x5u header. + * This method checks if PASSporT verifies successfully with a public key retrieved from obtained certificate. + * Optionally (if Verification Service is configured to do so with stirshaken_vs_verify_x509_cert_path param set to 1) + * this method checks if certificate is trusted, by execution of X509 certificate path check. + * + * Optionally: + * - retrieve PASSporT from SIP Identity Header + * - retrieve certificate referenced in PASSporT's x5u header + * - cache certificate + * + * Kamailio config usage example: + * + * stirshaken_check_identity(); + */ +static int w_stirshaken_check_identity(sip_msg_t *msg, char *str1, char *str2) +{ + LM_INFO("identity check\n"); + + if (!vs) { + LM_ERR("Cannot perform identity check involving certificate downloading, caching, or X509 cert pach checking, " + "because Verification Service is not running. Please turn on and configure Verification Service, " + "otherwise only stirshaken_check_identity_with_cert() and stirshaken_check_identity_with_key() " + "methods will be available. Check log file for module's initialisation errors\n"); + return -1; + } + + return ki_stirshaken_check_identity(msg); +} + +static int ki_stirshaken_check_identity_with_cert(sip_msg_t *msg, str *cert_path) +{ + str ibody = STR_NULL; + hdr_field_t *hf = NULL; + + stir_shaken_context_t ss = { 0 }; + stir_shaken_passport_t *passport_out = NULL; + stir_shaken_cert_t cert = { 0 }; + + for (hf = msg->headers; hf; hf = hf->next) { + if (hf->name.len == STIRSHAKEN_HDR_IDENTITY_LEN + && strncasecmp(hf->name.s, STIRSHAKEN_HDR_IDENTITY, + STIRSHAKEN_HDR_IDENTITY_LEN) == 0) + break; + } + + if (hf == NULL) { + LM_DBG("no identity header\n"); + goto fail; + } + + ibody = hf->body; + + if (!(cert.x = stir_shaken_load_x509_from_file(&ss, cert_path->s))) { + LM_DBG("Cannot load X509 from file\n"); + stirshaken_print_error_details(&ss); + goto fail; + } + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_sih_verify_with_cert(&ss, ibody.s, &cert, &passport_out)) { + LM_ERR("SIP Identity Header did not pass verification against certificate\n"); + stirshaken_print_error_details(&ss); + goto fail; + } + + // Check that PASSporT applies to the current moment in time + if (STIR_SHAKEN_STATUS_OK != stir_shaken_passport_validate_iat_against_freshness(&ss, passport_out, stirshaken_vs_identity_expire_s)) { + + stir_shaken_error_t error_code = 0; + stir_shaken_get_error(&ss, &error_code); + + if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_FUTURE) { + LM_ERR("PASSporT not valid yet\n"); + } else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT_VALUE_EXPIRED) { + LM_ERR("PASSporT expired\n"); + } else if (error_code == STIR_SHAKEN_ERROR_PASSPORT_INVALID_IAT) { + LM_ERR("PASSporT is missing @iat grant\n"); + } + + LM_ERR("PASSporT doesn't apply to the current moment in time\n"); + goto fail; + } + + LM_DBG("identity check: ok (%s)\n", ss.x509_cert_path_checked ? "with X509 cert path check" : "without X509 cert path check"); + + stir_shaken_passport_destroy(&passport_out); + stir_shaken_cert_deinit(&cert); + return 1; + +fail: + stir_shaken_passport_destroy(&passport_out); + stir_shaken_cert_deinit(&cert); + LM_ERR("identity check: fail\n"); + return -1; +} + +/** + * Verify SIP Identity Header against specified certificate (does not involve HTTP(s) GET request). + * WARNING: + * This method only checks if SIP Identity Header was signed by a key from certificate given as argument. + * This method doesn't attempt to obtain certificate referenced in PASSporT (but PASSporT should be checked with key corresponding to that certificate). + * Therefore it is possible that this check will be successful, while PASSporT is not valid (could be signed with key that doesn't match certificate referenced in x5u header). + * If you want a complete Shaken check or if you are not sure what you're doing, then you should execute w_stirshaken_check_identity() instead + * (and configure Verification Service to perform X509 certificate path verification with stirshaken_vs_verify_x509_cert_path param set to 1). + * + * Kamailio config usage example: + * + * stirshaken_check_identity_with_cert("/path/to/cert"); + */ +static int w_stirshaken_check_identity_with_cert(sip_msg_t *msg, char *cert_path, char *str2) +{ + str keyval = STR_NULL; + + LM_INFO("identity check using certificate\n"); + + if(fixup_get_svalue(msg, (gparam_t*)cert_path, &keyval)<0) { + LM_ERR("failed to get certificate path parameter\n"); + return -1; + } + + return ki_stirshaken_check_identity_with_cert(msg, &keyval); +} + +static int ki_stirshaken_check_identity_with_key(sip_msg_t *msg, str *keypath) +{ + str ibody = STR_NULL; + hdr_field_t *hf = NULL; + + stir_shaken_context_t ss = { 0 }; + stir_shaken_passport_t *passport_out = NULL; + unsigned char key[STIR_SHAKEN_PUB_KEY_RAW_BUF_LEN] = { 0 }; + uint32_t key_len = STIR_SHAKEN_PUB_KEY_RAW_BUF_LEN; + + for (hf = msg->headers; hf; hf = hf->next) { + if (hf->name.len == STIRSHAKEN_HDR_IDENTITY_LEN + && strncasecmp(hf->name.s, STIRSHAKEN_HDR_IDENTITY, + STIRSHAKEN_HDR_IDENTITY_LEN) == 0) + break; + } + + if (hf == NULL) { + LM_DBG("no identity header\n"); + goto fail; + } + + ibody = hf->body; + + if (keypath && keypath->s) { + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_load_key_raw(&ss, keypath->s, key, &key_len)) { + LM_ERR("Failed to load private key\n"); + stirshaken_print_error_details(&ss); + goto fail; + } + } + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_sih_verify_with_key(&ss, ibody.s, key, key_len, &passport_out)) { + LM_ERR("SIP Identity Header did not pass verification against key\n"); + stirshaken_print_error_details(&ss); + goto fail; + } + + // We can do something with PASSporT here or just realease it + stir_shaken_passport_destroy(&passport_out); + + LM_DBG("identity check: ok (%s)\n", ss.x509_cert_path_checked ? "with X509 cert path check" : "without X509 cert path check"); + return 1; + +fail: + stir_shaken_passport_destroy(&passport_out); + LM_ERR("identity check: fail\n"); + return -1; +} + +/** + * Verify SIP Identity Header against specified public key (does not involve HTTP(s) GET request). + * WARNING: + * This method only checks if SIP Identity Header was signed by a key corresponding to specified public key. + * This method doesn't attempt to obtain certificate referenced in PASSporT (but PASSporT should be checked with key corresponding to that certificate). + * Therefore it is possible that this check will be successful, while PASSporT is not valid (could be signed with key that doesn't match certificate referenced in x5u header). + * If you want a complete Shaken check or if you are not sure what you're doing, then you should execute w_stirshaken_check_identity() instead + * (and configure Verification Service to perform X509 certificate path verification with stirshaken_vs_verify_x509_cert_path param set to 1). + * + * Kamailio config usage example: + * + * stirshaken_check_identity_with_key("/path/to/key"); + */ +static int w_stirshaken_check_identity_with_key(sip_msg_t *msg, char *pkey_path, char *str2) +{ + str keypath = STR_NULL; + + LM_INFO("identity check using public key\n"); + + if(fixup_get_svalue(msg, (gparam_t*)pkey_path, &keypath)<0) { + LM_ERR("failed to get key path parameter\n"); + return -1; + } + + return ki_stirshaken_check_identity_with_key(msg, &keypath); +} + +static int ki_stirshaken_add_identity_with_key(sip_msg_t *msg, str *x5u, str *attest, + str *origtn_val, str *desttn_val, str *origid, str *keypath) +{ + stir_shaken_context_t ss = { 0 }; + char *sih = NULL; + stir_shaken_passport_t *passport = NULL; + str ibody = STR_NULL; + str hdr = STR_NULL; + sr_lump_t *anchor = NULL; + stir_shaken_passport_params_t params = { + .x5u = x5u ? x5u->s : NULL, + .attest = attest ? attest->s : NULL, + .desttn_key = "tn", + .desttn_val = desttn_val ? desttn_val->s : NULL, + .iat = time(NULL), + .origtn_key = "tn", + .origtn_val = origtn_val ? origtn_val->s : NULL, + .origid = origid ? origid->s : NULL + }; + char uuid_str[37] = { 0 }; + + if (!params.origid || !strlen(params.origid)) { + + uuid_t uuid; + + uuid_generate(uuid); + uuid_unparse_lower(uuid, uuid_str); + params.origid = uuid_str; + } + + if (keypath && keypath->s) { + + unsigned char key[STIR_SHAKEN_PRIV_KEY_RAW_BUF_LEN]; + uint32_t key_len = STIR_SHAKEN_PRIV_KEY_RAW_BUF_LEN; + + if (STIR_SHAKEN_STATUS_OK != stir_shaken_load_key_raw(&ss, keypath->s, key, &key_len)) { + LM_ERR("Failed to load private key\n"); + stirshaken_print_error_details(&ss); + goto error; + } + + sih = stir_shaken_authenticate_to_sih_with_key(&ss, ¶ms, &passport, key, key_len); + if (!sih) { + LM_ERR("Failed to create SIP Identity Header with key %s\n", keypath->s); + stirshaken_print_error_details(&ss); + goto error; + } + + } else { + + if (!as) { + LM_ERR("Authentication Service not started. Is default key set for Authentication Service? Please check @as_default_key param\n"); + goto error; + } + + sih = stir_shaken_as_authenticate_to_sih(&ss, as, ¶ms, &passport); + if (!sih) { + LM_ERR("Failed to create SIP Identity Header with default key. Is a default key set for Authentication Service? Please check @as_default_key param\n"); + stirshaken_print_error_details(&ss); + goto error; + } + } + + // We can do something with PASSporT here or just realease it + stir_shaken_passport_destroy(&passport); + + ibody.s = sih; + ibody.len = strlen(sih); + + if (ibody.len <= 0) { + LM_DBG("Got SIP Identity Header with 0 length\n"); + goto error; + } + + LM_DBG("appending identity: %.*s\n", ibody.len, ibody.s); + if (parse_headers(msg, HDR_EOH_F, 0) == -1) { + LM_ERR("error while parsing message\n"); + goto error; + } + + hdr.len = STIRSHAKEN_HDR_IDENTITY_LEN + 1 + 1 + ibody.len + 2; + hdr.s = (char*)pkg_malloc(hdr.len + 1); + if (hdr.s == NULL) { + PKG_MEM_ERROR; + goto error; + } + memcpy(hdr.s, STIRSHAKEN_HDR_IDENTITY, STIRSHAKEN_HDR_IDENTITY_LEN); + *(hdr.s + STIRSHAKEN_HDR_IDENTITY_LEN) = ':'; + *(hdr.s + STIRSHAKEN_HDR_IDENTITY_LEN + 1) = ' '; + + memcpy(hdr.s + STIRSHAKEN_HDR_IDENTITY_LEN + 2, ibody.s, ibody.len); + *(hdr.s + STIRSHAKEN_HDR_IDENTITY_LEN + ibody.len + 2) = '\r'; + *(hdr.s + STIRSHAKEN_HDR_IDENTITY_LEN + ibody.len + 3) = '\n'; + + /* anchor after last header */ + anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0); + if((anchor == NULL) + || (insert_new_lump_before(anchor, hdr.s, hdr.len, 0) == 0)) { + LM_ERR("cannot insert identity header\n"); + pkg_free(hdr.s); + goto error; + } + + if(sih) { + free(sih); + } + return 1; + +error: + stir_shaken_passport_destroy(&passport); + if(sih) { + free(sih); + } + return -1; +} + +/** + * Add SIP Identity Header to the call using specific key. Authenticate call with STIR-Shaken. + * + * Kamailio config usage example: + * + * stirshaken_add_identity_with_key("https://sp.com/sp.pem", "B", "+44100", "+44200", "ref", "/path/to/key"); + */ +static int w_stirshaken_add_identity_with_key(sip_msg_t *msg, str *px5u, str *pattest, + str *porigtn_val, str *pdesttn_val, str *porigid, str *pkeypath) +{ + str x5u = STR_NULL; + str attest = STR_NULL; + str origtn_val = STR_NULL; + str desttn_val = STR_NULL; + str origid = STR_NULL; + str keypath = STR_NULL; + + if (fixup_get_svalue(msg, (gparam_t*)px5u, &x5u)<0) { + LM_ERR("failed to get x5u parameter\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)pattest, &attest)<0) { + LM_ERR("failed to get attest parameter\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)porigtn_val, &origtn_val)<0) { + LM_ERR("failed to get origtn_val parameter\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)pdesttn_val, &desttn_val)<0) { + LM_ERR("failed to get desttn_val parameter\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)porigid, &origid)<0) { + LM_ERR("failed to get origid parameter\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)pkeypath, &keypath)<0) { + LM_ERR("failed to get keypath parameter\n"); + return -1; + } + + return ki_stirshaken_add_identity_with_key(msg, &x5u, &attest, &origtn_val, &desttn_val, &origid, &keypath); +} + +static int ki_stirshaken_add_identity(sip_msg_t *msg, str *x5u, str *attest, str *origtn_val, str *desttn_val, str *origid) +{ + return ki_stirshaken_add_identity_with_key(msg, x5u, attest, origtn_val, desttn_val, origid, NULL); +} + +/** + * Add SIP Identity Header to the call using default private key. Authenticate call with STIR-Shaken. + * + * Kamailio config usage example: + * + * stirshaken_add_identity("https://sp.com/sp.pem", "B", "+44100", "+44200", "ref"); + */ +static int w_stirshaken_add_identity(sip_msg_t *msg, str *px5u, str *pattest, str *porigtn_val, str *pdesttn_val, str *porigid) +{ + str x5u = STR_NULL; + str attest = STR_NULL; + str origtn_val = STR_NULL; + str desttn_val = STR_NULL; + str origid = STR_NULL; + + if (fixup_get_svalue(msg, (gparam_t*)px5u, &x5u)<0) { + LM_ERR("failed to get x5u parameter\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)pattest, &attest)<0) { + LM_ERR("failed to get attest parameter\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)porigtn_val, &origtn_val)<0) { + LM_ERR("failed to get origtn_val parameter\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)pdesttn_val, &desttn_val)<0) { + LM_ERR("failed to get desttn_val parameter\n"); + return -1; + } + + if (fixup_get_svalue(msg, (gparam_t*)porigid, &origid)<0) { + LM_ERR("failed to get origid parameter\n"); + return -1; + } + + return ki_stirshaken_add_identity(msg, &x5u, &attest, &origtn_val, &desttn_val, &origid); +} + + +/** + * + */ +/* clang-format off */ +static sr_kemi_t sr_kemi_stirshaken_exports[] = { + { str_init("stirshaken"), str_init("stirshaken_check_identity"), + SR_KEMIP_INT, ki_stirshaken_check_identity, + { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, + { str_init("stirshaken"), str_init("stirshaken_check_identity_with_cert"), + SR_KEMIP_INT, ki_stirshaken_check_identity_with_cert, + { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, + { str_init("stirshaken"), str_init("stirshaken_check_identity_with_key"), + SR_KEMIP_INT, ki_stirshaken_check_identity_with_key, + { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, + { str_init("stirshaken"), str_init("stirshaken_add_identity"), + SR_KEMIP_INT, ki_stirshaken_add_identity, + { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR, + SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE } + }, + { str_init("stirshaken"), str_init("stirshaken_add_identity_with_key"), + SR_KEMIP_INT, ki_stirshaken_add_identity_with_key, + { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR, + SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR } + }, + { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } } +}; +/* clang-format on */ + +/** + * + */ +int mod_register(char *path, int *dlflags, void *p1, void *p2) +{ + sr_kemi_modules_add(sr_kemi_stirshaken_exports); + return 0; +} diff --git a/src/modules/textops/README b/src/modules/textops/README index 49a34750c91..b811e08c1ed 100644 --- a/src/modules/textops/README +++ b/src/modules/textops/README @@ -41,63 +41,64 @@ Ovidiu Sas 4.1. search(re) 4.2. search_body(re) - 4.3. search_hf(hf, re, flags) - 4.4. search_append(re, txt) - 4.5. search_append_body(re, txt) - 4.6. replace(re, txt) - 4.7. replace_body(re, txt) - 4.8. replace_hdrs(re, txt) - 4.9. replace_all(re, txt) - 4.10. replace_body_all(re, txt) - 4.11. replace_body_atonce(re, txt) - 4.12. replace_str(match, repl, mode) - 4.13. replace_body_str(match, repl, mode) - 4.14. replace_hdrs_str(match, repl, mode) - 4.15. subst('/re/repl/flags') - 4.16. subst_uri('/re/repl/flags') - 4.17. subst_user('/re/repl/flags') - 4.18. subst_body('/re/repl/flags') - 4.19. subst_hf(hf, subexp, flags) - 4.20. set_body(txt,content_type) - 4.21. set_reply_body(txt,content_type) - 4.22. filter_body(content_type) - 4.23. append_to_reply(txt) - 4.24. append_hf(txt[, hdr]) - 4.25. insert_hf(txt[, hdr]) - 4.26. append_urihf(prefix, suffix) - 4.27. is_present_hf(hf_name) - 4.28. is_present_hf_pv(hf_name) - 4.29. is_present_hf_re(hf_name_re) - 4.30. is_present_hf_re_pv(hf_name_re) - 4.31. append_time() - 4.32. append_time_to_request() - 4.33. is_method(name) - 4.34. remove_hf(hname) - 4.35. remove_hf_pv(hname) - 4.36. remove_hf_re(re) - 4.37. remove_hf_re_pv(re) - 4.38. remove_hf_exp(expmatch, expskip) - 4.39. remove_hf_exp_pv(expmatch, expskip) - 4.40. has_body(), has_body(mime) - 4.41. is_audio_on_hold() - 4.42. is_privacy(privacy_type) - 4.43. in_list(subject, list, separator) - 4.44. in_list_prefix(subject, list, separator) - 4.45. cmp_str(str1, str2) - 4.46. cmp_istr(str1, str2) - 4.47. starts_with(str1, str2) - 4.48. ends_with(str1, str2) - 4.49. set_body_multipart([txt,content_type][,boundary]) - 4.50. append_body_part(txt,content_type[, + 4.3. search_str(text. re) + 4.4. search_hf(hf, re, flags) + 4.5. search_append(re, txt) + 4.6. search_append_body(re, txt) + 4.7. replace(re, txt) + 4.8. replace_body(re, txt) + 4.9. replace_hdrs(re, txt) + 4.10. replace_all(re, txt) + 4.11. replace_body_all(re, txt) + 4.12. replace_body_atonce(re, txt) + 4.13. replace_str(match, repl, mode) + 4.14. replace_body_str(match, repl, mode) + 4.15. replace_hdrs_str(match, repl, mode) + 4.16. subst('/re/repl/flags') + 4.17. subst_uri('/re/repl/flags') + 4.18. subst_user('/re/repl/flags') + 4.19. subst_body('/re/repl/flags') + 4.20. subst_hf(hf, subexp, flags) + 4.21. set_body(txt,content_type) + 4.22. set_reply_body(txt,content_type) + 4.23. filter_body(content_type) + 4.24. append_to_reply(txt) + 4.25. append_hf(txt[, hdr]) + 4.26. insert_hf(txt[, hdr]) + 4.27. append_urihf(prefix, suffix) + 4.28. is_present_hf(hf_name) + 4.29. is_present_hf_pv(hf_name) + 4.30. is_present_hf_re(hf_name_re) + 4.31. is_present_hf_re_pv(hf_name_re) + 4.32. append_time() + 4.33. append_time_to_request() + 4.34. is_method(name) + 4.35. remove_hf(hname) + 4.36. remove_hf_pv(hname) + 4.37. remove_hf_re(re) + 4.38. remove_hf_re_pv(re) + 4.39. remove_hf_exp(expmatch, expskip) + 4.40. remove_hf_exp_pv(expmatch, expskip) + 4.41. has_body(), has_body(mime) + 4.42. is_audio_on_hold() + 4.43. is_privacy(privacy_type) + 4.44. in_list(subject, list, separator) + 4.45. in_list_prefix(subject, list, separator) + 4.46. cmp_str(str1, str2) + 4.47. cmp_istr(str1, str2) + 4.48. starts_with(str1, str2) + 4.49. ends_with(str1, str2) + 4.50. set_body_multipart([txt,content_type][,boundary]) + 4.51. append_body_part(txt,content_type[, content_disposition]) - 4.51. append_body_part_hex(txt,content_type[, + 4.52. append_body_part_hex(txt,content_type[, content_disposition]) - 4.52. get_body_part(content_type, opv) - 4.53. get_body_part_raw(content_type, opv) - 4.54. remove_body_part(content_type) - 4.55. regex_substring(itext, regexp, mindex, mcount, dpv) + 4.53. get_body_part(content_type, opv) + 4.54. get_body_part_raw(content_type, opv) + 4.55. remove_body_part(content_type) + 4.56. regex_substring(itext, regexp, mindex, mcount, dpv) 2. Developer Guide @@ -109,60 +110,61 @@ Ovidiu Sas 1.1. search usage 1.2. search_body usage - 1.3. search_hf usage - 1.4. search_append usage - 1.5. search_append_body usage - 1.6. replace usage - 1.7. replace_body usage - 1.8. replace_hdrs usage - 1.9. replace_all usage - 1.10. replace_body_all usage - 1.11. replace_body_atonce usage - 1.12. replace_str usage - 1.13. replace_body_str usage - 1.14. replace_hdrs_str usage - 1.15. subst usage - 1.16. subst_uri usage - 1.17. subst usage - 1.18. subst_body usage - 1.19. subst_hf usage - 1.20. set_body usage - 1.21. set_reply_body usage - 1.22. filter_body usage - 1.23. append_to_reply usage - 1.24. append_hf usage - 1.25. insert_hf usage - 1.26. append_urihf usage - 1.27. is_present_hf usage - 1.28. is_present_hf_pv usage - 1.29. is_present_hf_re usage - 1.30. is_present_hf_re_pv usage - 1.31. append_time usage - 1.32. append_time_to_request usage - 1.33. is_method usage - 1.34. remove_hf usage - 1.35. remove_hf_pv usage - 1.36. remove_hf_re usage - 1.37. remove_hf_re_pv usage - 1.38. remove_hf_exp usage - 1.39. remove_hf_exp_pv usage - 1.40. has_body usage - 1.41. is_audio_on_hold usage - 1.42. is_privacy usage - 1.43. in_list() usage + 1.3. search_str usage + 1.4. search_hf usage + 1.5. search_append usage + 1.6. search_append_body usage + 1.7. replace usage + 1.8. replace_body usage + 1.9. replace_hdrs usage + 1.10. replace_all usage + 1.11. replace_body_all usage + 1.12. replace_body_atonce usage + 1.13. replace_str usage + 1.14. replace_body_str usage + 1.15. replace_hdrs_str usage + 1.16. subst usage + 1.17. subst_uri usage + 1.18. subst usage + 1.19. subst_body usage + 1.20. subst_hf usage + 1.21. set_body usage + 1.22. set_reply_body usage + 1.23. filter_body usage + 1.24. append_to_reply usage + 1.25. append_hf usage + 1.26. insert_hf usage + 1.27. append_urihf usage + 1.28. is_present_hf usage + 1.29. is_present_hf_pv usage + 1.30. is_present_hf_re usage + 1.31. is_present_hf_re_pv usage + 1.32. append_time usage + 1.33. append_time_to_request usage + 1.34. is_method usage + 1.35. remove_hf usage + 1.36. remove_hf_pv usage + 1.37. remove_hf_re usage + 1.38. remove_hf_re_pv usage + 1.39. remove_hf_exp usage + 1.40. remove_hf_exp_pv usage + 1.41. has_body usage + 1.42. is_audio_on_hold usage + 1.43. is_privacy usage 1.44. in_list() usage - 1.45. cmp_str usage + 1.45. in_list() usage 1.46. cmp_str usage - 1.47. starts_with usage - 1.48. ends_with usage - 1.49. set_body_multipart usage - 1.50. append_body_part usage - 1.51. append_body_part with headers - 1.52. append_body_part_hex usage - 1.53. get_body_part usage - 1.54. get_body_part_raw usage - 1.55. remove_body_part usage - 1.56. _regex_substring usage + 1.47. cmp_str usage + 1.48. starts_with usage + 1.49. ends_with usage + 1.50. set_body_multipart usage + 1.51. append_body_part usage + 1.52. append_body_part with headers + 1.53. append_body_part_hex usage + 1.54. get_body_part usage + 1.55. get_body_part_raw usage + 1.56. remove_body_part usage + 1.57. _regex_substring usage Chapter 1. Admin Guide @@ -179,61 +181,62 @@ Chapter 1. Admin Guide 4.1. search(re) 4.2. search_body(re) - 4.3. search_hf(hf, re, flags) - 4.4. search_append(re, txt) - 4.5. search_append_body(re, txt) - 4.6. replace(re, txt) - 4.7. replace_body(re, txt) - 4.8. replace_hdrs(re, txt) - 4.9. replace_all(re, txt) - 4.10. replace_body_all(re, txt) - 4.11. replace_body_atonce(re, txt) - 4.12. replace_str(match, repl, mode) - 4.13. replace_body_str(match, repl, mode) - 4.14. replace_hdrs_str(match, repl, mode) - 4.15. subst('/re/repl/flags') - 4.16. subst_uri('/re/repl/flags') - 4.17. subst_user('/re/repl/flags') - 4.18. subst_body('/re/repl/flags') - 4.19. subst_hf(hf, subexp, flags) - 4.20. set_body(txt,content_type) - 4.21. set_reply_body(txt,content_type) - 4.22. filter_body(content_type) - 4.23. append_to_reply(txt) - 4.24. append_hf(txt[, hdr]) - 4.25. insert_hf(txt[, hdr]) - 4.26. append_urihf(prefix, suffix) - 4.27. is_present_hf(hf_name) - 4.28. is_present_hf_pv(hf_name) - 4.29. is_present_hf_re(hf_name_re) - 4.30. is_present_hf_re_pv(hf_name_re) - 4.31. append_time() - 4.32. append_time_to_request() - 4.33. is_method(name) - 4.34. remove_hf(hname) - 4.35. remove_hf_pv(hname) - 4.36. remove_hf_re(re) - 4.37. remove_hf_re_pv(re) - 4.38. remove_hf_exp(expmatch, expskip) - 4.39. remove_hf_exp_pv(expmatch, expskip) - 4.40. has_body(), has_body(mime) - 4.41. is_audio_on_hold() - 4.42. is_privacy(privacy_type) - 4.43. in_list(subject, list, separator) - 4.44. in_list_prefix(subject, list, separator) - 4.45. cmp_str(str1, str2) - 4.46. cmp_istr(str1, str2) - 4.47. starts_with(str1, str2) - 4.48. ends_with(str1, str2) - 4.49. set_body_multipart([txt,content_type][,boundary]) - 4.50. append_body_part(txt,content_type[, content_disposition]) - 4.51. append_body_part_hex(txt,content_type[, + 4.3. search_str(text. re) + 4.4. search_hf(hf, re, flags) + 4.5. search_append(re, txt) + 4.6. search_append_body(re, txt) + 4.7. replace(re, txt) + 4.8. replace_body(re, txt) + 4.9. replace_hdrs(re, txt) + 4.10. replace_all(re, txt) + 4.11. replace_body_all(re, txt) + 4.12. replace_body_atonce(re, txt) + 4.13. replace_str(match, repl, mode) + 4.14. replace_body_str(match, repl, mode) + 4.15. replace_hdrs_str(match, repl, mode) + 4.16. subst('/re/repl/flags') + 4.17. subst_uri('/re/repl/flags') + 4.18. subst_user('/re/repl/flags') + 4.19. subst_body('/re/repl/flags') + 4.20. subst_hf(hf, subexp, flags) + 4.21. set_body(txt,content_type) + 4.22. set_reply_body(txt,content_type) + 4.23. filter_body(content_type) + 4.24. append_to_reply(txt) + 4.25. append_hf(txt[, hdr]) + 4.26. insert_hf(txt[, hdr]) + 4.27. append_urihf(prefix, suffix) + 4.28. is_present_hf(hf_name) + 4.29. is_present_hf_pv(hf_name) + 4.30. is_present_hf_re(hf_name_re) + 4.31. is_present_hf_re_pv(hf_name_re) + 4.32. append_time() + 4.33. append_time_to_request() + 4.34. is_method(name) + 4.35. remove_hf(hname) + 4.36. remove_hf_pv(hname) + 4.37. remove_hf_re(re) + 4.38. remove_hf_re_pv(re) + 4.39. remove_hf_exp(expmatch, expskip) + 4.40. remove_hf_exp_pv(expmatch, expskip) + 4.41. has_body(), has_body(mime) + 4.42. is_audio_on_hold() + 4.43. is_privacy(privacy_type) + 4.44. in_list(subject, list, separator) + 4.45. in_list_prefix(subject, list, separator) + 4.46. cmp_str(str1, str2) + 4.47. cmp_istr(str1, str2) + 4.48. starts_with(str1, str2) + 4.49. ends_with(str1, str2) + 4.50. set_body_multipart([txt,content_type][,boundary]) + 4.51. append_body_part(txt,content_type[, content_disposition]) + 4.52. append_body_part_hex(txt,content_type[, content_disposition]) - 4.52. get_body_part(content_type, opv) - 4.53. get_body_part_raw(content_type, opv) - 4.54. remove_body_part(content_type) - 4.55. regex_substring(itext, regexp, mindex, mcount, dpv) + 4.53. get_body_part(content_type, opv) + 4.54. get_body_part_raw(content_type, opv) + 4.55. remove_body_part(content_type) + 4.56. regex_substring(itext, regexp, mindex, mcount, dpv) 1. Overview @@ -274,59 +277,60 @@ From: medabeda 4.1. search(re) 4.2. search_body(re) - 4.3. search_hf(hf, re, flags) - 4.4. search_append(re, txt) - 4.5. search_append_body(re, txt) - 4.6. replace(re, txt) - 4.7. replace_body(re, txt) - 4.8. replace_hdrs(re, txt) - 4.9. replace_all(re, txt) - 4.10. replace_body_all(re, txt) - 4.11. replace_body_atonce(re, txt) - 4.12. replace_str(match, repl, mode) - 4.13. replace_body_str(match, repl, mode) - 4.14. replace_hdrs_str(match, repl, mode) - 4.15. subst('/re/repl/flags') - 4.16. subst_uri('/re/repl/flags') - 4.17. subst_user('/re/repl/flags') - 4.18. subst_body('/re/repl/flags') - 4.19. subst_hf(hf, subexp, flags) - 4.20. set_body(txt,content_type) - 4.21. set_reply_body(txt,content_type) - 4.22. filter_body(content_type) - 4.23. append_to_reply(txt) - 4.24. append_hf(txt[, hdr]) - 4.25. insert_hf(txt[, hdr]) - 4.26. append_urihf(prefix, suffix) - 4.27. is_present_hf(hf_name) - 4.28. is_present_hf_pv(hf_name) - 4.29. is_present_hf_re(hf_name_re) - 4.30. is_present_hf_re_pv(hf_name_re) - 4.31. append_time() - 4.32. append_time_to_request() - 4.33. is_method(name) - 4.34. remove_hf(hname) - 4.35. remove_hf_pv(hname) - 4.36. remove_hf_re(re) - 4.37. remove_hf_re_pv(re) - 4.38. remove_hf_exp(expmatch, expskip) - 4.39. remove_hf_exp_pv(expmatch, expskip) - 4.40. has_body(), has_body(mime) - 4.41. is_audio_on_hold() - 4.42. is_privacy(privacy_type) - 4.43. in_list(subject, list, separator) - 4.44. in_list_prefix(subject, list, separator) - 4.45. cmp_str(str1, str2) - 4.46. cmp_istr(str1, str2) - 4.47. starts_with(str1, str2) - 4.48. ends_with(str1, str2) - 4.49. set_body_multipart([txt,content_type][,boundary]) - 4.50. append_body_part(txt,content_type[, content_disposition]) - 4.51. append_body_part_hex(txt,content_type[, content_disposition]) - 4.52. get_body_part(content_type, opv) - 4.53. get_body_part_raw(content_type, opv) - 4.54. remove_body_part(content_type) - 4.55. regex_substring(itext, regexp, mindex, mcount, dpv) + 4.3. search_str(text. re) + 4.4. search_hf(hf, re, flags) + 4.5. search_append(re, txt) + 4.6. search_append_body(re, txt) + 4.7. replace(re, txt) + 4.8. replace_body(re, txt) + 4.9. replace_hdrs(re, txt) + 4.10. replace_all(re, txt) + 4.11. replace_body_all(re, txt) + 4.12. replace_body_atonce(re, txt) + 4.13. replace_str(match, repl, mode) + 4.14. replace_body_str(match, repl, mode) + 4.15. replace_hdrs_str(match, repl, mode) + 4.16. subst('/re/repl/flags') + 4.17. subst_uri('/re/repl/flags') + 4.18. subst_user('/re/repl/flags') + 4.19. subst_body('/re/repl/flags') + 4.20. subst_hf(hf, subexp, flags) + 4.21. set_body(txt,content_type) + 4.22. set_reply_body(txt,content_type) + 4.23. filter_body(content_type) + 4.24. append_to_reply(txt) + 4.25. append_hf(txt[, hdr]) + 4.26. insert_hf(txt[, hdr]) + 4.27. append_urihf(prefix, suffix) + 4.28. is_present_hf(hf_name) + 4.29. is_present_hf_pv(hf_name) + 4.30. is_present_hf_re(hf_name_re) + 4.31. is_present_hf_re_pv(hf_name_re) + 4.32. append_time() + 4.33. append_time_to_request() + 4.34. is_method(name) + 4.35. remove_hf(hname) + 4.36. remove_hf_pv(hname) + 4.37. remove_hf_re(re) + 4.38. remove_hf_re_pv(re) + 4.39. remove_hf_exp(expmatch, expskip) + 4.40. remove_hf_exp_pv(expmatch, expskip) + 4.41. has_body(), has_body(mime) + 4.42. is_audio_on_hold() + 4.43. is_privacy(privacy_type) + 4.44. in_list(subject, list, separator) + 4.45. in_list_prefix(subject, list, separator) + 4.46. cmp_str(str1, str2) + 4.47. cmp_istr(str1, str2) + 4.48. starts_with(str1, str2) + 4.49. ends_with(str1, str2) + 4.50. set_body_multipart([txt,content_type][,boundary]) + 4.51. append_body_part(txt,content_type[, content_disposition]) + 4.52. append_body_part_hex(txt,content_type[, content_disposition]) + 4.53. get_body_part(content_type, opv) + 4.54. get_body_part_raw(content_type, opv) + 4.55. remove_body_part(content_type) + 4.56. regex_substring(itext, regexp, mindex, mcount, dpv) 4.1. search(re) @@ -335,6 +339,9 @@ From: medabeda Meaning of the parameters is as follows: * re - Regular expression. + Note: it performs Posix regex matching and the 're' parameter is + compiled with the flags REG_EXTENDED|REG_ICASE|REG_NEWLINE. + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. @@ -358,7 +365,24 @@ if ( search("[Ss][Ii][Pp]") ) { /*....*/ }; if ( search_body("[Ss][Ii][Pp]") ) { /*....*/ }; ... -4.3. search_hf(hf, re, flags) +4.3. search_str(text. re) + + Searches for the re in the body of the message. + + Meaning of the parameters is as follows: + * text - text to perform regex searching over it. + * re - regular expression to match over the 'text' parameter. + + Both parameters can contain variables. + + This function can be used from ANY_ROUTE. + + Example 1.3. search_str usage +... +if ( search_str("$ru", ";transport=tcp") ) { /*....*/ }; +... + +4.4. search_hf(hf, re, flags) Searches for the re in the body of a header field. @@ -372,12 +396,12 @@ if ( search_body("[Ss][Ii][Pp]") ) { /*....*/ }; This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.3. search_hf usage + Example 1.4. search_hf usage ... if ( search_hf("From", ":test@", "a") ) { /*....*/ }; ... -4.4. search_append(re, txt) +4.5. search_append(re, txt) Searches for the first match of re and appends txt after it. @@ -388,12 +412,12 @@ if ( search_hf("From", ":test@", "a") ) { /*....*/ }; This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.4. search_append usage + Example 1.5. search_append usage ... search_append("[Oo]pen[Ss]er", " SIP Proxy"); ... -4.5. search_append_body(re, txt) +4.6. search_append_body(re, txt) Searches for the first match of re in the body of the message and appends txt after it. @@ -405,12 +429,12 @@ search_append("[Oo]pen[Ss]er", " SIP Proxy"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.5. search_append_body usage + Example 1.6. search_append_body usage ... search_append_body("[Oo]pen[Ss]er", " SIP Proxy"); ... -4.6. replace(re, txt) +4.7. replace(re, txt) Replaces the first occurrence of re with txt. @@ -421,12 +445,12 @@ search_append_body("[Oo]pen[Ss]er", " SIP Proxy"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.6. replace usage + Example 1.7. replace usage ... replace("openser", "Kamailio SIP Proxy"); ... -4.7. replace_body(re, txt) +4.8. replace_body(re, txt) Replaces the first occurrence of re in the body of the message with txt. @@ -438,12 +462,12 @@ replace("openser", "Kamailio SIP Proxy"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.7. replace_body usage + Example 1.8. replace_body usage ... replace_body("openser", "Kamailio SIP Proxy"); ... -4.8. replace_hdrs(re, txt) +4.9. replace_hdrs(re, txt) Replaces the first occurrence of re in the SIP headers of the message with txt. @@ -454,12 +478,12 @@ replace_body("openser", "Kamailio SIP Proxy"); This function can be used from ANY_ROUTE. - Example 1.8. replace_hdrs usage + Example 1.9. replace_hdrs usage ... replace_hdrs("Kamailio", "Kamailio SIP Proxy"); ... -4.9. replace_all(re, txt) +4.10. replace_all(re, txt) Replaces all occurrence of re with txt. @@ -470,12 +494,12 @@ replace_hdrs("Kamailio", "Kamailio SIP Proxy"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.9. replace_all usage + Example 1.10. replace_all usage ... replace_all("openser", "Kamailio SIP Proxy"); ... -4.10. replace_body_all(re, txt) +4.11. replace_body_all(re, txt) Replaces all occurrence of re in the body of the message with txt. Matching is done on a per-line basis. @@ -487,12 +511,12 @@ replace_all("openser", "Kamailio SIP Proxy"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.10. replace_body_all usage + Example 1.11. replace_body_all usage ... replace_body_all("openser", "Kamailio SIP Proxy"); ... -4.11. replace_body_atonce(re, txt) +4.12. replace_body_atonce(re, txt) Replaces all occurrence of re in the body of the message with txt. Matching is done over the whole body. @@ -504,14 +528,14 @@ replace_body_all("openser", "Kamailio SIP Proxy"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.11. replace_body_atonce usage + Example 1.12. replace_body_atonce usage ... # strip the whole body from the message: if(has_body() && replace_body_atonce("^.+$", "")) remove_hf("Content-Type"); ... -4.12. replace_str(match, repl, mode) +4.13. replace_str(match, repl, mode) Replaces the first or all occurrence of 'match' with 'repl' by doing string comparison for matching. It is applied over headers and message @@ -524,12 +548,12 @@ if(has_body() && replace_body_atonce("^.+$", "")) This function can be used from ANY_ROUTE. - Example 1.12. replace_str usage + Example 1.13. replace_str usage ... replace_str("Kamailio", "Kamailio SIP Proxy", "a"); ... -4.13. replace_body_str(match, repl, mode) +4.14. replace_body_str(match, repl, mode) Replaces the first or all occurrence of 'match' with 'repl' by doing string comparison for matching. It is applied over the message body. @@ -541,12 +565,12 @@ replace_str("Kamailio", "Kamailio SIP Proxy", "a"); This function can be used from ANY_ROUTE. - Example 1.13. replace_body_str usage + Example 1.14. replace_body_str usage ... replace_body_str("Kamailio", "Kamailio SIP Proxy", "a"); ... -4.14. replace_hdrs_str(match, repl, mode) +4.15. replace_hdrs_str(match, repl, mode) Replaces the first or all occurrence of 'match' with 'repl' by doing string comparison for matching. It is applied over the part with @@ -559,12 +583,12 @@ replace_body_str("Kamailio", "Kamailio SIP Proxy", "a"); This function can be used from ANY_ROUTE. - Example 1.14. replace_hdrs_str usage + Example 1.15. replace_hdrs_str usage ... replace_hdrs_str("Kamailio", "Kamailio SIP Proxy", "a"); ... -4.15. subst('/re/repl/flags') +4.16. subst('/re/repl/flags') Replaces re with repl (sed or perl like). @@ -579,7 +603,7 @@ replace_hdrs_str("Kamailio", "Kamailio SIP Proxy", "a"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.15. subst usage + Example 1.16. subst usage ... # replace the uri in to: with the message uri (just an example) if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1\u\2/ig') ) {}; @@ -590,7 +614,7 @@ if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1$avp(sip_address)\2/ig') ) ... -4.16. subst_uri('/re/repl/flags') +4.17. subst_uri('/re/repl/flags') Runs the re substitution on the message uri (like subst but works only on the uri) @@ -606,7 +630,7 @@ if ( subst('/^To:(.*)sip:[^@]*@[a-zA-Z0-9.]+(.*)$/t:\1$avp(sip_address)\2/ig') ) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.16. subst_uri usage + Example 1.17. subst_uri usage ... # adds 3463 prefix to numeric uris, and save the original uri (\0 match) # as a parameter: orig_uri (just an example) @@ -618,7 +642,7 @@ if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:$avp(uri_prefix)\1@\2;orig_uri=\0/i')){$ ... -4.17. subst_user('/re/repl/flags') +4.18. subst_user('/re/repl/flags') Runs the re substitution on the message uri (like subst_uri but works only on the user portion of the uri) @@ -634,7 +658,7 @@ if (subst_uri('/^sip:([0-9]+)@(.*)$/sip:$avp(uri_prefix)\1@\2;orig_uri=\0/i')){$ This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.17. subst usage + Example 1.18. subst usage ... # adds 3463 prefix to uris ending with 3642 (just an example) if (subst_user('/3642$/36423463/')){$ @@ -645,7 +669,7 @@ if (subst_user('/(.*)3642$/$avp(user_prefix)\13642/')){$ ... -4.18. subst_body('/re/repl/flags') +4.19. subst_body('/re/repl/flags') Replaces re with repl (sed or perl like) in the body of the message. @@ -660,13 +684,13 @@ if (subst_user('/(.*)3642$/$avp(user_prefix)\13642/')){$ This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.18. subst_body usage + Example 1.19. subst_body usage ... if ( subst_body('/^o=(.*) /o=$fU /') ) {}; ... -4.19. subst_hf(hf, subexp, flags) +4.20. subst_hf(hf, subexp, flags) Perl-like substitutions in the body of a header field. @@ -681,12 +705,12 @@ if ( subst_body('/^o=(.*) /o=$fU /') ) {}; This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.19. subst_hf usage + Example 1.20. subst_hf usage ... if ( subst_hf("From", "/:test@/:best@/", "a") ) { /*....*/ }; ... -4.20. set_body(txt,content_type) +4.21. set_body(txt,content_type) Set body to a SIP message. @@ -698,12 +722,12 @@ if ( subst_hf("From", "/:test@/:best@/", "a") ) { /*....*/ }; This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.20. set_body usage + Example 1.21. set_body usage ... set_body("test", "text/plain"); ... -4.21. set_reply_body(txt,content_type) +4.22. set_reply_body(txt,content_type) Set body to a SIP reply to be generated by Kamailio. @@ -715,12 +739,12 @@ set_body("test", "text/plain"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.21. set_reply_body usage + Example 1.22. set_reply_body usage ... set_reply_body("test", "text/plain"); ... -4.22. filter_body(content_type) +4.23. filter_body(content_type) Filters multipart/mixed body by leaving out all other body parts except the first body part of given type. @@ -731,7 +755,7 @@ set_reply_body("test", "text/plain"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.22. filter_body usage + Example 1.23. filter_body usage ... if (has_body("multipart/mixed")) { if (filter_body("application/sdp") { @@ -743,7 +767,7 @@ if (has_body("multipart/mixed")) { } ... -4.23. append_to_reply(txt) +4.24. append_to_reply(txt) Append txt as header to the reply that is going to be generated by Kamailio (e.g., via sl_send_reply(...)). @@ -756,13 +780,13 @@ if (has_body("multipart/mixed")) { This function can be used from REQUEST_ROUTE, BRANCH_ROUTE, FAILURE_ROUTE, ERROR_ROUTE. - Example 1.23. append_to_reply usage + Example 1.24. append_to_reply usage ... append_to_reply("Foo: bar\r\n"); append_to_reply("Foo: $rm at $Ts\r\n"); ... -4.24. append_hf(txt[, hdr]) +4.25. append_hf(txt[, hdr]) Appends 'txt' as header at the end of the all headers, or after last header named 'hdr' if the second parameter is provided. @@ -777,13 +801,13 @@ append_to_reply("Foo: $rm at $Ts\r\n"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.24. append_hf usage + Example 1.25. append_hf usage ... append_hf("P-hint: VOICEMAIL\r\n"); append_hf("From-username: $fU\r\n", "Call-ID"); ... -4.25. insert_hf(txt[, hdr]) +4.26. insert_hf(txt[, hdr]) Inserts 'txt' as header before the first header field, or before first header named 'hdr'if the second parameter is provided. @@ -798,7 +822,7 @@ append_hf("From-username: $fU\r\n", "Call-ID"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.25. insert_hf usage + Example 1.26. insert_hf usage ... insert_hf("P-hint: VOICEMAIL\r\n"); insert_hf("To-username: $tU\r\n"); @@ -806,7 +830,7 @@ insert_hf("P-hint: VOICEMAIL\r\n", "Call-ID"); insert_hf("To-username: $tU\r\n", "Call-ID"); ... -4.26. append_urihf(prefix, suffix) +4.27. append_urihf(prefix, suffix) Append header field name with original Request-URI in middle. @@ -817,12 +841,12 @@ insert_hf("To-username: $tU\r\n", "Call-ID"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.26. append_urihf usage + Example 1.27. append_urihf usage ... append_urihf("CC-Diversion: ", "\r\n"); ... -4.27. is_present_hf(hf_name) +4.28. is_present_hf(hf_name) Return true if a header field is present in message. @@ -838,22 +862,22 @@ Note This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.27. is_present_hf usage + Example 1.28. is_present_hf usage ... if (is_present_hf("From")) xlog("From HF Present"); ... -4.28. is_present_hf_pv(hf_name) +4.29. is_present_hf_pv(hf_name) Same as is_present_hf() function, but the parameter can contain variables. - Example 1.28. is_present_hf_pv usage + Example 1.29. is_present_hf_pv usage ... if (is_present_hf_pv("$var(hname)")) xinfo("Header $var(hname) is present\n"); ... -4.29. is_present_hf_re(hf_name_re) +4.30. is_present_hf_re(hf_name_re) Return true if a header field whose name matches regular expression 'hf_name_re' is present in message. @@ -865,24 +889,24 @@ if (is_present_hf_pv("$var(hname)")) xinfo("Header $var(hname) is present\n"); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.29. is_present_hf_re usage + Example 1.30. is_present_hf_re usage ... if (is_present_hf_re("^P-")) xlog("There are headers starting with P-\n"); ... -4.30. is_present_hf_re_pv(hf_name_re) +4.31. is_present_hf_re_pv(hf_name_re) Same as is_present_hf_re() function, but the parameter can contain variables. - Example 1.30. is_present_hf_re_pv usage + Example 1.31. is_present_hf_re_pv usage ... if (is_present_hf_re_pv_("^$var(prefix)")) xlog("There are headers starting with $var(prefix)\n"); ... -4.31. append_time() +4.32. append_time() Adds a time header to the reply of the request. You must use it before functions that are likely to send a reply, e.g., save() from @@ -901,12 +925,12 @@ if (is_present_hf_re_pv_("^$var(prefix)")) This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.31. append_time usage + Example 1.32. append_time usage ... append_time(); ... -4.32. append_time_to_request() +4.33. append_time_to_request() Adds a time header to the request. Header format is: “Date: %a, %d %b %Y %H:%M:%S GMT”, with the legend: @@ -923,13 +947,13 @@ append_time(); This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.32. append_time_to_request usage + Example 1.33. append_time_to_request usage ... if(!is_present_hf("Date")) append_time_to_request(); ... -4.33. is_method(name) +4.34. is_method(name) Check if the method of the message matches the name. If name is a known method (invite, cancel, ack, bye, options, info, update, register, @@ -954,7 +978,7 @@ if(!is_present_hf("Date")) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, and BRANCH_ROUTE. - Example 1.33. is_method usage + Example 1.34. is_method usage ... if(is_method("INVITE")) { @@ -966,7 +990,7 @@ if(is_method("OPTION|UPDATE")) } ... -4.34. remove_hf(hname) +4.35. remove_hf(hname) Remove from message all headers with name “hname”. Header matching is case-insensitive. Matches and removes also the compact header forms. @@ -981,7 +1005,7 @@ if(is_method("OPTION|UPDATE")) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. - Example 1.34. remove_hf usage + Example 1.35. remove_hf usage ... if(remove_hf("User-Agent")) { @@ -993,16 +1017,16 @@ remove_hf("Contact"); remove_hf("m"); ... -4.35. remove_hf_pv(hname) +4.36. remove_hf_pv(hname) Same as remove_hf() function, but the parameter can contain variables. - Example 1.35. remove_hf_pv usage + Example 1.36. remove_hf_pv usage ... remove_hf_pv("$var(hname)"); ... -4.36. remove_hf_re(re) +4.37. remove_hf_re(re) Remove from message all headers with name matching regular expression “re” @@ -1014,7 +1038,7 @@ remove_hf_pv("$var(hname)"); This function can be used from ANY_ROUTE. - Example 1.36. remove_hf_re usage + Example 1.37. remove_hf_re usage ... if(remove_hf_re("^P-")) { @@ -1022,12 +1046,12 @@ if(remove_hf_re("^P-")) } ... -4.37. remove_hf_re_pv(re) +4.38. remove_hf_re_pv(re) Same as remove_hf_re() function, but the parameter can contain variables. - Example 1.37. remove_hf_re_pv usage + Example 1.38. remove_hf_re_pv usage ... if(remove_hf_re_pv("^$var(prefix)")) { @@ -1035,7 +1059,7 @@ if(remove_hf_re_pv("^$var(prefix)")) } ... -4.38. remove_hf_exp(expmatch, expskip) +4.39. remove_hf_exp(expmatch, expskip) Remove from message all headers with name matching regular expression “expmatch”, but not matching regular expression “expskip”. @@ -1050,7 +1074,7 @@ if(remove_hf_re_pv("^$var(prefix)")) This function can be used from ANY_ROUTE. - Example 1.38. remove_hf_exp usage + Example 1.39. remove_hf_exp usage ... if(remove_hf_exp("^P-", "^P-Keep-")) { @@ -1059,12 +1083,12 @@ if(remove_hf_exp("^P-", "^P-Keep-")) } ... -4.39. remove_hf_exp_pv(expmatch, expskip) +4.40. remove_hf_exp_pv(expmatch, expskip) Same as remove_hf_exp() function, but the parameters can contain variabes. - Example 1.39. remove_hf_exp_pv usage + Example 1.40. remove_hf_exp_pv usage ... if(remove_hf_exp_pv("^$var(match)", "^$var(keep)")) { @@ -1073,7 +1097,7 @@ if(remove_hf_exp_pv("^$var(match)", "^$var(keep)")) } ... -4.40. has_body(), has_body(mime) +4.41. has_body(), has_body(mime) The function returns true if the SIP message has a body attached. The checked includes also the “Content-Length” header presence and value. @@ -1088,7 +1112,7 @@ if(remove_hf_exp_pv("^$var(match)", "^$var(keep)")) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. - Example 1.40. has_body usage + Example 1.41. has_body usage ... if(has_body("application/sdp")) { @@ -1096,7 +1120,7 @@ if(has_body("application/sdp")) } ... -4.41. is_audio_on_hold() +4.42. is_audio_on_hold() The function returns true if the SIP message has a body attached and at least one audio stream in on hold. The return code of the function @@ -1107,7 +1131,7 @@ if(has_body("application/sdp")) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. - Example 1.41. is_audio_on_hold usage + Example 1.42. is_audio_on_hold usage ... if(is_audio_on_hold()) { @@ -1123,7 +1147,7 @@ if(is_audio_on_hold()) } ... -4.42. is_privacy(privacy_type) +4.43. is_privacy(privacy_type) The function returns true if the SIP message has a Privacy header field that includes the given privacy_type among its privacy values. See @@ -1133,7 +1157,7 @@ if(is_audio_on_hold()) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. - Example 1.42. is_privacy usage + Example 1.43. is_privacy usage ... if(is_privacy("id")) { @@ -1141,7 +1165,7 @@ if(is_privacy("id")) } ... -4.43. in_list(subject, list, separator) +4.44. in_list(subject, list, separator) Function checks if subject string is found in list string where list items are separated by separator string. Subject and list strings may @@ -1150,7 +1174,7 @@ if(is_privacy("id")) Function can be used from all kinds of routes. - Example 1.43. in_list() usage + Example 1.44. in_list() usage ... $var(subject) = "fi"; $var(list) = "dk,fi,no,se"; @@ -1159,7 +1183,7 @@ if (in_list("$var(subject)", "$var(list)", ",")) { } ... -4.44. in_list_prefix(subject, list, separator) +4.45. in_list_prefix(subject, list, separator) Function checks if any element in list string is a prefix for subject string where list items are separated by separator string. Subject and @@ -1168,7 +1192,7 @@ if (in_list("$var(subject)", "$var(list)", ",")) { Function can be used from all kinds of routes. - Example 1.44. in_list() usage + Example 1.45. in_list() usage ... $var(subject) = "final"; $var(list) = "dk,fi,no,se"; @@ -1177,7 +1201,7 @@ if (in_list_prefix("$var(subject)", "$var(list)", ",")) { } ... -4.45. cmp_str(str1, str2) +4.46. cmp_str(str1, str2) The function returns true if the two parameters matches as string case sensitive comparison. @@ -1185,7 +1209,7 @@ if (in_list_prefix("$var(subject)", "$var(list)", ",")) { This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. - Example 1.45. cmp_str usage + Example 1.46. cmp_str usage ... if(cmp_str("$rU", "kamailio")) { @@ -1193,7 +1217,7 @@ if(cmp_str("$rU", "kamailio")) } ... -4.46. cmp_istr(str1, str2) +4.47. cmp_istr(str1, str2) The function returns true if the two parameters matches as string case insensitive comparison. @@ -1201,7 +1225,7 @@ if(cmp_str("$rU", "kamailio")) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. - Example 1.46. cmp_str usage + Example 1.47. cmp_str usage ... if(cmp_istr("$rU@you", "kamailio@YOU")) { @@ -1209,7 +1233,7 @@ if(cmp_istr("$rU@you", "kamailio@YOU")) } ... -4.47. starts_with(str1, str2) +4.48. starts_with(str1, str2) The function returns true if the first string starts with the second string. @@ -1217,7 +1241,7 @@ if(cmp_istr("$rU@you", "kamailio@YOU")) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. - Example 1.47. starts_with usage + Example 1.48. starts_with usage ... if (starts_with("$rU", "+358")) { @@ -1225,7 +1249,7 @@ if (starts_with("$rU", "+358")) } ... -4.48. ends_with(str1, str2) +4.49. ends_with(str1, str2) The function returns true if the first string ends with the second string. The parameters can contain variables. @@ -1233,7 +1257,7 @@ if (starts_with("$rU", "+358")) This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE and BRANCH_ROUTE. - Example 1.48. ends_with usage + Example 1.49. ends_with usage ... if (ends_with("$rU", "8800")) { @@ -1241,7 +1265,7 @@ if (ends_with("$rU", "8800")) } ... -4.49. set_body_multipart([txt,content_type][,boundary]) +4.50. set_body_multipart([txt,content_type][,boundary]) Set multipart body to a SIP message. If called with no parameters, will convert present body to multipart. @@ -1262,7 +1286,7 @@ if (ends_with("$rU", "8800")) Note: it may be required that msg_apply_changes() from textopsx module has to be executed if there are other operations over the new body. - Example 1.49. set_body_multipart usage + Example 1.50. set_body_multipart usage ... set_body_multipart("test", "text/plain", "delimiter"); msg_apply_changes(); @@ -1285,7 +1309,7 @@ text --delimiter ... -4.50. append_body_part(txt,content_type[, content_disposition]) +4.51. append_body_part(txt,content_type[, content_disposition]) Append a part on multipart body SIP message. Will use "unique-boundary-1" as boundary. @@ -1306,7 +1330,7 @@ text Note: it may be required that msg_apply_changes() from textopsx module has to be executed if there are other operations over the new body. - Example 1.50. append_body_part usage + Example 1.51. append_body_part usage ... $var(b) = "7e Od 04 55 75 69 20 4d 61 6b 65 43 61 6c 6c"; append_body_part("$var(b)", "application/vnd.cirpack.isdn-ext", "signal;handling @@ -1329,7 +1353,7 @@ Content-Disposition: signal;handling=required appended after the value of the content-type parameter, separated by `\r\n` (at the very end do not add the '\r\n'). - Example 1.51. append_body_part with headers + Example 1.52. append_body_part with headers ... $var(b) = "7e Od 04 55 75 69 20 4d 61 6b 65 43 61 6c 6c"; append_body_part("$var(b)", "application/vnd.cirpack.isdn-ext\r\nX-Header: xyz", @@ -1349,7 +1373,7 @@ Content-Disposition: signal;handling=required --unique-boundary-1 ... -4.51. append_body_part_hex(txt,content_type[, content_disposition]) +4.52. append_body_part_hex(txt,content_type[, content_disposition]) Append a part on multipart body SIP message, with the content provided in hexa format. Will use "unique-boundary-1" as boundary. @@ -1373,7 +1397,7 @@ Content-Disposition: signal;handling=required Note: it may be required that msg_apply_changes() from textopsx module has to be executed if there are other operations over the new body. - Example 1.52. append_body_part_hex usage + Example 1.53. append_body_part_hex usage ... $var(b) = "6b 61 6d 61 69 6c 69 6f"; append_body_part_hex("$var(b)", "application/my-custom-ext"); @@ -1393,7 +1417,7 @@ kamailio If other headers are wanted to be added for a body part, see the docs for append_body_part(...) function. -4.52. get_body_part(content_type, opv) +4.53. get_body_part(content_type, opv) Return the content of a multipart body SIP message, storing it in opv. @@ -1406,12 +1430,12 @@ kamailio This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE. - Example 1.53. get_body_part usage + Example 1.54. get_body_part usage ... get_body_part("application/vnd.cirpack.isdn-ext", "$var(pbody)"); ... -4.53. get_body_part_raw(content_type, opv) +4.54. get_body_part_raw(content_type, opv) Return the content of a multipart body SIP message, including headers and boundary string, storing it in opv. @@ -1425,12 +1449,12 @@ get_body_part("application/vnd.cirpack.isdn-ext", "$var(pbody)"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE. - Example 1.54. get_body_part_raw usage + Example 1.55. get_body_part_raw usage ... get_body_part("application/vnd.cirpack.isdn-ext", "$var(hbody)"); ... -4.54. remove_body_part(content_type) +4.55. remove_body_part(content_type) Remove a part on a multipart body SIP message. @@ -1448,13 +1472,13 @@ get_body_part("application/vnd.cirpack.isdn-ext", "$var(hbody)"); Note: it may be required that msg_apply_changes() from textopsx module has to be executed if there are other operations over the new body. - Example 1.55. remove_body_part usage + Example 1.56. remove_body_part usage ... remove_body_part("application/vnd.cirpack.isdn-ext"); msg_apply_changes(); ... -4.55. regex_substring(itext, regexp, mindex, mcount, dpv) +4.56. regex_substring(itext, regexp, mindex, mcount, dpv) Search in text with given regular expression then sets dpv pseudo-variable with the matched token at provided index. @@ -1474,7 +1498,7 @@ msg_apply_changes(); Note that the regular expression extended is used. More info at: https://www.regular-expressions.info/posix.html. - Example 1.56. _regex_substring usage + Example 1.57. _regex_substring usage ... regex_substring("___ abc123def ___ ghi456 ___", "([a-z]*)([0-9]+)([a-z]* )", diff --git a/src/modules/textops/doc/textops_admin.xml b/src/modules/textops/doc/textops_admin.xml index e9acba38694..479b8f1cd17 100644 --- a/src/modules/textops/doc/textops_admin.xml +++ b/src/modules/textops/doc/textops_admin.xml @@ -90,6 +90,10 @@ From: medabeda + + Note: it performs Posix regex matching and the 're' parameter + is compiled with the flags REG_EXTENDED|REG_ICASE|REG_NEWLINE. + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. @@ -128,6 +132,42 @@ if ( search("[Ss][Ii][Pp]") ) { /*....*/ }; ... if ( search_body("[Ss][Ii][Pp]") ) { /*....*/ }; ... + + +
+ +
+ + <function moreinfo="none">search_str(text. re)</function> + + + Searches for the re in the body of the message. + + Meaning of the parameters is as follows: + + + text - text to perform regex searching + over it. + + + + re - regular expression to match over + the 'text' parameter. + + + + + Both parameters can contain variables. + + + This function can be used from ANY_ROUTE. + + + <function>search_str</function> usage + +... +if ( search_str("$ru", ";transport=tcp") ) { /*....*/ }; +...
diff --git a/src/modules/textops/textops.c b/src/modules/textops/textops.c index 6f3c6f9b5e2..1b045dc89c5 100644 --- a/src/modules/textops/textops.c +++ b/src/modules/textops/textops.c @@ -148,6 +148,7 @@ static int is_present_hf_re_pv_f(sip_msg_t* msg, char* key, char* foo); static int is_audio_on_hold_f(struct sip_msg *msg, char *str1, char *str2 ); static int regex_substring_f(struct sip_msg *msg, char *input, char *regex, char *matched_index, char *match_count, char *dst); +static int w_search_str(sip_msg_t *msg, char *ptext, char *pre); static int fixup_substre(void**, int); static int hname_fixup(void** param, int param_no); static int free_hname_fixup(void** param, int param_no); @@ -314,6 +315,9 @@ static cmd_export_t cmds[]={ {"cmp_istr", (cmd_function)cmp_istr_f, 2, fixup_spve_spve, 0, ANY_ROUTE}, + {"search_str", (cmd_function)w_search_str, 2, + fixup_spve_spve, 0, + ANY_ROUTE}, {"starts_with", (cmd_function)starts_with_f, 2, fixup_spve_spve, 0, ANY_ROUTE}, @@ -4593,6 +4597,61 @@ static int fixup_subst_hf(void** param, int param_no) return 0; } +/** + * + */ +static int ki_search_str(sip_msg_t *msg, str *stext, str *sre) +{ + int ret; + regex_t re; + regmatch_t pmatch; + + + if(sre==NULL || sre->len<=0) { + return 2; + } + + if(stext==NULL || stext->len<=0) { + return -2; + } + + memset(&re, 0, sizeof(regex_t)); + if (regcomp(&re, sre->s, REG_EXTENDED|REG_ICASE|REG_NEWLINE)!=0) { + LM_ERR("failed to compile regex: %.*s\n", sre->len, sre->s); + return -2; + } + + if (regexec(&re, stext->s, 1, &pmatch, 0)!=0) { + ret = -1; + } else { + ret = 1; + } + + regfree(&re); + + return ret; +} + +/** + * + */ +static int w_search_str(sip_msg_t *msg, char *ptext, char *pre) +{ + str stext; + str sre; + + if(fixup_get_svalue(msg, (gparam_t*)ptext, &stext)!=0) { + LM_ERR("cannot get first parameter\n"); + return -2; + } + if(fixup_get_svalue(msg, (gparam_t*)pre, &sre)!=0) { + LM_ERR("cannot get second parameter\n"); + return -2; + } + + return ki_search_str(msg, &stext, &sre); +} + /** * */ @@ -4945,6 +5004,11 @@ static sr_kemi_t sr_kemi_textops_exports[] = { { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } }, + { str_init("textops"), str_init("search_str"), + SR_KEMIP_INT, ki_search_str, + { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, { str_init("textops"), str_init("starts_with"), SR_KEMIP_INT, ki_starts_with, { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE, diff --git a/src/modules/tm/t_cancel.c b/src/modules/tm/t_cancel.c index 69fdb9f77e5..170330395c6 100644 --- a/src/modules/tm/t_cancel.c +++ b/src/modules/tm/t_cancel.c @@ -527,7 +527,9 @@ unsigned int t_uac_cancel( str *headers, str *body, LM_ERR("send failed\n"); goto error1; } - start_retr(cancel); + if(start_retr(cancel)!=0) { + LM_CRIT("failed to start retransmission for cancel %p\n", cancel); + } /* */ return ret; diff --git a/src/modules/topos/README b/src/modules/topos/README index 1dcff2ad07c..39a113ce368 100644 --- a/src/modules/topos/README +++ b/src/modules/topos/README @@ -10,7 +10,13 @@ Daniel-Constantin Mierla +Frederic Gaisnon + + + Copyright © 2016 FhG FOKUS + + Copyright © 2021 MomentTech __________________________________________________________________ Table of Contents @@ -112,8 +118,8 @@ Chapter 1. Admin Guide It also works for SIP MESSAGE or other requests that do not create a dialog -- record_route() must be used for them as well, the headers are not going to be in the messages sent to the network, they are needed to - know local addresses used to communicate with each side. At this moment - it is not designed to work for presence (SUBSCRIBE-based) dialogs. The + know local addresses used to communicate with each side. This module is + designed to work for presence (SUBSCRIBE-based) dialogs too. The REGISTER and PUBLISH requests are skipped from processing by this module, expected to be terminated on a local SIP server. @@ -225,7 +231,10 @@ modparam("topos", "branch_expire", 300) mind that the module does not update the dialog timestamp after the initial call setup on re-INVITEs or other in-dialog messages. So set a large enough value (according your longest call duration) to prevent - problems in re-writing messages. + problems in re-writing messages. This key is only relevant for INVITE + dialog. SUBSCRIBE dialog records lifetime are based on value set in + expires header. Moreover each re-SUBSCRIBEs update the dialog + timestamp. Default value is 10800 (3 hours). diff --git a/src/modules/topos/doc/topos.xml b/src/modules/topos/doc/topos.xml index 38d3c0cea63..55a72868a93 100644 --- a/src/modules/topos/doc/topos.xml +++ b/src/modules/topos/doc/topos.xml @@ -23,11 +23,20 @@ Mierla miconda@gmail.com + + Frederic + Gaisnon + frederic.gaisnon@gmail.com + 2016 &fhg; + + 2021 + MomentTech + diff --git a/src/modules/topos/doc/topos_admin.xml b/src/modules/topos/doc/topos_admin.xml index 959e7e0f3e5..1cabfccf076 100644 --- a/src/modules/topos/doc/topos_admin.xml +++ b/src/modules/topos/doc/topos_admin.xml @@ -30,8 +30,8 @@ a dialog -- record_route() must be used for them as well, the headers are not going to be in the messages sent to the network, they are needed to know local addresses used to communicate with each side. - At this moment it is not designed to work for presence (SUBSCRIBE-based) - dialogs. The REGISTER and PUBLISH requests are skipped from processing + This module is designed to work for presence (SUBSCRIBE-based) dialogs too. + The REGISTER and PUBLISH requests are skipped from processing by this module, expected to be terminated on a local SIP server. @@ -199,6 +199,9 @@ modparam("topos", "branch_expire", 300) after the initial call setup on re-INVITEs or other in-dialog messages. So set a large enough value (according your longest call duration) to prevent problems in re-writing messages. + This key is only relevant for INVITE dialog. + SUBSCRIBE dialog records lifetime are based on value set in expires + header. Moreover each re-SUBSCRIBEs update the dialog timestamp. diff --git a/src/modules/topos/topos_mod.c b/src/modules/topos/topos_mod.c index a248e606002..a8dbf5b9be7 100644 --- a/src/modules/topos/topos_mod.c +++ b/src/modules/topos/topos_mod.c @@ -389,10 +389,6 @@ int tps_msg_received(sr_event_param_t *evp) } } else { /* reply */ - if(msg.first_line.u.reply.statuscode==100) { - /* nothing to do - it should be absorbed */ - goto done; - } tps_response_received(&msg); } diff --git a/src/modules/topos/tps_msg.c b/src/modules/topos/tps_msg.c index e3616391add..8ee2fb355f0 100644 --- a/src/modules/topos/tps_msg.c +++ b/src/modules/topos/tps_msg.c @@ -905,6 +905,11 @@ int tps_request_received(sip_msg_t *msg, int dialog) goto error; } } + if((get_cseq(msg)->method_id)&(METHOD_SUBSCRIBE)) { + if(tps_storage_update_dialog(msg, &mtsd, &stsd, TPS_DBU_CONTACT|TPS_DBU_TIME)<0) { + goto error; + } + } } return 0; @@ -926,11 +931,6 @@ int tps_response_received(sip_msg_t *msg) LM_DBG("handling incoming response\n"); - if(msg->first_line.u.reply.statuscode==100) { - /* nothing to do - it should be absorbed */ - return 0; - } - memset(&mtsd, 0, sizeof(tps_data_t)); memset(&stsd, 0, sizeof(tps_data_t)); memset(&btsd, 0, sizeof(tps_data_t)); diff --git a/src/modules/topos/tps_storage.c b/src/modules/topos/tps_storage.c index e23352962ea..c0bf33be357 100644 --- a/src/modules/topos/tps_storage.c +++ b/src/modules/topos/tps_storage.c @@ -41,6 +41,7 @@ #include "../../core/parser/contact/parse_contact.h" #include "../../core/parser/parse_from.h" #include "../../core/parser/parse_to.h" +#include "../../core/parser/parse_expires.h" #include "../../lib/srdb1/db.h" #include "../../core/utils/sruid.h" @@ -336,8 +337,11 @@ int tps_storage_fill_contact(sip_msg_t *msg, tps_data_t *td, str *uuid, int dir, td->cp += pv_val.rs.len; } } - *td->cp = '@'; - td->cp++; + + if (!((ctmode == 1) && (dir==TPS_DIR_DOWNSTREAM) && (curi.user.len <= 0))) { + *td->cp = '@'; + td->cp++; + } if (_tps_contact_host.len) { // using configured hostname in the contact header memcpy(td->cp, _tps_contact_host.s, _tps_contact_host.len); @@ -465,8 +469,8 @@ int tps_storage_link_msg(sip_msg_t *msg, tps_data_t *td, int dir) /* extract the contact address */ if(parse_headers(msg, HDR_CONTACT_F, 0)<0 || msg->contact==NULL) { - if(td->s_method_id != METHOD_INVITE) { - /* no mandatory contact unless is INVITE - done */ + if((td->s_method_id != METHOD_INVITE) && (td->s_method_id != METHOD_SUBSCRIBE)){ + /* no mandatory contact unless is INVITE or SUBSCRIBE - done */ return 0; } if(msg->first_line.type==SIP_REPLY) { @@ -504,6 +508,13 @@ int tps_storage_link_msg(sip_msg_t *msg, tps_data_t *td, int dir) } } + if (td->s_method_id == METHOD_SUBSCRIBE) { + if(msg->expires && (msg->expires->body.len > 0) && (msg->expires->parsed || (parse_expires(msg->expires) >= 0))) { + td->expires = ((exp_body_t *)msg->expires->parsed)->val; + } + } + + LM_DBG("downstream: %s - acontact: [%.*s] - bcontact: [%.*s]\n", (dir==TPS_DIR_DOWNSTREAM)?"yes":"no", td->a_contact.len, (td->a_contact.len>0)?td->a_contact.s:"", @@ -1036,6 +1047,8 @@ int tps_db_load_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, db_key_t db_cols[TPS_NR_KEYS]; db1_res_t* db_res = NULL; str sinv = str_init("INVITE"); + str ssub = str_init("SUBSCRIBE"); + int bInviteDlg = 1; int nr_keys; int nr_cols; int n; @@ -1047,6 +1060,10 @@ int tps_db_load_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, nr_keys = 0; nr_cols = 0; + if((get_cseq(msg)->method_id == METHOD_SUBSCRIBE) || ((get_cseq(msg)->method_id == METHOD_NOTIFY) && (msg->event->len > 0))) { + bInviteDlg = 0; + } + if(mode==0) { /* load same transaction using Via branch */ db_keys[nr_keys]=&tt_col_x_vbranch; @@ -1075,7 +1092,7 @@ int tps_db_load_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, db_ops[nr_keys]=OP_EQ; db_vals[nr_keys].type = DB1_STR; db_vals[nr_keys].nul = 0; - db_vals[nr_keys].val.str_val = sinv; + db_vals[nr_keys].val.str_val = bInviteDlg ? sinv : ssub; nr_keys++; } @@ -1407,7 +1424,7 @@ int tps_storage_update_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, if(msg==NULL || md==NULL || sd==NULL) return -1; - if(md->s_method_id != METHOD_INVITE) { + if((md->s_method_id != METHOD_INVITE) && (md->s_method_id != METHOD_SUBSCRIBE)) { return 0; } @@ -1514,6 +1531,14 @@ int tps_db_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, } } } + if ((mode & TPS_DBU_TIME) && ((sd->b_tag.len > 0) + && ((md->direction == TPS_DIR_UPSTREAM) && (msg->first_line.type==SIP_REQUEST)) + && (msg->first_line.u.request.method_value == METHOD_SUBSCRIBE))) { + db_ucols[nr_ucols] = &td_col_rectime; + db_uvals[nr_ucols].type = DB1_DATETIME; + db_uvals[nr_ucols].val.time_val = time(NULL); + nr_ucols++; + } if(nr_ucols==0) { return 0; @@ -1543,7 +1568,7 @@ int tps_storage_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, if(msg==NULL || md==NULL || sd==NULL) return -1; - if(md->s_method_id != METHOD_INVITE) { + if((md->s_method_id != METHOD_INVITE) && (md->s_method_id != METHOD_SUBSCRIBE)) { return 0; } if(msg->first_line.type==SIP_REPLY) { @@ -1575,7 +1600,7 @@ int tps_db_end_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd) if(msg==NULL || md==NULL || sd==NULL || _tps_db_handle==NULL) return -1; - if(md->s_method_id != METHOD_BYE) { + if((md->s_method_id != METHOD_BYE) && !((md->s_method_id == METHOD_SUBSCRIBE) && (md->expires == 0))) { return 0; } diff --git a/src/modules/topos/tps_storage.h b/src/modules/topos/tps_storage.h index aeda1962ec1..6183ba6b476 100644 --- a/src/modules/topos/tps_storage.h +++ b/src/modules/topos/tps_storage.h @@ -41,6 +41,7 @@ #define TPS_DBU_RPLATTRS (1<<1) #define TPS_DBU_ARR (1<<2) #define TPS_DBU_BRR (1<<3) +#define TPS_DBU_TIME (1<<4) #define TPS_DBU_ALL (0xffffffff) #define TPS_DATA_SIZE 8192 @@ -79,6 +80,7 @@ typedef struct tps_data { int32_t iflags; int32_t direction; uint32_t s_method_id; + int32_t expires; } tps_data_t; int tps_storage_dialog_find(sip_msg_t *msg, tps_data_t *td); diff --git a/src/modules/topos_redis/README b/src/modules/topos_redis/README index c1b038dffc8..619902729f9 100644 --- a/src/modules/topos_redis/README +++ b/src/modules/topos_redis/README @@ -10,9 +10,15 @@ Daniel-Constantin Mierla +Frederic Gaisnon + + + Copyright © 2017 kamailio.org Copyright © 2017 flowroute.com + + Copyright © 2021 MomentTech __________________________________________________________________ Table of Contents diff --git a/src/modules/topos_redis/doc/topos_redis.xml b/src/modules/topos_redis/doc/topos_redis.xml index f6a5314d825..4634eb7ebdc 100644 --- a/src/modules/topos_redis/doc/topos_redis.xml +++ b/src/modules/topos_redis/doc/topos_redis.xml @@ -23,6 +23,11 @@ Mierla miconda@gmail.com + + Frederic + Gaisnon + frederic.gaisnon@gmail.com + 2017 @@ -32,6 +37,10 @@ 2017 flowroute.com + + 2021 + MomentTech + diff --git a/src/modules/topos_redis/topos_redis_storage.c b/src/modules/topos_redis/topos_redis_storage.c index 47205c8f133..93f02da0fd3 100644 --- a/src/modules/topos_redis/topos_redis_storage.c +++ b/src/modules/topos_redis/topos_redis_storage.c @@ -265,7 +265,12 @@ int tps_redis_insert_dialog(tps_data_t *td) argvlen[argc] = rkey.len; argc++; - lval = (unsigned long)_tps_api.get_dialog_expire(); + if(td->s_method.len==9 && strncmp(td->s_method.s, "SUBSCRIBE", 9)==0) { + lval = (unsigned long)td->expires; + } else { + lval = (unsigned long)_tps_api.get_dialog_expire(); + } + if(lval==0) { return 0; } @@ -297,7 +302,7 @@ int tps_redis_clean_dialogs(void) /** * */ -int tps_redis_insert_invite_branch(tps_data_t *td) +int tps_redis_insert_initial_method_branch(tps_data_t *td) { char* argv[TPS_REDIS_NR_KEYS]; size_t argvlen[TPS_REDIS_NR_KEYS]; @@ -328,8 +333,9 @@ int tps_redis_insert_invite_branch(tps_data_t *td) rp = _tps_redis_cbuf; rkey.len = snprintf(rp, TPS_REDIS_DATA_SIZE-128, - "%.*sINVITE:%.*s:%.*s", + "%.*s%.*s:%.*s:%.*s", _tps_redis_bprefix.len, _tps_redis_bprefix.s, + td->s_method.len, td->s_method.s, td->a_callid.len, td->a_callid.s, td->b_tag.len, td->b_tag.s); if(rkey.len<0 || rkey.len>=TPS_REDIS_DATA_SIZE-128) { @@ -360,8 +366,8 @@ int tps_redis_insert_invite_branch(tps_data_t *td) } return -1; } - LM_DBG("inserting invite branch record for [%.*s] with argc %d\n", - rkey.len, rkey.s, argc); + LM_DBG("inserting %.*s branch record for [%.*s] with argc %d\n", + td->s_method.len, td->s_method.s,rkey.len, rkey.s, argc); freeReplyObject(rrpl); @@ -552,7 +558,7 @@ int tps_redis_clean_branches(void) /** * */ -int tps_redis_load_invite_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd) +int tps_redis_load_initial_method_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd) { char* argv[TPS_REDIS_NR_KEYS]; size_t argvlen[TPS_REDIS_NR_KEYS]; @@ -588,8 +594,9 @@ int tps_redis_load_invite_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd) rp = _tps_redis_cbuf; rkey.len = snprintf(rp, TPS_REDIS_DATA_SIZE, - "%.*sINVITE:%.*s:%.*s", + "%.*s%.*s:%.*s:%.*s", _tps_redis_bprefix.len, _tps_redis_bprefix.s, + md->s_method.len, md->s_method.s, md->a_callid.len, md->a_callid.s, md->b_tag.len, md->b_tag.s); if(rkey.len<0 || rkey.len>=TPS_REDIS_DATA_SIZE) { @@ -733,9 +740,9 @@ int tps_redis_load_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, /* load same transaction using Via branch */ xvbranch1 = &md->x_vbranch1; } else { - /* load corresponding INVITE transaction using call-id + to-tag */ - if(tps_redis_load_invite_branch(msg, md, &id)<0) { - LM_ERR("failed to load the INVITE branch value\n"); + /* load corresponding INVITE or SUBSCRIBE transaction using call-id + to-tag */ + if(tps_redis_load_initial_method_branch(msg, md, &id)<0) { + LM_ERR("failed to load the %.*s branch value\n", md->s_method.len, md->s_method.s); return -1; } xvbranch1 = &id.x_vbranch1; @@ -1122,11 +1129,18 @@ int tps_redis_update_branch(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, } if(md->s_method.len==6 && strncmp(md->s_method.s, "INVITE", 6)==0) { - if(tps_redis_insert_invite_branch(md)<0) { + if(tps_redis_insert_initial_method_branch(md)<0) { LM_ERR("failed to insert INVITE extra branch data\n"); return -1; } } + if(md->s_method.len==9 && strncmp(md->s_method.s, "SUBSCRIBE", 9)==0) { + if(tps_redis_insert_initial_method_branch(md)<0) { + LM_ERR("failed to insert SUBSCRIBE extra branch data\n"); + return -1; + } + } + rsrv = _tps_redis_api.get_server(&_topos_redis_serverid); if(rsrv==NULL) { LM_ERR("cannot find redis server [%.*s]\n", @@ -1209,6 +1223,7 @@ int tps_redis_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, redisc_server_t *rsrv = NULL; redisReply *rrpl = NULL; int32_t liflags; + unsigned long lval = 0; if(sd->a_uuid.len<=0 && sd->b_uuid.len<=0) { LM_INFO("no uuid for this message\n"); @@ -1297,6 +1312,11 @@ int tps_redis_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, } } + if (mode & TPS_DBU_TIME) { + lval = (unsigned long)time(NULL); + TPS_REDIS_SET_ARGN(lval, rp, &rval, argc, &td_key_rectime, argv, argvlen); + } + if(argc<=2) { return 0; } @@ -1313,6 +1333,37 @@ int tps_redis_update_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd, rkey.len, rkey.s, argc); freeReplyObject(rrpl); + if (mode & TPS_DBU_TIME) { + /* reset expire for the key */ + argc = 0; + + argv[argc] = "EXPIRE"; + argvlen[argc] = 6; + argc++; + + argv[argc] = rkey.s; + argvlen[argc] = rkey.len; + argc++; + + lval = (unsigned long)md->expires; + if(lval==0) { + return 0; + } + TPS_REDIS_SET_ARGNV(lval, rp, &rval, argc, argv, argvlen); + + rrpl = _tps_redis_api.exec_argv(rsrv, argc, (const char **)argv, argvlen); + if(rrpl==NULL) { + LM_ERR("failed to execute expire redis command\n"); + if(rsrv->ctxRedis->err) { + LM_ERR("redis error: %s\n", rsrv->ctxRedis->errstr); + } + return -1; + } + LM_DBG("expire %lu set on dialog record for [%.*s] with argc %d\n", lval, + rkey.len, rkey.s, argc); + freeReplyObject(rrpl); + } + return 0; } @@ -1333,7 +1384,7 @@ int tps_redis_end_dialog(sip_msg_t *msg, tps_data_t *md, tps_data_t *sd) int32_t liflags; unsigned long lval = 0; - if(md->s_method_id != METHOD_BYE) { + if((md->s_method_id != METHOD_BYE) && !((md->s_method_id == METHOD_SUBSCRIBE) && (md->expires == 0))) { return 0; } diff --git a/src/modules/uac/README b/src/modules/uac/README index 74b0f134424..8075773a68f 100644 --- a/src/modules/uac/README +++ b/src/modules/uac/README @@ -63,13 +63,14 @@ Ramona-Elena Modroiu 4.5. uac_replace_to(uri) 4.6. uac_restore_to() 4.7. uac_auth([mode]) - 4.8. uac_req_send() - 4.9. uac_reg_lookup(uuid, dst) - 4.10. uac_reg_status(uuid) - 4.11. uac_reg_request_to(user, mode) - 4.12. uac_reg_enable(attr, val) - 4.13. uac_reg_disable(attr, val) - 4.14. uac_reg_refresh(luuid) + 4.8. uac_auth_mode(vmode) + 4.9. uac_req_send() + 4.10. uac_reg_lookup(uuid, dst) + 4.11. uac_reg_status(uuid) + 4.12. uac_reg_request_to(user, mode) + 4.13. uac_reg_enable(attr, val) + 4.14. uac_reg_disable(attr, val) + 4.15. uac_reg_refresh(luuid) 5. Pseudo Variables 6. Event Routes @@ -123,25 +124,26 @@ Ramona-Elena Modroiu 1.27. uac_replace_to usage 1.28. uac_restore_to usage 1.29. uac_auth usage - 1.30. uac_req_send usage - 1.31. uac_reg_lookup usage - 1.32. uac_reg_status usage - 1.33. uac_reg_request_to usage - 1.34. uac_reg_enable usage - 1.35. uac_reg_disable usage - 1.36. uac_reg_refresh usage - 1.37. event_route[uac:reply] usage - 1.38. uac.reg_dump usage - 1.39. uac.reg_info usage - 1.40. uac.reg_enable usage - 1.41. uac.reg_disable usage - 1.42. uac.reg_unregister usage - 1.43. uac.reg_reload usage - 1.44. uac.reg_refresh usage - 1.45. uac.reg_active usage - 1.46. uac.reg_add usage - 1.47. uac.reg_remove usage - 1.48. lookup remote registrations usage + 1.30. uac_auth_mode usage + 1.31. uac_req_send usage + 1.32. uac_reg_lookup usage + 1.33. uac_reg_status usage + 1.34. uac_reg_request_to usage + 1.35. uac_reg_enable usage + 1.36. uac_reg_disable usage + 1.37. uac_reg_refresh usage + 1.38. event_route[uac:reply] usage + 1.39. uac.reg_dump usage + 1.40. uac.reg_info usage + 1.41. uac.reg_enable usage + 1.42. uac.reg_disable usage + 1.43. uac.reg_unregister usage + 1.44. uac.reg_reload usage + 1.45. uac.reg_refresh usage + 1.46. uac.reg_active usage + 1.47. uac.reg_add usage + 1.48. uac.reg_remove usage + 1.49. lookup remote registrations usage Chapter 1. Admin Guide @@ -187,13 +189,14 @@ Chapter 1. Admin Guide 4.5. uac_replace_to(uri) 4.6. uac_restore_to() 4.7. uac_auth([mode]) - 4.8. uac_req_send() - 4.9. uac_reg_lookup(uuid, dst) - 4.10. uac_reg_status(uuid) - 4.11. uac_reg_request_to(user, mode) - 4.12. uac_reg_enable(attr, val) - 4.13. uac_reg_disable(attr, val) - 4.14. uac_reg_refresh(luuid) + 4.8. uac_auth_mode(vmode) + 4.9. uac_req_send() + 4.10. uac_reg_lookup(uuid, dst) + 4.11. uac_reg_status(uuid) + 4.12. uac_reg_request_to(user, mode) + 4.13. uac_reg_enable(attr, val) + 4.14. uac_reg_disable(attr, val) + 4.15. uac_reg_refresh(luuid) 5. Pseudo Variables 6. Event Routes @@ -631,13 +634,14 @@ end 4.5. uac_replace_to(uri) 4.6. uac_restore_to() 4.7. uac_auth([mode]) - 4.8. uac_req_send() - 4.9. uac_reg_lookup(uuid, dst) - 4.10. uac_reg_status(uuid) - 4.11. uac_reg_request_to(user, mode) - 4.12. uac_reg_enable(attr, val) - 4.13. uac_reg_disable(attr, val) - 4.14. uac_reg_refresh(luuid) + 4.8. uac_auth_mode(vmode) + 4.9. uac_req_send() + 4.10. uac_reg_lookup(uuid, dst) + 4.11. uac_reg_status(uuid) + 4.12. uac_reg_request_to(user, mode) + 4.13. uac_reg_enable(attr, val) + 4.14. uac_reg_disable(attr, val) + 4.15. uac_reg_refresh(luuid) 4.1. uac_replace_from(display,uri) @@ -821,7 +825,50 @@ failure_route[TRUNKAUTH] { } ... -4.8. uac_req_send() +4.8. uac_auth_mode(vmode) + + This function can be called only from failure route and will build the + authentication response header and insert it into the request without + sending anything. + + If mode is set to 1, then the password has to be provided in HA1 + format. The parameter can be a static integer or a variable holding an + integer value. + + This function can be used from FAILURE_ROUTE. + + Example 1.30. uac_auth_mode usage +... +modparam("uac","auth_username_avp","$avp(auser)") +modparam("uac","auth_password_avp","$avp(apass)") +modparam("uac","auth_realm_avp","$avp(arealm)") + +request_route { + ... + if(is_method("INVITE")) { + t_on_failure("TRUNKAUTH"); + } + ... +} + +failure_route[TRUNKAUTH] { + + if (t_is_canceled()) { + exit; + } + if(t_check_status("401|407")) { + $avp(auser) = "test"; + $avp(apass) = "test"; + # $avp(apass) = "36d0a02793542b4961e8348347236dbf"; + if (uac_auth_mode("1")) { + t_relay(); + } + exit; + } +} +... + +4.9. uac_req_send() This function sends a SIP message from the configuration file. The message is built out of $uac_req(...) pseudo-variable. @@ -829,7 +876,7 @@ failure_route[TRUNKAUTH] { This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE. - Example 1.30. uac_req_send usage + Example 1.31. uac_req_send usage ... $uac_req(method)="OPTIONS"; $uac_req(ruri)="sip:kamailio.org"; @@ -839,14 +886,14 @@ $uac_req(callid)=$(mb{s.md5}); uac_req_send(); ... -4.9. uac_reg_lookup(uuid, dst) +4.10. uac_reg_lookup(uuid, dst) This function sets the PV dst to SIP URI that correspond to uuid in uac registrations table. uuid and dst must be pseudo-variables. This function can be used from ANY_ROUTE. - Example 1.31. uac_reg_lookup usage + Example 1.32. uac_reg_lookup usage ... if(uac_reg_lookup("$rU", "$ru")) @@ -855,7 +902,7 @@ if(uac_reg_lookup("$rU", "$ru")) } ... -4.10. uac_reg_status(uuid) +4.11. uac_reg_status(uuid) This function returns the current registration status for the uuid. @@ -870,12 +917,12 @@ if(uac_reg_lookup("$rU", "$ru")) This function can be used from ANY_ROUTE. - Example 1.32. uac_reg_status usage + Example 1.33. uac_reg_status usage ... $var(status) = uac_reg_status("$rU"); ... -4.11. uac_reg_request_to(user, mode) +4.12. uac_reg_request_to(user, mode) This function can be used to send an authenticated request to a remote user in the uac registrations table. It sets the request-uri, dst-uri @@ -895,7 +942,7 @@ $var(status) = uac_reg_status("$rU"); This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE. - Example 1.33. uac_reg_request_to usage + Example 1.34. uac_reg_request_to usage ... if(uac_reg_request_to("$fU", 0)) @@ -914,7 +961,7 @@ failure_route[REMOTE_AUTH] { } ... -4.12. uac_reg_enable(attr, val) +4.13. uac_reg_enable(attr, val) Enable a remote registration record based on a filter specified by attribute and value. The attribute can be: l_uuid, l_username, @@ -923,12 +970,12 @@ failure_route[REMOTE_AUTH] { The SIP processing is done on the next timer routine. - Example 1.34. uac_reg_enable usage + Example 1.35. uac_reg_enable usage ... uac_reg_enable("l_uuid", "account123"); ... -4.13. uac_reg_disable(attr, val) +4.14. uac_reg_disable(attr, val) Disable a remote registration record based on a filter specified by attribute and value. The attribute can be: l_uuid, l_username, @@ -937,18 +984,18 @@ failure_route[REMOTE_AUTH] { The SIP processing is done on the next timer routine. - Example 1.35. uac_reg_disable usage + Example 1.36. uac_reg_disable usage ... uac_reg_disable("l_uuid", "account123"); ... -4.14. uac_reg_refresh(luuid) +4.15. uac_reg_refresh(luuid) Refresh the uac remote registration record based on local uuid. If the record was already loaded, new values are taken from database, otherwise a new record is created. - Example 1.36. uac_reg_refresh usage + Example 1.37. uac_reg_refresh usage ... uac_reg_refresh("account123"); ... @@ -972,7 +1019,7 @@ failure_route[REMOTE_AUTH] { then the event_route is executed twice, first for 401/407 and second for final reply of the transaction. - Example 1.37. event_route[uac:reply] usage + Example 1.38. event_route[uac:reply] usage ... $uac_req(method)="OPTIONS"; $uac_req(ruri)="sip:kamailio.org"; @@ -1012,7 +1059,7 @@ event_route[uac:reply] { Dump the content of remote registration table from memory. - Example 1.38. uac.reg_dump usage + Example 1.39. uac.reg_dump usage ... kamcmd uac.reg_dump ... @@ -1033,7 +1080,7 @@ event_route[uac:reply] { * 16 (2^4) - registration initialized (after loading from database, the registration process was initialized) - Example 1.39. uac.reg_info usage + Example 1.40. uac.reg_info usage ... kamcmd uac.reg_info l_uuid account123 kamcmd uac.reg_info l_uuid s:12345678 @@ -1047,7 +1094,7 @@ event_route[uac:reply] { matched against the value of the attribute in the remote registration record. - Example 1.40. uac.reg_enable usage + Example 1.41. uac.reg_enable usage ... kamcmd uac.reg_enable l_uuid account123 kamcmd uac.reg_enable l_uuid s:12345678 @@ -1061,7 +1108,7 @@ event_route[uac:reply] { matched against the value of the attribute in the remote registration record. - Example 1.41. uac.reg_disable usage + Example 1.42. uac.reg_disable usage ... kamcmd uac.reg_disable l_uuid account123 kamcmd uac.reg_disable l_uuid s:12345678 @@ -1075,7 +1122,7 @@ event_route[uac:reply] { should be matched against the value of the attribute in the remote registration record. - Example 1.42. uac.reg_unregister usage + Example 1.43. uac.reg_unregister usage ... kamcmd uac.reg_unregister l_uuid account123 kamcmd uac.reg_unregister l_uuid s:12345678 @@ -1088,7 +1135,7 @@ event_route[uac:reply] { 150 seconds between reloads -- see the reg_gc_interval parameter for more details. - Example 1.43. uac.reg_reload usage + Example 1.44. uac.reg_reload usage ... kamcmd uac.reg_reload ... @@ -1099,7 +1146,7 @@ event_route[uac:reply] { the record exists in memory, it will be replaced with the new values loaded from database. - Example 1.44. uac.reg_refresh usage + Example 1.45. uac.reg_refresh usage ... kamcmd uac.reg_refresh account123 kamcmd uac.reg_refresh s:12345678 @@ -1111,7 +1158,7 @@ event_route[uac:reply] { 1 enables remote registrations for all records and 0 disables doing them. - Example 1.45. uac.reg_active usage + Example 1.46. uac.reg_active usage ... kamctl rpc uac.reg_active 0 kamctl rpc uac.reg_active 1 @@ -1142,7 +1189,7 @@ event_route[uac:reply] { Use a dot (.) if no value should be set for auth_password, auth_ha1, or contact_addr. - Example 1.46. uac.reg_add usage + Example 1.47. uac.reg_add usage ... kamcmd uac.reg_add ... ... @@ -1151,7 +1198,7 @@ event_route[uac:reply] { Remove a UAC remote registration record by l_uuid. - Example 1.47. uac.reg_remove usage + Example 1.48. uac.reg_remove usage ... kamcmd uac.reg_remove my_l_uuid ... @@ -1207,7 +1254,7 @@ event_route[uac:reply] { if the call is coming from a remote SIP provider and can change the R-URI to local username@domain. Afterwards you can run location lookup. - Example 1.48. lookup remote registrations usage + Example 1.49. lookup remote registrations usage ... if(uac_reg_lookup("$rU", "$ru")) { xlog("request from a remote SIP provider [$ou => $ru]\n"); diff --git a/src/modules/uac/doc/uac_admin.xml b/src/modules/uac/doc/uac_admin.xml index a738c84b819..c8271974a36 100644 --- a/src/modules/uac/doc/uac_admin.xml +++ b/src/modules/uac/doc/uac_admin.xml @@ -884,6 +884,57 @@ failure_route[TRUNKAUTH] { exit; } } +... + + + +
+ + <function moreinfo="none">uac_auth_mode(vmode)</function> + + + This function can be called only from failure route and will + build the authentication response header and insert it into the + request without sending anything. + + + If mode is set to 1, then the password has to be provided in HA1 format. + The parameter can be a static integer or a variable holding an integer value. + + + This function can be used from FAILURE_ROUTE. + + + <function>uac_auth_mode</function> usage + +... +modparam("uac","auth_username_avp","$avp(auser)") +modparam("uac","auth_password_avp","$avp(apass)") +modparam("uac","auth_realm_avp","$avp(arealm)") + +request_route { + ... + if(is_method("INVITE")) { + t_on_failure("TRUNKAUTH"); + } + ... +} + +failure_route[TRUNKAUTH] { + + if (t_is_canceled()) { + exit; + } + if(t_check_status("401|407")) { + $avp(auser) = "test"; + $avp(apass) = "test"; + # $avp(apass) = "36d0a02793542b4961e8348347236dbf"; + if (uac_auth_mode("1")) { + t_relay(); + } + exit; + } +} ... diff --git a/src/modules/uac/uac.c b/src/modules/uac/uac.c index 0988669f67a..486fed28949 100644 --- a/src/modules/uac/uac.c +++ b/src/modules/uac/uac.c @@ -135,6 +135,8 @@ static cmd_export_t cmds[]={ REQUEST_ROUTE | BRANCH_ROUTE }, {"uac_restore_to", (cmd_function)w_restore_to, 0, 0, 0, REQUEST_ROUTE }, {"uac_auth", (cmd_function)w_uac_auth, 0, 0, 0, FAILURE_ROUTE }, + {"uac_auth", (cmd_function)w_uac_auth_mode, 1, + fixup_igp_null, fixup_free_igp_null, FAILURE_ROUTE }, {"uac_auth_mode", (cmd_function)w_uac_auth_mode, 1, fixup_igp_null, fixup_free_igp_null, FAILURE_ROUTE }, {"uac_req_send", (cmd_function)w_uac_req_send, 0, 0, 0, ANY_ROUTE}, diff --git a/src/modules/uac_redirect/README b/src/modules/uac_redirect/README index 6ca8d1d451c..88e9e22859f 100644 --- a/src/modules/uac_redirect/README +++ b/src/modules/uac_redirect/README @@ -31,6 +31,7 @@ Bogdan-Andrei Iancu 4.5. acc_db_table (string) 4.6. bflags (int) 4.7. flags_hdr_mode (int) + 4.8. q_value (int) 5. Functions @@ -50,11 +51,12 @@ Bogdan-Andrei Iancu 1.5. Set acc_db_table parameter 1.6. Set bflags module parameter 1.7. Set flags_hdr_mode parameter - 1.8. set_deny_filter usage - 1.9. set_accept_filter usage - 1.10. get_redirects usage + 1.8. Set q_value parameter + 1.9. set_deny_filter usage + 1.10. set_accept_filter usage 1.11. get_redirects usage - 1.12. Redirection script example + 1.12. get_redirects usage + 1.13. Redirection script example Chapter 1. Admin Guide @@ -76,6 +78,7 @@ Chapter 1. Admin Guide 4.5. acc_db_table (string) 4.6. bflags (int) 4.7. flags_hdr_mode (int) + 4.8. q_value (int) 5. Functions @@ -149,6 +152,7 @@ Chapter 1. Admin Guide 4.5. acc_db_table (string) 4.6. bflags (int) 4.7. flags_hdr_mode (int) + 4.8. q_value (int) 4.1. default_filter (string) @@ -273,6 +277,20 @@ branch_route[1] { modparam("uac_redirect","flags_hdr_mode",2) ... +4.8. q_value (int) + + Specifies the q-value to asign to contacts without one. Because + Kamailio doesn't support float parameter types, the value in the + parameter is divided by 1000 and stored as float. For example, if you + want q value to be 0.38, use value 380 here. + + The default value is 10 (0.01). + + Example 1.8. Set q_value parameter +... +modparam("uac_redirect","q_value",0) +... + 5. Functions 5.1. set_deny_filter(filter,flags) @@ -295,7 +313,7 @@ modparam("uac_redirect","flags_hdr_mode",2) This function can be used from FAILURE_ROUTE. - Example 1.8. set_deny_filter usage + Example 1.9. set_deny_filter usage ... set_deny_filter(".*@domain2.net","reset_all"); set_deny_filter(".*@domain1.net",""); @@ -316,7 +334,7 @@ set_deny_filter(".*@domain1.net",""); This function can be used from FAILURE_ROUTE. - Example 1.9. set_accept_filter usage + Example 1.10. set_accept_filter usage ... set_accept_filter(".*@domain2.net","reset_added"); set_accept_filter(".*@domain1.net",""); @@ -345,7 +363,7 @@ set_accept_filter(".*@domain1.net",""); This function can be used from FAILURE_ROUTE. - Example 1.10. get_redirects usage + Example 1.11. get_redirects usage ... # max 2 contacts per branch, but no overall limit get_redirects("*:2"); @@ -369,14 +387,14 @@ get_redirects("*"); This function can be used from FAILURE_ROUTE. - Example 1.11. get_redirects usage + Example 1.12. get_redirects usage ... get_redirects("4:1","Redirected"); ... 6. Script Example - Example 1.12. Redirection script example + Example 1.13. Redirection script example loadmodule "modules/sl/sl.so" loadmodule "modules/usrloc/usrloc.so" loadmodule "modules/registrar/registrar.so" diff --git a/src/modules/uac_redirect/doc/uac_redirect_admin.xml b/src/modules/uac_redirect/doc/uac_redirect_admin.xml index b044d38c250..eb66416d059 100644 --- a/src/modules/uac_redirect/doc/uac_redirect_admin.xml +++ b/src/modules/uac_redirect/doc/uac_redirect_admin.xml @@ -337,6 +337,28 @@ branch_route[1] { ... modparam("uac_redirect","flags_hdr_mode",2) +... + + +
+
+ <varname>q_value</varname> (int) + + Specifies the q-value to asign to contacts without one. Because + Kamailio doesn't support float parameter types, the value in the + parameter is divided by 1000 and stored as float. For example, if + you want q value to be 0.38, use value 380 here. + + + + The default value is 10 (0.01). + + + + Set <varname>q_value</varname> parameter + +... +modparam("uac_redirect","q_value",0) ... diff --git a/src/modules/uac_redirect/rd_funcs.c b/src/modules/uac_redirect/rd_funcs.c index 5b105d1011c..7d8e7fd1d49 100644 --- a/src/modules/uac_redirect/rd_funcs.c +++ b/src/modules/uac_redirect/rd_funcs.c @@ -33,9 +33,9 @@ extern sruid_t _redirect_sruid; +extern int _redirect_q_value; #define MAX_CONTACTS_PER_REPLY 16 -#define DEFAULT_Q_VALUE 10 static int shmcontact2dset(struct sip_msg *req, struct sip_msg *shrpl, long max, struct acc_param *reason, unsigned int bflags); @@ -147,7 +147,7 @@ static int sort_contacts(hdr_field_t *chdr, contact_t **ct_array, /* does the contact has a q val? */ q_para = ct_list->q; if (q_para==0 || q_para->body.len==0) { - q = DEFAULT_Q_VALUE; + q = _redirect_q_value; } else { if (str2q( &q, q_para->body.s, q_para->body.len)!=0) { LM_ERR("invalid q param\n"); diff --git a/src/modules/uac_redirect/uac_redirect.c b/src/modules/uac_redirect/uac_redirect.c index 57ec038b391..2c4802beabb 100644 --- a/src/modules/uac_redirect/uac_redirect.c +++ b/src/modules/uac_redirect/uac_redirect.c @@ -52,6 +52,9 @@ int flags_hdr_mode = 0; #define ACCEPT_RULE_STR "accept" #define DENY_RULE_STR "deny" +#define DEFAULT_Q_VALUE 10 + +int _redirect_q_value = DEFAULT_Q_VALUE; /* sruid to get internal uid */ sruid_t _redirect_sruid; @@ -88,6 +91,7 @@ static param_export_t params[] = { {"acc_db_table", PARAM_STRING, &acc_db_table }, {"bflags", INT_PARAM, &bflags }, {"flags_hdr_mode", INT_PARAM, &flags_hdr_mode }, + {"q_value", INT_PARAM, &_redirect_q_value }, {0, 0, 0} }; diff --git a/src/modules/usrloc/README b/src/modules/usrloc/README index d075042834c..bcabbe4db22 100644 --- a/src/modules/usrloc/README +++ b/src/modules/usrloc/README @@ -97,6 +97,7 @@ Carsten Bock 3.55. ka_timeout (int) 3.56. ka_loglevel (int) 3.57. ka_logmsg (str) + 3.58. load_rank (int) 4. RPC Commands @@ -201,6 +202,7 @@ Carsten Bock 1.55. Set ka_timeout parameter 1.56. ka_loglevel parameter usage 1.57. ka_logmsg parameter usage + 1.58. load_rank parameter usage Chapter 1. Admin Guide @@ -274,6 +276,7 @@ Chapter 1. Admin Guide 3.55. ka_timeout (int) 3.56. ka_loglevel (int) 3.57. ka_logmsg (str) + 3.58. load_rank (int) 4. RPC Commands @@ -410,6 +413,7 @@ Chapter 1. Admin Guide 3.55. ka_timeout (int) 3.56. ka_loglevel (int) 3.57. ka_logmsg (str) + 3.58. load_rank (int) 3.1. nat_bflag (int) @@ -1215,6 +1219,18 @@ modparam("usrloc", "ka_loglevel", 1) modparam("usrloc", "ka_logmsg", " to-uri: [$tu] remote-addr: [$sas]") ... +3.58. load_rank (int) + + Allows to set the rank of the child SIP worker to load the location + records. + + Default value is “1” (PROC_SIPINIT). + + Example 1.58. load_rank parameter usage +... +modparam("usrloc", "load_rank", 1) +... + 4. RPC Commands 4.1. ul.dump diff --git a/src/modules/usrloc/doc/usrloc_admin.xml b/src/modules/usrloc/doc/usrloc_admin.xml index 4b624fb81f6..25124abcc17 100644 --- a/src/modules/usrloc/doc/usrloc_admin.xml +++ b/src/modules/usrloc/doc/usrloc_admin.xml @@ -1498,6 +1498,25 @@ modparam("usrloc", "ka_logmsg", " to-uri: [$tu] remote-addr: [$sas]")
+
+ <varname>load_rank</varname> (int) + + Allows to set the rank of the child SIP worker to load the location + records. + + + Default value is 1 (PROC_SIPINIT). + + + <varname>load_rank</varname> parameter usage + +... +modparam("usrloc", "load_rank", 1) +... + + +
+
diff --git a/src/modules/usrloc/ul_rpc.c b/src/modules/usrloc/ul_rpc.c index 63c45381b5f..d45a8e2783c 100644 --- a/src/modules/usrloc/ul_rpc.c +++ b/src/modules/usrloc/ul_rpc.c @@ -402,7 +402,7 @@ static void ul_rpc_lookup(rpc_t* rpc, void* ctx) /* look for table */ dom = rpc_find_domain( &table ); if (dom == NULL) { - rpc->fault(ctx, 500, "Domain not found"); + rpc->fault(ctx, 500, "Domain table not found"); return; } @@ -476,7 +476,7 @@ static void ul_rpc_rm_aor(rpc_t* rpc, void* ctx) /* look for table */ dom = rpc_find_domain( &table ); if (dom == NULL) { - rpc->fault(ctx, 500, "Domain not found"); + rpc->fault(ctx, 500, "Domain table not found"); return; } @@ -520,7 +520,7 @@ static void ul_rpc_rm_contact(rpc_t* rpc, void* ctx) /* look for table */ dom = rpc_find_domain( &table ); if (dom == NULL) { - rpc->fault(ctx, 500, "Domain not found"); + rpc->fault(ctx, 500, "Domain table not found"); return; } @@ -661,7 +661,7 @@ static void ul_rpc_add(rpc_t* rpc, void* ctx) /* look for table */ dom = rpc_find_domain( &table ); if (dom == NULL) { - rpc->fault(ctx, 500, "Domain not found"); + rpc->fault(ctx, 500, "Domain table not found"); return; } diff --git a/src/modules/usrloc/usrloc_mod.c b/src/modules/usrloc/usrloc_mod.c index 860fb715b54..733c16a3ff7 100644 --- a/src/modules/usrloc/usrloc_mod.c +++ b/src/modules/usrloc/usrloc_mod.c @@ -116,6 +116,7 @@ int ul_db_raw_fetch_type = 0; int ul_rm_expired_delay = 0; int ul_version_table = 1; +int ul_load_rank = PROC_SIPINIT; str ul_xavp_contact_name = {0}; str ul_ka_from = str_init("sip:server@kamailio.org"); @@ -261,6 +262,7 @@ static param_export_t params[] = { {"ka_timeout", PARAM_INT, &ul_keepalive_timeout}, {"ka_loglevel", PARAM_INT, &ul_ka_loglevel}, {"ka_logmsg", PARAM_STR, &ul_ka_logmsg}, + {"load_rank", PARAM_INT, &ul_load_rank}, {0, 0, 0} }; @@ -472,7 +474,7 @@ static int child_init(int _rank) return -1; } /* _rank==PROC_SIPINIT is used even when fork is disabled */ - if (_rank==PROC_SIPINIT && ul_db_mode!=DB_ONLY && ul_db_load) { + if (_rank==ul_load_rank && ul_db_mode!=DB_ONLY && ul_db_load) { /* if cache is used, populate domains from DB */ for(ptr=_ksr_ul_root ; ptr ; ptr=ptr->next) { if (preload_udomain(ul_dbh, ptr->d) < 0) { diff --git a/src/modules/xmlrpc/xmlrpc.c b/src/modules/xmlrpc/xmlrpc.c index dd64cf06f90..266283b963e 100644 --- a/src/modules/xmlrpc/xmlrpc.c +++ b/src/modules/xmlrpc/xmlrpc.c @@ -264,6 +264,8 @@ static str member_prefix = STR_STATIC_INIT(""); static str member_suffix = STR_STATIC_INIT(""); static str name_prefix = STR_STATIC_INIT(""); static str name_suffix = STR_STATIC_INIT(""); +static str empty_value = STR_STATIC_INIT(""); +static str nil_value = STR_STATIC_INIT(""); /** Garbage collection data structure. * @@ -1066,17 +1068,29 @@ static int print_value(struct xmlrpc_reply* res, break; case 's': - prefix = string_prefix; - suffix = string_suffix; body.s = va_arg(*ap, char*); - body.len = strlen(body.s); + if(body.s!=NULL) { + prefix = string_prefix; + suffix = string_suffix; + body.len = strlen(body.s); + } else { + prefix = empty_value; + suffix = empty_value; + body = nil_value; + } break; case 'S': - prefix = string_prefix; - suffix = string_suffix; sp = va_arg(*ap, str*); - body = *sp; + if(sp!=NULL && sp->s!=NULL) { + prefix = string_prefix; + suffix = string_suffix; + body = *sp; + } else { + prefix = empty_value; + suffix = empty_value; + body = nil_value; + } break; default: