diff --git a/kamailio/accounting-role.cfg b/kamailio/accounting-role.cfg index 7c58cb5..13b866d 100644 --- a/kamailio/accounting-role.cfg +++ b/kamailio/accounting-role.cfg @@ -1,8 +1,7 @@ ####### Flags ####### -flags - FLAG_ACC: 8, - FLAG_ACCMISSED: 9, - FLAG_ACCFAILED: 10; +#!trydef FLAG_ACC 13 +#!trydef FLAG_ACCMISSED 14 +#!trydef FLAG_ACCFAILED 15 ######## Accounting module ######## loadmodule "acc.so" diff --git a/kamailio/acl-role.cfg b/kamailio/acl-role.cfg index 664298d..f4fc1df 100644 --- a/kamailio/acl-role.cfg +++ b/kamailio/acl-role.cfg @@ -265,3 +265,9 @@ event_route[kazoo:consumer-event-acl-acl-flush] } } + +route[ACL_BINDINGS] +{ + $var(payload) = $_s({"name": "acl-role", "exchange" : "frontier_acl" , "type" : "topic", "queue" : "FRONTIERACL-FLUSH-MY_HOSTNAME", "routing" : "flush" }); + kazoo_subscribe("$var(payload)"); +} \ No newline at end of file diff --git a/kamailio/antiflood-role.cfg b/kamailio/antiflood-role.cfg index 342a555..3a98318 100644 --- a/kamailio/antiflood-role.cfg +++ b/kamailio/antiflood-role.cfg @@ -4,9 +4,17 @@ #!trydef ANTIFLOOD_RATE_EXPIRE 4 #!trydef ANTIFLOOD_FAILED_AUTH_WINDOW 300 #!trydef ANTIFLOOD_FAILED_AUTH_DENSITY 4 +#!trydef ANTIFLOOD_FAILED_AUTH_USE_PORT 1 +#!trydef ANTIFLOOD_FAILED_AUTH_ACTION 2 +#!trydef ANTIFLOOD_RATE_LIMIT_ENABLED 1 +#!trydef ANTIFLOOD_AUTH_LIMIT_ENABLED 1 +#!trydef ANTIFLOOD_RATE_DROP 1 +#!trydef ANTIFLOOD_CACHE_PERIOD 300 + +#!substdef "!ANTIFLOOD_SUBST_CACHE_PERIOD!$def(ANTIFLOOD_CACHE_PERIOD)!g" ######## Flood Prevention Hash Tables ######## -modparam("htable", "htable", "antiflood=>size=16;autoexpire=ANTIFLOOD_CACHE_PERIOD;initval=0") +modparam("htable", "htable", "antiflood=>size=16;autoexpire=ANTIFLOOD_SUBST_CACHE_PERIOD;initval=0") ######## Flood Prevention Module ######## loadmodule "pike.so" @@ -14,6 +22,21 @@ modparam("pike", "sampling_time_unit", ANTIFLOOD_RATE_WINDOW) modparam("pike", "reqs_density_per_unit", ANTIFLOOD_RATE_DENSITY) modparam("pike", "remove_latency", ANTIFLOOD_RATE_EXPIRE) +kazoo.antiflood_failed_auth_use_port = ANTIFLOOD_FAILED_AUTH_USE_PORT descr "should we keep track of ip and port for auth failures" +kazoo.antiflood_failed_auth_action = ANTIFLOOD_FAILED_AUTH_ACTION descr "0 - log, 1 - drop, 2 - reply with 403" +kazoo.antiflood_rate_limit_enabled = ANTIFLOOD_RATE_LIMIT_ENABLED descr "antiflood rate limit enabled" +kazoo.antiflood_auth_limit_enabled = ANTIFLOOD_AUTH_LIMIT_ENABLED descr "antiflood auth limit enabled" +kazoo.antiflood_rate_drop = ANTIFLOOD_RATE_DROP descr "should we drop on rate limit" + +route[ANTIFLOOD_LIMIT] +{ + if($sel(cfg_get.kazoo.antiflood_rate_limit_enabled) == 1) { + route(ANTIFLOOD_RATE_LIMIT); + } + if($sel(cfg_get.kazoo.antiflood_auth_limit_enabled) == 1) { + route(ANTIFLOOD_AUTH_LIMIT); + } +} route[ANTIFLOOD_RATE_LIMIT] { @@ -25,13 +48,16 @@ route[ANTIFLOOD_RATE_LIMIT] # use pike to control the rates if (!pike_check_req()) { - xlog("L_WARN", "$ci|end|dropping request from $fu due to rate of requests with source $si:$sp\n"); - drop(); - exit; + if($sel(cfg_get.kazoo.antiflood_rate_drop) == 1) { + xlog("L_WARN", "$ci|end|dropping request from $fu due to rate of requests with source $si:$sp\n"); + drop(); + } else { + xlog("L_WARN", "$ci|allowed|request from $fu exceeded rate of requests with source $si:$sp\n"); + } } } -route[ANITFLOOD_AUTH_LIMIT] +route[ANTIFLOOD_AUTH_LIMIT] { if (has_totag() || isflagset(FLAG_TRUSTED_SOURCE) @@ -39,21 +65,38 @@ route[ANITFLOOD_AUTH_LIMIT] return(1); } - if ($Au != $null && - $sht(antiflood=>$Au::$si::count) >= ANTIFLOOD_FAILED_AUTH_DENSITY + $var(auth_key) = ""; + if($sel(cfg_get.kazoo.antiflood_failed_auth_use_port) == 1) { + $var(auth_key) = $_s("$Au::$si::$sp"); + } else { + $var(auth_key) = $_s("$Au::$si"); + } + if ($Au != $null && + $sht(antiflood=>$var(auth_key)::count) >= ANTIFLOOD_FAILED_AUTH_DENSITY ) { - xlog("L_NOTICE", "$ci|end|request at authorization failure limit for $Au $si:$sp\n"); - $shtex(antiflood=>$Au::$si::count) = ANTIFLOOD_FAILED_AUTH_WINDOW; - $sht(antiflood=>$Au::$si::last) = $Ts; - append_to_reply("Retry-After: 3600\r\n"); - send_reply("500", "Retry Later"); - exit; + $shtex(antiflood=>$var(auth_key)::count) = ANTIFLOOD_FAILED_AUTH_WINDOW; + $sht(antiflood=>$var(auth_key)::last) = $Ts; + if($sel(cfg_get.kazoo.antiflood_failed_auth_action) == 1) { + xlog("L_WARNING", "$ci|end|dropping request authorization failure limit $def(ANTIFLOOD_FAILED_AUTH_DENSITY) for $Au $si:$sp\n"); + drop(); + } else if($sel(cfg_get.kazoo.antiflood_failed_auth_action) == 2) { + xlog("L_NOTICE", "$ci|end|authorization failure limit $def(ANTIFLOOD_FAILED_AUTH_DENSITY) for $Au $si:$sp\n"); + append_to_reply("Retry-After: 3600\r\n"); + send_reply("403", "Forbidden"); + exit; + } else { + xlog("L_NOTICE", "$ci|log|authorization failure limit $def(ANTIFLOOD_FAILED_AUTH_DENSITY) for $Au $si:$sp\n"); + } } } route[ANTIFLOOD_SUCCESSFUL_AUTH] { - sht_rm_name_re("antiflood=>$(Au{re.subst,/\\./\\\\./g})::$(si{re.subst,/\\./\\\\./g})::.*"); + if($sel(cfg_get.kazoo.antiflood_failed_auth_use_port) == 1) { + sht_rm_name_re("antiflood=>$(Au{re.subst,/\\./\\\\./g})::$(si{re.subst,/\\./\\\\./g})::$sp::.*"); + } else { + sht_rm_name_re("antiflood=>$(Au{re.subst,/\\./\\\\./g})::$(si{re.subst,/\\./\\\\./g})::.*"); + } } route[ANTIFLOOD_RESET_AUTH] @@ -68,23 +111,30 @@ route[ANITFLOOD_FAILED_AUTH] return; } - $var(count) = $shtinc(antiflood=>$Au::$si::count); - $sht(antiflood=>$Au::$si::last) = $Ts; + $var(auth_key) = ""; + if($sel(cfg_get.kazoo.antiflood_failed_auth_use_port) == 1) { + $var(auth_key) = $_s("$Au::$si::$sp"); + } else { + $var(auth_key) = $_s("$Au::$si"); + } + + $var(count) = $shtinc(antiflood=>$var(auth_key)::count); + $sht(antiflood=>$var(auth_key)::last) = $Ts; xlog("L_INFO", "$ci|log|$var(count) errounous authorization response for $Au $si:$sp\n"); - if ($var(count) >= ANTIFLOOD_FAILED_AUTH_DENSITY) { + if ($var(count) >= ANTIFLOOD_FAILED_AUTH_DENSITY) { $var(exp) = $Ts - ANTIFLOOD_FAILED_AUTH_WINDOW; - if($sht(antiflood=>$Au::$si::last) > $var(exp)){ - xlog("L_NOTICE", "$ci|end|request at authorization failure limit for $Au $si:$sp\n"); + if($sht(antiflood=>$var(auth_key)::last) > $var(exp)) { + xlog("L_NOTICE", "$ci|end|request at authorization failure limit $def(ANTIFLOOD_FAILED_AUTH_DENSITY) for $Au $si:$sp\n"); append_to_reply("Retry-After: 3600\r\n"); - send_reply("500", "Retry Later"); + send_reply("403", "Forbidden"); exit; } - } + } } -event_route[htable:expired:antiflood] +event_route[htable:expired:antiflood] { xlog("L_NOTICE", "antiflood expired record $shtrecord(key) => $shtrecord(value)\n"); } diff --git a/kamailio/auth.cfg b/kamailio/auth.cfg new file mode 100644 index 0000000..aca5bec --- /dev/null +++ b/kamailio/auth.cfg @@ -0,0 +1,105 @@ + + +route[AUTH] +{ + if (!is_method("INVITE|MESSAGE|REFER")) { + return; + } + + #!ifdef DISPATCHER_ROLE + if (!isflagset(FLAG_INTERNALLY_SOURCED)) { + route(SETUP_AUTH_HEADERS); + } + #!endif +} + +route[AUTH_HEADERS] +{ + remove_hf_re("^X-"); + + xavp_params_implode("hf", "$var(outx)"); + $var(out) = $(var(outx){re.subst,/^(.*);$$/\1/}); + $var(c) = $(var(out){param.count}); + xlog("L_DEBUG", "$ci|auth|headers $var(c) => $var(out) => $var(outx)\n"); + while($var(c) > 0) { + $var(idx) = $var(c) - 1; + xlog("L_DEBUG", "$ci|auth|adding $(var(out){param.name,$var(idx)}): $(var(out){param.valueat,$var(idx)}{s.unescape.param})\n"); + append_hf("$(var(out){param.name,$var(idx)}): $(var(out){param.valueat,$var(idx)}{s.unescape.param})\r\n"); + $var(c) = $var(c) - 1; + } + +} + +route[SETUP_AUTH_HEADERS] +{ + + $xavp(hf=>X-AUTH-IP) = $si; + $xavp(hf[0]=>X-AUTH-PORT) = $sp; + + #!ifdef REGISTRAR_ROLE + #!ifdef WITH_AUTH_TOKEN + route(AUTH_TOKEN); + #!else + route(AUTH_CCVS); + #!endif + #!endif + +} + +#!ifdef REGISTRAR_ROLE + +route[AUTH_TOKEN] +{ + $xavp(regcfg=>match_received) = $su; + if (registered("location","$fu", 2, 1) == 1) { + if($(xavp(ulattrs=>token){s.len}) > 0) { + $xavp(hf[0]=>X-AUTH-Token) = $xavp(ulattrs=>token); + } else { + if($(xavp(ulattrs=>Authorizing-ID){s.len}) > 0 && $(xavp(ulattrs=>Account-ID){s.len})) { + $xavp(hf[0]=>X-AUTH-Token) = $_s($(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-ID})@$(xavp(ulattrs=>custom_channel_vars){kz.json,Account-ID})); + } + } + } +} + +route[AUTH_CCVS] +{ + $xavp(regcfg=>match_received) = $su; + if (registered("location","$fu", 2, 1) != 1) return; + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Account-ID}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Account-ID) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Account-ID}); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-Type}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Authorizing-Type) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-Type}); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Account-ID}{s.len}) > 0 && $(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-Type}{s.len}) > 0) + $xavp(hf[0]=>X-AUTH-Token) = $_s($(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-ID})@$(xavp(ulattrs=>custom_channel_vars){kz.json,Account-ID})); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-ID}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Authorizing-ID) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-ID}); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Username}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Username) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Username}); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Realm}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Realm) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Realm}); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Account-Realm}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Account-Realm) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Account-Realm}); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Account-Name}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Account-Name) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Account-Name}{s.escape.param}); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Presence-ID}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Presence-ID) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Presence-ID}); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Owner-ID}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Owner-ID) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Owner-ID}); + + if($(xavp(ulattrs=>custom_channel_vars){kz.json,Hotdesk-Current-ID}{s.len}) > 0) + $xavp(hf[0]=>X-ecallmgr_Hotdesk-Current-ID) = $(xavp(ulattrs=>custom_channel_vars){kz.json,Hotdesk-Current-ID}); + +} + +#!endif diff --git a/kamailio/authorization-role.cfg b/kamailio/authorization-role.cfg new file mode 100644 index 0000000..5b55be2 --- /dev/null +++ b/kamailio/authorization-role.cfg @@ -0,0 +1,23 @@ +## to be enhanced + +route[AUTHORIZATION_CHECK] +{ + if (!is_method("MESSAGE|NOTIFY|SUBSCRIBE|PUBLISH")) + return; + + if(has_totag()) + return; + + if (isflagset(FLAG_INTERNALLY_SOURCED)) + return; + + if (isflagset(FLAG_TRUSTED_SOURCE)) + return; + + $xavp(regcfg=>match_received) = $su; + if(!(registered("location", "$fu", 2, 1) == 1 && $(xavp(ulattrs=>custom_channel_vars){s.len}) > 1)) { + xlog("L_INFO", "$ci|log|not authorized $fu from $si:$sp\n"); + send_reply("503", "Not Registered"); + exit; + } +} diff --git a/kamailio/blocker-role.cfg b/kamailio/blocker-role.cfg new file mode 100644 index 0000000..8da3adf --- /dev/null +++ b/kamailio/blocker-role.cfg @@ -0,0 +1,39 @@ +######## BLOCK BY IP[PORT] ######## + +#!trydef KZ_BLOCK_ENABLE 1 +#!trydef KZ_BLOCK_LOG_LEVEL 1 +#!trydef KZ_BLOCK_LOG_BUFFER 0 +#!trydef KZ_BLOCK_DRY_RUN 0 + +#!ifdef KZ_BLOCK_COLD_CACHE +#!substdef "!BLOCK_S_WARM_CACHE!!g" +#!else +#!substdef "!BLOCK_S_WARM_CACHE!dbtable=block_cache;dbmode=1;!g" +#!endif + +modparam("htable", "htable", "block=>size=8;BLOCK_S_WARM_CACHE") +modparam("statistics","variable", "block:blocked_requests") + +kazoo.block_enable = KZ_BLOCK_ENABLE descr "enable block processing" +kazoo.block_log_level = KZ_BLOCK_LOG_LEVEL descr "block log level" +kazoo.block_log_buffer = KZ_BLOCK_LOG_BUFFER descr "log the received buffer" +kazoo.block_dry_run = KZ_BLOCK_DRY_RUN descr "log but keep processing" + +## global param to enable route +received_route_mode=1 + +event_route[core:msg-received] +{ + if($sel(cfg_get.kazoo.block_enable) == 1) { + if($sht(block=>$rcv(srcip)) || $sht(block=>$rcv(srcip)::$rcv(srcport))) { + if($sel(cfg_get.kazoo.block_log_buffer) == 1) { + xlog("$(sel(cfg_get.kazoo.block_log_level){s.int})", "|block|request from [$rcv(srcip):$rcv(srcport)] to [$rcv(rcvip):$rcv(rcvport)] was blocked => [$rcv(buf)]\n"); + } else { + xlog("$(sel(cfg_get.kazoo.block_log_level){s.int})", "|block|request from [$rcv(srcip):$rcv(srcport)] to [$rcv(rcvip):$rcv(rcvport)] was blocked\n"); + } + if($sel(cfg_get.kazoo.block_dry_run) == 0) { + drop; + } + } + } +} diff --git a/kamailio/db_kazoo.cfg b/kamailio/db_kazoo.cfg new file mode 100644 index 0000000..4989a32 --- /dev/null +++ b/kamailio/db_kazoo.cfg @@ -0,0 +1,14 @@ +#### db_kazoo module ### + +#!trydef KZ_DB_HOOK_TRACE 1 +#!trydef KZ_DB_TRACE 0 +#!trydef KZ_DB_TRACE_LOG_LEVEL 3 +#!trydef KZ_DB_TRACE_FILTER 110 + +loadmodule "db_kazoo.so" +modparam("db_kazoo", "trace_hook", KZ_DB_HOOK_TRACE) +modparam("db_kazoo", "trace_enable", KZ_DB_TRACE) +modparam("db_kazoo", "trace_log_level", KZ_DB_TRACE_LOG_LEVEL) +modparam("db_kazoo", "trace_filter", KZ_DB_TRACE_FILTER) + +include_file "db_queries_kazoo.cfg" diff --git a/kamailio/db_mysql.cfg b/kamailio/db_mysql.cfg new file mode 100644 index 0000000..9000b57 --- /dev/null +++ b/kamailio/db_mysql.cfg @@ -0,0 +1,4 @@ +#### db_mysql module ### +loadmodule "db_mysql.so" + +include_file "db_queries_mysql.cfg" diff --git a/kamailio/db_postgres.cfg b/kamailio/db_postgres.cfg new file mode 100644 index 0000000..51dbd44 --- /dev/null +++ b/kamailio/db_postgres.cfg @@ -0,0 +1,4 @@ +#### db_postgres module ### +loadmodule "db_postgres.so" + +include_file "db_queries_postgres.cfg" diff --git a/kamailio/db_queries_kazoo.cfg b/kamailio/db_queries_kazoo.cfg index b7e542d..f3de127 100644 --- a/kamailio/db_queries_kazoo.cfg +++ b/kamailio/db_queries_kazoo.cfg @@ -1,15 +1,27 @@ ####### Database queries ######## -#!substdef "!KZQ_CHECK_MEDIA_SERVER_INSERT!insert into dispatcher (setid, destination) select \$var(SetId), \"\$var(MediaUrl)\" where not exists(select * from dispatcher where destination = \"\$var(MediaUrl)\")!g" -#!substdef "!KZQ_COUNT_ALL_SUBSCRIBERS!select a.event, count(distinct watcher_username || \"@\" || watcher_domain) count_unique, count(*) count from event_list a, active_watchers b where b.event = a.event group by a.event!g" +#!substdef "!KZQ_CHECK_MEDIA_SERVER_INSERT!insert into dispatcher (setid, destination, flags, attrs, description) select \$var(SetId), \"\$var(MediaUrl)\", \$var(flags), \"\$var(attrs)\", \"added by nodes role\" where not exists(select * from dispatcher where destination = \"\$var(MediaUrl)\")!g" +#!substdef "!KZQ_COUNT_ALL_SUBSCRIBERS!select a.event, count(distinct watcher_uri) count_unique, count(*) count from event_list a left outer join active_watchers b on a.event = b.event group by a.event!g" + #!substdef "!KZQ_COUNT_PRESENTITIES!select event, (select count(*) from presentity b where username = \"\$(var(payload){kz.json,From}{uri.user})\" and domain = \"\$(var(payload){kz.json,From}{uri.domain})\" and b.event = a.event) count from event_list a!g" -#!substdef "!KZQ_COUNT_SUBSCRIBERS!select event, (select count(*) from active_watchers b where presentity_uri = \"\$var(presentity)\" and b.event = a.event) count from event_list a!g" +#!substdef "!KZQ_COUNT_SUBSCRIBERS!select event, (select count(*) from active_watchers b where presentity_uri = \"\$var(presentity)\" and b.event = a.event) count from event_list a union all select \"self\", count(distinct callid) from presentities where presentity_uri = \"\$var(presentity)\" and callid <> \"\$var(callid)\" and state in('early', 'confirmed', 'onthephone', 'busy', 'ringing')!g" + #!substdef "!KZQ_EVENT_PRESENCE_RESET_DELETE!delete from presentity where domain=\"\$(kzE{kz.json,Realm})\" and username = \"\$(kzE{kz.json,Username})\"!g" #!substdef "!KZQ_HANDLE_NEW_SUBSCRIBE_DELETE1!delete from active_watchers where callid = \"\$ci\"!g" -#!substdef "!KZQ_HANDLE_NEW_SUBSCRIBE_DELETE2!delete from active_watchers where watcher_username=\"\$fU\" and presentity_uri=\"\$var(presentity_uri)\" and to_user=\"\$tU\" and watcher_domain=\"\$fd\" and event=\"\$hdr(Event)\"!g" -#!substdef "!KZQ_RESET_ACCOUNT_DELETE!delete from presentity where domain=\"\$(kzE{kz.json,Realm})\"!g" -#!substdef "!KZQ_RESET_ACCOUNT_UPDATE!update active_watchers set expires = \$TS where watcher_domain=\"\$(kzE{kz.json,Realm})\"!g" -#!substdef "!KZQ_RESET_PUBLISHER_UPDATE!update active_watchers set expires = \$TS where id in (select b.id from presentity a inner join active_watchers b on a.username = b.to_user and a.domain = b.to_domain and a.event = b.event where a.sender = \"\$var(MediaUrl)\")!g" -#!substdef "!KZQ_PRESENCE_SEARCH_DETAIL!select * from active_watchers_log where presentity_uri = \"\$var(presentity_uri)\"!g" -#!substdef "!KZQ_PRESENCE_SEARCH_SUMMARY!select * from active_watchers where watcher_domain = \"\$var(Domain)\"!g" +#!substdef "!KZQ_HANDLE_NEW_SUBSCRIBE_DELETE2!delete from active_watchers where presentity_uri=\"\$var(presentity_uri)\" and event=\"\$hdr(Event)\" and watcher_username=\"\$fU\" and to_user=\"\$tU\" and watcher_domain=\"\$fd\"!g" + +#!substdef "!KZQ_PRESENCE_SEARCH_SUMMARY!select * from active_watchers where to_domain = \"\$var(Domain)\"!g" +#!substdef "!KZQ_PRESENCE_SEARCH_DETAIL!select a.*, b.time, b.result, b.sent_msg, b.received_msg from active_watchers a left outer join active_watchers_log b on a.presentity_uri = b.presentity_uri and a.event = b.event and a.callid = b.callid where a.presentity_uri = \"\$var(presentity_uri)\" !g" + #!substdef "!KZQ_HAS_PRESENTITY!select count(*) as count from presentity where username = \"\$subs(to_user)\" and domain = \"\$subs(to_domain)\" and event = \"\$subs(event)\"!g" +#!substdef "!KZQ_REPLACE_WATCHERS_LOG!REPLACE INTO active_watchers_log (presentity_uri, watcher_username, watcher_domain, event, callid, to_user, to_domain, user_agent, time, result, sent_msg, received_msg) VALUES (\"\$subs(uri)\", \"\$subs(watcher_username)\", \"\$subs(watcher_domain)\", \"\$subs(event)\",\"\$subs(callid)\",\"\$subs(to_user)\",\"\$subs(to_domain)\", '\$(subs(user_agent){s.escape.common}{s.replace,\\\',''}{s.replace,\$\$,})', \$TS, \$notify_reply(\$rs), '\$(mb{s.escape.common}{s.replace,\\\',''}{s.replace,\$\$,})', '\$(notify_reply(\$mb){s.escape.common}{s.replace,\\\',''}{s.replace,\$\$,})')!g" + +# # #!substdef "!KZQ_RESET_PUBLISHER_UPDATE!update active_watchers set expires = \$TS where id in (select b.id from presentity a inner join active_watchers b on a.username = b.to_user and a.domain = b.to_domain and a.event = b.event where a.sender = \"\$var(MediaUrl)\")!g" +#!substdef "!KZQ_RESET_PUBLISHER_UPDATE!INSERT INTO tmp_probe select distinct a.event, a.presentity_uri, 0 from presentities a inner join active_watchers b on a.presentity_uri = b.presentity_uri and a.event = b.event where sender = \"\$var(MediaUrl)\" and state in('early', 'confirmed', 'onthephone', 'busy', 'ringing')!g" #!substdef "!KZQ_PRESENCE_RESET!delete from presentity where sender = \"\$var(MediaUrl)\"!g" + +# # #!substdef "!KZQ_RESET_ACCOUNT_UPDATE!update active_watchers set expires = \$TS where watcher_domain=\"\$(kzE{kz.json,Realm})\"!g" +#!substdef "!KZQ_RESET_ACCOUNT_UPDATE!INSERT INTO tmp_probe select distinct a.event, a.presentity_uri, 0 from presentities a inner join active_watchers b on a.presentity_uri = b.presentity_uri and a.event = b.event where domain=\"\$(kzE{kz.json,Realm})\" and state in('early', 'confirmed', 'onthephone', 'busy', 'ringing')!g" +#!substdef "!KZQ_RESET_ACCOUNT_RESET!delete from presentity where domain=\"\$(kzE{kz.json,Realm})\"!g" + +#!substdef "!KZQ_RESET_PUBLISHER_ZONE_UPDATE!INSERT INTO tmp_probe select distinct a.event, a.presentity_uri, 0 from presentities a inner join wdispatcher c on a.sender = c.destination inner join active_watchers b on a.presentity_uri = b.presentity_uri and a.event = b.event where zone = \"\$var(Zone)\" and state in('early', 'confirmed', 'onthephone', 'busy', 'ringing')!g" +#!substdef "!KZQ_PRESENCE_ZONE_RESET!delete from presentity where id in(select a.id from presentities a join wdispatcher c on a.sender = c.destination where zone = \"\$var(Zone)\")!g" diff --git a/kamailio/db_queries_mysql.cfg b/kamailio/db_queries_mysql.cfg index 9dcd2a1..7385add 100644 --- a/kamailio/db_queries_mysql.cfg +++ b/kamailio/db_queries_mysql.cfg @@ -11,4 +11,5 @@ #!substdef "!KZQ_RESET_PUBLISHER_UPDATE!update active_watchers set expires = \$TS where id in (select b.id from presentity a inner join active_watchers b on a.username = b.to_user and a.domain = b.to_domain and a.event = b.event where a.sender = \"\$var(MediaUrl)\")!g" #!substdef "!KZQ_PRESENCE_SEARCH_DETAIL!select * from active_watchers_log where presentity_uri = \"\$var(presentity_uri)\"!g" #!substdef "!KZQ_PRESENCE_SEARCH_SUMMARY!select * from active_watchers where watcher_domain = \"\$var(Domain)\"!g" +#!substdef "!KZQ_HAS_PRESENTITY!select count(*) as count from presentity where username = \"\$subs(to_user)\" and domain = \"\$subs(to_domain)\" and event = \"\$subs(event)\"!g" #!substdef "!KZQ_PRESENCE_RESET!delete from presentity where sender = \"\$var(MediaUrl)\"!g" diff --git a/kamailio/db_scripts/check-kazoodb-sql.sh b/kamailio/db_scripts/check-kazoodb-sql.sh new file mode 100755 index 0000000..4ff700a --- /dev/null +++ b/kamailio/db_scripts/check-kazoodb-sql.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +TEMP_DB_LOCATION=/tmp/db +TEMP_DB=${TEMP_DB_LOCATION}/kazoo.db + +rm -rf ${TEMP_DB_LOCATION} +. $(dirname $0)/kazoodb-sql.sh --source-only + +file=$(sql_db_prepare) +sql_setup $file ${TEMP_DB_LOCATION} + +DB_VERSION=`KazooDB -db ${TEMP_DB} "select sum(table_version) from version;"` + +DB_CURRENT_DB=${DB_LOCATION:-/etc/kazoo/kamailio}/kazoo.db +DB_CURRENT_VERSION=`KazooDB -db ${DB_CURRENT_DB} "select sum(table_version) from version;"` + + +if [[ $DB_CURRENT_VERSION -ne $DB_VERSION ]]; then + echo "db required version is ${DB_VERSION}, existing version is ${DB_CURRENT_VERSION}, applying diff" + KazooDB-diff --schema ${DB_CURRENT_DB} ${TEMP_DB} | KazooDB -db ${DB_CURRENT_DB} + KazooDB-diff --primarykey --table version ${DB_CURRENT_DB} ${TEMP_DB} | KazooDB -db ${DB_CURRENT_DB} + KazooDB-diff --primarykey --table event_list ${DB_CURRENT_DB} ${TEMP_DB} | KazooDB -db ${DB_CURRENT_DB} +fi + + +for VIEW in `ls ${DB_SCRIPT_DIR}/vw_*.sql`; do + filename=$(basename -- "$VIEW") + filename="${filename%.*}" + viewname=${filename#*_} + v1=$(KazooDB -db ${DB_CURRENT_DB} "select sql from sqlite_master where type='view' and name='$viewname'" 2> /dev/null | tr -d ' ' | md5sum | cut -d ' ' -f1) + v2=$(cat $VIEW | tr -d ' ' | md5sum | cut -d ' ' -f1) + if [[ "$v1" != "$v2" ]]; then + echo "rebuilding view $viewname" + KazooDB -db ${DB_CURRENT_DB} "drop view if exists $viewname;" + KazooDB -db ${DB_CURRENT_DB} < $VIEW + fi +done + +if [ -f ${DB_SCRIPT_DIR}/db_extra_check.sql ]; then + . ${DB_SCRIPT_DIR}/db_extra_check.sql --source-only + do_db_extra_check; +fi + +for INIT in `ls ${DB_SCRIPT_DIR}/db_init_*.sql`; do + KazooDB -db ${DB_CURRENT_DB} < $INIT +done diff --git a/kamailio/db_scripts/create-kazoodb-sql.sh b/kamailio/db_scripts/create-kazoodb-sql.sh index 0663a0e..b720415 100755 --- a/kamailio/db_scripts/create-kazoodb-sql.sh +++ b/kamailio/db_scripts/create-kazoodb-sql.sh @@ -1,104 +1,9 @@ #!/bin/sh -e -##################################################################################### -## -## If you want prepare SQL file for PostgreSQL or MySQL server, then need to execute -## DB_ENGINE=postgres ./create-kazoodb-sql.sh -## -##################################################################################### -KAMAILIO_SHARE_DIR=${KAMAILIO_SHARE_DIR:-/usr/share/kamailio} -DB_ENGINE=${DB_ENGINE:-db_kazoo} -RESULTED_SQL=${RESULTED_SQL:-/tmp/kamailio_initdb.sql} +. $(dirname $0)/kazoodb-sql.sh --source-only -. $(dirname $0)/$DB_ENGINE-specific --source-only - -sql_filelist() { -cat << EOF -acc-create.sql -lcr-create.sql -domain-create.sql -group-create.sql -permissions-create.sql -registrar-create.sql -usrloc-create.sql -msilo-create.sql -alias_db-create.sql -uri_db-create.sql -speeddial-create.sql -avpops-create.sql -auth_db-create.sql -pdt-create.sql -dialog-create.sql -dispatcher-create.sql -dialplan-create.sql -topos-create.sql -presence-create.sql -rls-create.sql -imc-create.sql -cpl-create.sql -siptrace-create.sql -domainpolicy-create.sql -carrierroute-create.sql -userblacklist-create.sql -htable-create.sql -purple-create.sql -uac-create.sql -pipelimit-create.sql -mtree-create.sql -sca-create.sql -mohqueue-create.sql -rtpproxy-create.sql -uid_auth_db-create.sql -uid_avp_db-create.sql -uid_domain-create.sql -uid_gflags-create.sql -uid_uri_db-create.sql -EOF -} - -sql_all_header() { -cat << EOF -CREATE TABLE version ( - table_name VARCHAR(32) NOT NULL, - table_version INTEGER DEFAULT 0 NOT NULL, - CONSTRAINT version_table_name_idx UNIQUE (table_name) -); -INSERT INTO version VALUES('version',1); - -EOF -} - -sql_all_extra_tables() { -cat << EOF - -CREATE TABLE event_list ( event varchar(25) PRIMARY KEY NOT NULL); -INSERT INTO event_list VALUES('dialog'); -INSERT INTO event_list VALUES('presence'); -INSERT INTO event_list VALUES('message-summary'); -INSERT INTO version VALUES('event_list',1); - -EOF -} - -sql_all_footer() { -cat << EOF -COMMIT; -EOF -} - -echo "Creating kamailio database init file in '$RESULTED_SQL'" - -sql_db_pre_setup > $RESULTED_SQL -sql_all_header >> $RESULTED_SQL -sql_header >> $RESULTED_SQL -for i in $(sql_filelist); do - cat $KAMAILIO_SHARE_DIR/$DB_ENGINE/$i >> $RESULTED_SQL -done -sql_all_extra_tables >> $RESULTED_SQL -sql_extra_tables >> $RESULTED_SQL -sql_footer >> $RESULTED_SQL -sql_all_footer >> $RESULTED_SQL - -sql_setup $RESULTED_SQL +file=$(sql_db_prepare) +echo "setting up kazoo db from init script $file" +sql_setup $file exit 0 diff --git a/kamailio/db_scripts/db_extra_check.sql b/kamailio/db_scripts/db_extra_check.sql new file mode 100644 index 0000000..88210bd --- /dev/null +++ b/kamailio/db_scripts/db_extra_check.sql @@ -0,0 +1,34 @@ + +do_db_extra_check() { + +# location +if [[ $RESET_NON_UDP_ENABLED == "true" ]]; then + KazooDB -db ${DB_CURRENT_DB} "delete from location where socket not like 'udp:%';" +fi + +##KazooDB -db ${DB_CURRENT_DB} "delete from location where expires > 0 and datetime(expires) < datetime('now', '-30 seconds');" +KazooDB -db ${DB_CURRENT_DB} "delete from location_attrs where not exists(select id from location where ruid = location_attrs.ruid);" + +## presence +if [[ $RESET_NON_UDP_ENABLED == "true" ]]; then + KazooDB -db ${DB_CURRENT_DB} "delete from active_watchers where socket_info not like 'udp:%';" +fi +KazooDB -db ${DB_CURRENT_DB} "delete from active_watchers where expires > 0 and datetime(expires, 'unixepoch') < datetime('now', '-10 seconds');" +KazooDB -db ${DB_CURRENT_DB} "delete from presentity where expires > 0 AND datetime(expires, 'unixepoch') < datetime('now', '-10 seconds');" +KazooDB -db ${DB_CURRENT_DB} "delete from presentity where id in(select id from presentities where state in('terminated','available'));" +KazooDB -db ${DB_CURRENT_DB} "delete from active_watchers_log where id in(select id from active_watchers_log a where not exists(select callid from active_watchers b where b.callid = a.callid and b.watcher_username = a.watcher_username and b.watcher_domain = a.watcher_domain));" +KazooDB -db ${DB_CURRENT_DB} "delete from presentity where id in(select id from presentities a where not exists(select * from active_watchers where presentity_uri = a.presentity_uri));" + +## notify watchers of pending calls +## 'create temp table as' because it will be dropped as soon as we ended the session +KazooDB -db ${DB_CURRENT_DB} "drop table if exists tmp_probe;" +KazooDB -db ${DB_CURRENT_DB} "create table tmp_probe as select distinct a.event, a.presentity_uri, cast(2 as integer) action from presentities a inner join active_watchers b on a.presentity_uri = b.presentity_uri and a.event = b.event where state in('early', 'confirmed', 'onthephone', 'busy');" +KazooDB -db ${DB_CURRENT_DB} "delete from presentity where id in(select id from presentities where state in('early', 'confirmed', 'onthephone', 'busy'));" + +## keepalive +if [[ $RESET_NON_UDP_ENABLED == "true" ]]; then + KazooDB -db ${DB_CURRENT_DB} "delete from keepalive where sockinfo NOT LIKE 'udp%';" +fi +KazooDB -db ${DB_CURRENT_DB} "update keepalive set selected = 0, time_sent = datetime('now') where selected < 3;" + +} diff --git a/kamailio/db_scripts/db_init_watcher_triggers.sql b/kamailio/db_scripts/db_init_watcher_triggers.sql new file mode 100644 index 0000000..8e5a202 --- /dev/null +++ b/kamailio/db_scripts/db_init_watcher_triggers.sql @@ -0,0 +1,14 @@ +CREATE TRIGGER if not exists active_watchers_watcher_uri_insert +AFTER INSERT ON active_watchers +FOR EACH ROW +BEGIN + UPDATE active_watchers SET watcher_uri = "sip:" || NEW.watcher_username || "@" || NEW.watcher_domain where id = NEW.id; +END; + +CREATE TRIGGER if not exists active_watchers_watcher_uri_update +AFTER UPDATE ON active_watchers +FOR EACH ROW +WHEN OLD.watcher_username <> NEW.watcher_username OR OLD.watcher_domain <> NEW.watcher_domain +BEGIN + UPDATE active_watchers SET watcher_uri = "sip:" || NEW.watcher_username || "@" || NEW.watcher_domain where id = NEW.id; +END; diff --git a/kamailio/db_scripts/db_kazoo-specific b/kamailio/db_scripts/db_kazoo-specific index 014fd78..2f87516 100644 --- a/kamailio/db_scripts/db_kazoo-specific +++ b/kamailio/db_scripts/db_kazoo-specific @@ -6,15 +6,16 @@ cat << EOF PRAGMA foreign_keys=OFF; PRAGMA wal=on; PRAGMA journal_mode=WAL; +PRAGMA wal_autocheckpoint=25; BEGIN TRANSACTION; EOF } sql_setup() { - DB_KAZOO_LOCATION=${DB_KAZOO_LOCATION:-/etc/kazoo/kamailio/db} - mkdir -p ${DB_KAZOO_LOCATION} - KazooDB -db ${DB_KAZOO_LOCATION}/kazoo.db < $1 + DB_KAZOO_LOCATION=${2:-${DB_KAZOO_LOCATION:-/etc/kazoo/kamailio/db}} + mkdir -p ${DB_KAZOO_LOCATION} + KazooDB -db ${DB_KAZOO_LOCATION}/kazoo.db < $1 > /dev/null } sql_header() { @@ -40,6 +41,108 @@ CREATE TABLE active_watchers_log ( user_agent VARCHAR(255) DEFAULT '' COLLATE NOCASE, CONSTRAINT active_watchers_active_watchers_log_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) ); +INSERT INTO version (table_name, table_version) values ('active_watchers_log','1'); + +CREATE TABLE keepalive ( + id INTEGER PRIMARY KEY NOT NULL, + contact VARCHAR(2048) NOT NULL COLLATE NOCASE, + received VARCHAR(32) NOT NULL COLLATE NOCASE, + sockinfo VARCHAR(128) NOT NULL COLLATE NOCASE, + time_inserted timestamp DEFAULT CURRENT_TIMESTAMP, + time_sent timestamp DEFAULT CURRENT_TIMESTAMP, + slot INTEGER NOT NULL, + selected INTEGER DEFAULT 0, + failed INTEGER DEFAULT 0, + CONSTRAINT keepalive_idx UNIQUE (contact), + CONSTRAINT keepalive_idx_2 UNIQUE (slot, failed, contact) + ); + +CREATE INDEX keepalive_idx_3 ON keepalive (slot, selected, time_sent); +CREATE INDEX keepalive_idx_4 ON keepalive (received, selected); + +INSERT INTO version (table_name, table_version) values ('keepalive','4'); + +ALTER TABLE active_watchers ADD COLUMN watcher_uri varchar(64) NOT NULL DEFAULT "sip:no_watcher@no_domain"; + +CREATE TRIGGER active_watchers_watcher_uri_insert +AFTER INSERT ON active_watchers +FOR EACH ROW +BEGIN + UPDATE active_watchers SET watcher_uri = "sip:" || NEW.watcher_username || "@" || NEW.watcher_domain where id = NEW.id; +END; + +CREATE TRIGGER active_watchers_watcher_uri_update +AFTER UPDATE ON active_watchers +FOR EACH ROW +WHEN OLD.watcher_username <> NEW.watcher_username OR OLD.watcher_domain <> NEW.watcher_domain +BEGIN + UPDATE active_watchers SET watcher_uri = "sip:" || NEW.watcher_username || "@" || NEW.watcher_domain where id = NEW.id; +END; + +CREATE UNIQUE INDEX active_watchers_contact ON active_watchers (contact, id); +CREATE INDEX active_watchers_event_watcher_uri ON active_watchers (event, watcher_uri); + + +CREATE INDEX location_attrs_ruid ON location_attrs (ruid); +CREATE UNIQUE INDEX location_ruid ON location (ruid); + +create table auth_cache as select * from htable; +INSERT INTO version (table_name, table_version) select 'auth_cache', table_version from version where table_name = 'htable'; + +create table block_cache as select * from htable; +INSERT INTO version (table_name, table_version) select 'block_cache', table_version from version where table_name = 'htable'; + + + create view presentities as select id, cast(printf("sip:%s@%s",username,domain) as varchar(64)) presentity_uri , + username, domain, event, cast(substr(etag, instr(etag,"@")+1) as varchar(64)) callid, + datetime(received_time, 'unixepoch') as received, + datetime(expires, 'unixepoch') as expire_date, + expires, cast(sender as varchar(30)) sender, + lower(cast( case when event = "dialog" + then substr(body, instr(BODY,"")+7, instr(body,"") - instr(body,"") - 7) + when event = "presence" + then case when instr(body,"") == 0 + then replace(substr(body, instr(body,"")+6, instr(body,"") - instr(body,"") - 6), " ", "") + else replace(substr(body, instr(body,"")+9, instr(body,"") - instr(body,"") - 9), " ", "") + end + when event = "message-summary" + then case when instr(body,"Messages-Waiting: yes") = 0 + then "Waiting" + else "Not-Waiting" + end + end as varchar(12))) state + from presentity; + + create view wdispatcher as select *, + cast(substr(attrs, instr(attrs, "zone=")+5, instr(attrs, ";profile")-instr(attrs, "zone=")-5) as varchar(20)) zone, + cast(substr(attrs, instr(attrs, "idx=")+4, instr(attrs, ";node")-instr(attrs, "idx=")-4) as integer) idx, + cast(substr(attrs, instr(attrs, "node=")+5) as varchar(50)) node + from dispatcher; + + create unique index if not exists idx_dispatcher_destination on dispatcher(destination); + + +CREATE VIEW w_keepalive_contact as +SELECT id, slot, selected, failed, case when instr(contact,";") > 0 + then substr(contact, 1, instr(contact,";")-1) + else contact + end as contact +from keepalive; + +CREATE VIEW w_location_contact as +SELECT id, ruid, case when instr(contact,";") > 0 + then substr(contact, 1, instr(contact,";")-1) + else contact + end as contact +from location; + +CREATE VIEW w_watchers_contact as +select id, case when instr(contact,";") > 0 + then substr(contact, 1, instr(contact,";")-1) + else contact + end as contact +from active_watchers; + EOF } diff --git a/kamailio/db_scripts/kazoodb-sql.sh b/kamailio/db_scripts/kazoodb-sql.sh new file mode 100755 index 0000000..6351266 --- /dev/null +++ b/kamailio/db_scripts/kazoodb-sql.sh @@ -0,0 +1,56 @@ +#!/bin/sh -e + +KAMAILIO_SHARE_DIR=${KAMAILIO_SHARE_DIR:-/usr/share/kamailio} +DB_ENGINE=${DB_ENGINE:-db_kazoo} +RESULTED_SQL=${RESULTED_SQL:-/tmp/kamailio_initdb.sql} + +. $(dirname $0)/$DB_ENGINE-specific --source-only + +sql_filelist() { + echo `ls -A1 ${KAMAILIO_SHARE_DIR}/${DB_ENGINE}/*.sql | grep -v standard | tr '\n' '\0' | xargs -0 -n 1 basename | sort` +} + +sql_all_header() { +cat << EOF +CREATE TABLE version ( + table_name VARCHAR(32) NOT NULL, + table_version INTEGER DEFAULT 0 NOT NULL, + PRIMARY KEY(table_name) +); +INSERT INTO version VALUES('version',1); + +EOF +} + +sql_all_extra_tables() { +cat << EOF + +CREATE TABLE event_list ( event varchar(25) PRIMARY KEY NOT NULL); +INSERT INTO event_list VALUES('dialog'); +INSERT INTO event_list VALUES('presence'); +INSERT INTO event_list VALUES('message-summary'); +INSERT INTO version VALUES('event_list',1); + +EOF +} + +sql_all_footer() { +cat << EOF +COMMIT; +EOF +} + +sql_db_prepare() { + sql_db_pre_setup > $RESULTED_SQL + sql_all_header >> $RESULTED_SQL + sql_header >> $RESULTED_SQL + for i in $(sql_filelist); do + cat $KAMAILIO_SHARE_DIR/$DB_ENGINE/$i >> $RESULTED_SQL + done + sql_all_extra_tables >> $RESULTED_SQL + sql_extra_tables >> $RESULTED_SQL + sql_footer >> $RESULTED_SQL + sql_all_footer >> $RESULTED_SQL + + echo "$RESULTED_SQL" +} diff --git a/kamailio/db_scripts/vw_presentities.sql b/kamailio/db_scripts/vw_presentities.sql new file mode 100644 index 0000000..4c95a38 --- /dev/null +++ b/kamailio/db_scripts/vw_presentities.sql @@ -0,0 +1,20 @@ +CREATE VIEW presentities as +select id, cast(printf("sip:%s@%s",username,domain) as varchar(64)) presentity_uri , + username, domain, event, cast(substr(etag, instr(etag,"@")+1) as varchar(64)) callid, + datetime(received_time, 'unixepoch') as received, + datetime(expires, 'unixepoch') as expire_date, + expires, cast(sender as varchar(30)) sender, + lower(cast( case when event = "dialog" + then substr(body, instr(BODY,"")+7, instr(body,"") - instr(body,"") - 7) + when event = "presence" + then case when instr(body,"") == 0 + then replace(substr(body, instr(body,"")+6, instr(body,"") - instr(body,"") - 6), " ", "") + else replace(substr(body, instr(body,"")+9, instr(body,"") - instr(body,"") - 9), " ", "") + end + when event = "message-summary" + then case when instr(body,"Messages-Waiting: yes") = 0 + then "Waiting" + else "Not-Waiting" + end + end as varchar(12))) state + from presentity diff --git a/kamailio/db_scripts/vw_w_keepalive_contact.sql b/kamailio/db_scripts/vw_w_keepalive_contact.sql new file mode 100644 index 0000000..1639650 --- /dev/null +++ b/kamailio/db_scripts/vw_w_keepalive_contact.sql @@ -0,0 +1,6 @@ +CREATE VIEW w_keepalive_contact as + select id, slot, selected, failed, case when instr(contact,";") > 0 + then substr(contact, 1, instr(contact,";")-1) + else contact + end as contact + from keepalive diff --git a/kamailio/db_scripts/vw_w_location_contact.sql b/kamailio/db_scripts/vw_w_location_contact.sql new file mode 100644 index 0000000..6914519 --- /dev/null +++ b/kamailio/db_scripts/vw_w_location_contact.sql @@ -0,0 +1,6 @@ +CREATE VIEW w_location_contact as + select id, ruid, case when instr(contact,";") > 0 + then substr(contact, 1, instr(contact,";")-1) + else contact + end as contact + from location diff --git a/kamailio/db_scripts/vw_w_watchers_contact.sql b/kamailio/db_scripts/vw_w_watchers_contact.sql new file mode 100644 index 0000000..96d0186 --- /dev/null +++ b/kamailio/db_scripts/vw_w_watchers_contact.sql @@ -0,0 +1,6 @@ +CREATE VIEW w_watchers_contact as + select id, case when instr(contact,";") > 0 + then substr(contact, 1, instr(contact,";")-1) + else contact + end as contact + from active_watchers diff --git a/kamailio/db_scripts/vw_wdispatcher.sql b/kamailio/db_scripts/vw_wdispatcher.sql new file mode 100644 index 0000000..1643b92 --- /dev/null +++ b/kamailio/db_scripts/vw_wdispatcher.sql @@ -0,0 +1,6 @@ +CREATE VIEW wdispatcher as + select *, + cast(substr(attrs, instr(attrs, "zone=")+5, instr(attrs, ";profile")-instr(attrs, "zone=")-5) as varchar(20)) zone, + cast(substr(attrs, instr(attrs, "idx=")+4, instr(attrs, ";node")-instr(attrs, "idx=")-4) as integer) idx, + cast(substr(attrs, instr(attrs, "node=")+5) as varchar(50)) node + from dispatcher diff --git a/kamailio/default.cfg b/kamailio/default.cfg index bf51561..72b12a7 100644 --- a/kamailio/default.cfg +++ b/kamailio/default.cfg @@ -1,15 +1,19 @@ ## NOTE: DO NOT CHANGE THIS FILE, EDIT local.cfg ## ####### Flags ####### -flags - FLAG_INTERNALLY_SOURCED: 1, - FLAG_ASSOCIATE_SERVER: 2, - FLAG_SKIP_NAT_CORRECTION: 3, - FLAG_ASSOCIATE_USER: 4, - FLAG_TRUSTED_SOURCE: 5, - FLAG_SESSION_PROGRESS: 6, - FLAG_IS_REPLY: 7, - FLAG_SIP_TRACE: 8; +#!trydef FLAG_INTERNALLY_SOURCED 1 +#!trydef FLAG_ASSOCIATE_SERVER 2 +#!trydef FLAG_SKIP_NAT_CORRECTION 3 +#!trydef FLAG_ASSOCIATE_USER 4 +#!trydef FLAG_TRUSTED_SOURCE 5 +#!trydef FLAG_SESSION_PROGRESS 6 +#!trydef FLAG_IS_REPLY 7 +#!trydef FLAG_SIP_TRACE 8 +#!trydef FLT_AOR 9 +#!trydef FLT_T38 10 +#!trydef FLT_NATS 11 +#!trydef FLAG_LOCAL_REQUEST 12 +#!trydef FLAG_LOCAL_ROUTE 17 ####### Global Parameters ######### fork = yes @@ -22,15 +26,17 @@ mlock_pages = yes phone2tel = 1 max_while_loops = MAX_WHILE_LOOPS -pv_buffer_size=65536 +pv_buffer_size = PV_BUFFER_SIZE +pv_buffer_slots = PV_BUFFER_SLOTS + mem_join=1 ####### Logging Parameters ######### debug = KAZOO_LOG_LEVEL memdbg = 10 -memlog = L_INFO +memlog = L_BUG corelog = L_ERR -mem_summary = 12 +mem_summary = 0 log_stderror = no log_facility = LOG_LOCAL0 log_name="kamailio" @@ -44,22 +50,22 @@ tos = IPTOS_LOWDELAY ####### TCP Parameters ######### tcp_children = TCP_CHILDREN disable_tcp = no -tcp_max_connections = 4096 -tcp_connection_lifetime = 3605 +tcp_max_connections = TCP_MAX_CONNECTIONS +tcp_connection_lifetime = TCP_CONNECTION_LIFETIME tcp_accept_aliases = no tcp_async = yes -tcp_connect_timeout = 10 +tcp_connect_timeout = TCP_CONNECTION_TIMEOUT tcp_conn_wq_max = 65536 tcp_crlf_ping = yes tcp_delayed_ack = yes tcp_fd_cache = yes -tcp_keepalive = yes -tcp_keepcnt = 3 -tcp_keepidle = 30 -tcp_keepintvl = 10 +tcp_keepalive = TCP_KEEP_ALIVE +tcp_keepcnt = TCP_KEEP_COUNT +tcp_keepidle = TCP_KEEP_IDLE +tcp_keepintvl = TCP_KEEP_INTERVAL tcp_linger2 = 30 tcp_rd_buf_size = 80000 -tcp_send_timeout = 10 +tcp_send_timeout = TCP_SEND_TIMEOUT tcp_wq_blk_size = 2100 tcp_wq_max = 10485760 @@ -88,6 +94,9 @@ dns_srv_lb = off ####### SCTP Parameters ######### disable_sctp = yes +####### multi homed ######### +mhomed=KZ_MULTI_HOMED + ######## Kamailio mqueue module ######## loadmodule "mqueue.so" @@ -100,14 +109,16 @@ loadmodule "stun.so" ######## Kamailio path module ######## loadmodule "path.so" -######## Kamailio ipops module ######## -loadmodule "ipops.so" - ######## Kamailio control connector module ######## loadmodule "ctl.so" modparam("ctl", "binrpc_buffer_size", 4096) loadmodule "cfg_rpc.so" + +######## Kamailio config utils module ######## loadmodule "cfgutils.so" +modparam("cfgutils", "lock_set_size", 4) + +######## Kamailio corex module ######## loadmodule "corex.so" ######## Kamailio uuid module ######## @@ -124,6 +135,10 @@ modparam("tm", "auto_inv_100_reason", "Attempting to connect your call") modparam("tm", "cancel_b_method", 2) modparam("tm", "ruri_matching", 0) modparam("tm", "failure_reply_mode", 3) +modparam("tm", "failure_exec_mode", 1) +modparam("tm", "reparse_on_dns_failover", 0) + + # modparam("tm", "fr_timer", 30000) # modparam("tm", "fr_inv_timer", 120000) @@ -132,8 +147,9 @@ loadmodule "sl.so" ######## Record-Route and Route module ######## loadmodule "rr.so" -modparam("rr", "enable_full_lr", 1) -modparam("rr", "enable_double_rr", 1) +modparam("rr", "enable_full_lr", RR_FULL_LR) +modparam("rr", "enable_double_rr", RR_DOUBLE_RR) +modparam("rr", "force_send_socket", RR_FORCE_SOCKET) ######## Max-Forward processor module ######## loadmodule "maxfwd.so" @@ -142,23 +158,25 @@ modparam("maxfwd", "max_limit", 50) ######## SIP utilities [requires sl] ######## loadmodule "siputils.so" -######## SIP message formatting sanity checks [requires sl] ######## -loadmodule "sanity.so" -modparam("sanity", "default_checks", 1511) -modparam("sanity", "uri_checks", 7) -modparam("sanity", "autodrop", 0) - ######## Text operations module ######## loadmodule "textops.so" loadmodule "textopsx.so" +######## sdp operations module ######## +loadmodule "sdpops.so" + ######## Generic Hash Table container in shared memory ######## loadmodule "htable.so" modparam("htable", "htable", "associations=>size=16;autoexpire=7200") modparam("htable", "htable", "redirects=>size=16;autoexpire=5") +modparam("htable", "db_url", "KAZOO_DB_URL") -######## Pseudo-Variables module ######## -loadmodule "pv.so" +####### RTIMER module ########## +loadmodule "rtimer.so" + +####### evrexec module ########## +loadmodule "evrexec.so" +modparam("evrexec", "exec", "name=evrexec:DEFERRED_INIT;wait=20000000;workers=1;") ######## Advanced logger module ######## loadmodule "xlog.so" @@ -181,54 +199,25 @@ loadmodule "sqlops.so" modparam("sqlops","sqlcon", "cb=>KAZOO_DB_URL") modparam("sqlops","sqlcon", "exec=>KAZOO_DB_URL") -####### DATABASE module ########## -loadmodule "db_KAMAILIO_DBMS.so" - -####### Kazoo Integration module ########## -loadmodule "kazoo.so" -modparam("kazoo", "pua_mode", MY_AMQP_PUA_MODE) -modparam("kazoo", "amqp_primary_zone", "MY_AMQP_ZONE") -modparam("kazoo", "amqp_query_timeout_avp", "$avp(kz_timeout)") -modparam("kazoo", "node_hostname", "MY_HOSTNAME") -modparam("kazoo", "amqp_heartbeats", MY_AMQP_HEARTBEATS) -modparam("kazoo", "amqp_max_channels", MY_AMQP_MAX_CHANNELS) -modparam("kazoo", "amqp_consumer_processes", MY_AMQP_CONSUMER_PROCESSES) -modparam("kazoo", "amqp_consumer_workers", MY_AMQP_CONSUMER_WORKERS) -## amqp connections -modparam("kazoo", "amqp_connection", "MY_AMQP_URL") -#!ifdef MY_AMQP_SECONDARY_URL -modparam("kazoo", "amqp_connection", "MY_AMQP_SECONDARY_URL") -#!endif -#!ifdef MY_AMQP_TERTIARY_URL -modparam("kazoo", "amqp_connection", "MY_AMQP_TERTIARY_URL") -#!endif -#!ifdef MY_AMQP_QUATERNARY_URL -modparam("kazoo", "amqp_connection", "MY_AMQP_QUATERNARY_URL") -#!endif -#!ifdef MY_AMQP_QUINARY_URL -modparam("kazoo", "amqp_connection", "MY_AMQP_QUINARY_URL") -#!endif -#!ifdef MY_AMQP_SENARY_URL -modparam("kazoo", "amqp_connection", "MY_AMQP_SENARY_URL") -#!endif -#!ifdef MY_AMQP_SEPTENARY_URL -modparam("kazoo", "amqp_connection", "MY_AMQP_SEPTENARY_URL") -#!endif -#!ifdef MY_AMQP_OCTONARY_URL -modparam("kazoo", "amqp_connection", "MY_AMQP_OCTONARY_URL") -#!endif -#!ifdef MY_AMQP_NONARY_URL -modparam("kazoo", "amqp_connection", "MY_AMQP_NONARY_URL") -#!endif -#!ifdef MY_AMQP_DENARY_URL -modparam("kazoo", "amqp_connection", "MY_AMQP_DENARY_URL") -#!endif +####### DEBUG ###### +loadmodule "debugger.so" +modparam("debugger", "mod_hash_size", 5) +modparam("debugger", "mod_level_mode", 1) +modparam("debugger", "mod_level", "core=1") + +####### STATISTICS ###### +loadmodule "statistics.so" +####### DATABASE module ########## +include_file "db_KAMAILIO_DBMS.cfg" ###### kazoo bindings ###### include_file "kazoo-bindings.cfg" ####### Role Configurations ########## +#!ifdef AUTHORIZATION_ROLE +include_file "authorization-role.cfg" +#!endif #!ifdef DISPATCHER_ROLE include_file "dispatcher-role.cfg" #!endif @@ -277,37 +266,39 @@ include_file "nodes-role.cfg" #!ifdef SIP_TRACE_ROLE include_file "sip_trace-role.cfg" #!endif +#!ifdef KEEPALIVE_ROLE +include_file "keepalive-role.cfg" +#!endif +#!ifdef BLOCKER_ROLE +include_file "blocker-role.cfg" +#!endif +## sanity ## +include_file "sanity.cfg" + +## auth ## +include_file "auth.cfg" ####### Permissions module ########## loadmodule "permissions.so" modparam("permissions", "db_url", "KAZOO_DB_URL") modparam("permissions", "db_mode", 1) - -####### DEBUG ###### -loadmodule "debugger.so" -modparam("debugger", "mod_hash_size", 5) -modparam("debugger", "mod_level_mode", 1) +###### local route ###### +tcp_children = 5 +listen=tcp:127.0.0.1:5090 ####### Routing Logic ######## route { - route(SANITY_CHECK); - - route(CHECK_RETRANS); + route(LOCAL_REQUEST); - # log the basic info regarding this call - xlog("L_INFO", "$ci|start|received $pr request $rm $ou\n"); - xlog("L_INFO", "$ci|log|source $si:$sp -> $Ri:$Rp\n"); - xlog("L_INFO", "$ci|log|from $fu\n"); - xlog("L_INFO", "$ci|log|to $tu\n"); + route(SANITY_CHECK); - route(CLASSIFY_SOURCE); + route(CHECK_RETRANS); #!ifdef ANTIFLOOD_ROLE - route(ANTIFLOOD_RATE_LIMIT); - route(ANITFLOOD_AUTH_LIMIT); + route(ANTIFLOOD_LIMIT); #!endif #!ifdef TRAFFIC_FILTER_ROLE @@ -322,12 +313,20 @@ route route(DOS_PREVENTION); #!endif - #!ifdef WEBSOCKETS_ROLE - route(HANDLE_WEBSOCKETS); + route(LOG_REQUEST); + + route(CLASSIFY_SOURCE); + + #!ifdef NAT_TRAVERSAL_ROLE + route(NAT_DETECT); #!endif route(HANDLE_OPTIONS); + #!ifdef SIP_TRACE_ROLE + route(SIP_TRACE); + #!endif + route(HANDLE_NOTIFY); #!ifdef AUTHORIZATION_ROLE @@ -345,6 +344,8 @@ route route(HANDLE_REGISTER); #!endif + route(HANDLE_REFER); + route(HANDLE_IN_DIALOG_REQUESTS); route(PREPARE_INITIAL_REQUESTS); @@ -359,13 +360,28 @@ route } #!endif - #!ifdef DISPATCHER_ROLE - if (!isflagset(FLAG_INTERNALLY_SOURCED)) { - route(DISPATCHER_FIND_ROUTES); + route(AUTH); + + route(SETUP); + +} + +#!trydef KZ_LOG_REQUEST_OPTIONS 0 +kazoo.log_request_options = KZ_LOG_REQUEST_OPTIONS descr "log OPTIONS requests, default is 0 for preserving log size" + +route[LOG_REQUEST] +{ + if($sel(cfg_get.kazoo.log_request_options) == 0 && is_method("OPTIONS")) { + $var(log_request_level) = L_DBG; + } else { + $var(log_request_level) = L_INFO; } - #!endif - route(RELAY); + # log the basic info regarding this call + xlog("$var(log_request_level)", "$ci|start|received $pr request $rm $ou\n"); + xlog("$var(log_request_level)", "$ci|log|source $si:$sp -> $RAi:$RAp\n"); + xlog("$var(log_request_level)", "$ci|log|from $fu\n"); + xlog("$var(log_request_level)", "$ci|log|to $tu\n"); } route[CHECK_RETRANS] @@ -380,114 +396,94 @@ route[CHECK_RETRANS] } } -route[SANITY_CHECK] -{ - ## CVE-2018-14767 - if($(hdr(To)[1]) != $null) { - xlog("second To header not null - dropping message"); - drop; - } - - if (!sanity_check()) { - xlog("L_WARN", "$ci|end|message from $si:$sp is insane\n"); - exit; - } - - if (!mf_process_maxfwd_header("10")) { - xlog("L_WARN", "$ci|end|too much hops, not enough barley from $si:$sp\n"); - send_reply("483", "Too Many Hops"); - exit; - } - - if ($ua == "friendly-scanner" || - $ua == "sundayddr" || - $ua == "pplsip" || - $ua =~ "NiceGuy" || - $ua =~ "PortSIP" || - $ua =~ "sipcli" ) { - xlog("L_WARN", "$ci|end|dropping message with user-agent $ua from $si:$sp\n"); - exit; - } -} - route[CLASSIFY_SOURCE] { #!ifdef DISPATCHER_ROLE route(DISPATCHER_CLASSIFY_SOURCE); #!endif - if (isflagset(FLAG_INTERNALLY_SOURCED) || allow_source_address(TRUSTED_ADR_GROUP) || is_myself($si)) { - xlog("L_INFO", "$ci|log|request from trusted IP\n"); + if (allow_source_address(TRUSTED_ADR_GROUP)) { + xlog("$var(log_request_level)", "$ci|log|request from trusted IP\n"); + setflag(FLAG_TRUSTED_SOURCE); + } + + if (isflagset(FLAG_INTERNALLY_SOURCED) || is_myself($si)) { setflag(FLAG_TRUSTED_SOURCE); } + } route[HANDLE_OPTIONS] { - if (is_method("OPTIONS")) { - if (isflagset(FLAG_INTERNALLY_SOURCED)) { - route(INTERNAL_TO_EXTERNAL_RELAY); - } else { - #!ifdef TRAFFIC_FILTER_ROLE - if (!isflagset(FLAG_TRUSTED_SOURCE)) { - route(FILTER_REQUEST_DOMAIN); - } - #!endif - - #!ifdef NAT_TRAVERSAL_ROLE - route(NAT_TEST_AND_CORRECT); - #!endif + if (!is_method("OPTIONS")) { + return; + } - sl_send_reply("200", "Rawr!!"); + if (isflagset(FLAG_INTERNALLY_SOURCED)) { + route(INTERNAL_TO_EXTERNAL_RELAY); + } else { + #!ifdef TRAFFIC_FILTER_ROLE + if (!isflagset(FLAG_TRUSTED_SOURCE)) { + route(FILTER_REQUEST_DOMAIN); } - exit; + #!endif + + sl_send_reply("200", "Rawr!!"); + + #!ifdef KEEPALIVE_ROLE + route(KEEPALIVE_ON_OPTIONS); + #!endif } + exit; } route[HANDLE_NOTIFY] { - if (has_totag()) - return; + if (!is_method("NOTIFY")) return; - if (is_method("NOTIFY")) { - if (isflagset(FLAG_INTERNALLY_SOURCED)) { - if (loose_route()) { - xlog("L_INFO", "$ci|log|Able to loose-route. Cool beans!\n"); - } + if (has_totag()) return; - #!ifdef REGISTRAR_ROLE - if (registered("location")) { - lookup("location"); - xlog("L_INFO", "$ci|log|routing to $ruid\n"); - } - #!endif + if (isflagset(FLAG_INTERNALLY_SOURCED)) { + if (loose_route()) { + xlog("L_INFO", "$ci|log|Able to loose-route. Cool beans!\n"); + } - route(INTERNAL_TO_EXTERNAL_RELAY); - } else { - #!ifdef TRAFFIC_FILTER_ROLE - if (!isflagset(FLAG_TRUSTED_SOURCE)) { - route(FILTER_REQUEST_DOMAIN); - } - #!endif + #!ifdef REGISTRAR_ROLE + if (registered("location")) { + lookup("location"); + xlog("L_INFO", "$ci|log|routing to $ruid\n"); + } + #!endif - #!ifdef WEBSOCKETS_ROLE - route(NAT_WEBSOCKETS_CORRECT); - #!endif + ## verify we're not routing to ourselves + if(is_myself($du)) { + xlog("L_INFO", "$ci|log|notify from internal to invalid destination $ruid\n"); + sl_send_reply("200", "Rawr!!"); + exit; + } - #!ifdef NAT_TRAVERSAL_ROLE - route(NAT_TEST_AND_CORRECT); - #!endif + route(INTERNAL_TO_EXTERNAL_RELAY); + } else { + #!ifdef TRAFFIC_FILTER_ROLE + if (!isflagset(FLAG_TRUSTED_SOURCE)) { + route(FILTER_REQUEST_DOMAIN); + } + #!endif - if($hdr(Event) == "keep-alive") { - xlog("L_INFO", "$ci|stop|replying to keep alive\n"); - sl_send_reply("405", "Stay Alive / Method Not Allowed"); - } else { - xlog("L_INFO", "$ci|stop|consuming event $hdr(Event)\n"); - sl_send_reply("200", "Rawr!!"); - } + if($hdr(Event) == "keep-alive") { + xlog("L_INFO", "$ci|stop|replying to keep alive\n"); + sl_send_reply("405", "Stay Alive / Method Not Allowed"); + } else { + xlog("L_INFO", "$ci|stop|consuming event $hdr(Event)\n"); + sl_send_reply("200", "Rawr!!"); } - exit; + + #!ifdef KEEPALIVE_ROLE + route(KEEPALIVE_ON_NOTIFY); + #!endif + } + exit; } route[HANDLE_MESSAGE] @@ -508,7 +504,7 @@ route[HANDLE_MESSAGE] exit(); #!endif } else { - xlog("L_WARN", "$ci|end|MESSAGE $(hdr(Content-Type))\n"); + xlog("L_WARN", "$ci|end|MESSAGE $hdr(Content-Type)\n"); if( $hdr(Content-Type) == "application/im-iscomposing+xml" ) { xlog("L_WARN", "$ci|end|dropping MESSAGE application/im-iscomposing+xml\n"); sl_send_reply("200", "OK"); @@ -532,54 +528,86 @@ route[HANDLE_MESSAGE] #!endif } +route[HANDLE_REFER] +{ + if (!is_method("REFER")) { + return; + } + + if(is_present_hf("Referred-By")) { + $var(referred_by) = $hdr(Referred-By); + } else { + $var(referred_by) = $_s(;created=true); + } + $xavp(regcfg=>match_received) = $su; + if(registered("location", "$rz:$Au", 2, 1) == 1) { + $var(referred_by) = $_s($var(referred_by);endpoint_id=$(xavp(ulattrs=>token){re.subst,/(.*)@(.*)/\1/});account_id=$(xavp(ulattrs=>token){re.subst,/(.*)@(.*)/\2/})); + } + + remove_hf_re("^Referred-By"); + append_hf("Referred-By: $var(referred_by)\r\n"); + +} route[HANDLE_IN_DIALOG_REQUESTS] { - if (has_totag()) { - if (is_method("INVITE")) { - setflag(FLAG_SESSION_PROGRESS); - record_route(); + if (!has_totag()) return; + + if (is_method("INVITE")) { + setflag(FLAG_SESSION_PROGRESS); + } + + if (loose_route()) { + + #!ifdef NAT_TRAVERSAL_ROLE + if(!isdsturiset()) { + handle_ruri_alias(); } - if (loose_route()) { + if ( is_method("ACK") ) { + # ACK is forwarded statelessly + route(NAT_MANAGE); + } + #!endif - #!ifdef ACCOUNTING_ROLE - if (is_method("BYE")) { - setflag(FLAG_ACC); - setflag(FLAG_ACCFAILED); - } - #!endif + #!ifdef ACCOUNTING_ROLE + if (is_method("BYE")) { + setflag(FLAG_ACC); + setflag(FLAG_ACCFAILED); + } + #!endif - #!ifdef NAT_TRAVERSAL_ROLE - if(!isdsturiset()) { - handle_ruri_alias(); - } - #!endif - - xlog("L_INFO", "$ci|log|loose_route in-dialog message\n"); - # Called on in-dialog requests - # If the request in an Invite for on hold from external to internal, - # associate the contact with the media server - # if Invite for on hold, we need to associate the contact URI with the next hop - if (is_method("INVITE") && !isflagset(FLAG_INTERNALLY_SOURCED) && is_audio_on_hold()) { - setflag(FLAG_ASSOCIATE_USER); - } - if ( is_method("NOTIFY") ) { - # Add Record-Route for in-dialog NOTIFY as per RFC 6665. - record_route(); - } - route(RELAY); - } else if (isflagset(FLAG_INTERNALLY_SOURCED)) { - xlog("L_INFO", "$ci|log|relay internally sourced in-dialog message without loose_route\n"); - route(RELAY); - } else if (t_check_trans()) { - xlog("L_INFO", "$ci|log|allow message for a known transaction\n"); - route(RELAY); - } else { - xlog("L_INFO", "$ci|log|message had a to-tag but can't be loose routed\n"); - sl_send_reply("481", "Call Leg/Transaction Does Not Exist"); + xlog("L_INFO", "$ci|log|loose_route in-dialog message\n"); + # Called on in-dialog requests + # If the request in an Invite for on hold from external to internal, + # associate the contact with the media server + # if Invite for on hold, we need to associate the contact URI with the next hop + if (is_method("INVITE") && !isflagset(FLAG_INTERNALLY_SOURCED) && is_audio_on_hold()) { + setflag(FLAG_ASSOCIATE_USER); } - exit(); + + # If the request in an Invite for t38 from internal, + # mark the request with FLT_T38 + if (is_method("INVITE") && isflagset(FLAG_INTERNALLY_SOURCED) && sdp_with_media("image")) { + xlog("L_DEBUG", "$ci|log|T38 RE-INVITE\n"); + setflag(FLT_T38); + } + + if ( is_method("NOTIFY") ) { + # Add Record-Route for in-dialog NOTIFY as per RFC 6665. + record_route(); + } + route(RELAY); + } else if (isflagset(FLAG_INTERNALLY_SOURCED)) { + xlog("L_INFO", "$ci|log|relay internally sourced in-dialog message without loose_route\n"); + route(RELAY); + } else if (t_check_trans()) { + xlog("L_INFO", "$ci|log|allow message for a known transaction\n"); + route(RELAY); + } else { + xlog("L_INFO", "$ci|log|message had a to-tag but can't be loose routed\n"); + sl_send_reply("481", "Call Leg/Transaction Does Not Exist"); } + exit(); } route[PREPARE_INITIAL_REQUESTS] @@ -628,12 +656,50 @@ route[PREPARE_INITIAL_REQUESTS] record_route(); } -route[RELAY] +route[SETUP] { - #!ifdef SIP_TRACE_ROLE - route(SEND_SIP_TRACE); + #!ifdef DISPATCHER_ROLE + if (!isflagset(FLAG_INTERNALLY_SOURCED)) { + route(DISPATCHER_FIND_ROUTES); + } + #!endif + + #!ifdef REGISTRAR_ROLE + if (isflagset(FLAG_INTERNALLY_SOURCED)) { + route(ROUTE_TO_AOR); + } + #!endif + + route(RELAY); +} + +route[BRANCH_HEADERS] +{ + if (!isflagset(FLAG_INTERNALLY_SOURCED)) { + route(AUTH_HEADERS); + } else { + remove_hf_re("^X-"); + } + +} + +# Manage outgoing branches +branch_route[MANAGE_BRANCH] { + xlog("L_DEBUG", "$ci|branch|new branch [$T_branch_idx] to $ru => $du\n"); + #!ifdef NAT_TRAVERSAL_ROLE + route(NAT_MANAGE); #!endif + route(BRANCH_HEADERS); + +} + +route[RELAY] +{ + if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) { + if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH"); + } + if (isflagset(FLAG_INTERNALLY_SOURCED)) { route(INTERNAL_TO_EXTERNAL_RELAY); #!ifdef MESSAGE_ROLE @@ -656,30 +722,6 @@ route[INTERNAL_TO_EXTERNAL_RELAY] } #!endif - #!ifdef REGISTRAR_ROLE - if ($hdr(X-KAZOO-AOR) != $null) { - xlog("L_INFO", "$ci|log|using AOR $hdr(X-KAZOO-AOR)\n"); - if ($hdr(X-KAZOO-INVITE-FORMAT) == "contact") { - if(lookup("location", "$hdr(X-KAZOO-AOR)") > 0){ - xlog("L_INFO", "$ci|end|routing to contact $ru\n"); - } else { - xlog("L_INFO", "$ci|end|lookup for AOR $hdr(X-KAZOO-AOR) failed\n"); - sl_send_reply("410", "Not registered"); - exit; - } - } else if (reg_fetch_contacts("location", "$hdr(X-KAZOO-AOR)", "callee")) { - $du = $(ulc(callee=>received)); - $fs = $(ulc(callee=>socket)); - xlog("L_INFO", "$ci|log|routing $hdr(X-KAZOO-AOR) to $du via $fs\n"); - } else { - xlog("L_INFO", "$ci|end|user is not registered\n"); - sl_send_reply("410", "Not registered"); - exit; - } - } - #!endif - - remove_hf_re("^X-.*"); t_on_reply("EXTERNAL_REPLY"); @@ -697,21 +739,6 @@ route[EXTERNAL_TO_INTERNAL_RELAY] } #!endif - #!ifdef NAT_TRAVERSAL_ROLE - if (!isflagset(FLAG_INTERNALLY_SOURCED)) { - route(NAT_TEST_AND_CORRECT); - } - #!endif - - remove_hf_re("^X-.*"); - - append_hf("X-AUTH-IP: $si\r\n"); - append_hf("X-AUTH-PORT: $sp\r\n"); - - #!ifdef REGISTRAR_ROLE - route(ADD_AUTHORIZATION_HEADERS); - #!endif - t_on_reply("INTERNAL_REPLY"); t_on_failure("INTERNAL_FAULT"); @@ -722,14 +749,12 @@ route[EXTERNAL_TO_INTERNAL_RELAY] onreply_route[EXTERNAL_REPLY] { - xlog("L_INFO", "$ci|log|external reply $T_reply_code\n"); - - #!ifdef WEBSOCKETS_ROLE - route(NAT_WEBSOCKETS_CORRECT); - #!endif + xlog("L_INFO", "$ci|log|external reply $T_reply_code $T_reply_reason\n"); #!ifdef NAT_TRAVERSAL_ROLE - route(NAT_TEST_AND_CORRECT); + if(status=~"[12][0-9][0-9]") { + route(NAT_MANAGE); + } #!endif #!ifdef ACL_ROLE @@ -741,7 +766,7 @@ onreply_route[EXTERNAL_REPLY] setflag(FLAG_IS_REPLY); route(DOS_PREVENTION); #!endif - + #!ifdef MESSAGE_ROLE if (is_method("MESSAGE")) { route(MESSAGE_REPLY); @@ -752,11 +777,15 @@ onreply_route[EXTERNAL_REPLY] onreply_route[INTERNAL_REPLY] { # this route handles replies that are comming from our media server - xlog("L_INFO", "$ci|start|received internal reply $T_reply_code $rr\n"); - xlog("L_INFO", "$ci|log|source $si:$sp\n"); + if ($rs < 300) { + xlog("L_INFO", "$ci|log|internal reply $T_reply_code $T_reply_reason\n"); + xlog("L_DEBUG", "$ci|log|source $si:$sp\n"); + } - #!ifdef WEBSOCKETS_ROLE - route(NAT_WEBSOCKETS_CORRECT); + #!ifdef NAT_TRAVERSAL_ROLE + if(status=~"[12][0-9][0-9]") { + route(NAT_MANAGE); + } #!endif #!ifdef ACL_ROLE @@ -787,7 +816,6 @@ onreply_route[INTERNAL_REPLY] xlog("L_INFO", "$ci|pass|$T_req($si):$T_req($sp)\n"); } - $var(reply_reason) = $rr; } failure_route[INTERNAL_FAULT] @@ -824,32 +852,34 @@ failure_route[INTERNAL_FAULT] # change 6xx to 4xx if (t_check_status("6[0-9][0-9]") && !t_check_status("600|603|604|606")) { $var(new_code) = "4" + $(T_reply_code{s.substr,1,0}); - xlog("L_INFO", "$ci|log|sending 6XX reply as $var(new_code) $var(reply_reason)\n"); - t_reply("$(var(new_code){s.int})", "$var(reply_reason)"); + xlog("L_INFO", "$ci|failure|sending $T_reply_code reply as $var(new_code) $T_reply_reason\n"); + t_reply("$(var(new_code){s.int})", "$T_reply_reason"); # if the failure case was something that we should recover # from then try to find a new media server - } else if ($var(reply_reason) =~ "call barred") { - xlog("L_INFO", "$ci|log|failure route ignoring call barred\n"); + } else if ($T_reply_reason =~ "call barred") { + xlog("L_INFO", "$ci|failure|ignoring call barred\n"); } else if (isflagset(FLAG_SESSION_PROGRESS)) { - xlog("L_INFO", "$ci|log|failure route ignoring failure after session progress\n"); - } else if (t_check_status("403") && $var(reply_reason)=="Forbidden") { - xlog("L_WARNING", "$ci|log|failure route ignoring. Failed auth from IP $si\n"); - } else if (t_check_status("(401)|(407)|(486)|(403)")) { - xlog("L_INFO", "$ci|log|failure route ignoring auth reply $T_reply_code $var(reply_reason)\n"); + xlog("L_INFO", "$ci|failure|ignoring failure after session progress\n"); + } else if (t_check_status("403") && $T_reply_reason=="Forbidden") { + xlog("L_WARNING", "$ci|failure|Failed auth from IP $si\n"); + } else if (t_check_status("(401)|(407)|(486)")) { + xlog("L_INFO", "$ci|failure|auth reply $T_reply_code $T_reply_reason\n"); } else if (t_check_status("402")) { - xlog("L_INFO", "$ci|log|failure route overriding reply code 402 with 486\n"); + xlog("L_INFO", "$ci|failure|overriding reply code 402 with 486\n"); send_reply("486", "Insufficient Funds"); } else if (t_check_status("(4[0-9][0-9])|(5[0-9][0-9])")) { - xlog("L_INFO", "$ci|start|received failure reply $T_reply_code $rr\n"); + xlog("L_INFO", "$ci|failure|internal reply $T_reply_code $T_reply_reason\n"); #!ifdef DISPATCHER_ROLE route(DISPATCHER_NEXT_ROUTE); #!endif send_reply("486", "Unable to Comply"); + } else { - xlog("L_INFO", "$ci|log|failure route ignoring reply $T_reply_code $rr\n"); + xlog("L_INFO", "$ci|failure|internal reply $T_reply_code $T_reply_reason\n"); + send_reply("$T_reply_code", "$T_reply_reason"); } xlog("L_INFO", "$ci|pass|$si:$sp\n"); } @@ -881,53 +911,94 @@ onsend_route { $sht(associations=>$var(user_source))= "sip:" + $sndto(ip) + ":" + $sndto(port); } - xlog("L_INFO", "$ci|pass|$sndfrom(ip):$sndfrom(port) -> $sndto(ip):$sndto(port)\n"); + #!ifdef SIP_TRACE_ROLE + if (is_method("ACK") && isflagset(FLAG_SIP_TRACE)) { + sip_trace(); + } + #!endif + + if(!isflagset(FLAG_LOCAL_ROUTE)) { + xlog("L_INFO", "$ci|pass|$sndfrom(ip):$sndfrom(port) -> $sndto(ip):$sndto(port)\n"); + } } -#!ifdef REGISTRAR_ROLE -route[ADD_AUTHORIZATION_HEADERS] +route[ROUTE_TO_AOR] { - if (!is_method("INVITE|MESSAGE|REFER")) { + if ($hdr(X-KAZOO-AOR) == $null) { return; } - $xavp(regcfg=>match_received) = $su; - if (registered("location","$fu", 2, 1) == 1) { - if($(xavp(ulattrs=>custom_channel_vars){kz.json,Account-ID}{s.len}) > 0) - append_hf("X-ecallmgr_Account-ID: $(xavp(ulattrs=>custom_channel_vars){kz.json,Account-ID})\r\n"); - - if($(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-Type}{s.len}) > 0) - append_hf("X-ecallmgr_Authorizing-Type: $(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-Type})\r\n"); - - if($(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-ID}{s.len}) > 0) - append_hf("X-ecallmgr_Authorizing-ID: $(xavp(ulattrs=>custom_channel_vars){kz.json,Authorizing-ID})\r\n"); - - if($(xavp(ulattrs=>custom_channel_vars){kz.json,Username}{s.len}) > 0) - append_hf("X-ecallmgr_Username: $(xavp(ulattrs=>custom_channel_vars){kz.json,Username})\r\n"); - - if($(xavp(ulattrs=>custom_channel_vars){kz.json,Realm}{s.len}) > 0) - append_hf("X-ecallmgr_Realm: $(xavp(ulattrs=>custom_channel_vars){kz.json,Realm})\r\n"); - - if($(xavp(ulattrs=>custom_channel_vars){kz.json,Account-Realm}{s.len}) > 0) - append_hf("X-ecallmgr_Account-Realm: $(xavp(ulattrs=>custom_channel_vars){kz.json,Account-Realm})\r\n"); - - if($(xavp(ulattrs=>custom_channel_vars){kz.json,Account-Name}{s.len}) > 0) - append_hf("X-ecallmgr_Account-Name: $(xavp(ulattrs=>custom_channel_vars){kz.json,Account-Name})\r\n"); - - if($(xavp(ulattrs=>custom_channel_vars){kz.json,Presence-ID}{s.len}) > 0) - append_hf("X-ecallmgr_Presence-ID: $(xavp(ulattrs=>custom_channel_vars){kz.json,Presence-ID})\r\n"); - - if($(xavp(ulattrs=>custom_channel_vars){kz.json,Owner-ID}{s.len}) > 0) - append_hf("X-ecallmgr_Owner-ID: $(xavp(ulattrs=>custom_channel_vars){kz.json,Owner-ID})\r\n"); - } + xlog("L_INFO", "$ci|log|using AOR $hdr(X-KAZOO-AOR)\n"); + if ($hdr(X-KAZOO-INVITE-FORMAT) == "contact") { + if(lookup("location", "$hdr(X-KAZOO-AOR)") > 0){ + xlog("L_INFO", "$ci|end|routing to contact $ru\n"); + handle_ruri_alias(); + } else { + xlog("L_INFO", "$ci|end|lookup for AOR $hdr(X-KAZOO-AOR) failed\n"); + sl_send_reply("410", "Not registered"); + exit; + } + } else if (reg_fetch_contacts("location", "$hdr(X-KAZOO-AOR)", "callee")) { + $du = $(ulc(callee=>received)); + $fs = $(ulc(callee=>socket)); + xlog("L_INFO", "$ci|log|routing $hdr(X-KAZOO-AOR) to $du via $fs\n"); + + } else { + xlog("L_INFO", "$ci|end|user is not registered\n"); + sl_send_reply("410", "Not registered"); + exit; + } } -#!endif -#!ifdef PRESENCE_NOTIFY_INIT event_route[tm:local-request] { - route(PRESENCE_LOCAL_NOTIFY); + setflag(FLAG_LOCAL_REQUEST); + xlog("L_DEBUG", "$ci|local|start $pr request $rm $ou\n"); + xlog("L_DEBUG", "$ci|local|source $si:$sp -> $dd:$dp\n"); + xlog("L_DEBUG", "$ci|local|from $fu\n"); + xlog("L_DEBUG", "$ci|local|to $tu\n"); + + #!ifdef SIP_TRACE_ROLE + route(SIP_TRACE); + #!endif + + #!ifdef PRESENCE_ROLE + route(PRESENCE_LOCAL_REQUEST); + #!endif + } -#!endif + +event_route[evrexec:DEFERRED_INIT] +{ + xlog("L_INFO", "processing deferred init\n"); + + #!ifdef PRESENCE_ROLE + route(PRESENCE_DEFERRED_INIT); + #!endif + + #!import_file "custom-init.cfg" + +} + +route[LOCAL_REQUEST] +{ + if(src_ip != myself || $hdr(X-TM-Local) == $null) { + return; + } + + xlog("L_DEBUG", "internal route $hdr(X-TM-Local)\n"); + + setflag(FLAG_LOCAL_ROUTE); + + #!ifdef SIP_TRACE_ROLE + route(SIP_TRACE); + #!endif + + $var(LocalRoute) = $hdr(X-TM-Local); + remove_hf_re("^X-TM-Local"); + route_if_exists("$var(LocalRoute)"); + exit; +} + # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab diff --git a/kamailio/defs-amqp.cfg b/kamailio/defs-amqp.cfg new file mode 100644 index 0000000..fa116c9 --- /dev/null +++ b/kamailio/defs-amqp.cfg @@ -0,0 +1,88 @@ +## NOTE: DO NOT CHANGE THIS FILE, EDIT local.cfg ## + + +####### amqp defs ######## +#!ifndef AMQP_DEFAULTS_INCLUDED +#!define AMQP_DEFAULTS_INCLUDED + +#!trydef MY_AMQP_MAX_CHANNELS 25 +#!trydef MY_AMQP_CONSUMER_PROCESSES 4 +#!trydef MY_AMQP_CONSUMER_WORKERS 16 +#!trydef MY_AMQP_HEARTBEATS 5 + +#!ifndef MY_AMQP_ZONE +#!substdef "!MY_AMQP_ZONE!local!g" +#!endif + +#!ifdef PRESENCE_ROLE +#!trydef MY_AMQP_PUA_MODE 1 +#!else +#!trydef MY_AMQP_PUA_MODE 0 +#!endif + +#!ifndef MY_AMQP_URL +#!ifdef AMQP_URL1 +#!substdef "!MY_AMQP_URL!$def(AMQP_URL1)!g" +#!else +#!substdef "!MY_AMQP_URL!amqp://guest:guest@127.0.0.1:5672!g" +#!endif +#!endif + +#!ifndef MY_AMQP_SECONDARY_URL +#!ifdef AMQP_URL2 +#!substdef "!MY_AMQP_SECONDARY_URL!$def(AMQP_URL2)!g" +#!endif +#!endif + +#!ifndef MY_AMQP_TERTIARY_URL +#!ifdef AMQP_URL3 +#!substdef "!MY_AMQP_TERTIARY_URL!$def(AMQP_URL3)!g" +#!endif +#!endif + +#!ifndef MY_AMQP_QUATERNARY_URL +#!ifdef AMQP_URL4 +#!substdef "!MY_AMQP_QUATERNARY_URL!$def(AMQP_URL4)!g" +#!endif +#!endif + +#!ifndef MY_AMQP_QUINARY_URL +#!ifdef AMQP_URL5 +#!substdef "!MY_AMQP_QUINARY_URL!$def(AMQP_URL5)!g" +#!endif +#!endif + +#!ifndef MY_AMQP_SENARY_URL +#!ifdef AMQP_URL6 +#!substdef "!MY_AMQP_SENARY_URL!$def(AMQP_URL6)!g" +#!endif +#!endif + +#!ifndef MY_AMQP_SEPTENARY_URL +#!ifdef AMQP_URL7 +#!substdef "!MY_AMQP_SEPTENARY_URL!$def(AMQP_URL7)!g" +#!endif +#!endif + +#!ifndef MY_AMQP_OCTONARY_URL +#!ifdef AMQP_URL8 +#!substdef "!MY_AMQP_OCTONARY_URL!$def(AMQP_URL8)!g" +#!endif +#!endif + +#!ifndef MY_AMQP_NONARY_URL +#!ifdef AMQP_URL9 +#!substdef "!MY_AMQP_NONARY_URL!$def(AMQP_URL9)!g" +#!endif +#!endif + +#!ifndef MY_AMQP_DENARY_URL +#!ifdef AMQP_URL10 +#!substdef "!MY_AMQP_DENARY_URL!$def(AMQP_URL10)!g" +#!endif +#!endif + + +#!endif + +# vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab diff --git a/kamailio/defs.cfg b/kamailio/defs.cfg index 2cb4cc7..753828b 100644 --- a/kamailio/defs.cfg +++ b/kamailio/defs.cfg @@ -2,125 +2,69 @@ ####### defs ######## +#!ifndef DEFAULTS_INCLUDED +#!define DEFAULTS_INCLUDED -#!ifndef KAZOO_LOG_LEVEL -#!define KAZOO_LOG_LEVEL L_INFO +#!ifndef MY_HOSTNAME +#!substdef "!MY_HOSTNAME!$HN(f)!g" #!endif -#!ifndef KAMAILIO_DBMS -#!substdef "!KAMAILIO_DBMS!kazoo!g" -#!endif - -#!ifndef KAZOO_DATA_DIR -#!substdef "!KAZOO_DATA_DIR!/etc/kazoo/kamailio/db!g" -#!endif - -#!ifndef KAZOO_DB_URL -#!substdef "!KAZOO_DB_URL!kazoo:///KAZOO_DATA_DIR/kazoo.db!g" -#!endif - -#!ifndef MAX_WHILE_LOOPS -#!substdef "!MAX_WHILE_LOOPS!500!g" -#!endif - -#!ifndef CHILDREN -#!define CHILDREN 25 -#!endif - -#!ifndef TCP_CHILDREN -#!define TCP_CHILDREN 25 +#!ifndef MY_IP_ADDRESS +#!ifdef MY_LOCAL_IP +#!substdef "!MY_IP_ADDRESS!$def(MY_LOCAL_IP)!g" +#!else +#!substdef "!MY_IP_ADDRESS!$HN(i)!g" #!endif - -#!ifndef OPENBTS_AUTH_SECRET -#!substdef "!OPENBTS_AUTH_SECRET!b3a54fa8317c7d9cb1d89d8970947b30eda273124d97fc3a079ccc98ecc2569b!g" #!endif -#!ifndef KZ_USE_DISPATCHER_LIST -#!trydef KZ_USE_DISPATCHER_TABLE +#!ifndef WEBSOCKET_NO_ORIGIN_RESTRICTION +#!ifndef MY_WEBSOCKET_DOMAIN +#!substdef "!MY_WEBSOCKET_DOMAIN!$HN(d)!g" #!endif - -#!ifndef ANTIFLOOD_CACHE_PERIOD -#!substdef "!ANTIFLOOD_CACHE_PERIOD!600!g" #!endif -#!ifndef MY_AMQP_MAX_CHANNELS -#!define MY_AMQP_MAX_CHANNELS 25 -#!endif +#!trydef KAZOO_LOG_LEVEL L_INFO -#!ifndef MY_AMQP_CONSUMER_PROCESSES -#!define MY_AMQP_CONSUMER_PROCESSES 4 -#!endif +#!trydef PV_BUFFER_SIZE 16384 +#!trydef PV_BUFFER_SLOTS 30 -#!ifndef MY_AMQP_CONSUMER_WORKERS -#!define MY_AMQP_CONSUMER_WORKERS 16 -#!endif +#!trydef KZ_DB_MODULE kazoo +#!substdef "!KAMAILIO_DBMS!$def(KZ_DB_MODULE)!g" -#!ifndef MY_AMQP_HEARTBEATS -#!define MY_AMQP_HEARTBEATS 5 +#!ifndef KAZOO_DATA_DIR +#!substdef "!KAZOO_DATA_DIR!/etc/kazoo/kamailio/db!g" #!endif -#!ifndef BLF_USE_SINGLE_DIALOG -#!define BLF_USE_SINGLE_DIALOG 1 +#!ifndef KAZOO_DB_URL +#!substdef "!KAZOO_DB_URL!kazoo:///KAZOO_DATA_DIR/kazoo.db!g" #!endif -#!ifndef MY_AMQP_ZONE -#!substdef "!MY_AMQP_ZONE!local!g" +#!ifndef MAX_WHILE_LOOPS +#!substdef "!MAX_WHILE_LOOPS!500!g" #!endif -#!ifndef DISPATCHER_ADD_SERVERS -#!define DISPATCHER_ADD_SERVERS 1 -#!endif +#### tcp parameters ## +#!trydef CHILDREN 25 +#!trydef TCP_CHILDREN 25 -#!ifndef PRESENCE_RESET_BLF_DEFER_UPDATE -#!define PRESENCE_RESET_BLF_DEFER_UPDATE 0 -#!endif +#!trydef TCP_MAX_CONNECTIONS 4096 +#!trydef TCP_CONNECTION_LIFETIME 60 +#!trydef TCP_CONNECTION_TIMEOUT 5 +#!trydef TCP_KEEP_ALIVE yes +#!trydef TCP_KEEP_COUNT 3 +#!trydef TCP_KEEP_IDLE 30 +#!trydef TCP_KEEP_INTERVAL 5 +#!trydef TCP_SEND_TIMEOUT 3 -#!ifndef MY_AMQP_PUA_MODE -#!ifdef PRESENCE_ROLE -#!define MY_AMQP_PUA_MODE 1 -#!else -#!define MY_AMQP_PUA_MODE 0 -#!endif -#!endif +#!include_file "defs-amqp.cfg" #!ifndef MEDIA_SERVERS_HASH_SIZE #!substdef "!MEDIA_SERVERS_HASH_SIZE!256!g" #!endif -#!ifndef KZ_PRESENCE_AMQP_PUBLISH -#!define KZ_PRESENCE_AMQP_PUBLISH 0 -#!endif - -#!ifndef KZ_PRESENCE_REQUEST_RESUBSCRIBE_PROBE -#!define KZ_PRESENCE_REQUEST_RESUBSCRIBE_PROBE 0 -#!endif - -#!ifndef KZ_PRESENCE_REQUEST_PROBE -#!define KZ_PRESENCE_REQUEST_PROBE 1 -#!endif - -#!ifndef NAT_UAC_TEST_LEVEL -#!substdef "!NAT_UAC_TEST_LEVEL!3!g" -#!endif - -################################# -## Defs related to SIP_TRACE_ROLE -## -#!ifdef SIP_TRACE_ROLE -#!trydef KZ_TRACE 0 -#!trydef KZ_TRACE_INTERNAL 1 -#!trydef KZ_TRACE_EXTERNAL 1 -#!trydef KZ_TRACE_INTERNAL_INCOMING 1 -#!trydef KZ_TRACE_INTERNAL_OUTGOING 1 -#!trydef KZ_TRACE_EXTERNAL_INCOMING 1 -#!trydef KZ_TRACE_EXTERNAL_OUTGOING 1 -#!ifndef SIP_TRACE_URI -#!substdef "!SIP_TRACE_URI!sip:127.0.0.1:9060!g" -#!endif -#!ifndef HEP_CAPTURE_ID -#!substdef "!HEP_CAPTURE_ID!1!g" -#!endif -#!endif +#!trydef RR_FULL_LR 1 +#!trydef RR_DOUBLE_RR 1 +#!trydef RR_FORCE_SOCKET 1 #!ifndef KZ_DISABLE_WEBSOCKETS_REGISTRAR_PORT #!trydef KZ_WEBSOCKETS_REGISTRAR_PORT 7000 @@ -130,7 +74,9 @@ #!trydef KZ_TLS_REGISTRAR_PORT 7000 #!endif -#!trydef KZ_FAST_PICKUP_COOKIES 1 -#!trydef KZ_FAST_PICKUP_REALTIME 1 +#!trydef KZ_MULTI_HOMED 0 + + +#!endif # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab diff --git a/kamailio/dispatcher-role.cfg b/kamailio/dispatcher-role.cfg index 24b26cc..46bd6b4 100644 --- a/kamailio/dispatcher-role.cfg +++ b/kamailio/dispatcher-role.cfg @@ -1,7 +1,31 @@ ######## Generic Hash Table container in shared memory ######## modparam("htable", "htable", "failover=>size=16;autoexpire=120") +#!trydef KZ_DISPATCHER_PROBE_MODE 1 +#!trydef DISPATCHER_ADD_SERVERS 1 +#!trydef DISPATCHER_ADD_SECONDARY_IP 1 +#!trydef DISPATCHER_SECONDARY_IP_GROUP 3 +#!trydef DISPATCHER_ALG 0 +#!trydef KZ_DISPATCHER_ADD_FLAGS 10 +#!trydef KZ_DISPATCHER_PRIMARY_GROUP 1 +#!trydef KZ_DISPATCHER_SECONDARY_GROUP 2 +#!trydef KZ_DISPATCHER_CLASSIFY_GROUP 3 +#!trydef KZ_DISPATCHER_ALTNET1_PRIMARY_GROUP 51 +#!trydef KZ_DISPATCHER_ALTNET1_SECONDARY_GROUP 52 +#!trydef KZ_DISPATCHER_ALTNET2_PRIMARY_GROUP 53 +#!trydef KZ_DISPATCHER_ALTNET2_SECONDARY_GROUP 54 +#!trydef KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP 10 +#!trydef KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP 11 +#!trydef KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP 20 +#!trydef KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP 21 + kazoo.dispatcher_auto_add = DISPATCHER_ADD_SERVERS descr "adds media servers reported by ecallmgr" +kazoo.dispatcher_add_secondary_ip = DISPATCHER_ADD_SECONDARY_IP descr "adds internal ip from media servers reported by ecallmgr" +kazoo.dispatcher_add_secondary_ip_group = DISPATCHER_SECONDARY_IP_GROUP descr "sets the group where to add internal ip from media servers reported by ecallmgr" + +kazoo.dispatcher_algorithm = DISPATCHER_ALG descr "dispatcher algorithm to use" +kazoo.dispatcher_primary_group = KZ_DISPATCHER_PRIMARY_GROUP descr "dispatcher primary group" +kazoo.dispatcher_secondary_group = KZ_DISPATCHER_SECONDARY_GROUP descr "dispatcher secondary group" ####### Dispatcher module ######## loadmodule "dispatcher.so" @@ -18,7 +42,7 @@ modparam("dispatcher", "setid_pvname", "$var(setid)") modparam("dispatcher", "ds_ping_method", "OPTIONS") modparam("dispatcher", "ds_ping_interval", 10) modparam("dispatcher", "ds_probing_threshold", 3) -modparam("dispatcher", "ds_probing_mode", 1) +modparam("dispatcher", "ds_probing_mode", KZ_DISPATCHER_PROBE_MODE) modparam("dispatcher", "ds_ping_reply_codes", "501,403,404,400,200") modparam("dispatcher", "ds_ping_from", "sip:sipcheck@MY_HOSTNAME") @@ -31,25 +55,34 @@ modparam("dispatcher", "ds_ping_from", "sip:sipcheck@MY_HOSTNAME") ## 10 - Presence servers (if not locally handled) ## 20 - Registrar servers (if not locally handled) + +modparam("rtimer", "timer", "name=dispatcher_reload;interval=20;mode=1;") +modparam("rtimer", "exec", "timer=dispatcher_reload;route=DISPATCHER_RELOAD") + ####### Dispatcher Logic ######## route[DISPATCHER_CLASSIFY_SOURCE] { #!import_file "dispatcher-network-classify.cfg" - + if (is_myself("$ou")) { - xlog("L_INFO", "$ci|log|original R-URI ($ou) is this proxy, treating as external sources\n"); + xlog("$var(log_request_level)", "$ci|log|original R-URI ($ou) is this proxy, treating as external sources\n"); } else if ( - ds_is_from_list(1, 3) || - ds_is_from_list(2, 3) || - ds_is_from_list(3, 3) || - ds_is_from_list(10, 3) || - ds_is_from_list(20, 3) + ds_is_from_list(KZ_DISPATCHER_PRIMARY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_SECONDARY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_CLASSIFY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_ALTNET1_PRIMARY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_ALTNET1_SECONDARY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_ALTNET2_PRIMARY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_ALTNET2_SECONDARY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP, 3) || + ds_is_from_list(KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP, 3) ) { - xlog("L_INFO", "$ci|log|originated from internal sources\n"); - + xlog("$var(log_request_level)", "$ci|log|originated from internal sources\n"); setflag(FLAG_INTERNALLY_SOURCED); } else { - xlog("L_INFO", "$ci|log|originated from external sources\n"); + xlog("$var(log_request_level)", "$ci|log|originated from external sources\n"); } } @@ -62,34 +95,36 @@ route[DISPATCHER_FIND_ROUTES] return; } - $var(ds_primary_group) = 1; - $var(ds_backup_group) = 2; - + $var(ds_primary_group) = $sel(cfg_get.kazoo.dispatcher_primary_group); + $var(ds_backup_group) = $sel(cfg_get.kazoo.dispatcher_secondary_group); + #!ifndef PRESENCE_ROLE if (is_method("SUBSCRIBE")) { - $var(ds_primary_group) = 10; - $var(ds_backup_group) = 11; + $var(ds_primary_group) = KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP; + $var(ds_backup_group) = KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP; + add_path(); } #!endif #!ifndef REGISTRAR_ROLE if (is_method("REGISTER")) { - $var(ds_primary_group) = 20; - $var(ds_backup_group) = 21; + $var(ds_primary_group) = KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP; + $var(ds_backup_group) = KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP; + add_path(); } #!endif - #!ifdef FAST_PICKUP_ROLE - route(FAST_PICKUP_ATTEMPT); + #!ifdef PRESENCE_ROLE + route(PRESENCE_FAST_PICKUP_ATTEMPT); #!endif #!import_file "dispatcher-network-find.cfg" $var(ds_group) = $var(ds_primary_group); - if (!ds_select_dst("$var(ds_primary_group)", "0") || $(avp(ds_dst)[0]) == $null) { + if (!ds_select_dst("$var(ds_primary_group)", "$sel(cfg_get.kazoo.dispatcher_algorithm)") || $(avp(ds_dst)[0]) == $null) { # we selected from primary group, try again in backup group - if (!ds_select_dst("$var(ds_backup_group)", "0") || $(avp(ds_dst)[0]) == $null) { + if (!ds_select_dst("$var(ds_backup_group)", "$sel(cfg_get.kazoo.dispatcher_algorithm)") || $(avp(ds_dst)[0]) == $null) { xlog("L_WARN", "$ci|end|no servers available in primary or backup group\n"); sl_send_reply("480", "All servers busy"); exit; @@ -111,7 +146,7 @@ route[DISPATCHER_FIND_ROUTES] $var(prefered_route) = $sht(associations=>$var(user_source)); xlog("L_INFO", "$ci|log|found association for contact uri $var(user_source)\n"); if (!route(DISPATCHER_REORDER_ROUTES)) { - $sht(associations=>$var(association)) = $null; + $sht(associations=>$var(user_source)) = $null; } } } @@ -198,12 +233,8 @@ route[DISPATCHER_NEXT_ROUTE] # reset the final reply timer $avp(final_reply_timer) = 3; - t_on_reply("INTERNAL_REPLY"); - - t_on_failure("INTERNAL_FAULT"); - # relay the request to the new media server - route(EXTERNAL_TO_INTERNAL_RELAY); + route(RELAY); exit(); } @@ -211,28 +242,57 @@ route[DISPATCHER_NEXT_ROUTE] event_route[dispatcher:dst-down] { - xlog("L_ERR", "Destination down: $ru\n"); + xlog("L_WARNING", "Destination down: $ru\n"); } event_route[dispatcher:dst-up] { - xlog("L_WARNING", "Destination up: $ru\n"); + xlog("L_NOTICE", "Destination up: $ru\n"); } route[DISPATCHER_CHECK_MEDIA_SERVER] { - if(@cfg_get.kazoo.dispatcher_auto_add == 1) { + $var(check_media_server_ret) = 0; + if($sel(cfg_get.kazoo.dispatcher_auto_add) == 1) { $var(SetId) = 1; if($var(Zone) != "MY_AMQP_ZONE") { $var(SetId) = 2; } + $var(flags) = KZ_DISPATCHER_ADD_FLAGS; + $var(attrs) = $_s(zone=$var(Zone);profile=$var(MediaProfile);idx=$(var(MediaUrl){s.corehash, MEDIA_SERVERS_HASH_SIZE});node=$var(MediaName)); + #!import_file "dispatcher-custom-media-check.cfg" sql_query("exec", "KZQ_CHECK_MEDIA_SERVER_INSERT"); if($sqlrows(exec) > 0) { - xlog("L_WARNING", "reloading dispatcher table\n"); - ds_reload(); + $shv(dispatcher_reload) = 1; + $var(check_media_server_ret) = 1; + } + + if($sel(cfg_get.kazoo.dispatcher_add_secondary_ip) == 1) { + if($var(MediaIP) != "" && $var(MediaIP) != $(var(MediaUrl){uri.host})) { + $var(MediaUrlBack) = $var(MediaUrl); + $var(MediaUrl) = $_s($(var(MediaUrlBack){uri.scheme}):$var(MediaIP):$(var(MediaUrlBack){uri.port})); + $var(SetId) = $sel(cfg_get.kazoo.dispatcher_add_secondary_ip_group); + sql_query("exec", "KZQ_CHECK_MEDIA_SERVER_INSERT"); + if($sqlrows(exec) > 0) { + $shv(dispatcher_reload) = 1; + $var(check_media_server_ret) = 1; + } + $var(MediaUrl) = $var(MediaUrlBack); + } } + } + return $var(check_media_server_ret); +} + +route[DISPATCHER_RELOAD] +{ + if($shv(dispatcher_reload) == 1) { + xlog("L_NOTICE", "reloading dispatcher table\n"); + ds_reload(); + }; + $shv(dispatcher_reload) = 0; } route[DISPATCHER_STATUS] diff --git a/kamailio/kamailio.cfg b/kamailio/kamailio.cfg index 2528128..e515b9b 100644 --- a/kamailio/kamailio.cfg +++ b/kamailio/kamailio.cfg @@ -10,6 +10,7 @@ #!define L_NOTICE 1 #!define L_INFO 2 #!define L_DBG 3 +#!define L_DEBUG 4 #!define AVP_RECV_PARAM "recv_param" #!define AVP_LOG_LEVEL "log_level" @@ -23,15 +24,26 @@ #!define FLB_UAC_REDIRECT 3 #!define TRUSTED_ADR_GROUP 1 +################ +# Kamailio modules to help substdef setup +# these need to go before local.cfg +# so they can be used +# +# ipops - ip , domain, hostname +# pv - $def(existing definition) +# +# +################ +loadmodule "ipops.so" +loadmodule "pv.so" + + ####### Local Configuration ######## include_file "local.cfg" ####### defaults not configured in local ######## include_file "defs.cfg" -####### DBMS query selection ######## -include_file "db_queries_KAMAILIO_DBMS.cfg" - ####### Default Configuration ###### include_file "default.cfg" diff --git a/kamailio/kazoo-bindings.cfg b/kamailio/kazoo-bindings.cfg index c4c520f..d63bcf7 100644 --- a/kamailio/kazoo-bindings.cfg +++ b/kamailio/kazoo-bindings.cfg @@ -1,21 +1,49 @@ ######## kazoo bindings ######## -### use this simple form of binding a listener -### kazoo_subscribe("dialoginfo", "direct", "BLF-QUEUE-MY_HOSTNAME", "BLF-MY_HOSTNAME"); -### -### or unleash the power of rabbit to kazoo-blf -### -### 'no_ack' : 1 => needs ack, -### 'wait_for_consumer_ack' -### : 1 => when it receives, it processses on the AMQP Worker ad after that it confirms -### : 0 => when it receives, it acks then processes in the AMQP Worker -### only works if no_ack : 0 -### -### Rabbit Policy for ha-mode -### pattern : ^BLF -### definition : ha-mode: all ### ### +####### Kazoo Integration module ########## +loadmodule "kazoo.so" +modparam("kazoo", "pua_mode", MY_AMQP_PUA_MODE) +modparam("kazoo", "amqp_primary_zone", "MY_AMQP_ZONE") +modparam("kazoo", "amqp_query_timeout_avp", "$avp(kz_timeout)") +modparam("kazoo", "node_hostname", "MY_HOSTNAME") +modparam("kazoo", "amqp_heartbeats", MY_AMQP_HEARTBEATS) +modparam("kazoo", "amqp_max_channels", MY_AMQP_MAX_CHANNELS) +modparam("kazoo", "amqp_consumer_processes", MY_AMQP_CONSUMER_PROCESSES) +modparam("kazoo", "amqp_consumer_workers", MY_AMQP_CONSUMER_WORKERS) +## amqp connections +#!ifdef MY_AMQP_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_URL") +#!endif +#!ifdef MY_AMQP_SECONDARY_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_SECONDARY_URL") +#!endif +#!ifdef MY_AMQP_TERTIARY_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_TERTIARY_URL") +#!endif +#!ifdef MY_AMQP_QUATERNARY_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_QUATERNARY_URL") +#!endif +#!ifdef MY_AMQP_QUINARY_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_QUINARY_URL") +#!endif +#!ifdef MY_AMQP_SENARY_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_SENARY_URL") +#!endif +#!ifdef MY_AMQP_SEPTENARY_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_SEPTENARY_URL") +#!endif +#!ifdef MY_AMQP_OCTONARY_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_OCTONARY_URL") +#!endif +#!ifdef MY_AMQP_NONARY_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_NONARY_URL") +#!endif +#!ifdef MY_AMQP_DENARY_URL +modparam("kazoo", "amqp_connection", "MY_AMQP_DENARY_URL") +#!endif + event_route[kazoo:mod-init] { @@ -36,8 +64,7 @@ event_route[kazoo:mod-init] #!endif #!ifdef ACL_ROLE - $var(payload) = "{ 'exchange' : 'frontier_acl' , 'type' : 'topic', 'queue' : 'FRONTIERACL-FLUSH-MY_HOSTNAME', 'routing' : 'flush' }"; - kazoo_subscribe("$var(payload)"); + route(ACL_BINDINGS); #!endif #!import_file "kazoo-custom-bindings.cfg" @@ -46,17 +73,81 @@ event_route[kazoo:mod-init] event_route[kazoo:consumer-event] { - xlog("L_INFO","unhandled AMQP event, payload: $kzE\n"); + xlog("L_DEBUG","unhandled AMQP event, payload: $kzE\n"); } event_route[kazoo:consumer-event-connection-open] { - xlog("L_INFO","connection to $(kzE{kz.json,host}) opened\n"); + xlog("L_DEBUG","connection to $(kzE{kz.json,host}) opened\n"); +} + +event_route[kazoo:consumer-event-connection-error] +{ + xlog("L_ERR","amqp|error|$(kzE{kz.json,zone})|$(kzE{kz.json,name})|$(kzE{kz.json,message})\n"); +} + +event_route[kazoo:consumer-event-connection-message] +{ + xlog("L_DEBUG","amqp|msg|$(kzE{kz.json,zone})|$(kzE{kz.json,name})|$(kzE{kz.json,message})\n"); } event_route[kazoo:consumer-event-connection-closed] { - xlog("L_INFO","connection to $(kzE{kz.json,host}) closed\n"); + xlog("L_DEBUG","amqp|closed|$(kzE{kz.json,zone})|$(kzE{kz.json,name})|connection to $(kzE{kz.json,host}) closed\n"); +} + +event_route[kazoo:consumer-event-connection-zone-available] +{ + xlog("L_DEBUG","amqp zone $(kzE{kz.json,zone}) is available\n"); +} + +event_route[kazoo:consumer-event-connection-zone-unavailable] +{ + xlog("L_DEBUG","amqp zone $(kzE{kz.json,zone}) is unavailable\n"); +} + +event_route[kazoo:consumer-event-connection-available] +{ + xlog("L_DEBUG","amqp zone $(kzE{kz.json,zone}) is available\n"); +} + +event_route[kazoo:consumer-event-connection-unavailable] +{ + xlog("L_DEBUG","amqp zone $(kzE{kz.json,zone}) is unavailable\n"); +} + +event_route[kazoo:consumer-event-connection-zone-listener-available] +{ + xlog("L_DEBUG","amqp zone $(kzE{kz.json,zone}) is available\n"); +} + +event_route[kazoo:consumer-event-connection-zone-listener-unavailable] +{ + xlog("L_DEBUG","amqp zone $(kzE{kz.json,zone}) is unavailable\n"); +} + +event_route[kazoo:consumer-event-connection-listener-zone-available] +{ + xlog("L_NOTICE","amqp|connection|$(kzE{kz.json,zone})|$(kzE{kz.json,name})|listener available\n"); +} + +event_route[kazoo:consumer-event-connection-listener-zone-unavailable] +{ + xlog("L_WARN","amqp|connection|$(kzE{kz.json,zone})|$(kzE{kz.json,name})|listener unavailable\n"); + + #!ifdef PRESENCE_ROLE + route(PRESENCE_ZONE_UNAVAILABLE); + #!endif +} + +event_route[kazoo:consumer-event-connection-listener-available] +{ + xlog("L_DEBUG","amqp|connection|$(kzE{kz.json,zone})|$(kzE{kz.json,name})|listener available\n"); +} + +event_route[kazoo:consumer-event-connection-listener-unavailable] +{ + xlog("L_DEBUG","amqp zone $(kzE{kz.json,zone}) is unavailable\n"); } # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab diff --git a/kamailio/keepalive-role.cfg b/kamailio/keepalive-role.cfg new file mode 100644 index 0000000..b7d28e1 --- /dev/null +++ b/kamailio/keepalive-role.cfg @@ -0,0 +1,343 @@ +######## KEEPALIVE PINGING ######## + +#!trydef KEEPALIVE_ENABLED 1 +#!trydef KEEPALIVE_NAT_ONLY 0 +#!trydef KEEPALIVE_UDP_ONLY 0 +#!trydef KEEPALIVE_TIMERS 4 +#!trydef KEEPALIVE_INTERVAL 60 +#!trydef KEEPALIVE_TIMEOUT 5000 +#!trydef KEEPALIVE_FAILED_THRESHOLD 2 +#!trydef KEEPALIVE_EXPIRE_SUBSCRIPTIONS 1 +#!trydef KEEPALIVE_EXPIRE_REGISTRATIONS 1 +#!trydef KEEPALIVE_FAILED_ACTION 1 +#!trydef KEEPALIVE_FAILED_LOG_LEVEL 0 +#!trydef KEEPALIVE_EXPIRED_SUBSCRIPTION_ACTION 1 +#!trydef KEEPALIVE_EXPIRED_REGISTRATION_ACTION 1 +#!trydef KEEPALIVE_ON_SUBSCRIPTION_ACTION 1 +#!trydef KEEPALIVE_ON_REGISTRATION_ACTION 1 + +#!substdef "!KEEPALIVE_S_FROM_URI!sip:keepalive@MY_HOSTNAME!g" +#!substdef "!KEEPALIVE_S_TIMERS!$def(KEEPALIVE_TIMERS)!g" + +kazoo.keepalive_udp_only = KEEPALIVE_UDP_ONLY descr "should we send keepalive for udp only" +kazoo.keepalive_nat_only = KEEPALIVE_NAT_ONLY descr "should we send keepalive for nat phones only" +kazoo.keepalive_timeout = KEEPALIVE_TIMEOUT descr "timeout in ms for keepalive transaction" +kazoo.keepalive_failed_threshold = KEEPALIVE_FAILED_THRESHOLD descr "how many times can a device fail to respond to OPTIONS" +kazoo.keepalive_expire_subscriptions = KEEPALIVE_EXPIRE_SUBSCRIPTIONS descr "expires subscriptions that do not respond to OPTIONS" +kazoo.keepalive_expire_registrations = KEEPALIVE_EXPIRE_REGISTRATIONS descr "expires registrations that do not respond to OPTIONS" +kazoo.keepalive_failed_log_level = KEEPALIVE_FAILED_LOG_LEVEL descr "loglevel for keepalive failed reply" +kazoo.keepalive_failed_action = KEEPALIVE_FAILED_ACTION descr "action for devices that exceed the threshold. 1 = disable, 2 = delete" +kazoo.keepalive_interval = KEEPALIVE_INTERVAL descr "interval in seconds between attempts to send OPTIONS to device" +kazoo.keepalive_expired_registration_action = KEEPALIVE_EXPIRED_REGISTRATION_ACTION descr "action when registrar expires a registration, 1 = delete , 2 = disable, 0 = none" +kazoo.keepalive_expired_subscription_action = KEEPALIVE_EXPIRED_SUBSCRIPTION_ACTION descr "action when presence expires a subscription, 1 = delete , 2 = disable, 0 = none" +kazoo.keepalive_on_registration_action = KEEPALIVE_ON_REGISTRATION_ACTION descr "action on registration, 1 = insert in keepalive , 0 = none" +kazoo.keepalive_on_subscription_action = KEEPALIVE_ON_SUBSCRIPTION_ACTION descr "action on subscription, 1 = insert in keepalive , 0 = none" +kazoo.keepalive_enable = KEEPALIVE_ENABLED descr "enable keepalive, 1 = on , 0 = off" + +modparam("rtimer", "timer", "name=keepalive_timer;interval=1;mode=KEEPALIVE_S_TIMERS;") +modparam("rtimer", "exec", "timer=keepalive_timer;route=KEEPALIVE_TIMER") + +modparam("rtimer", "timer", "name=keepalive_db_timer;interval=1;mode=1;") +modparam("rtimer", "exec", "timer=keepalive_db_timer;route=KEEPALIVE_DB_TIMER") + +##modparam("rtimer", "timer", "name=keepalive_cleanup;interval=5;mode=1;") +##modparam("rtimer", "exec", "timer=keepalive_cleanup;route=KEEPALIVE_CLEANUP") + +modparam("mqueue","mqueue", "name=keepalive_db_queue") + +modparam("statistics","variable", "keepalive:success") +modparam("statistics","variable", "keepalive:failure") +modparam("statistics","variable", "keepalive:db:success") +modparam("statistics","variable", "keepalive:db:failure") +modparam("statistics","variable", "keepalive:client_options") +modparam("statistics","variable", "keepalive:client_notify") +modparam("statistics","variable", "keepalive:disabled") +modparam("statistics","variable", "keepalive:removed") +modparam("statistics","variable", "keepalive:expired_registrations") +modparam("statistics","variable", "keepalive:expired_subscriptions") + +modparam("statistics","variable", "keepalive:from_registration") +modparam("statistics","variable", "keepalive:from_subscription") + +modparam("statistics","variable", "keepalive:removed_from_registration") +modparam("statistics","variable", "keepalive:removed_from_subscription") + +modparam("statistics","variable", "keepalive:disabled_from_expired_registration") +modparam("statistics","variable", "keepalive:removed_from_expired_registration") + +modparam("statistics","variable", "keepalive:disabled_from_expired_subscription") +modparam("statistics","variable", "keepalive:removed_from_expired_subscription") + +modparam("htable", "htable", "keepalive=>size=32;") + +route[KEEPALIVE_DB_TIMER] +{ + $var(runloop) = 1; + while(mq_fetch("keepalive_db_queue") == 1 && $var(runloop) < MAX_WHILE_LOOPS) { + $var(ci) = $mqk(keepalive_db_queue); + xlog("L_DEBUG", "Query : $var(ci) => $mqv(keepalive_db_queue)\n"); + $var(sqlres) = sql_query("cb", "$mqv(keepalive_db_queue)"); + xlog("L_DEBUG", "Query result : $var(sqlres)\n"); + if($var(sqlres) < 0) { + xlog("L_ERROR", "$var(ci)|log|error running query : $mqv(keepalive_db_queue)\n"); + } else { + $var(stat_update) = $_s(+$sqlrows(cb)); + update_stat("$var(ci)", "$var(stat_update)"); + + $var(nrows) = $sqlrows(cb); + xlog("L_DEBUG", "$var(ci)|log|end UPDATED $var(nrows) => $var(stat_update)\n"); + if($var(nrows) == 0) { + xlog("L_DEBUG", "$var(ci)|log|error no rows affected when running query\n"); + } + } + $var(runloop) = $var(runloop) + 1; + } +} + +route[KEEPALIVE_CLEANUP] +{ + if($sel(cfg_get.kazoo.keepalive_enable) == 0) return; + + $var(Query) = $_s(UPDATE keepalive SET SELECTED = 9 WHERE slot = $var(slot) AND selected = 0 and failed > $sel(cfg_get.kazoo.keepalive_failed_threshold)); +# $var(Query) = $_s(UPDATE keepalive SET SELECTED = 9 where selected < 3 and failed > $sel(cfg_get.kazoo.keepalive_failed_threshold)); + sql_query("cb", "$var(Query)"); + + if($sqlrows(cb) > 0) { + + if($sel(cfg_get.kazoo.keepalive_expire_registrations) == 1) { + if($def(REGISTRAR_DB_MODE) == 3) { + $var(Query) = $_s(update location set expires = last_modified where id in(select b.id from w_keepalive_contact a inner join w_location_contact b on a.contact = b.contact where selected = 9)); + sql_query("cb", "$var(Query)"); + $var(stat_update) = $_s(+$sqlrows(cb)); + update_stat("keepalive:expired_registrations", "$var(stat_update)"); + } else { + $var(Query) = $_s(update location set expires = last_modified where id in(select b.id from w_keepalive_contact a inner join w_location_contact b on a.contact = b.contact where selected = 9)); + sql_query("cb", "$var(Query)"); + $var(stat_update) = $_s(+$sqlrows(cb)); + update_stat("keepalive:expired_registrations", "$var(stat_update)"); + } + } + + if($sel(cfg_get.kazoo.keepalive_expire_subscriptions) == 1) { + $var(Query) = $_s(DELETE FROM active_watchers where id in(select b.id from w_keepalive_contact a inner join w_watchers_contact b on a.contact = b.contact where selected = 9)); + sql_query("cb", "$var(Query)"); + $var(stat_update) = $_s(+$sqlrows(cb)); + update_stat("keepalive:expired_subscriptions", "$var(stat_update)"); + } + + if($sel(cfg_get.kazoo.keepalive_failed_action) == 2) { + ## disable + $var(Query) = $_s(UPDATE keepalive SET SELECTED = 10 where selected = 9); + $var(stat) = "keepalive:disabled"; + } else if($sel(cfg_get.kazoo.keepalive_failed_action) == 1) { + ## delete - will be recreated on registration/subscription with same contact + $var(Query) = $_s(DELETE FROM keepalive where selected = 9); + $var(stat) = "keepalive:removed"; + } + sql_query("cb", "$var(Query)"); + $var(stat_update) = $_s(+$sqlrows(cb)); + update_stat("$var(stat)", "$var(stat_update)"); + } +} + +route[KEEPALIVE_TIMER] +{ + if($sel(cfg_get.kazoo.keepalive_enable) == 0) return; + + $var(base_slot) = $rtimer_worker * $sel(cfg_get.kazoo.keepalive_interval); + $var(slot) = $var(base_slot) + $var(tick); + $var(Query) = $_s(UPDATE keepalive SET selected = 1 WHERE slot = $var(slot) AND selected = 0 AND time_sent < datetime('now', '-$sel(cfg_get.kazoo.keepalive_interval) seconds')); +## xlog("L_NOTICE", "SQLTIMER ($var(base_slot) + $var(tick))> $var(Query)\n"); + $var(sqlres) = sql_query("cb", "$var(Query)"); + if($var(sqlres) < 0) { + xlog("L_ERROR", "$rtimer_worker|$var(tick)|log|error running query : $var(Query)\n"); + } else { + $var(nrows) = $sqlrows(cb); + xlog("L_DEBUG", "$rtimer_worker|$var(tick)|log|selected $var(nrows) endpoints to ping\n"); + } + + route(KEEPALIVE_CLEANUP); + + $var(Query) = $_s(SELECT id, contact, sockinfo from keepalive WHERE slot = $var(slot) AND selected = 1); + xlog("L_DEBUG", "$rtimer_worker|$var(tick)|timer|SQL => $var(Query)\n"); + $var(result) =sql_xquery("cb", "$var(Query)", "ra"); + + if($var(result) == 1) { + while($xavp(ra) != $null) { + $var(loop) = 0; + while($xavp(ra) != $null && $var(loop) < MAX_WHILE_LOOPS) { + route(KEEPALIVE_SEND_PING); + pv_unset("$xavp(ra)"); + $var(loop) = $var(loop) + 1; + } + } + } + + $var(Query) = $_s(UPDATE keepalive SET selected = 2 WHERE slot = $var(slot) AND selected = 1); + $var(sqlres) = sql_query("cb", "$var(Query)"); + if($var(sqlres) < 0) { + xlog("L_ERROR", "$rtimer_worker|$var(tick)|log|error running query : $var(Query)\n"); + } + + $var(tick) = $var(tick) + 1; + if($var(tick) > $sel(cfg_get.kazoo.keepalive_interval)) { + $var(tick) = 0; + } + +} + +route[KEEPALIVE_SEND_PING] +{ + $var(CallId) = $uuid(g); + xlog("L_DEBUG", "$var(CallId)|$rtimer_worker|timer|SENDING PING FROM $xavp(ra=>local_contact) TO => $xavp(ra=>contact)\n"); + + $uac_req(method)="OPTIONS"; + $uac_req(hdrs) = "X-TM-Local: KEEPALIVE_PING\r\nX-TM-SockInfo: " + $xavp(ra=>sockinfo) + "\r\n"; + $uac_req(turi) = $xavp(ra=>contact); + $uac_req(ruri) = $xavp(ra=>contact); + $uac_req(furi) = $_s(KEEPALIVE_S_FROM_URI;nat_id=$xavp(ra=>id)); + $uac_req(ouri) = "sip:127.0.0.1:5090;transport=tcp"; + $uac_req(callid) = $var(CallId); + uac_req_send(); + +} + +onreply_route[KEEPALIVE_REPLY] +{ + xlog("L_DEBUG", "$ci|keepalive|KEEPALIVE REPLY $(tu{nameaddr.uri})\n"); + $var(Query) = $_s(UPDATE keepalive SET selected = 0, failed = 0, time_sent = datetime('now') WHERE id = $(fu{uri.param,nat_id}) AND SELECTED = 2); + xlog("L_DEBUG", "$ci|keepalive|KEEPALIVE UPDATE SQL => '$var(Query)'\n"); + mq_add("keepalive_db_queue", "keepalive:db:success", "$var(Query)"); + update_stat("keepalive:success", "+1"); + resetflag(FLAG_SIP_TRACE); +} + +failure_route[KEEPALIVE_FAULT] +{ + xlog("$(sel(cfg_get.kazoo.keepalive_failed_log_level){s.int})", "$ci|keepalive|received error $T_reply_code $T_reply_reason from $(tu{nameaddr.uri})\n"); + $var(Query) = $_s(UPDATE keepalive SET selected = 0, failed = failed + 1, time_sent = datetime('now') WHERE id = $(fu{uri.param,nat_id}) AND SELECTED = 2); + xlog("L_DEBUG", "$ci|keepalive|KEEPALIVE REMOVE SQL => '$var(Query)'\n"); + mq_add("keepalive_db_queue", "keepalive:db:failure", "$var(Query)"); + update_stat("keepalive:failure", "+1"); + resetflag(FLAG_SIP_TRACE); +} + +route[KEEPALIVE_PING] +{ + $fs = $hdr(X-TM-SockInfo); + remove_hf_re("^X-TM-SockInfo"); + + force_rport(); + handle_ruri_alias(); + record_route(); + xlog("L_DEBUG", "$ci|local|sending $proto keepalive using $fs to $ru => $du => $tu\n"); + + t_on_reply("KEEPALIVE_REPLY"); + t_on_failure("KEEPALIVE_FAULT"); + t_set_fr(0, $sel(cfg_get.kazoo.keepalive_timeout)); + + t_relay(); +} + +route[KEEPALIVE_ON_REGISTRATION] +{ + if($sel(cfg_get.kazoo.keepalive_enable) == 0) return; + + if($sel(cfg_get.kazoo.keepalive_on_registration_action) == 0) { + return; + } + + if($proto == "ws" || $proto == "wss") { + return; + } + + if($sht(keepalive=>$si~$sp~$prid) != $null) { + return; + } + + if (isbflagset(FLB_NATB)) { + if(!isbflagset(FLB_NATSIPPING)) { + return; + } + } else { + if($sel(cfg_get.kazoo.keepalive_nat_only) == 1) { + return; + } + } + + $var(alias) = $(avp(AVP_RECV_PARAM){uri.host}) + "~" + $(avp(AVP_RECV_PARAM){uri.port}) + "~" + $prid; + $var(contact) = $(ct{nameaddr.uri}) + ";alias=" + $var(alias); + $var(local_contact) = "sip:" + $Ri + ":" + $Rp + ";transport=" + $proto; + xlog("L_DEBUG", "$ci|keepalive|KEEPALIVE ON REG $var(save_result) $proto $RAut $var(contact) $var(alias) $(ct{nameaddr.uri}) $ct $avp(AVP_RECV_PARAM) $tu $xavp(ulrcd=>ruid) , $xavp(ulrcd=>contact) , $xavp(ulrcd=>expires)\n"); + + if($var(save_result) == 3) { + $var(sql) = $_s(DELETE FROM keepalive WHERE contact = "$var(contact)"); + $var(stat) = "keepalive:removed_from_registration"; + } else { + $var(max_slots) = $sel(cfg_get.kazoo.keepalive_interval) * KEEPALIVE_S_TIMERS; + $var(slot) = $(var(contact){s.corehash, $var(max_slots)}); + $var(sql) = $_s(INSERT OR IGNORE INTO keepalive (contact, received, sockinfo, slot) values("$var(contact)", "$var(alias)", "$(RAut{uri.tosocket})", $var(slot))); + $var(stat) = "keepalive:from_registration"; + } + mq_add("keepalive_db_queue", "$var(stat)", "$var(sql)"); + + return; +} + +route[KEEPALIVE_ON_SUBSCRIBE] +{ + if($sel(cfg_get.kazoo.keepalive_enable) == 0) return; + + if($sel(cfg_get.kazoo.keepalive_on_subscription_action) == 0) { + return; + } + + if($sht(keepalive=>$si~$sp~$prid) != $null) { + return; + } + + $var(max_slots) = $sel(cfg_get.kazoo.keepalive_interval) * KEEPALIVE_S_TIMERS; + $var(slot) = $(subs(contact){s.corehash, $var(max_slots)}); + $var(alias) = $(subs(contact){uri.param,alias}); + $var(sql) = $_s(INSERT OR IGNORE INTO keepalive (contact, received, sockinfo, slot) values("$subs(contact)", "$var(alias)", "$subs(sockinfo)", $var(slot))); + mq_add("keepalive_db_queue", "keepalive:from_subscription", "$var(sql)"); + +} + +route[KEEPALIVE_ON_EXPIRED_REGISTRATION] +{ + if($sel(cfg_get.kazoo.keepalive_enable) == 0) return; + + if($sel(cfg_get.kazoo.keepalive_expired_registration_action) == 2) { + ## disable + $var(Query) = $_s(UPDATE keepalive SET SELECTED = 10 where selected < 3 and contact like "$ulc(exp=>addr)%"); + mq_add("keepalive_db_queue", "keepalive:disabled_from_expired_registration", "$var(Query)"); + } else if($sel(cfg_get.kazoo.keepalive_expired_registration_action) == 1) { + ## delete - will be recreated on registration with same contact + $var(Query) = $_s(DELETE FROM keepalive where selected < 3 and contact like "$ulc(exp=>addr)%"); + mq_add("keepalive_db_queue", "keepalive:removed_from_expired_registration", "$var(Query)"); + } + +} + +route[KEEPALIVE_ON_OPTIONS] +{ + if($sel(cfg_get.kazoo.keepalive_enable) == 0) return; + + if($shtinc(keepalive=>$si~$sp~$prid) == 1) { + $var(Query) = $_s(UPDATE keepalive set selected = 3 where received = "$si~$sp~$prid" and selected <> 3 ); + mq_add("keepalive_db_queue", "keepalive:client_options", "$var(Query)"); + } + +} + +route[KEEPALIVE_ON_NOTIFY] +{ + if($sel(cfg_get.kazoo.keepalive_enable) == 0) return; + + if($shtinc(keepalive=>$si~$sp~$prid) == 1) { + $var(Query) = $_s(UPDATE keepalive set selected = 4 where received = "$si~$sp~$prid" and selected <> 4 ); + mq_add("keepalive_db_queue", "keepalive:client_notify", "$var(Query)"); + } + +} diff --git a/kamailio/local.cfg b/kamailio/local.cfg index 36b0363..5cb844d 100644 --- a/kamailio/local.cfg +++ b/kamailio/local.cfg @@ -8,8 +8,6 @@ #!trydef PRESENCE_ROLE #!trydef RESPONDER_ROLE #!trydef NODES_ROLE -#!trydef FAST_PICKUP_ROLE -#!trydef PRESENCE_QUERY_ROLE ## Disabled Roles - remove all but the last '#' to enable # # #!trydef TRAFFIC_FILTER_ROLE @@ -27,26 +25,38 @@ ################################################################################ ## SERVER INFORMATION ################################################################################ -## CHANGE "kamailio.2600hz.com" TO YOUR SERVERS HOSTNAME -#!substdef "!MY_HOSTNAME!kamailio.2600hz.com!g" +## UNCOMMENT & CHANGE "kamailio.2600hz.com" TO YOUR SERVERS HOSTNAME +# # #!substdef "!MY_HOSTNAME!kamailio.2600hz.com!g" -## CHANGE "127.0.0.1" TO YOUR SERVERS IP ADDRESS +## UNCOMMENT & CHANGE "127.0.0.1" TO YOUR SERVERS IP ADDRESS ## Usually your public IP. If you need ## to listen on addtional ports or IPs ## add them in "BINDINGS" at the bottom. -#!substdef "!MY_IP_ADDRESS!0.0.0.0!g" +# # #!substdef "!MY_IP_ADDRESS!127.0.0.1!g" ## CHANGE "kazoo://guest:guest@127.0.0.1:5672" TO THE AMQP URL ## This should be the primary RabbitMQ server ## in the zone that this server will service. -#!substdef "!MY_AMQP_URL!kazoo://guest:guest@127.0.0.1:5672!g" +# # #!substdef "!MY_AMQP_URL!amqp://guest:guest@127.0.0.1:5672!g" -## This parameter is only required if you are using websockets +################################################################################ +## WEBSOCKETS +################################################################################ +## +## These parameters are only required if you are using websockets +## +## MY_WEBSOCKET_DOMAIN ## This value must be present in the HTTP ## Origin header on a new websocket request -## or it will be rejected. If you remove -## it completely the validation will be disabled. -#!substdef "!MY_WEBSOCKET_DOMAIN!2600hz.com!g" +## or it will be rejected. default value is +## domain of this server. +## #!substdef "!MY_WEBSOCKET_DOMAIN!2600hz.com!g" +## +## WEBSOCKET_NO_ORIGIN_RESTRICTION +## if defined, it will disable the origin validation. +## +## #!trydef WEBSOCKET_NO_ORIGIN_RESTRICTION +## ################################################################################ ## DATABASE @@ -75,13 +85,14 @@ ## BINDINGS ################################################################################ ## This parameter is OPTIONAL. +## when set to 1, ## It will try to locate outbound interface ## on multihomed host. By default forward ## requests use the incoming socket disregarding ## the destination location. When enabled Kamailio ## will select a socket that can reach the ## destination. This reduces performance. -mhomed=0 +##!define KZ_MULTI_HOMED 1 ################################################################################ ## KZ_DISABLE_REGISTRAR_PORTS @@ -106,23 +117,16 @@ mhomed=0 ##!define KZ_UDP_REGISTRAR_PORT 7000 ##!define KZ_TCP_REGISTRAR_PORT 7000 -################################################################################ -## NAT -################################################################################ -## These parameters are OPTIONAL. -## It allows overriding the nat_uac_test with a different value than "3" -## (default) as it is proven that it fixes the issue reported here: -## https://lists.kamailio.org/pipermail/sr-users/2016-August/094211.html -# # #!substdef "!NAT_UAC_TEST_LEVEL!1!g" - ################################################################################ ## SIP traffic mirroring to SIP_TRACE server ################################################################################ ## This parameter is OPTIONAL. ## If you have installed SIP_TRACE server (Homer as example), ## then you can mirror INVITE and MESSAGE here -# # #!substdef "!SIP_TRACE_URI!sip:127.0.0.1:9060!g" -# # #!substdef "!HEP_CAPTURE_ID!1!g" +# # #!define SIP_TRACE_URI "sip:127.0.0.1:9060" +# # #!define HEP_CAPTURE_ID 1 + +include_file "defs.cfg" ## YOU SHOULD NOT HAVE TO CHANGE THESE! ## By setting MY_IP_ADDRESS above these will resolve diff --git a/kamailio/message-role.cfg b/kamailio/message-role.cfg index a0fa312..1122a1e 100644 --- a/kamailio/message-role.cfg +++ b/kamailio/message-role.cfg @@ -40,7 +40,7 @@ route[MESSAGE_REPLY] } $var(Payload) = '{ "Event-Category" : "message", "Event-Name" : "delivery", "Call-ID" : "$(sht(msg=>$ci){kz.json,Call-ID})", "Message-ID" : "$(sht(msg=>$ci){kz.json,Message-ID})" , "Delivery-Result-Code" : "sip:$T_reply_code", "Msg-ID" : "$(sht(msg=>$ci){kz.json,Msg-ID})" , "Status" : "$var(Result)"}'; - + $var(RoutingKey) = $(sht(msg=>$ci){kz.json,Server-ID}); $var(exchange) = "targeted"; if($var(RoutingKey) == "") { @@ -54,7 +54,7 @@ route[MESSAGE_REPLY] route[MESSAGE_BINDINGS] { $var(key) = "kamailio@MY_HOSTNAME"; - $var(payload) = "{ 'exchange' : 'sms' , 'type' : 'topic', 'queue' : 'MSG-QUEUE-MY_HOSTNAME', 'routing' : 'message.route." + $(var(key){kz.encode}) + ".*', 'no_ack' : 0 }"; + $var(payload) = $_s({"name": "sms", "exchange": "sms", "type": "topic", "queue": "MSG-QUEUE-MY_HOSTNAME", "routing": "message.route.$(var(key){kz.encode}).*", "no_ack": 0 }); kazoo_subscribe("$var(payload)"); } diff --git a/kamailio/nat-traversal-role.cfg b/kamailio/nat-traversal-role.cfg index 7166446..6d62690 100644 --- a/kamailio/nat-traversal-role.cfg +++ b/kamailio/nat-traversal-role.cfg @@ -1,45 +1,116 @@ ######## NAT Traversal module - signaling functions ######## #!ifndef NATHELPER_LOADED loadmodule "nathelper.so" +#!trydef NATHELPER_LOADED #!endif modparam("nathelper", "received_avp", "$avp(AVP_RECV_PARAM)") -modparam("nathelper", "sipping_from", "sip:sipcheck@MY_HOSTNAME") +modparam("nathelper", "sipping_from", "sip:registrar@MY_HOSTNAME") + +#!ifdef WEBSOCKETS_ROLE +#!trydef KZ_NAT_DETECT 83 +#!else +#!trydef KZ_NAT_DETECT 19 +#!endif + +#!trydef KZ_NAT_FIX_SDP_REQUEST 1 +#!trydef KZ_NAT_FIX_SDP_REPLY 1 +#!trydef KZ_NAT_SDP_TEST 8 +#!trydef KZ_NAT_SDP_FIX 10 + + +kazoo.nat_fix_sdp_request = KZ_NAT_FIX_SDP_REQUEST descr "performs request sdp replacement of private addresses" +kazoo.nat_fix_sdp_reply = KZ_NAT_FIX_SDP_REPLY descr "performs reply sdp replacement of private addresses" ####### NAT Traversal Logic ######## -route[NAT_TEST_AND_CORRECT] +route[NAT_SDP] +{ + if( has_body("application/sdp")) { + if( nat_uac_test(KZ_NAT_SDP_TEST)) { + xlog("L_DEBUG", "$ci|log|applying sdp nat fix\n"); + $var(ret) = fix_nated_sdp(KZ_NAT_SDP_FIX); + xlog("L_DEBUG", "$ci|log|result of applying sdp nat fix is $var(ret)\n"); + } else if( is_method("INVITE") && !isflagset(FLAG_INTERNALLY_SOURCED) && is_audio_on_hold()) { + xlog("L_DEBUG", "$ci|log|applying sdp nat fix for held channel\n"); + $var(ret) = fix_nated_sdp(KZ_NAT_SDP_FIX); + xlog("L_DEBUG", "$ci|log|result of applying sdp nat fix for held channel is $var(ret)\n"); + } + } +} + +route[NAT_DETECT] +{ + + if($sel(cfg_get.kazoo.nat_fix_sdp_request) == 1) { + route(NAT_SDP); + } + + if ($Rp == "5080") { + setflag(FLAG_SKIP_NAT_CORRECTION); + xlog("L_DEBUG", "$ci|log|skipping nat correction on PORT 5080\n"); + } else { + if (is_present_hf("Record-Route")) { + $var(i) = 0; + $var(rr_count) = $rr_count; + while($var(i) < $var(rr_count)) { + $var(rr_base) = $(hdr(Record-Route)[$var(i)]); + $var(rr_idx) = 0; + $var(rr) = $(var(rr_base){s.select,$var(rr_idx),,}); + while($var(rr) != $null && $var(rr) != "") { + $var(i) = $var(i) + 1; + if (!is_myself("$(var(rr){nameaddr.uri})")) { + setflag(FLAG_SKIP_NAT_CORRECTION); + xlog("L_DEBUG", "$ci|log|skipping nat correction on record-route $(var(rr){nameaddr.uri})\n"); + } + $var(rr_idx) = $var(rr_idx) + 1; + $var(rr) = $(var(rr_base){s.select,$var(rr_idx),,}); + } + } + } + } + + if (isflagset(FLAG_SKIP_NAT_CORRECTION)) { + xlog("L_DEBUG", "$ci|log|skipping nat detection\n"); + return; + } + + force_rport(); + + if(nat_uac_test(KZ_NAT_DETECT)) { + xlog("L_DEBUG", "$ci|log|detected nat request\n"); + setflag(FLT_NATS); + if (!is_method("REGISTER")) { + if(is_first_hop()) set_contact_alias(); + } + } + +} + +route[NAT_MANAGE] { - if (is_present_hf("Record-Route")) { - $var(i) = 0; - $var(rr_count) = $rr_count; - while($var(i) < $var(rr_count)) { - $var(rr_base) = $(hdr(Record-Route)[$var(i)]); - $var(rr_idx) = 0; - $var(rr) = $(var(rr_base){s.select,$var(rr_idx),,}); - while($var(rr) != $null && $var(rr) != "") { - $var(i) = $var(i) + 1; - if (!is_myself("$(var(rr){nameaddr.uri})")) { - setflag(FLAG_SKIP_NAT_CORRECTION); - } - $var(rr_idx) = $var(rr_idx) + 1; - $var(rr) = $(var(rr_base){s.select,$var(rr_idx),,}); - } - } - } else if ($Rp == "5080") { - setflag(FLAG_SKIP_NAT_CORRECTION); - } - - if (isflagset(FLAG_SKIP_NAT_CORRECTION)) { - return(); - } - - if (nat_uac_test("NAT_UAC_TEST_LEVEL")) { - force_rport(); - fix_nated_contact(); - } - - if (has_body("application/sdp") && nat_uac_test("8")) { - fix_nated_sdp("10"); - } + if( is_reply() && $sel(cfg_get.kazoo.nat_fix_sdp_reply) == 1) { + route(NAT_SDP); + } + + if ( is_request() && isflagset(FLAG_INTERNALLY_SOURCED)) { + setbflag(FLB_NATB); + } + + if ( is_request() && has_totag() ) { + setbflag(FLB_NATB); + } + + if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))) { + return; + } + + if (is_reply()) { + if(isflagset(FLT_NATS) || isbflagset(FLB_NATB)) { + if(is_first_hop()) { + set_contact_alias(); + } + } + } + } # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab diff --git a/kamailio/nodes-role.cfg b/kamailio/nodes-role.cfg index c3df110..cb985a2 100644 --- a/kamailio/nodes-role.cfg +++ b/kamailio/nodes-role.cfg @@ -1,21 +1,11 @@ ######## Nodes role - pushes info to kazoo ######## -#!ifndef NODES_EXPIRE -#!define NODES_EXPIRE 10 -#!endif - -#!ifndef NODES_FUDGE_FACTOR -#!define NODES_FUDGE_FACTOR 10 -#!endif +#!trydef NODES_EXPIRE 10 +#!trydef NODES_FUDGE_EXPIRE 45 modparam("htable", "htable", "nodes=>size=8;initval=0;autoexpire=60"); modparam("htable", "htable", "media=>size=8;initval=0;autoexpire=60"); -####### RTIMER module ########## -#!ifndef RTIMER_LOADED -loadmodule "rtimer.so" -#!trydef RTIMER_LOADED -#!endif modparam("rtimer", "timer", "name=ta;interval=2;mode=2;") modparam("rtimer", "timer", "name=retry;interval=5;mode=2;") modparam("rtimer", "timer", "name=pub;interval=10;mode=1;") @@ -61,24 +51,24 @@ route(LISTENER_STATUS); #!endif $var(Roles) = $_s("Roles" : {"Proxy" : $var(listeners) $var(Dispatcher) $var(Presence) $var(Registrar)}); - $var(Payload) = '{"Event-Category" : "nodes", "Event-Name" : "advertise", "Expires" : 15000, "Used-Memory" : $(stat(real_used_size){s.int}), "Startup" : $Tb, "WhApps" : {"kamailio" : {"Startup" : $Tb }}, $var(Roles)}'; + $var(Payload) = $_s({"Event-Category" : "nodes", "Event-Name" : "advertise", "Expires" : 15000, "Used-Memory" : $(stat(real_used_size){s.int}), "Startup" : $Tb, "WhApps" : {"kamailio" : {"Startup" : $Tb }}, $var(Roles)}); kazoo_publish("nodes", "", $var(Payload)); } event_route[kazoo:consumer-event-nodes-advertise] { - $var(count) = $shtinc(nodes=>$(kzE{kz.json,Node})::count); + $var(count) = $shtinc(nodes=>$(kzE{kz.json,Node})::count); if($var(count) == 0) { - xlog("L_WARNING", "$(kzE{kz.json,Msg-ID})|nodes|heartbeat for reconnected node $(kzE{kz.json,Node})\n"); + xlog("L_NOTICE", "$(kzE{kz.json,Msg-ID})|nodes|heartbeat for reconnected node $(kzE{kz.json,Node})\n"); $var(count) = $shtinc(nodes=>$(kzE{kz.json,Node})::count); } else { if($var(count) == 1) { - xlog("L_WARNING", "$(kzE{kz.json,Msg-ID})|nodes|heartbeat from new node $(kzE{kz.json,Node})\n"); + xlog("L_NOTICE", "$(kzE{kz.json,Msg-ID})|nodes|heartbeat from new node $(kzE{kz.json,Node})\n"); } else { xlog("L_DEBUG", "$(kzE{kz.json,Msg-ID})|nodes|heartbeat from existing node $(kzE{kz.json,Node})\n"); } - } + } mq_add("node_heartbeat", "$(kzE{kz.json,Node})", "$kzE"); } @@ -115,11 +105,11 @@ route[NODE_HEARTBEAT_ROUTE] xlog("L_DEBUG", "$(var(Payload){kz.json,Msg-ID})|nodes|processing heartbeat for node $var(Node)\n"); route(CHECK_MEDIA_SERVERS); - + $sht(nodes=>$var(Node)) = $var(Payload); - $shtex(nodes=>$var(Node)) = ($(var(Payload){kz.json,Expires}{s.int}) / 1000) + NODES_FUDGE_FACTOR; + $shtex(nodes=>$var(Node)) = ($(var(Payload){kz.json,Expires}{s.int}) / 1000) + NODES_FUDGE_EXPIRE; $var(runloop) = $var(runloop) + 1; - } + } } route[CHECK_MEDIA_SERVERS] @@ -148,13 +138,22 @@ route[CHECK_MEDIA_SERVERS] $var(MediaProfile) = $(avp(ProfileKeys)[$var(ProfileIdx)]); $var(MediaRawUrl) = $(var(Payload){kz.json,Media-Servers.$var(MediaKey).Interfaces.$var(MediaProfile).URL}); $var(MediaUrl) = $(var(MediaRawUrl){re.subst,/^sip:(.*)@(.*)/sip:\2/}); + $var(MediaInstanceUUID) = $(var(Payload){kz.json,Media-Servers.$var(MediaKey).Instance-UUID}); + $var(MediaIP) = $(var(Payload){kz.json,Media-Servers.$var(MediaKey).Interfaces.$var(MediaProfile).SIP-IP}); if($shtinc(media=>$var(MediaUrl)::count) == 1) { $sht(media=>$var(MediaUrl)::zone) = $var(Zone); $shtex(media=>$var(MediaUrl)::zone) = 0; + $sht(media=>$var(MediaUrl)::uuid) = $var(MediaInstanceUUID); + $shtex(media=>$var(MediaUrl)::uuid) = 0; route(MEDIA_SERVER_UP); + } else if($sht(media=>$var(MediaUrl)::uuid) != $var(MediaInstanceUUID)) { + $sht(media=>$var(MediaUrl)::uuid) = $var(MediaInstanceUUID); + $shtex(media=>$var(MediaUrl)::uuid) = 0; + $var(Zone) = $sht(media=>$var(MediaUrl)::zone); + route(MEDIA_SERVER_RESTART); }; - $var(MediaExpire) = ($(var(Payload){kz.json,Expires}{s.int}) / 1000) + NODES_FUDGE_FACTOR; - xlog("L_DEBUG", "nodes|media|$var(Node) media expiration $var(MediaExpire) for $var(MediaUrl)\n"); + $var(MediaExpire) = ($(var(Payload){kz.json,Expires}{s.int}) / 1000) + NODES_FUDGE_EXPIRE; + xlog("L_DEBUG", "nodes|media|$var(Node) media expiration $var(MediaExpire) for $var(MediaUrl)\n"); $shtex(media=>$var(MediaUrl)::count) = $var(MediaExpire); $var(ProfileIdx) = $var(ProfileIdx) + 1; } @@ -173,14 +172,14 @@ event_route[htable:expired:media] route[MEDIA_SERVER_UP] { - xlog("L_WARNING", "nodes|media|$var(Node) reported new media server $var(MediaUrl) in zone $var(Zone)\n"); + xlog("L_NOTICE", "nodes|media|$var(Node) reported new media server $var(MediaUrl) in zone $var(Zone)\n"); #!ifdef DISPATCHER_ROLE route(DISPATCHER_CHECK_MEDIA_SERVER); #!endif -#!ifdef FAST_PICKUP_ROLE - route(FAST_PICKUP_START); +#!ifdef PRESENCE_ROLE + route(PRESENCE_FAST_PICKUP_START); #!endif } @@ -188,7 +187,17 @@ route[MEDIA_SERVER_UP] route[MEDIA_SERVER_DOWN] { xlog("L_WARNING", "htable|media|heartbeat expired for media server $var(MediaUrl) in zone $var(Zone)\n"); - + +#!ifdef PRESENCE_ROLE + route(RESET_PUBLISHER); +#!endif + +} + +route[MEDIA_SERVER_RESTART] +{ + xlog("L_NOTICE", "htable|media|media server $var(MediaUrl) restarted in zone $var(Zone)\n"); + #!ifdef PRESENCE_ROLE route(RESET_PUBLISHER); #!endif @@ -198,7 +207,7 @@ route[MEDIA_SERVER_DOWN] #!ifndef NODES_CUSTOM_BINDINGS route[NODES_BINDINGS] { - $var(payload) = "{ 'exchange' : 'nodes' , 'type' : 'fanout', 'queue' : 'nodes-MY_HOSTNAME', 'exclusive' : 0, 'federate' : 1}"; + $var(payload) = $_s({"name": "nodes", "exchange" : "nodes" , "type" : "fanout", "queue" : "nodes-MY_HOSTNAME", "exclusive":0, "federate":1 }); kazoo_subscribe("$var(payload)"); } #!endif @@ -214,17 +223,25 @@ route[LISTENER_STATUS] $var(listener) = $(jsonrpl(body){kz.json,result[$var(loop)]}); $var(proto) = $(var(listener){kz.json,PROTO}); $var(address) = $(var(listener){kz.json,ADDRLIST.ADDR}); - $var(port) = $(var(listener){kz.json,PORT}); - $var(uri) = $_s($var(proto):$var(address):$var(port)); - if($(var(listener){kz.json,ADVERTISE}) != "-") { - $var(advertise) = $_s( , "advertise" : "$(var(listener){kz.json,ADVERTISE})"); - } else { - $var(advertise) = ""; + if($var(address) != "127.0.0.1") { + $var(port) = $(var(listener){kz.json,PORT}); + if($var(port) == "WS_PORT") { + $var(proto) = "ws"; + } + if($var(port) == "WSS_PORT") { + $var(proto) = "wss"; + } + $var(uri) = $_s($var(proto):$var(address):$var(port)); + if($(var(listener){kz.json,ADVERTISE}) != "-") { + $var(advertise) = $_s( , "advertise" : "$(var(listener){kz.json,ADVERTISE})"); + } else { + $var(advertise) = ""; + } + $var(x) = $_s("$var(uri)" : {"proto" : "$var(proto)", "address" : "$var(address)", "port" : $var(port) $var(advertise) }); + $var(listeners) = $_s($var(listeners)$var(sep)$var(x)); + $var(sep) = " , "; } - $var(x) = $_s("$var(uri)" : {"proto" : "$var(proto)", "address" : "$var(address)", "port" : $var(port) $var(advertise) }); - $var(listeners) = $_s($var(listeners)$var(sep)$var(x)); $var(loop) = $var(loop) + 1; - $var(sep) = " , "; } $var(listeners) = $_s({"Listeners" : { $var(listeners) }}); } diff --git a/kamailio/fast-pickup-role.cfg b/kamailio/presence-fast-pickup.cfg similarity index 60% rename from kamailio/fast-pickup-role.cfg rename to kamailio/presence-fast-pickup.cfg index 36c05e7..692a32e 100644 --- a/kamailio/fast-pickup-role.cfg +++ b/kamailio/presence-fast-pickup.cfg @@ -2,18 +2,22 @@ modparam("htable", "htable", "park=>size=16;autoexpire=600") modparam("htable", "htable", "fp=>size=8"); -kazoo.fast_pickup_cookies = KZ_FAST_PICKUP_COOKIES descr "maintains a hash table for correlating call-ids with media servers" -kazoo.fast_pickup_realtime = KZ_FAST_PICKUP_REALTIME descr "queries channels api for realtime status of call-id" +#!trydef KZ_PRESENCE_FAST_PICKUP_COOKIES 1 +#!trydef KZ_PRESENCE_FAST_PICKUP_REALTIME 1 +#!trydef KZ_PRESENCE_FAST_PICKUP_STAR_5 1 -route[FAST_PICKUP_START] +kazoo.presence_fast_pickup_cookies = KZ_PRESENCE_FAST_PICKUP_COOKIES descr "maintains a hash table for correlating call-ids with media servers" +kazoo.presence_fast_pickup_realtime = KZ_PRESENCE_FAST_PICKUP_REALTIME descr "queries channels api for realtime status of call-id" +kazoo.presence_fast_pickup_star_5 = KZ_PRESENCE_FAST_PICKUP_STAR_5 descr "treats *5 as park pickup, queries state of *3" + +route[PRESENCE_FAST_PICKUP_START] { $sht(fp=>count) = 0; } -route[FAST_PICKUP_LOAD] +route[PRESENCE_FAST_PICKUP_LOAD] { sht_reset("fp"); -#!ifndef KZ_USE_DISPATCHER_LIST xlog("L_INFO", "$ci|log|fast|initializing fastpick hash table from dispatcher\n"); if (sql_xquery("exec", "select destination from dispatcher", "ra") == 1) { while($xavp(ra) != $null) { @@ -22,21 +26,17 @@ route[FAST_PICKUP_LOAD] $var(destination) = $xavp(ra=>destination); $var(i) = 0; if(!is_ip("$var(host)")) { - if(dns_query("$var(host)", "xyz")) { - $var(destination) = $_s(sip:$dns(xyz=>addr[$var(i)]):$var(port)); - } + xlog("L_INFO", "$ci|log|fast|ignoring $var(host) since its not a ip address\n"); + } else { + xlog("L_INFO", "$ci|log|fast|adding key $(var(destination){s.md5}) for $var(destination)\n"); + $sht(fp=>$(var(destination){s.md5})) = $var(destination); } - xlog("L_INFO", "$ci|log|fast|adding key $(var(destination){s.md5}) for $var(destination)\n"); - $sht(fp=>$(var(destination){s.md5})) = $var(destination); pv_unset("$xavp(ra)"); } } -#!else - xlog("L_INFO", "$ci|log|fast|cannot initialize hash table from dispatcher. check KZ_USE_DISPATCHER_LIST option in local.cfg\n"); -#!endif } -route[FAST_PICKUP_ATTEMPT] +route[PRESENCE_FAST_PICKUP_ATTEMPT] { if (!is_method("INVITE")) { return; @@ -50,18 +50,18 @@ route[FAST_PICKUP_ATTEMPT] if($var(replaced_call_id) =~ "kfp+") { if($shtinc(fp=>count) == 1) { - route(FAST_PICKUP_LOAD); + route(PRESENCE_FAST_PICKUP_LOAD); } remove_hf_re("^Replaces"); $var(PickupOptions) = $(var(replaced_call_id){re.subst,/^kfp\+(.{2})([^@]*)@(.*)/\1/}{s.decode.hexa}); $var(md5) = $(var(replaced_call_id){re.subst,/^kfp\+(.{2})([^@]*)@(.*)/\2/}); $var(replaced_call_id) = $(var(replaced_call_id){re.subst,/^kfp\+(.{2})([^@]*)@(.*)/\3/}); if( $sht(fp=>$var(md5)) != $null) { - route(FAST_PICKUP_OPTION); + route(PRESENCE_FAST_PICKUP_OPTION); $du = $sht(fp=>$var(md5)); append_hf("Replaces: $var(replaced_call_id)$var(Pickup)\r\n"); xlog("L_INFO", "$ci|log|fast|found shortcut for call-id $var(replaced_call_id) , redirecting ($(ru{uri.user})) to $du\n"); - route(EXTERNAL_TO_INTERNAL_RELAY); + route(RELAY); exit(); } else { $var(replaced_call_id) = "none"; @@ -69,7 +69,7 @@ route[FAST_PICKUP_ATTEMPT] } } - if(@cfg_get.kazoo.fast_pickup_realtime == 1) { + if($sel(cfg_get.kazoo.presence_fast_pickup_realtime) == 1) { if($var(replaced_call_id) != "none") { xlog("L_INFO", "$ci|log|request has replaces call-id $var(replaced_call_id)\n"); $var(amqp_payload_request) = '{"Event-Category" : "call_event" , "Event-Name" : "channel_status_req", "Call-ID" : "' + $var(replaced_call_id) + '", "Active-Only" : true }'; @@ -89,7 +89,7 @@ route[FAST_PICKUP_ATTEMPT] remove_hf_re("^Replaces"); append_hf("Replaces: $var(rep)\r\n"); xlog("L_INFO", "$ci|log|call-id $var(replaced_call_id) found, redirecting call ($(ru{uri.user})) to $du => $var(rep)\n"); - route(EXTERNAL_TO_INTERNAL_RELAY); + route(RELAY); exit(); } else { xlog("L_WARN", "$ci|log|call-id $var(replaced_call_id) not found in cluster, proceeding with normal dispatch\n"); @@ -101,15 +101,48 @@ route[FAST_PICKUP_ATTEMPT] } ##### CALL-PARK #### + + ##### STAR 5 CHECK #### + if($sel(cfg_get.kazoo.presence_fast_pickup_star_5) == 1) { + if($(ru{uri.user}) =~ "\*5") { + $var(park) = $_s(*3$(ru{uri.user}{s.substr,2,0})@$(ru{uri.domain})); + if($sht(park=>$var(park)) != $null) { + $du = $sht(park=>$var(park)); + xlog("L_INFO", "$ci|log|redirecting park request to $du , callid : $sht(park=>$var(park)::callid)\n"); + route(RELAY); + exit(); + } + } + } + if($sht(park=>$(ru{uri.user})@$(ru{uri.domain})) != $null) { $du = $sht(park=>$(ru{uri.user})@$(ruri{uri.domain})); - xlog("L_INFO", "$ci|log|redirecting park request to $du\n"); - route(EXTERNAL_TO_INTERNAL_RELAY); + xlog("L_INFO", "$ci|log|redirecting park request to $du, callid: $sht(park=>$(ru{uri.user})@$(ruri{uri.domain})::callid)\n"); + route(RELAY); exit(); } + + ##### CALL-PARK IN KAZOO #### + $var(park_extension) = "\*3"; + if($sel(cfg_get.kazoo.presence_fast_pickup_star_5) == 1) { + $var(park_extension) = "\*[3,5]"; + } + if($(ru{uri.user}) =~ $var(park_extension) && !($rd =~ "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}") ) { + xlog("L_INFO", "$ci|log|checking park request to $(ru{uri.user})@$(ru{uri.domain})\n"); + $var(amqp_payload_request) = '{"Event-Category" : "call_event" , "Event-Name" : "query_user_channels_req", "Username" : "*3$(ru{uri.user}{s.substr,2,0})", "Realm" : "$(ru{uri.domain})", "Active-Only" : true }'; + $var(amqp_routing_key) = "call.status_req." + $(ci{kz.encode}); + if(kazoo_query("callevt", $var(amqp_routing_key), $var(amqp_payload_request))) { + $du = $(kzR{kz.json,Channels[0].switch_url}); + if($du != $null) { + xlog("L_INFO", "$ci|log|redirecting park request to $du from realtime query reply\n"); + route(RELAY); + exit(); + } + } + } } -route[FAST_PICKUP_OPTION] +route[PRESENCE_FAST_PICKUP_OPTION] { $var(Pickup) = ""; switch($var(PickupOptions)) @@ -125,7 +158,7 @@ route[FAST_PICKUP_OPTION] } } -route[FAST_PICKUP_INIT] +route[PRESENCE_FAST_PICKUP_INIT] { $var(AppName) = $(kzE{kz.json,App-Name}); @@ -133,14 +166,15 @@ route[FAST_PICKUP_INIT] if($var(AppName) == "park") { if($(kzE{kz.json,State}) == "terminated") { $sht(park=>$(kzE{kz.json,Presence-ID})) = $null; + $sht(park=>$(kzE{kz.json,Presence-ID})::callid) = $null; } else { $sht(park=>$(kzE{kz.json,Presence-ID})) = $(kzE{kz.json,Switch-URI}); + $sht(park=>$(kzE{kz.json,Presence-ID})::callid) = $(kzE{kz.json,Call-ID}); } - } ## fast pickup with cookies - if(@cfg_get.kazoo.fast_pickup_cookies == 1) { + if($sel(cfg_get.kazoo.presence_fast_pickup_cookies) == 1) { if($var(AppName) == "park") { $var(Pickup) = 1; #";a-leg=true"; } else { @@ -151,7 +185,7 @@ route[FAST_PICKUP_INIT] $var(Cookie) = $(kzE{kz.json,Switch-URI}{s.md5}); $var(call_id) = $(kzE{kz.json,Call-ID}); $var(JObj) = $(kzE{re.subst,/"Call-ID"\s*\:\s*"([^"]*)"/"Call-ID" : "kfp+$var(Option)$var(Cookie)@\1"/}); - xlog("L_INFO", "$var(call_id)|fast|shortcut ($var(Pickup)) kfp+$var(Option)$var(Cookie)@$var(call_id)\n"); + xlog("L_DEBUG", "$var(call_id)|fast|shortcut ($var(Pickup)) kfp+$var(Option)$var(Cookie)@$var(call_id)\n"); } } diff --git a/kamailio/presence-notify.cfg b/kamailio/presence-notify.cfg new file mode 100644 index 0000000..7107249 --- /dev/null +++ b/kamailio/presence-notify.cfg @@ -0,0 +1,213 @@ +#!trydef KZ_PRESENCE_REMOVE_WATCHER_ON_EXPIRED_REGISTRATION 0 +#!trydef KZ_PRESENCE_MAX_NOTIFY_ERROR 3 +#!trydef KZ_PRESENCE_NOTIFY_LOG_LEVEL 4 + + +kazoo.presence_notify = 1 descr "enable/disable sending notify callback to omnipresence" +kazoo.presence_notify_timeout = 5000 descr "timeout in ms waiting for notify reply" +kazoo.presence_notify_log_body = 0 descr "logs the body sent in the notification" +kazoo.presence_notify_log_resp_body = 0 descr "logs the body received from notification" +kazoo.presence_notify_log_to_table = 1 descr "logs notify/reply to active_watchers_log table" +kazoo.presence_notify_log_to_amqp = 0 descr "logs notify/reply to amqp" +kazoo.presence_notify_record_route = 1 descr "add record route header to notify msg sent" +kazoo.presence_notify_log_init_body = 0 descr "logs the body before its sent" +kazoo.presence_notify_force_send_socket = 1 descr "forces the send socket to the contact" +kazoo.presence_remove_watcher_on_expired_registration = KZ_PRESENCE_REMOVE_WATCHER_ON_EXPIRED_REGISTRATION descr "removes watcher on expired registration" +kazoo.presence_max_notify_error = KZ_PRESENCE_MAX_NOTIFY_ERROR descr "number of consecutive fails allowed before removing the subscription" +kazoo.presence_notify_log_level = KZ_PRESENCE_NOTIFY_LOG_LEVEL descr "loglevel for informational log messages" + +######## Generic Hash Table container in shared memory ######## +modparam("htable", "htable", "notify=>size=16;autoexpire=3600;updateexpire=1;initval=0") + +route[PRESENCE_LOCAL_REQ_NOTIFY] +{ + if($rm != "NOTIFY") { + return; + } + t_set_fr($sel(cfg_get.kazoo.presence_notify_timeout), $sel(cfg_get.kazoo.presence_notify_timeout)); + xlog("L_DEBUG", "$ci|log|init preparing $subs(event) notify to $subs(watcher_username)@$subs(watcher_domain) on behalf of $subs(pres_uri) : $du\n"); + if($sel(cfg_get.kazoo.presence_notify_log_init_body) == 1) { + xlog("L_INFO", "$ci|log|init|body $(mb{s.escape.common}{s.replace,\','}{s.replace,$$,})\n"); + } + if($sel(cfg_get.kazoo.presence_notify_force_send_socket) == 1) { + $fs = $_s($(pr{s.tolower}):$(hdr(Contact){nameaddr.uri}{uri.host}):$(hdr(Contact){nameaddr.uri}{uri.port})); + xlog("L_DEBUG", "$ci|log|init|forcing socket to $fs, $(pr{s.tolower}):$(hdr(Contact){nameaddr.uri}{uri.host}):$(hdr(Contact){nameaddr.uri}{uri.port}) , $ct\n"); + } + if($sel(cfg_get.kazoo.presence_notify_record_route) == 1) { + record_route(); + } + + #!ifdef NAT_TRAVERSAL_ROLE + if(!isdsturiset()) { + handle_ruri_alias(); + } + #!endif + +} + +modparam("mqueue","mqueue", "name=presence_last_notity") + +modparam("rtimer", "timer", "name=notifytimer;interval=1;mode=1;") +modparam("rtimer", "exec", "timer=notifytimer;route=PRESENCE_LOG_TIMER_ROUTE") + +modparam("rtimer", "timer", "name=pres_cleanup;interval=10;mode=1;") +modparam("rtimer", "exec", "timer=pres_cleanup;route=PRESENCE_CLEANUP") + +modparam("rtimer", "timer", "name=pres_publisher_cleanup;interval=5;mode=1;") +modparam("rtimer", "exec", "timer=pres_publisher_cleanup;route=PRESENCE_PUBLISHER_CLEANUP") + + +event_route[presence:notify-reply] +{ + if($sel(cfg_get.kazoo.presence_notify) != 1) + return; + + $xavp(pres=>delete_subscription) = 0; + + if($notify_reply($rs) == 200) { + $sht(notify=>$ci) = $null; + $sht(notify=>$ci::count) = 0; + xlog("$(sel(cfg_get.kazoo.presence_notify_log_level){s.int})", "$ci|end|notified $subs(watcher_username)@$subs(watcher_domain) on behalf of $subs(pres_uri)\n"); + } else if($subs(reason) == "timeout") { + $xavp(pres=>delete_subscription) = 1; + xlog("L_DEBUG", "$ci|end|deleting subscription $subs(pres_uri) for $subs(watcher_username)@$subs(watcher_domain) due to timeout\n"); + } else if($notify_reply($rs) == 481 && $subs(reason) == "timeout") { + xlog("L_DEBUG","$ci|end|sent subscription $hdr(Subscription-State)\n"); + } else if($notify_reply($rs) == 408) { + if($rP != "UDP") { + $xavp(pres=>delete_subscription) = 1; + xlog("L_ERROR", "$ci|warning|removing $rP watcher $subs(watcher_username)@$subs(watcher_domain) for $subs(pres_uri) with reply $notify_reply($rs)\n"); + } else { + $var(shtinc) = $shtinc(notify=>$ci::count); + if($var(shtinc) > $sel(cfg_get.kazoo.presence_max_notify_error)) { + $xavp(pres=>delete_subscription) = 1; + xlog("L_WARNING", "$ci|error|removing $rP watcher $subs(watcher_username)@$subs(watcher_domain) for $subs(pres_uri) with reply $notify_reply($rs)\n"); + } else { + $var(level) = 6 - $var(shtinc); + xlog("$var(level)", "$ci|error|received $notify_reply($rs) ($var(shtinc)/$sel(cfg_get.kazoo.presence_max_notify_error)) when notifying $subs(watcher_username)@$subs(watcher_domain) on behalf of $subs(pres_uri) with reply $notify_reply($rs)\n"); + } + } + } else { + $xavp(pres=>delete_subscription) = 1; + xlog("L_WARNING", "$ci|end|deleting subscription $subs(pres_uri) as $subs(watcher_username)@$subs(watcher_domain) replied with $notify_reply($rs)\n"); + } + + if($sel(cfg_get.kazoo.presence_notify_log_body) == 1) + xlog("L_INFO", "$ci|log|sent|body $(mb{s.escape.common}{s.replace,\','}{s.replace,$$,})\n"); + if($sel(cfg_get.kazoo.presence_notify_log_resp_body) == 1) + xlog("L_INFO", "$ci|log|resp|body $(notify_reply($mb){s.escape.common}{s.replace,\','}{s.replace,$$,})\n"); + + if($sel(cfg_get.kazoo.presence_notify_log_to_amqp) == 1) { + route(PRESENCE_NOTIFY_AMQP); + } + + if($sel(cfg_get.kazoo.presence_notify_log_to_table) == 1) { + if($xavp(pres=>delete_subscription) != 1 && $subs(reason) != "timeout") { + $var(Query) = $_s(KZQ_REPLACE_WATCHERS_LOG); + mq_add("presence_last_notity", "$subs(callid)", "$var(Query)"); + } + } +} + + +route[PRESENCE_LOG_TIMER_ROUTE] +{ + $var(runloop) = 1; + while(mq_fetch("presence_last_notity") == 1 && $var(runloop) < MAX_WHILE_LOOPS) { + $var(ci) = $mqk(presence_last_notity); + xlog("L_DEBUG", "Query : $mqv(presence_last_notity)\n"); + $var(sqlres) = sql_query("cb", "$mqv(presence_last_notity)"); + xlog("L_DEBUG", "Query result : $var(sqlres)\n"); + if($var(sqlres) < 0) { + xlog("L_ERROR", "$var(ci)|log|error running query : $mqv(presence_last_notity)\n"); + } else { + $var(nrows) = $sqlrows(cb); + xlog("L_DEBUG", "$var(ci)|log|end UPDATED $var(nrows)\n"); + if($var(nrows) == 0) { + xlog("L_DEBUG", "$var(ci)|log|error no rows affected when running query\n"); + } + } + $var(runloop) = $var(runloop) + 1; + } +} + +route[PRESENCE_NOTIFY_AMQP] +{ + $var(amqp_payload_request) = $_s({"Event-Category" : "presence", "Event-Name" : "notify", "Event-Package" : "$subs(event)", "Timestamp" : $TS, "Call-ID" : "$subs(callid)", "From" : "$fu", "To" : "$subs(to_user)@$subs(to_domain)", "Sent" : "$(TS{s.ftime,%Y-%m-%d %H:%M:%S})", "Body" : "Hostname : MY_HOSTNAME\r\nTimestamp : $(TS{s.ftime,%Y-%m-%d %H:%M:%S})\r\n$(mb{s.escape.common}{s.replace,\','}{s.replace,$$,})\r\nResponse\r\n$(notify_reply($mb){s.escape.common}{s.replace,\','}{s.replace,$$,})","Remote-CSeq" : $subs(remote_cseq), "Local-CSeq" : $subs(local_cseq), "Sequence" : $cs, "Version" : $subs(version), "Reply" : $notify_reply($rs) }); + $var(rk) = "notify." + $(subs(to_domain){kz.encode}) + "." + $(subs(to_user){kz.encode}); + kazoo_publish("omnipresence", "$var(rk)", $var(amqp_payload_request)); + xlog("L_INFO", "$ci|log|sent notify callback for event $subs(event) : $tu\n"); +} + + +route[PRESENCE_CLEANUP] +{ + $var(Query) = $_s(DELETE FROM active_watchers WHERE expires > 0 AND datetime(expires, 'unixepoch') < datetime('now', '-90 seconds');); + mq_add("presence_last_notity", "$uuid(g)", "$var(Query)"); + $var(Query) = $_s(DELETE FROM PRESENTITY WHERE expires > 0 AND datetime(expires, 'unixepoch') < datetime('now');); + mq_add("presence_last_notity", "$uuid(g)", "$var(Query)"); + $var(Query) = $_s(DELETE FROM PRESENTITY WHERE ID IN(select id from presentities where event = "dialog" and state = "terminated" and received < datetime('now', '-5 minutes'));); + mq_add("presence_last_notity", "$uuid(g)", "$var(Query)"); + $var(Query) = $_s(DELETE FROM ACTIVE_WATCHERS_LOG WHERE ID IN(select id from active_watchers_log a where not exists(select callid from active_watchers b where b.callid = a.callid and b.watcher_username = a.watcher_username and b.watcher_domain = a.watcher_domain));); + mq_add("presence_last_notity", "$uuid(g)", "$var(Query)"); +} + +route[PRESENCE_PUBLISHER_CLEANUP] +{ + xlog("L_DEBUG", "processing presence publisher cleanup\n"); + + $var(sqlres) = sql_query("cb", "update tmp_probe set action = 1 where action = 0"); + if($var(sqlres) < 0) { + xlog("L_ERROR", "$var(ci)|log|error cleaning tmp_probe\n"); + return; + } else { + $var(nrows) = $sqlrows(cb); + if($var(nrows) > 0) { + if (sql_xquery("cb", "select * from tmp_probe where action = 1", "cleanup_pres") == 1) + { + while($xavp(cleanup_pres) != $null) { + xlog("L_DEBUG", "processing $xavp(cleanup_pres=>event) notifies for $xavp(cleanup_pres=>presentity_uri)\n"); + pres_refresh_watchers("$xavp(cleanup_pres=>presentity_uri)", "$xavp(cleanup_pres=>event)", 1); + pv_unset("$xavp(cleanup_pres)"); + } + } + + $var(sqlres) = sql_query("cb", "delete from tmp_probe where action = 1"); + if($var(sqlres) < 0) { + xlog("L_ERROR", "$var(ci)|log|error cleaning tmp_probe\n"); + } else { + $var(nrows) = $sqlrows(cb); + if($var(nrows) > 0) { + xlog("L_DEBUG", "presence publisher cleanup processed $var(nrows) rows\n"); + } + } + } + } + +} + +route[PRESENCE_DEFERRED_INIT] +{ + xlog("L_INFO", "processing presence deferred init\n"); + $var(sqlres) = sql_query("cb", "update tmp_probe set action = 0 where action = 2"); + if($var(sqlres) < 0) { + xlog("L_ERROR", "$var(ci)|log|error cleaning tmp_probe\n"); + return; + } else { + $var(nrows) = $sqlrows(cb); + if($var(nrows) > 0) { + xlog("L_NOTICE", "scheduled update for $var(nrows) watched presentities/event\n"); + } + } +} + +route[PRESENCE_EXPIRED_REGISTRATION] +{ + if($sel(cfg_get.kazoo.presence_remove_watcher_on_expired_registration) == 1) { + $var(watcher) = $_s(sip:$ulc(exp=>aor)); + $var(watcher_username) = $(var(watcher){uri.user}); + $var(watcher_domain) = $(var(watcher){uri.host}); + $var(Query) = $_s(DELETE FROM active_watchers WHERE watcher_username = "$var(watcher_username)" and watcher_domain = "$var(watcher_domain)";); + mq_add("presence_last_notity", "$uuid(g)", "$var(Query)"); + } +} diff --git a/kamailio/presence_query-role.cfg b/kamailio/presence-query.cfg similarity index 75% rename from kamailio/presence_query-role.cfg rename to kamailio/presence-query.cfg index ac087cc..95fa96d 100644 --- a/kamailio/presence_query-role.cfg +++ b/kamailio/presence-query.cfg @@ -1,8 +1,11 @@ ######## Presence query server module ######## +#!trydef KZ_PRESENCE_QUERY_REPLY_ZONES 0 +kazoo.presence_query_reply_zones = KZ_PRESENCE_QUERY_REPLY_ZONES descr "0 - all, 1 - local, 2 - remote" + route[PRESENCE_SEARCH_SUMMARY] { - xlog("L_INFO", "processing presence summary query for $(kzE{kz.json,Realm})\n"); + xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|query|processing presence summary query for $(kzE{kz.json,Realm})\n"); $var(Queue) = $(kzE{kz.json,Server-ID}); $var(Event) = $(kzE{kz.json,Event-Package}); $var(Domain) = $(kzE{kz.json,Realm}); @@ -54,8 +57,9 @@ route[PRESENCE_SEARCH_SUMMARY] route[PRESENCE_SEARCH_DETAIL] { - xlog("L_INFO", "processing presence query detail for $(kzE{kz.json,Username}) in realm $(kzE{kz.json,Realm})\n"); + xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|query|processing presence query detail for $(kzE{kz.json,Username}) in realm $(kzE{kz.json,Realm})\n"); $var(Queue) = $(kzE{kz.json,Server-ID}); + $var(Msg-ID) = $(kzE{kz.json,Msg-ID}); $var(Event) = $(kzE{kz.json,Event-Package}); $var(Domain) = $(kzE{kz.json,Realm}); $var(Username) = $(kzE{kz.json,Username}); @@ -64,7 +68,7 @@ route[PRESENCE_SEARCH_DETAIL] $var(Items) = ""; $var(Query) = $_s(KZQ_PRESENCE_SEARCH_DETAIL); if($var(Event) != "") { - $var(Query) = $var(Query) + $_s( and event = "$var(Event)"); + $var(Query) = $var(Query) + $_s( and a.event = "$var(Event)"); } $var(Query) = $var(Query) + " order by event, watcher_username, callid"; xlog("L_DEBUG", "$ci| STATUS QUERY $var(Query)\n"); @@ -75,38 +79,35 @@ route[PRESENCE_SEARCH_DETAIL] while($xavp(ra) != $null && $var(Event) == $xavp(ra=>event)) { $var(Sub) = $_s("$var(Username)" : {"$xavp(ra=>event)" : { "$xavp(ra=>watcher_username)" : {"kamailio@MY_HOSTNAME" : {"$xavp(ra=>callid)" : {"time" : $xavp(ra=>time), "result" : $xavp(ra=>result), "sent" : "$(xavp(ra=>sent_msg){s.escape.common}{s.replace,\','}{s.replace,$$,})", "received" : "$(xavp(ra=>received_msg){s.escape.common}{s.replace,\','}{s.replace,$$,})", "user_agent" : "$(xavp(ra=>user_agent){s.escape.common}{s.replace,\','}{s.replace,$$,})"}}}}}); xlog("L_DEBUG", "$ci| RESULT \"Subscriptions\" : { $var(Sub) }\n"); - $var(amqp_payload_request) = '{"Event-Category" : "presence", "Event-Name" : "search_partial_resp", "Msg-ID" : "$(kzE{kz.json,Msg-ID})", "Subscriptions" : { $var(Sub) } }'; + $var(amqp_payload_request) = '{"Event-Category" : "presence", "Event-Name" : "search_partial_resp", "Msg-ID" : "$var(Msg-ID)", "Subscriptions" : { $var(Sub) } }'; kazoo_publish("targeted", "$var(Queue)", $var(amqp_payload_request)); pv_unset("$xavp(ra)"); } } } - $var(amqp_payload_request) = '{"Event-Category" : "presence", "Event-Name" : "search_resp", "Msg-ID" : "$(kzE{kz.json,Msg-ID})" }'; + $var(amqp_payload_request) = '{"Event-Category" : "presence", "Event-Name" : "search_resp", "Msg-ID" : "$var(Msg-ID)" }'; kazoo_publish("targeted", "$var(Queue)", $var(amqp_payload_request)); } - - event_route[kazoo:consumer-event-presence-search-req] { - switch($(kzE{kz.json,Search-Type})) { - case "summary": - route(PRESENCE_SEARCH_SUMMARY); - break; - case "detail": - route(PRESENCE_SEARCH_DETAIL); - break; - default: - xlog("L_INFO", "$ci|search type '$(kzE{kz.json,Search-Type})' not handled\n"); - } -} + $var(Zone) = $(kzE{kz.json,AMQP-Broker-Zone}); + if( ($var(Zone) == "MY_AMQP_ZONE" && $sel(cfg_get.kazoo.presence_query_reply_zones) != 2) || + ($var(Zone) != "MY_AMQP_ZONE" && $sel(cfg_get.kazoo.presence_query_reply_zones) != 1)) { -route[PRESENCE_QUERY_BINDINGS] -{ - $var(payload) = "{ 'exchange' : 'presence' , 'type' : 'topic', 'queue' : 'presence-search-MY_HOSTNAME', 'routing' : 'presence.search_req.*', 'exclusive' : 0, 'federate' : 1 }"; - kazoo_subscribe("$var(payload)"); + switch($(kzE{kz.json,Search-Type})) { + case "summary": + route(PRESENCE_SEARCH_SUMMARY); + break; + case "detail": + route(PRESENCE_SEARCH_DETAIL); + break; + default: + xlog("L_INFO", "$ci|search type '$(kzE{kz.json,Search-Type})' not handled\n"); + } + } } # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab diff --git a/kamailio/presence-reset.cfg b/kamailio/presence-reset.cfg index eafd672..61315e7 100644 --- a/kamailio/presence-reset.cfg +++ b/kamailio/presence-reset.cfg @@ -1,3 +1,4 @@ +#!trydef PRESENCE_RESET_BLF_DEFER_UPDATE 0 kazoo.presence_reset_blf_defer_update = PRESENCE_RESET_BLF_DEFER_UPDATE descr "defers blf reset" @@ -14,21 +15,10 @@ route[PRESENCE_RESET_ROUTE] } } -route[PRESENCE_RESET_BINDINGS] -{ - #!import_file "presence-reset-custom-bindings.cfg" - - #!ifndef PRESENCE_RESET_CUSTOM_BINDINGS - $var(payload) = "{ 'exchange' : 'presence' , 'queue' : 'presence-reset-MY_HOSTNAME', 'type' : 'topic', 'routing' : 'presence.reset.*.*', 'exclusive' : 0, 'federate' : 1 }"; - kazoo_subscribe("$var(payload)"); - #!endif - -} - route[RESET_PUBLISHER] { - xlog("L_INFO", "$var(Msg-ID)|reset|received presence reset for publisher $var(MediaUrl))\n"); - if(@cfg_get.kazoo.presence_reset_blf_defer_update == 0) { + xlog("L_INFO", "$var(Msg-ID)|reset|received presence reset for publisher $var(MediaUrl)\n"); + if($sel(cfg_get.kazoo.presence_reset_blf_defer_update) == 0) { $var(Query) = $_s(KZQ_RESET_PUBLISHER_UPDATE); sql_query("exec", "$var(Query)"); } @@ -36,6 +26,33 @@ route[RESET_PUBLISHER] sql_query("exec", "$var(Query)"); $var(presentities) = $sqlrows(exec); xlog("L_INFO", "$var(Msg-ID)|reset|removed $var(presentities) presentities from publisher $var(MediaUrl)\n"); + if($var(presentities) > 0) { + route(PRESENCE_PUBLISHER_CLEANUP); + } +} + +route[PRESENCE_ZONE_UNAVAILABLE] +{ + if($(kzE{kz.json,name}) == "presence") { + $var(Zone) = $(kzE{kz.json,zone}); + xlog("L_WARN", "amqp|reset|resetting publisher zone $var(Zone)\n"); + route(RESET_PUBLISHER_ZONE); + } +} + +route[RESET_PUBLISHER_ZONE] +{ + if($sel(cfg_get.kazoo.presence_reset_blf_defer_update) == 0) { + $var(Query) = $_s(KZQ_RESET_PUBLISHER_ZONE_UPDATE); + sql_query("exec", "$var(Query)"); + } + $var(Query) = $_s(KZQ_PRESENCE_ZONE_RESET); + sql_query("exec", "$var(Query)"); + $var(presentities) = $sqlrows(exec); + xlog("L_INFO", "amqp|reset|removed $var(presentities) presentities from zone $var(Zone)\n"); + if($var(presentities) > 0) { + route(PRESENCE_PUBLISHER_CLEANUP); + } } route[RESET_ALL] @@ -62,11 +79,14 @@ route[RESET_SERVER] route[RESET_ACCOUNT] { xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|received presence reset for realm $(kzE{kz.json,Realm})\n"); - sql_query("exec", 'KZQ_RESET_ACCOUNT_DELETE'); - $var(presentities) = $sqlrows(exec); sql_query("exec", 'KZQ_RESET_ACCOUNT_UPDATE'); $var(watchers) = $sqlrows(exec); + sql_query("exec", 'KZQ_RESET_ACCOUNT_RESET'); + $var(presentities) = $sqlrows(exec); xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|removed $var(presentities) presentities and expired $var(watchers) subscribers for realm $(kzE{kz.json,Realm})\n"); + if($var(watchers) > 0) { + route(PRESENCE_PUBLISHER_CLEANUP); + } } route[RESET_WILDCARD] @@ -98,7 +118,6 @@ route[RESET_WILDCARD] } - event_route[kazoo:consumer-event-presence-reset] { if($(kzE{kz.json,Username}) == "*" || $(kzE{kz.json,Realm}) == "*") { @@ -106,7 +125,7 @@ event_route[kazoo:consumer-event-presence-reset] exit(); } - xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|received presence reset for $(kzE{kz.json,Username})@$(kzE{kz.json,Realm})\n"); + xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|received presence reset for $(kzE{kz.json,Username})@$(kzE{kz.json,Realm})\n"); $var(presentity) = $_s(sip:$(kzE{kz.json,Username})@$(kzE{kz.json,Realm})); route(COUNT_SUBSCRIBERS); sql_query("exec", 'KZQ_EVENT_PRESENCE_RESET_DELETE'); @@ -115,21 +134,21 @@ event_route[kazoo:consumer-event-presence-reset] xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|notifying $xavp(watchers=>message-summary) message-summary subscribers of $var(presentity)\n"); pres_refresh_watchers("$var(presentity)", "message-summary", 1); } else { - xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|skip message-summary subscriber notification for $var(presentity)\n"); + xlog("L_DEBUG", "$(kzE{kz.json,Msg-ID})|reset|skip message-summary subscriber notification for $var(presentity)\n"); } if($xavp(watchers=>presence) > 0) { xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|notifying $xavp(watchers=>presence) presence subscribers of $var(presentity)\n"); pres_refresh_watchers("$var(presentity)", "presence", 1); } else { - xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|skip presence subscriber notification for $var(presentity)\n"); + xlog("L_DEBUG", "$(kzE{kz.json,Msg-ID})|reset|skip presence subscriber notification for $var(presentity)\n"); } if($xavp(watchers=>dialog) > 0) { xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|notifying $xavp(watchers=>dialog) dialog subscribers of $var(presentity)\n"); pres_refresh_watchers("$var(presentity)", "dialog", 1); } else { - xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|skip dialog subscriber notification for $var(presentity)\n"); + xlog("L_DEBUG", "$(kzE{kz.json,Msg-ID})|reset|skip dialog subscriber notification for $var(presentity)\n"); } } diff --git a/kamailio/presence-role.cfg b/kamailio/presence-role.cfg index 81af925..e7e4815 100644 --- a/kamailio/presence-role.cfg +++ b/kamailio/presence-role.cfg @@ -3,8 +3,36 @@ #!trydef PRESENCE_MIN_EXPIRES_ACTION 1 #!trydef PRESENCE_MAX_EXPIRES 3600 -modparam("htable", "htable", "p=>size=32;autoexpire=3600;") -modparam("htable", "htable", "first=>size=32;autoexpire=3600;initval =0;updateexpire=1;") +#!trydef KZ_PRESENCE_IGNORE_STATUS_PROBE_RESP 1 +#!trydef KZ_PRESENCE_CSEQ_OFFSET 2 +#!trydef KZ_PRESENCE_MAX_CALL_PER_PRESENTITY 20 +#!trydef BLF_USE_SINGLE_DIALOG 1 +#!trydef KZ_PRESENCE_AMQP_PUBLISH 0 +#!trydef KZ_PRESENCE_REQUEST_RESUBSCRIBE_PROBE 0 +#!trydef KZ_PRESENCE_REQUEST_PROBE 1 +#!trydef KZ_PRESENCE_NO_TARGETS_LOG_LEVEL L_DBG +#!trydef KZ_PRESENCE_WITH_TARGETS_LOG_LEVEL L_INFO +#!trydef KZ_PRESENCE_REQUIRE_AUTHN 0 +#!trydef KZ_PRESENCE_KEEPALIVE_NAT_ONLY 0 +#!trydef KZ_PRESENCE_KEEPALIVE_UDP_ONLY 0 + + +#!ifdef NAT_TRAVERSAL_ROLE +#!ifndef KEEPALIVE_ROLE +#!ifndef NAT_TRAVERSAL_LOADED +#!trydef NAT_TRAVERSAL_LOADED +loadmodule "nat_traversal.so" +#!endif + +modparam("nat_traversal", "keepalive_method", "OPTIONS") +modparam("nat_traversal", "keepalive_from", "sip:sipcheck@MY_HOSTNAME") +modparam("nat_traversal", "keepalive_state_file", "KAZOO_DATA_DIR/keep_alive_state") +modparam("nat_traversal", "keepalive_interval", 45) +#!endif +#!endif + + +modparam("htable", "htable", "first=>size=32;autoexpire=3600;initval=0;") loadmodule "presence.so" loadmodule "presence_dialoginfo.so" @@ -22,8 +50,10 @@ modparam("presence_xml", "disable_bla", 1) modparam("presence", "subs_db_mode", 3) modparam("presence", "expires_offset", 0) modparam("presence", "send_fast_notify", 1) -modparam("presence", "clean_period", 30) -modparam("presence", "db_update_period", 10) + +modparam("presence", "clean_period", 0) +modparam("presence", "db_update_period", 0) + modparam("presence", "publ_cache", 0) modparam("presence", "min_expires_action", PRESENCE_MIN_EXPIRES_ACTION) modparam("presence", "min_expires", PRESENCE_MIN_EXPIRES) @@ -37,49 +67,42 @@ modparam("presence", "local_log_level", 6) modparam("presence", "startup_mode", 0) modparam("presence", "force_delete", 1) modparam("presence", "timeout_rm_subs", 0) -modparam("presence", "cseq_offset", 1) +modparam("presence", "cseq_offset", KZ_PRESENCE_CSEQ_OFFSET) modparam("kazoo", "db_url", "KAZOO_DB_URL") modparam("kazoo", "pua_mode", 1) -#!ifdef NAT_TRAVERSAL_ROLE -#!ifndef NAT_TRAVERSAL_LOADED -#!trydef NAT_TRAVERSAL_LOADED -loadmodule "nat_traversal.so" -#!endif -modparam("nat_traversal", "keepalive_method", "OPTIONS") -modparam("nat_traversal", "keepalive_from", "sip:sipcheck@MY_HOSTNAME") -modparam("nat_traversal", "keepalive_state_file", "KAZOO_DATA_DIR/keep_alive_state") -modparam("nat_traversal", "keepalive_interval", 45) -#!endif - kazoo.presence_sync_amqp = KZ_PRESENCE_AMQP_PUBLISH descr "sync subscriptions to amqp" kazoo.presence_request_probe = KZ_PRESENCE_REQUEST_PROBE descr "request probe for new subscriptions" kazoo.presence_request_resubscribe_probe = KZ_PRESENCE_REQUEST_RESUBSCRIBE_PROBE descr "request probe for resubscriptions" - -#!ifdef FAST_PICKUP_ROLE -#!include_file "fast-pickup-role.cfg" -#!endif -#!ifdef PRESENCE_QUERY_ROLE -#!include_file "presence_query-role.cfg" -#!endif -#!ifdef PRESENCE_NOTIFY_SYNC_ROLE -#!include_file "presence_notify_sync-role.cfg" -#!endif +kazoo.presence_ignore_status_probe_resp = KZ_PRESENCE_IGNORE_STATUS_PROBE_RESP descr "ignore online/offline probe replies" +kazoo.presence_max_call_per_presentity = KZ_PRESENCE_MAX_CALL_PER_PRESENTITY descr "max number of calls per presentity" +kazoo.presence_no_targets_log_level = KZ_PRESENCE_NO_TARGETS_LOG_LEVEL descr "when a presence event is received and there no targets we can log at another level" +kazoo.presence_with_targets_log_level = KZ_PRESENCE_WITH_TARGETS_LOG_LEVEL descr "when a presence event is received and there are targets we can log at another level" +kazoo.presence_require_authn = KZ_PRESENCE_REQUIRE_AUTHN descr "require authenticated devices for presence" +kazoo.presence_keepalive_udp_only = KZ_PRESENCE_KEEPALIVE_UDP_ONLY descr "should we send keepalive for udp only" +kazoo.presence_keepalive_nat_only = KZ_PRESENCE_KEEPALIVE_NAT_ONLY descr "should we send keepalive for nat phones only" + +#!include_file "presence-query.cfg" +#!include_file "presence-notify.cfg" +#!include_file "presence-reset.cfg" +#!include_file "presence-fast-pickup.cfg" ####### Presence Logic ######## -#!ifdef NAT_TRAVERSAL_ROLE route[PRESENCE_NAT] { - force_rport(); - if (client_nat_test("3")) { - if(is_first_hop()) - set_contact_alias(); - } - nat_keepalive(); + if(!( ($sel(cfg_get.kazoo.presence_keepalive_udp_only) == 1 && $proto != "udp") + || (!isflagset(FLT_NATS) && $sel(cfg_get.kazoo.presence_keepalive_nat_only) == 1) + || ($proto == "ws" || $proto == "wss") + )) { + #!ifdef KEEPALIVE_ROLE + route(KEEPALIVE_ON_SUBSCRIBE); + #!else + nat_keepalive(); + #!endif + } } -#!endif route[HANDLE_SUBSCRIBE] { @@ -87,9 +110,20 @@ route[HANDLE_SUBSCRIBE] return; } - #!ifdef NAT_TRAVERSAL_ROLE - route(PRESENCE_NAT); - #!endif + if ($hdr(Event) != "dialog" + && $hdr(Event) != "presence" + && $hdr(Event) != "message-summary") { + xlog("L_INFO", "$ci|presence|subscribe event $hdr(Event) not supported\n"); + send_reply(489, "Bad Event"); + exit(); + } + + route(PRESENCE_AUTHN); + + if (!t_newtran()) { + sl_reply_error(); + exit; + } if(has_totag()) { loose_route(); @@ -97,11 +131,6 @@ route[HANDLE_SUBSCRIBE] record_route(); - if (!t_newtran()) { - sl_reply_error(); - exit; - } - if(has_totag()) { route(HANDLE_RESUBSCRIBE); } else { @@ -113,15 +142,22 @@ route[HANDLE_SUBSCRIBE] exit; } +route[DELETE_DUPLICATED_SUBSCRIPTIONS] +{ + sql_query("exec", '$_s(DELETE FROM ACTIVE_WATCHERS WHERE PRESENTITY_URI="$subs(uri)" AND EVENT="$subs(event)" AND FROM_USER = "$subs(from_user)" AND FROM_DOMAIN="$subs(from_domain)" AND CALLID <> "$subs(callid)")'); +} + route[HANDLE_RESUBSCRIBE] { - + if(handle_subscribe()) { if($subs(remote_cseq) < 5) { $sht(first=>$subs(callid)) = $null; $sht(first=>$subs(from_user)::$subs(pres_uri)::$subs(from_domain)::$subs(event)) = $null; } + route(DELETE_DUPLICATED_SUBSCRIPTIONS); route(SUBSCRIBE_AMQP); + route(REQUEST_PROBE); }; } @@ -161,8 +197,12 @@ route[HANDLE_NEW_SUBSCRIBE] if (handle_subscribe()) { xlog("L_INFO","$ci|end|new $hdr(Event) subscription from $fU to $tU in realm $fd : $sht(first=>$ci) : $sht(first=>$fU::$tU::$fd::$hdr(Event))\n"); + route(DELETE_DUPLICATED_SUBSCRIPTIONS); route(SUBSCRIBE_AMQP); route(REQUEST_PROBE); + #!ifdef NAT_TRAVERSAL_ROLE + route(PRESENCE_NAT); + #!endif } else { xlog("L_INFO", "$ci|stop|error $T_reply_code for new $hdr(Event) subscription from $fU to $tU in realm $fd\n"); } @@ -174,7 +214,7 @@ route[HANDLE_NEW_SUBSCRIBE] route[SUBSCRIBE_AMQP] { - if(@cfg_get.kazoo.presence_sync_amqp == 1) { + if($sel(cfg_get.kazoo.presence_sync_amqp) == 1) { $var(rk) = "subscribe." + $(subs(to_domain){kz.encode}) + "." + $(subs(to_user){kz.encode}); $var(amqp_payload_request) = $_s({"Event-Category" : "presence", "Event-Name" : "subscription", "Event-Package" : "$hdr(event)", "Expires" : $subs(expires), "Queue" : "BLF-MY_HOSTNAME", "Server-ID" : "BLF-MY_HOSTNAME" , "Contact" : "$(ct{s.escape.common}{s.replace,\','}{s.replace,$$,})", "Call-ID" : "$ci", "From" : "$fu", "User" : "$subs(uri)", "User-Agent" : "$(ua{s.escape.common}{s.replace,\','}{s.replace,$$,})" }); kazoo_publish("omnipresence", "$var(rk)", $var(amqp_payload_request)); @@ -183,9 +223,9 @@ route[SUBSCRIBE_AMQP] route[REQUEST_PROBE] { - if( (@cfg_get.kazoo.presence_request_probe == 1 && (!has_totag())) - || (@cfg_get.kazoo.presence_request_resubscribe_probe == 1 && has_totag()) ) { - if( route(HAS_PRESENTITY) == 0) { + if( ($sel(cfg_get.kazoo.presence_request_probe) == 1 && (!has_totag())) + || ($sel(cfg_get.kazoo.presence_request_resubscribe_probe) == 1 && has_totag()) ) { + if( route(HAS_PRESENTITY) == 0) { if($hdr(event) == "message-summary") { $var(mwi) = $tU; route(REQUEST_MWI); @@ -206,26 +246,40 @@ route[REQUEST_PROBE] route[REQUEST_MWI] { - xlog("L_INFO", "$ci|sub|requesting mwi probe for $var(mwi) in realm $subs(to_domain)\n"); - $var(rk) = "mwi_queries." + $(subs(to_domain){kz.encode}); - $var(amqp_payload_request) = $_s({"Event-Category" : "presence", "Event-Name" : "mwi_query", "Username" : "$var(mwi)", "Realm" : "$fd", "Call-ID" : "$ci"}); - kazoo_publish("presence", "$var(rk)", $var(amqp_payload_request)); + xlog("L_INFO", "$ci|sub|requesting mwi probe for $var(mwi) in realm $subs(to_domain)\n"); + $var(rk) = "mwi_queries." + $(subs(to_domain){kz.encode}); + $var(amqp_payload_request) = $_s({"Event-Category" : "presence", "Event-Name" : "mwi_query", "Username" : "$var(mwi)", "Realm" : "$fd", "Call-ID" : "$ci"}); + kazoo_publish("presence", "$var(rk)", $var(amqp_payload_request)); } route[HANDLE_PUBLISH] { - if (is_method("PUBLISH")) { - if (!t_newtran()) { - sl_reply_error(); - exit; - } - if($hdr(Sender)!= $null) - handle_publish("$hdr(Sender)"); - else - handle_publish(); - t_release(); + if(!is_method("PUBLISH")) { + return; + } + + if ($hdr(Event) != "dialog" + && $hdr(Event) != "presence" + && $hdr(Event) != "message-summary") { + xlog("L_INFO", "$ci|presence|publish event $hdr(Event) not supported\n"); + send_reply(489, "Bad Event"); + exit(); + } + + route(PRESENCE_AUTHN); + + if (!t_newtran()) { + sl_reply_error(); exit; } + + if($hdr(Sender)!= $null) { + handle_publish("$hdr(Sender)"); + } else { + handle_publish(); + } + t_release(); + exit; } route[HAS_PRESENTITY] @@ -245,7 +299,7 @@ route[HAS_PRESENTITY] route[COUNT_PRESENTITIES] { $var(Query) = $_s(KZQ_COUNT_PRESENTITIES); - $var(p) = $_s(uri=$var(presentity)); + $var(p) = $_s(presence_id='$var(presentity)'); if (sql_xquery("cb", "$var(Query)", "subs") == 1) { while($xavp(subs) != $null) { @@ -259,7 +313,7 @@ route[COUNT_PRESENTITIES] route[COUNT_ALL_PRESENTITIES] { $var(Query) = $_s(select event, (select count(*) from presentity b where b.event = a.event) count from event_list a); - $var(p) = "uri=none"; + $var(p) = "presence_id=none"; if (sql_xquery("cb", "$var(Query)", "subs") == 1) { while($xavp(subs) != $null) { @@ -290,7 +344,7 @@ route[COUNT_ALL_SUBSCRIBERS] route[COUNT_SUBSCRIBERS] { $var(Query) = $_s(KZQ_COUNT_SUBSCRIBERS); - $var(p) = $_s(uri=$var(presentity)); + $var(p) = $_s(presence_id='$var(presentity)'); if (sql_xquery("cb", "$var(Query)", "subs") == 1) { while($xavp(subs) != $null) { @@ -303,74 +357,157 @@ route[COUNT_SUBSCRIBERS] event_route[kazoo:consumer-event-presence-dialog-update] { - $var(Now) = $TS; - xlog("L_INFO", "$(kzE{kz.json,Call-ID})|log|received $(kzE{kz.json,Event-Package}) update for $(kzE{kz.json,From}) state $(kzE{kz.json,State}) from $(kzE{kz.json,Switch-URI}) at $(kzE{kz.json,AMQP-Received})/$var(Now)\n"); + $var(kz_presence_log_level) = $(sel(cfg_get.kazoo.presence_no_targets_log_level){s.int}); + $var(StartRoute) = $(TV(Sn){s.replace,.,}); + $var(delta_to_start) = $var(StartRoute) - $(kzE{kz.json,AMQP-Received}); + xlog("L_DEBUG", "$(kzE{kz.json,Call-ID})|log|received $(kzE{kz.json,Event-Package}) update for $(kzE{kz.json,From}) state $(kzE{kz.json,State}) from $(kzE{kz.json,AMQP-Broker-Zone}) => $(kzE{kz.json,Switch-URI}) (Δ1 $(kzE{kz.json,AMQP-Elapsed-Micro}) μs , Δ2 $var(delta_to_start) μs)\n"); + $var(JObj) = $kzE; - #!ifdef FAST_PICKUP_ROLE - route(FAST_PICKUP_INIT); - #!endif - + route(PRESENCE_FAST_PICKUP_INIT); + $var(presentity) = $(kzE{kz.json,From}); $var(payload) = $kzE; - + route(PRESENCE_UPDATE); - - xlog("L_INFO", "$(kzE{kz.json,Call-ID})|log|finished processing $(kzE{kz.json,Event-Package}) update for $(kzE{kz.json,From}) state $(kzE{kz.json,State}) from $(kzE{kz.json,Switch-URI}) at $(kzE{kz.json,AMQP-Received})/$var(Now)/$TS\n"); + $var(EndRoute) = $(TV(Sn){s.replace,.,}); + $var(delta_to_finish) = $var(EndRoute) - $var(StartRoute); + + xlog("$var(kz_presence_log_level)", "$(kzE{kz.json,Call-ID})|log|$(kzE{kz.json,Event-Package}) update for $(kzE{kz.json,From}) state $(kzE{kz.json,State}) from $(kzE{kz.json,AMQP-Broker-Zone}) => $(kzE{kz.json,Switch-URI}) (Δ1 $(kzE{kz.json,AMQP-Elapsed-Micro}) μs , Δ2 $var(delta_to_start) μs, Δ3 $var(delta_to_finish) μs)\n"); } event_route[kazoo:consumer-event-presence-mwi-update] { - xlog("L_INFO", "$(kzE{kz.json,Call-ID})|log|received message-summary update for $(kzE{kz.json,From})\n"); + $var(kz_presence_log_level) = $(sel(cfg_get.kazoo.presence_no_targets_log_level){s.int}); + $var(StartRoute) = $(TV(Sn){s.replace,.,}); + $var(delta_to_start) = $var(StartRoute) - $(kzE{kz.json,AMQP-Received}); + xlog("L_DBG", "$(kzE{kz.json,Call-ID})|log|received message-summary update for $(kzE{kz.json,From}) ($(kzE{kz.json,AMQP-Broker-Zone}))\n"); $var(presentity) = $(kzE{kz.json,From}); + $var(payload) = $kzE; route(COUNT_SUBSCRIBERS); if($xavp(watchers=>message-summary) > 0) { + $var(kz_presence_log_level) = $(sel(cfg_get.kazoo.presence_with_targets_log_level){s.int}); xlog("L_INFO", "$(kzE{kz.json,Call-ID})|log|publishing $(kzE{kz.json,From}) message-summary update for $xavp(watchers=>message-summary) watchers\n"); kazoo_pua_publish_mwi($kzE); pres_refresh_watchers("$(kzE{kz.json,From})", "message-summary", 1); } else { - xlog("L_DEBUG", "$(kzE{kz.json,Call-ID})|log|skip message-summary update for $(kzE{kz.json,From})\n"); +#!ifdef PRESENCE_TRACK_ALL_PKG_MWI + if($(kzE{kz.json,Event-Package}) == "message-summary") { + $var(kz_presence_log_level) = $(sel(cfg_get.kazoo.presence_with_targets_log_level){s.int}); + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|saving $var(presentity) from mwi update => $var(payload)\n"); + if(kazoo_pua_publish_mwi($kzE) != 1) { + xlog("L_ERR", "$(var(payload){kz.json,Call-ID})|log|error publishing $var(presentity) mwi update => $var(payload)\n"); + } + } +#!else + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|skip mwi update for $var(presentity)\n"); +#!endif } - + route(MWI_AS_PRESENCE); - xlog("L_INFO", "$(kzE{kz.json,Call-ID})|log|finished processing message-summary update for $(kzE{kz.json,From}) light should be on ? $(kzE{kz.json,Messages-Waiting}) at $(kzE{kz.json,AMQP-Received})/$var(Now)/$TS\n"); + $var(mwi_state) = "ON"; + if($(kzE{kz.json,Messages-Waiting}) == "no") { + $var(mwi_state) = "OFF"; + } + + $var(EndRoute) = $(TV(Sn){s.replace,.,}); + $var(delta_to_finish) = $var(EndRoute) - $var(StartRoute); + + xlog("$var(kz_presence_log_level)", "$(kzE{kz.json,Call-ID})|log|message-summary update for $(kzE{kz.json,From}) light should be $var(mwi_state) (Δ1 $(kzE{kz.json,AMQP-Elapsed-Micro}) μs , Δ2 $var(delta_to_start) μs, Δ3 $var(delta_to_finish) μs)\n"); } event_route[kazoo:consumer-event-presence-update] { - xlog("L_INFO", "$(kzE{kz.json,Call-ID})|log|received presence update for $(kzE{kz.json,Presence-ID}) : $kzE\n"); + $var(kz_presence_log_level) = $(sel(cfg_get.kazoo.presence_no_targets_log_level){s.int}); + $var(StartRoute) = $(TV(Sn){s.replace,.,}); + $var(delta_to_start) = $var(StartRoute) - $(kzE{kz.json,AMQP-Received}); + if($sel(cfg_get.kazoo.presence_ignore_status_probe_resp) == 1) { + if($(kzE{kz.json,State}) == "offline" || $(kzE{kz.json,State}) == "online") { + xlog("L_DEBUG", "$(kzE{kz.json,Call-ID})|log|ignoring $(kzE{kz.json,State}) state $(kzE{kz.json,Presence-ID})\n"); + return; + } + } + + xlog("L_DEBUG", "$(kzE{kz.json,Call-ID})|log|received presence update for $(kzE{kz.json,Presence-ID})"); $var(JObj) = $kzE; $var(presentity) = $_s(sip:$(kzE{kz.json,Presence-ID})); $var(payload) = $kzE; route(PRESENCE_UPDATE); - - xlog("L_INFO", "$(kzE{kz.json,Call-ID})|log|finished processing $(kzE{kz.json,Event-Package}) update for $(kzE{kz.json,From}) state $(kzE{kz.json,State}) from $(kzE{kz.json,Switch-URI}) at $(kzE{kz.json,AMQP-Received})/$var(Now)/$TS\n"); + + $var(EndRoute) = $(TV(Sn){s.replace,.,}); + $var(delta_to_finish) = $var(EndRoute) - $var(StartRoute); + + xlog("$var(kz_presence_log_level)", "$(kzE{kz.json,Call-ID})|log|$(kzE{kz.json,Event-Package}) update for $(kzE{kz.json,From}) state $(kzE{kz.json,State}) from $(kzE{kz.json,AMQP-Broker-Zone}) => $(kzE{kz.json,Switch-URI}) (Δ1 $(kzE{kz.json,AMQP-Elapsed-Micro}) μs , Δ2 $var(delta_to_start) μs, Δ3 $var(delta_to_finish) μs)\n"); } route[PRESENCE_UPDATE] { + $var(callid) = $(var(payload){kz.json,Call-ID}); if($(var(payload){kz.json,State}) == "terminated") { route(COUNT_PRESENTITIES); } else { route(COUNT_SUBSCRIBERS); } - if($xavp(watchers=>dialog) > 0) { - xlog("L_INFO", "$(var(payload){kz.json,Call-ID})|log|publishing $var(presentity) dialog update for $xavp(watchers=>dialog) watchers\n"); - kazoo_pua_publish_dialoginfo($var(JObj)); - pres_refresh_watchers("$var(presentity)", "dialog", 1); + if($xavp(watchers=>self) > $sel(cfg_get.kazoo.presence_max_call_per_presentity) && + ( $(var(payload){kz.json,State}) == "early" || + ($(var(payload){kz.json,State}) == "confirmed" && $(var(payload){kz.json,State}) == "initiator") + )) { + xlog("L_WARN", "$(var(payload){kz.json,Call-ID})|log|not publishing state $(var(payload){kz.json,State}) for presentity $var(presentity) with $xavp(watchers=>self) calls, policy limit of $sel(cfg_get.kazoo.presence_max_call_per_presentity) calls per presentity \n"); } else { - xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|skip dialog update for $var(presentity)\n"); - } + if($xavp(watchers=>dialog) > 0) { + $var(kz_presence_log_level) = $(sel(cfg_get.kazoo.presence_with_targets_log_level){s.int}); + if($(var(payload){kz.json,State}) == "terminated") { + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|publishing $var(presentity) dialog update for terminated dialog\n"); + } else { + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|publishing $var(presentity) dialog update for $xavp(watchers=>dialog) watchers\n"); + } + if(kazoo_pua_publish_dialoginfo($var(JObj)) == 1) { + pres_refresh_watchers("$var(presentity)", "dialog", 1); + } else { + xlog("L_ERR", "$(var(payload){kz.json,Call-ID})|log|error publishing $var(presentity) dialog update\n"); + }; + } else { +#!ifdef PRESENCE_TRACK_ALL_PKG_DIALOG + if($(kzE{kz.json,Event-Package}) == "dialog") { + $var(kz_presence_log_level) = $(sel(cfg_get.kazoo.presence_with_targets_log_level){s.int}); + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|saving $var(presentity) from dialog update => $var(payload)\n"); + if(kazoo_pua_publish_dialoginfo($var(JObj)) != 1) { + xlog("L_ERR", "$(var(payload){kz.json,Call-ID})|log|error publishing $var(presentity) dialog update => $var(payload)\n"); + } + } +#!else + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|skip dialog update for $var(presentity)\n"); +#!endif + } - if($xavp(watchers=>presence) > 0) { - xlog("L_INFO", "$(var(payload){kz.json,Call-ID})|log|publishing $var(presentity) presence update for $xavp(watchers=>presence) watchers\n"); - kazoo_pua_publish_presence($var(payload)); - pres_refresh_watchers("$var(presentity)", "presence", 1); - } else { - xlog("L_DEBUG", "$(kzE{kz.json,Call-ID})|log|skip presence update for $var(presentity)\n"); - } + if($xavp(watchers=>presence) > 0) { + $var(kz_presence_log_level) = $(sel(cfg_get.kazoo.presence_with_targets_log_level){s.int}); + if($(var(payload){kz.json,State}) == "terminated") { + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|publishing $var(presentity) presence update for terminated dialog\n"); + } else { + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|publishing $var(presentity) presence update for $xavp(watchers=>presence) watchers\n"); + } + if(kazoo_pua_publish_presence($var(JObj)) == 1) { + pres_refresh_watchers("$var(presentity)", "presence", 1); + } else { + xlog("L_ERR", "$(var(payload){kz.json,Call-ID})|log|error publishing $var(presentity) presence update\n"); + }; + } else { +#!ifdef PRESENCE_TRACK_ALL_PKG_PRESENCE + if($(kzE{kz.json,Event-Package}) == "presence") { + $var(kz_presence_log_level) = $(sel(cfg_get.kazoo.presence_with_targets_log_level){s.int}); + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|saving $var(presentity) from presence update => $var(payload)\n"); + if(kazoo_pua_publish_presence($var(JObj)) != 1) { + xlog("L_ERR", "$(var(payload){kz.json,Call-ID})|log|error publishing $var(presentity) presence update => $var(payload)\n"); + } + } +#!else + xlog("L_DEBUG", "$(var(payload){kz.json,Call-ID})|log|skip presence update for $var(presentity)\n"); +#!endif + } + } } @@ -396,44 +533,55 @@ route[MWI_AS_PRESENCE] $var(State) = "confirmed"; } else { $var(State) = "terminated"; - } + } $var(payload) = MWI_PRESENCE_BODY $var(JObj) = $var(payload); - route(PRESENCE_UPDATE); } -#!include_file "presence-reset.cfg" - route[PRESENCE_BINDINGS] { #!import_file "presence-custom-bindings.cfg" - + #!ifndef PRESENCE_CUSTOM_BINDINGS - $var(payload) = $_s({ "exchange" : "presence", "type" : "topic", "queue" : "presence-dialog-MY_HOSTNAME", "routing" : "dialog.*.*", "exclusive" : 0, "federate" : 1 }); + $var(payload) = $_s({ "name" : "presence", "exchange" : "presence", "type" : "topic", "queue" : "presence-dialog-MY_HOSTNAME", "routing" : ["dialog.*.*", "update.*.*", "mwi_updates.*.*"], "exclusive" : 0, "federate" : 1 }); kazoo_subscribe("$var(payload)"); - + #!endif - $var(payload) = $_s({ "exchange" : "presence", "type" : "topic", "queue" : "presence-presence-MY_HOSTNAME", "routing" : "update.*.*", "exclusive" : 0, "federate" : 1 }); - kazoo_subscribe("$var(payload)"); + route(PRESENCE_API_BINDINGS); - $var(payload) = $_s({ "exchange" : "presence", "type" : "topic", "queue" : "presence-mwi-MY_HOSTNAME", "routing" : "mwi_updates.*.*", "exclusive" : 0 , "federate" : 1 }); - kazoo_subscribe("$var(payload)"); - #!endif + route(PRESENCE_FAST_PICKUP_START); - route(PRESENCE_RESET_BINDINGS); - - #!ifdef PRESENCE_QUERY_ROLE - route(PRESENCE_QUERY_BINDINGS); - #!endif +} - #!ifdef FAST_PICKUP_ROLE - route(FAST_PICKUP_START); +route[PRESENCE_API_BINDINGS] +{ + #!import_file "presence-api-custom-bindings.cfg" + + #!ifndef PRESENCE_API_CUSTOM_BINDINGS + $var(payload) = $_s({"name": "presence-api", "exchange": "presence", "type": "topic", "queue": "presence-api-MY_HOSTNAME", "routing": ["presence.search_req.*", "presence.reset.*.*"], "exclusive": 0, "federate": 1 }); + kazoo_subscribe("$var(payload)"); #!endif } +route[PRESENCE_LOCAL_REQUEST] +{ + route(PRESENCE_LOCAL_REQ_NOTIFY); +} + +route[PRESENCE_AUTHN] +{ + if($sel(cfg_get.kazoo.presence_require_authn) == 1) { + $xavp(regcfg=>match_received) = $su; + if(registered("location", "$rz:$Au", 2, 1) != 1) { + xlog("L_WARNING", "$ci|stop|$rm from unregistered ($rz:$Au) user agent $ua => $su\n"); + send_reply(403, "Forbidden"); + exit; + } + } +} # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab diff --git a/kamailio/presence_notify_sync-role.cfg b/kamailio/presence_notify_sync-role.cfg deleted file mode 100644 index d8b4b8e..0000000 --- a/kamailio/presence_notify_sync-role.cfg +++ /dev/null @@ -1,117 +0,0 @@ -kazoo.presence_notify = 1 descr "enable/disable sending notify callback to omnipresence" -kazoo.presence_notify_timeout = 3000 descr "timeout in ms waiting for notify reply" -kazoo.presence_notify_log_body = 0 descr "logs the body sent in the notification" -kazoo.presence_notify_log_resp_body = 0 descr "logs the body received from notification" -kazoo.presence_notify_log_to_table = 1 descr "logs notify/reply to active_watchers_log table" -kazoo.presence_notify_log_to_amqp = 0 descr "logs notify/reply to amqp" -kazoo.presence_notify_record_route = 1 descr "add record route header to notify msg sent" -kazoo.presence_notify_log_init_body = 0 descr "logs the body before its sent" -kazoo.presence_notify_force_send_socket = 0 descr "forces the send socket to the contact" - -######## Generic Hash Table container in shared memory ######## -modparam("htable", "htable", "notify=>size=16;autoexpire=3600;updateexpire=1;initval=0") - -#!trydef PRESENCE_NOTIFY_INIT -#!trydef MAX_NOTIFY_ERROR 5 - -route[PRESENCE_LOCAL_NOTIFY] -{ - if($rm != "NOTIFY") { - return; - } - t_set_fr(@cfg_get.kazoo.presence_notify_timeout, @cfg_get.kazoo.presence_notify_timeout); - xlog("L_INFO", "$ci|log|init preparing $subs(event) notify to $subs(watcher_username)@$subs(watcher_domain) on behalf of $subs(pres_uri) : $du\n"); - if(@cfg_get.kazoo.presence_notify_log_init_body == 1) { - xlog("L_INFO", "$ci|log|init|body $(mb{s.escape.common}{s.replace,\','}{s.replace,$$,})\n"); - } - if(@cfg_get.kazoo.presence_notify_force_send_socket == 1) { - $fs = $_s($(pr{s.tolower}):$(hdr(Contact){nameaddr.uri}{uri.host}):$(hdr(Contact){nameaddr.uri}{uri.port})); - xlog("L_INFO", "$ci|log|init|forcing socket to $fs, $(pr{s.tolower}):$(hdr(Contact){nameaddr.uri}{uri.host}):$(hdr(Contact){nameaddr.uri}{uri.port}) , $ct\n"); - } - if(@cfg_get.kazoo.presence_notify_record_route == 1) { - record_route(); - } -} - -modparam("mqueue","mqueue", "name=presence_last_notity") - -####### RTIMER module ########## -#!ifndef RTIMER_LOADED -loadmodule "rtimer.so" -#!trydef RTIMER_LOADED -#!endif -modparam("rtimer", "timer", "name=notifytimer;interval=500000u;mode=2;") -modparam("rtimer", "exec", "timer=notifytimer;route=PRESENCE_LOG_TIMER_ROUTE") - - -event_route[presence:notify-reply] -{ - if(@cfg_get.kazoo.presence_notify != 1) - return; - - $xavp(pres=>delete_subscription) = 0; - - if($subs(reason) == "timeout") { - $xavp(pres=>delete_subscription) = 1; - xlog("L_INFO", "$ci|end|deleting subscribtion $subs(pres_uri) for $subs(watcher_username)@$subs(watcher_domain) due to timeout\n"); - } else if($notify_reply($rs) == 200) { - $sht(notify=>$ci) = $null; - $sht(notify=>$ci::count) = 0; - xlog("L_INFO", "$ci|end|notified $subs(watcher_username)@$subs(watcher_domain) on behalf of $subs(pres_uri)\n"); - } else { - xlog("L_ERROR", "$ci|error|received $notify_reply($rs) when notifying $subs(watcher_username)@$subs(watcher_domain) on behalf of $subs(pres_uri)\n"); - if($rP != "UDP") { - $xavp(pres=>delete_subscription) = 1; - xlog("L_ERROR", "$ci|error|removing $rP watcher $subs(watcher_username)@$subs(watcher_domain) for $subs(pres_uri)\n"); - } else { - $var(shtinc) = $shtinc(notify=>$ci::count); - if($var(shtinc) > MAX_NOTIFY_ERROR) { - $xavp(pres=>delete_subscription) = 1; - xlog("L_ERROR", "$ci|error|removing $rP watcher $subs(watcher_username)@$subs(watcher_domain) for $subs(pres_uri)\n"); - } - } - } - if(@cfg_get.kazoo.presence_notify_log_body == 1) - xlog("L_INFO", "$ci|log|sent|body $(mb{s.escape.common}{s.replace,\','}{s.replace,$$,})\n"); - if(@cfg_get.kazoo.presence_notify_log_resp_body == 1) - xlog("L_INFO", "$ci|log|resp|body $(notify_reply($mb){s.escape.common}{s.replace,\','}{s.replace,$$,})\n"); - - if(@cfg_get.kazoo.presence_notify_log_to_amqp == 1) { - route(PRESENCE_NOTIFY_AMQP); - } - - if(@cfg_get.kazoo.presence_notify_log_to_table == 1) { - $var(Query) = $_s(REPLACE INTO active_watchers_log (presentity_uri, watcher_username, watcher_domain, event, callid, to_user, to_domain, user_agent, time, result, sent_msg, received_msg) VALUES ("$subs(uri)", "$subs(watcher_username)", "$subs(watcher_domain)", "$subs(event)","$subs(callid)","$subs(to_user)","$subs(to_domain)", '$(subs(user_agent){s.escape.common}{s.replace,\',''}{s.replace,$$,})', $TS, $notify_reply($rs), '$(mb{s.escape.common}{s.replace,\',''}{s.replace,$$,})', '$(notify_reply($mb){s.escape.common}{s.replace,\',''}{s.replace,$$,})')); - mq_add("presence_last_notity", "$subs(callid)", "$var(Query)"); - } -} - - -route[PRESENCE_LOG_TIMER_ROUTE] -{ - $var(runloop) = 1; - while(mq_fetch("presence_last_notity") == 1 && $var(runloop) < MAX_WHILE_LOOPS) { - $var(ci) = $mqk(presence_last_notity); - xlog("L_DEBUG", "Query : $mqv(presence_last_notity)\n"); - $var(sqlres) = sql_query("cb", "$mqv(presence_last_notity)"); - xlog("L_DEBUG", "Query result : $var(sqlres)\n"); - if($var(sqlres) < 0) { - xlog("L_ERROR", "$var(ci)|log|error updating active_watchers_log\n"); - } else { - $var(nrows) = $sqlrows(cb); - xlog("L_DEBUG", "$var(ci)|log|end UPDATED $var(nrows)\n"); - if($var(nrows) == 0) { - xlog("L_ERROR", "$var(ci)|log|error no rows affected when updating active_watchers_log\n"); - } - } - $var(runloop) = $var(runloop) + 1; - } -} - -route[PRESENCE_NOTIFY_AMQP] -{ - $var(amqp_payload_request) = $_s({"Event-Category" : "presence", "Event-Name" : "notify", "Event-Package" : "$subs(event)", "Timestamp" : $TS, "Call-ID" : "$subs(callid)", "From" : "$fu", "To" : "$subs(to_user)@$subs(to_domain)", "Sent" : "$(TS{s.ftime,%Y-%m-%d %H:%M:%S})", "Body" : "Hostname : MY_HOSTNAME\r\nTimestamp : $(TS{s.ftime,%Y-%m-%d %H:%M:%S})\r\n$(mb{s.escape.common}{s.replace,\','}{s.replace,$$,})\r\nResponse\r\n$(notify_reply($mb){s.escape.common}{s.replace,\','}{s.replace,$$,})","Remote-CSeq" : $subs(remote_cseq), "Local-CSeq" : $subs(local_cseq), "Sequence" : $cs, "Version" : $subs(version), "Reply" : $notify_reply($rs) }); - $var(rk) = "notify." + $(subs(to_domain){kz.encode}) + "." + $(subs(to_user){kz.encode}); - kazoo_publish("omnipresence", "$var(rk)", $var(amqp_payload_request)); - xlog("L_INFO", "$ci|log|sent notify callback for event $subs(event) : $tu\n"); -} diff --git a/kamailio/pusher-role.cfg b/kamailio/pusher-role.cfg index 6097a06..8925ac3 100644 --- a/kamailio/pusher-role.cfg +++ b/kamailio/pusher-role.cfg @@ -3,8 +3,6 @@ ######## Generic Hash Table container in shared memory ######## modparam("htable", "htable", "push_cache=>autoexpire=60;") -modparam("tm", "failure_exec_mode", 1) - route[PUSHER_ROUTE] { if ( (!is_method("INVITE")) || (!isflagset(FLAG_INTERNALLY_SOURCED)) || $hdr(X-KAZOO-PUSHER-Token-ID) == $null) @@ -66,7 +64,7 @@ route[PUSHER_PREPARE_PUSH] route[PUSHER_PREPARE_PUSH_PAYLOAD] { - + $var(TokenID) = $hdr(X-KAZOO-PUSHER-Token-ID); $var(TokenType) = $hdr(X-KAZOO-PUSHER-Token-Type); $var(TokenApp) = $hdr(X-KAZOO-PUSHER-Token-App); @@ -90,13 +88,13 @@ route[PUSHER_PREPARE_PUSH_PAYLOAD] $var(from_user) = $(hdr(From){tobody.user}); $var(from_name) = $(hdr(From){tobody.display}{re.subst,/"//g}); } - + $var(from) = $_s($var(from_user) - $var(from_name)); $var(PushPayload) = $_s({"call-id" : "$ci", "proxy" : "$var(TokenProxy)", "caller-id-number" : "$var(from_user)", "caller-id-name" : "$var(from_name)", "registration-token" : "$var(TokenReg)"}); $var(Payload) = $_s({ "Event-Category" : "notification", "Event-Name" : "push_req", "Call-ID" : "$ci", "Token-ID" : "$var(TokenID)", "Token-Type" : "$var(TokenType)", "Token-App" : "$var(TokenApp)", "Alert-Key" : "IC_SIL", "Alert-Params" : ["$var(from)"], "Sound" : "ring.caf", "Payload" : $var(PushPayload) }); - + $avp(push_routing_key) = "notification.push." + $var(TokenType) + "." + $var(TokenID); $avp(push_payload) = $var(Payload); } @@ -126,6 +124,7 @@ route[PUSHER_ATTEMPT_REGISTRATION] $xavp(ulattrs=>custom_channel_vars) = "{}"; $xavp(ulattrs[0]=>x_token_reg) = $hdr(X-Token-Reg); route(SAVE_LOCATION); + exit; } else { xlog("L_INFO", "$ci|pusher|registration x-token-reg '$hdr(X-Token-Reg)' from header was not found\n"); } @@ -139,6 +138,7 @@ route[PUSHER_ATTEMPT_REGISTRATION] $xavp(ulattrs=>custom_channel_vars) = "{}"; $xavp(ulattrs[0]=>x_token_reg) = $(sel(contact.uri){uri.param,x-token-reg}); route(SAVE_LOCATION); + exit; } else { xlog("L_INFO", "$ci|pusher|registration x-token-reg from contact uri param '$(sel(contact.uri){uri.param,x-token-reg})' was not found\n"); } @@ -152,6 +152,7 @@ route[PUSHER_ATTEMPT_REGISTRATION] $xavp(ulattrs=>custom_channel_vars) = "{}"; $xavp(ulattrs[0]=>x_token_reg) = $(sel(contact){tobody.params}{param.value,x-token-reg}); route(SAVE_LOCATION); + exit; } else { xlog("L_INFO", "$ci|pusher|registration x-token-reg from contact param '$(sel(contact){tobody.params}{param.value,x-token-reg})' was not found\n"); } @@ -160,8 +161,8 @@ route[PUSHER_ATTEMPT_REGISTRATION] route[PUSHER_ON_REGISTRATION] { - if( ( $(xavp(ulattrs=>x_token_reg){s.len}) > 0 || - $(xavp(ulattrs=>custom_channel_vars){kz.json,Pusher-Application}{s.len}) > 0) && + if( ( $(xavp(ulattrs=>x_token_reg){s.len}) > 0 || + $(xavp(ulattrs=>custom_channel_vars){kz.json,Pusher-Application}{s.len}) > 0) && $var(Status) == "Registered") { if($sht(push_cache=>$(tu{s.tolower})) != $null) { xlog("L_INFO", "$ci|pusher|device registered, delivering the call\n"); diff --git a/kamailio/registrar-role.cfg b/kamailio/registrar-role.cfg index 3474fd0..52952be 100644 --- a/kamailio/registrar-role.cfg +++ b/kamailio/registrar-role.cfg @@ -1,62 +1,85 @@ +#### NAT PINGING PARAMS ### #!trydef REGISTRAR_NAT_PING_INTERVAL 30 + +## REGISTRAR_NAT_PING_TIMEOUT should be (REGISTRAR_NAT_PING_INTERVAL * 3 + 10) or 0 to disable +#!trydef REGISTRAR_NAT_PING_TIMEOUT 0 + +#!trydef REGISTRAR_NAT_PING_NAT_ONLY 1 #!trydef REGISTRAR_NAT_PING_WORKERS 5 +##### + #!trydef REGISTRAR_MIN_EXPIRES 300 #!trydef REGISTRAR_MAX_EXPIRES 3600 +#!trydef REGISTRAR_DEFAULT_EXPIRES 600 +#!trydef REGISTRAR_ERROR_MIN_EXPIRES 1 +#!trydef REGISTRAR_ERROR_MISSING_EXPIRES 1 #!trydef REGISTRAR_CONTACT_MAX_SIZE 2048 +#!trydef REGISTRAR_QUERY_TIMEOUT_MS 2500 #!trydef REGISTRAR_HANDLE_LOST_TCP 1 -#!trydef REGISTRAR_CLOSE_EXPIRED_TCP 1 - +#!trydef REGISTRAR_CLOSE_EXPIRED_TCP 0 #!trydef REGISTRAR_HANDLE_EXPIRED_TCP 1 #!trydef REGISTRAR_HANDLE_EXPIRED_UDP 0 #!trydef REGISTRAR_HANDLE_EXPIRED_TLS 1 #!trydef REGISTRAR_HANDLE_EXPIRED_WS 1 +#!trydef REGISTRAR_FORCE_QUERY 0 +#!trydef REGISTRAR_FORCE_FAILOVER 0 +#!trydef REGISTRAR_CHECK_AMQP_AVAILABILITY 1 +#!trydef KZ_REGISTRAR_KEEPALIVE_UDP_ONLY 0 +#!trydef REGISTRAR_AMQP_EXCHANGE callmgr +#!trydef REGISTRAR_AMQP_FLAGS 0 +#!trydef REGISTRAR_AMQP_RK_PREFIX authn.req. +#!trydef REGISTRAR_SEND_100 1 +#!trydef REGISTRAR_DB_MODE 2 +#!trydef REGISTRAR_DB_TIMER_CLEANUP 0 +#!trydef REGISTRAR_DB_REMOVE_EXPIRED_DELAY 0 +#!trydef REGISTRAR_SYNC_TIMER_INTERVAL 5 +#!trydef REGISTRAR_SYNC_TIMER_PROCS 1 + + +#!ifdef REGISTRAR_WARM_CACHE +#!substdef "!REGISTRAR_S_WARM_CACHE!dbtable=auth_cache;dbmode=1;!g" +#!else +#!substdef "!REGISTRAR_S_WARM_CACHE!!g" +#!endif ######## Generic Hash Table container in shared memory ######## -modparam("htable", "htable", "auth_cache=>size=16;autoexpire=7200;") +modparam("htable", "htable", "auth_cache=>size=16;autoexpire=7200;REGISTRAR_S_WARM_CACHE") ####### Authentication Interface module ########## loadmodule "auth.so" -#!ifdef OPENBTS_AUTH_ROLE -loadmodule "auth_openbts.so" -modparam("auth", "qop", "") -modparam("auth", "secret", "OPENBTS_AUTH_SECRET") -modparam("auth_openbts", "challenge_attr", "$avp(digest_challenge)") -#!endif - ####### User Location Implementation module ########## loadmodule "usrloc.so" modparam("usrloc", "db_update_as_insert", 0) modparam("usrloc", "use_domain", 1) modparam("usrloc", "nat_bflag", FLB_NATB) modparam("usrloc", "db_url", "KAZOO_DB_URL") -modparam("usrloc", "db_mode", 1) -modparam("usrloc", "handle_lost_tcp", 1) -modparam("usrloc", "xavp_contact", "ulattrs") + +modparam("usrloc", "db_mode", REGISTRAR_DB_MODE) + +modparam("usrloc", "db_timer_clean", REGISTRAR_DB_TIMER_CLEANUP) +modparam("usrloc", "handle_lost_tcp", REGISTRAR_HANDLE_LOST_TCP) +modparam("usrloc", "rm_expired_delay", REGISTRAR_DB_REMOVE_EXPIRED_DELAY) + modparam("usrloc", "db_check_update", 1) -modparam("usrloc", "timer_interval", 30) -modparam("usrloc", "timer_procs", 1) -modparam("usrloc", "db_timer_clean", 1) +modparam("usrloc", "db_ops_ruid", 1) + + +modparam("usrloc", "xavp_contact", "ulattrs") +modparam("usrloc", "timer_interval", REGISTRAR_SYNC_TIMER_INTERVAL) +modparam("usrloc", "timer_procs", REGISTRAR_SYNC_TIMER_PROCS) + modparam("usrloc", "fetch_rows", 400) modparam("usrloc", "handle_lost_tcp", REGISTRAR_HANDLE_LOST_TCP) modparam("usrloc", "close_expired_tcp", REGISTRAR_CLOSE_EXPIRED_TCP) -######## NAT Traversal module - signaling functions ######## -#!ifdef NAT_TRAVERSAL_ROLE -#!trydef NATHELPER_LOADED -loadmodule "nathelper.so" -modparam("nathelper", "natping_interval", REGISTRAR_NAT_PING_INTERVAL) -modparam("nathelper", "ping_nated_only", 0) -modparam("nathelper", "natping_processes", REGISTRAR_NAT_PING_WORKERS) -modparam("nathelper", "sipping_bflag", FLB_NATSIPPING) -#!endif - ####### SIP Registrar implementation module ########## loadmodule "registrar.so" modparam("registrar", "received_avp", "$avp(AVP_RECV_PARAM)") modparam("registrar", "min_expires", REGISTRAR_MIN_EXPIRES) modparam("registrar", "max_expires", REGISTRAR_MAX_EXPIRES) +modparam("registrar", "default_expires", REGISTRAR_DEFAULT_EXPIRES) modparam("registrar", "xavp_cfg", "regcfg") modparam("registrar", "gruu_enabled", 1) modparam("registrar", "outbound_mode", 1) @@ -64,172 +87,236 @@ modparam("registrar", "regid_mode", 1) modparam("registrar", "path_mode", 1) modparam("registrar", "use_path", 1) modparam("registrar", "received_param", "") -##modparam("registrar", "xavp_rcd", "ulrcd") +modparam("registrar", "xavp_rcd", "ulrcd") modparam("registrar", "contact_max_size", REGISTRAR_CONTACT_MAX_SIZE) -##### handle expired registrations realtime params ##### +####### NAT ########## +#!ifdef NAT_TRAVERSAL_ROLE +#!ifndef NATHELPER_LOADED +loadmodule "nathelper.so" +#!trydef NATHELPER_LOADED +#!endif +#!ifdef KEEPALIVE_ROLE +modparam("nathelper", "natping_interval", 0) +modparam("nathelper", "natping_processes", 0) +#!else +modparam("nathelper", "natping_interval", REGISTRAR_NAT_PING_INTERVAL) +modparam("nathelper", "ping_nated_only", REGISTRAR_NAT_PING_NAT_ONLY) +modparam("nathelper", "natping_processes", REGISTRAR_NAT_PING_WORKERS) +modparam("nathelper", "sipping_bflag", FLB_NATSIPPING) +modparam("nathelper", "keepalive_timeout", REGISTRAR_NAT_PING_TIMEOUT) +#!endif +#!endif + +## stats ## +modparam("statistics","variable", "registrar:force_failover") +modparam("statistics","variable", "registrar:cached") +modparam("statistics","variable", "registrar:ip_realm") +modparam("statistics","variable", "registrar:new_tran") +modparam("statistics","variable", "registrar:amqp_not_available") +modparam("statistics","variable", "registrar:challenge") +modparam("statistics","variable", "registrar:amqp_async_error") +modparam("statistics","variable", "registrar:amqp_returned") +modparam("statistics","variable", "registrar:amqp_timeout") +modparam("statistics","variable", "registrar:drops") +modparam("statistics","variable", "registrar:authn_perm_err") +modparam("statistics","variable", "registrar:authn_err") +modparam("statistics","variable", "registrar:authn_resp") +modparam("statistics","variable", "registrar:authn_unknown") +modparam("statistics","variable", "registrar:save_error") +modparam("statistics","variable", "registrar:missing_expires") +modparam("statistics","variable", "registrar:missing_expires_allowed") +modparam("statistics","variable", "registrar:min_expires") +modparam("statistics","variable", "registrar:min_expires_allowed") + + +##### registrar realtime params ##### +kazoo.registrar_error_min_expires = REGISTRAR_ERROR_MIN_EXPIRES descr "send error when UAS sends expires < min-expires" +kazoo.registrar_error_missing_expires = REGISTRAR_ERROR_MISSING_EXPIRES descr "send error when UAS do not send expires header" kazoo.registrar_handle_expired_tcp = REGISTRAR_HANDLE_EXPIRED_TCP descr "handles expired tcp registrations" kazoo.registrar_handle_expired_udp = REGISTRAR_HANDLE_EXPIRED_UDP descr "handles expired udp registrations" kazoo.registrar_handle_expired_tls = REGISTRAR_HANDLE_EXPIRED_TLS descr "handles expired tls registrations" kazoo.registrar_handle_expired_ws = REGISTRAR_HANDLE_EXPIRED_WS descr "handles expired ws registrations" +kazoo.registrar_query_timeout_ms = REGISTRAR_QUERY_TIMEOUT_MS descr "timeout waiting for reply from registrar" +kazoo.registrar_failover = REGISTRAR_FORCE_FAILOVER descr "force failover if 1" +kazoo.registrar_force_query = REGISTRAR_FORCE_QUERY descr "force query if 1" +kazoo.registrar_check_amqp_availability = REGISTRAR_CHECK_AMQP_AVAILABILITY descr "checks if amqp connection is available before querying registrar" +kazoo.registrar_keepalive_udp_only = KZ_REGISTRAR_KEEPALIVE_UDP_ONLY descr "should we keepalive nat phones for udp only" +kazoo.registrar_send_100 = REGISTRAR_SEND_100 descr "should we send 100 reply while doing directory search" ####### Registrar Logic ######## -route[HANDLE_REGISTER] -{ - if (is_method("REGISTER")) { - #!ifdef NAT_TRAVERSAL_ROLE - if (nat_uac_test("3")) { - xlog("L_INFO", "$ci|log|correcting NATed contact in registration\n"); - force_rport(); - } +route[REGISTRAR_NAT_FLAGS] +{ + if (isflagset(FLT_NATS)) { + xlog("L_DEBUG", "$ci|log|fixing contact for nat request\n"); + setbflag(FLB_NATB); fix_nated_register(); ## KAZOO-1846: Cisco SPA8000 freaks out on options pings if (!($ua =~ "Linksys/SPA8000" - || $ua =~ "OpenBTS" || $ua =~ "SIPp" || (af==INET6) + || ($sel(cfg_get.kazoo.registrar_keepalive_udp_only) == 1 && $proto != "udp") + || ($proto =="ws" || $proto == "wss") + || ($(xavp(ulattrs=>custom_channel_vars){kz.json,Keep-Alive}) == "false") )) { - setbflag(FLB_NATB); + xlog("L_DEBUG", "$ci|log|set nat pinging\n"); setbflag(FLB_NATSIPPING); } - #!endif - - route(ATTEMPT_AUTHORIZATION); - + } else { + $avp(AVP_RECV_PARAM) = $su; } } -route[AUTHORIZATION_CHECK] +route[HANDLE_REGISTER] { - if (!is_method("MESSAGE|NOTIFY|SUBSCRIBE|PUBLISH")) + if (!is_method("REGISTER")) { return; - - if(has_totag()) - return; - - if (isflagset(FLAG_INTERNALLY_SOURCED)) - return; - - if (isflagset(FLAG_TRUSTED_SOURCE)) - return; - - $xavp(regcfg=>match_received) = $su; - if(!(registered("location", "$fu", 2, 1) == 1 && $(xavp(ulattrs=>custom_channel_vars){s.len}) > 1)) { - xlog("L_INFO", "$ci|log|not authorized $fu from $si:$sp\n"); - send_reply("503", "Not Registered"); - exit; -# route(ATTEMPT_AUTHORIZATION); } -} -route[ATTEMPT_AUTHORIZATION] -{ #!ifdef PUSHER_ROLE route(PUSHER_ATTEMPT_REGISTRATION); #!endif - $var(nonce) = $(uuid(g){s.rm,-}); - #!ifdef OPENBTS_AUTH_ROLE - if($ua =~ "OpenBTS" && $sht(auth_cache=>$Au::nonce) != $null ) { - $var(nonce) = $sht(auth_cache=>$Au::nonce); + if($sel(cfg_get.kazoo.registrar_failover) == 1) { + xlog("L_INFO", "$ci|log|register|forcing failover\n"); + update_stat("registrar:force_failover", "+1"); + drop; } - #!endif - $xavp(regcfg=>match_received) = $su; - if($sht(auth_cache=>$Au) != $null && registered("location", "$rz:$Au", 2, 1) == 1 && $(xavp(ulattrs=>custom_channel_vars){s.len}) > 1) { - $var(password) = $sht(auth_cache=>$Au); - route(SAVE_LOCATION); + if($sel(cfg_get.kazoo.registrar_force_query) == 0) { + if($sht(auth_cache=>$Au) == "authn_perm_err") { + xlog("L_INFO", "$ci|end|issuing auth challenge to cached permanent failed registration attempt for $Au from IP $si:$sp\n"); + update_stat("registrar:authn_perm_err", "+1"); + #!ifdef ANTIFLOOD_ROLE + route(ANITFLOOD_FAILED_AUTH); + #!endif + update_stat("registrar:challenge", "+1"); + auth_challenge("$fd", "4"); + exit; + } else if($sht(auth_cache=>$Au) != $null) { + $xavp(regcfg=>match_received) = $su; + if(registered("location", "$rz:$Au", 2, 1) == 1) { + if($(xavp(ulattrs=>custom_channel_vars){s.len}) > 1) { + $var(password) = $sht(auth_cache=>$Au); + update_stat("registrar:cached", "+1"); + route(SAVE_LOCATION); + exit; + } + } + } } - if( is_present_hf("Authorization")) { - route(KAZOO_AUTHORIZATION); + if($td =~ "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}" || + $fd =~ "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}") { + xlog("L_INFO", "$ci|log|register|invalid domain\n"); + update_stat("registrar:ip_realm", "+1"); + drop; } - #!ifdef OPENBTS_AUTH_ROLE - if($ua =~ "OpenBTS") { - openbts_auth_challenge("$fd", "$var(nonce)"); - } else { - #!endif - auth_challenge("$fd", "0"); - #!ifdef OPENBTS_AUTH_ROLE + if($sel(cfg_get.kazoo.registrar_check_amqp_availability) == 1) { + if($xavp(amqpc=>default::MY_AMQP_ZONE) == 0) { + xlog("L_WARNING", "$ci|end|register|no amqp connection available for default worker in zone MY_AMQP_ZONE\n"); + update_stat("registrar:amqp_not_available", "+1"); + drop; + } } - #!endif - xlog("L_INFO", "$ci|end|issued auth challenge to new registration for $fu $si:$sp\n"); - exit; -} + route(REGISTRAR_BOUNDS); + + $var(auth) = pv_auth_check("$fd", "$uuid(g)", "0", "0"); + if($var(auth) != -2) { + xlog("L_INFO", "$ci|end|challenging $Au $si:$sp\n"); + $var(auth) = auth_challenge("$fd", "4"); + update_stat("registrar:challenge", "+1"); + if($var(auth) != 1) { + xlog("L_ERROR", "$ci|register|error creating or sending challenge to registration attempt for $fu from $si:$sp\n"); + drop; + } + exit; + } -route[KAZOO_AUTHORIZATION] -{ - $var(nonce) = $adn; - $var(amqp_payload_request) = $_s({"Event-Category" : "directory" , "Event-Name" : "authn_req", "Method" : "REGISTER", "Auth-Nonce" : "$adn", "Auth-Realm" : "$fd", "Auth-User" : "$fU", "From" : "$fu", "To" : "$tu", "Orig-IP" : "$si", "Orig-Port" : "$sp", "User-Agent" : "$(ua{s.escape.common}{s.replace,\','}{s.replace,$$,})", "Contact" : "$(ct{s.escape.common}{s.replace,\','}{s.replace,$$,})", "Call-ID" : "$ci" }); - $var(amqp_routing_key) = "authn.req." + $(fd{kz.encode}); - $avp(kz_timeout) = 2500; if (!t_newtran()) { - sl_reply_error(); - exit(); + xlog("L_ERROR", "$ci|log|failed to create transaction to query for authentication credentials for $Au $si:$sp\n"); + update_stat("registrar:new_tran", "+1"); + drop; } - if(kazoo_async_query("callmgr", $var(amqp_routing_key), $var(amqp_payload_request), "KAZOO_AUTHORIZATION_OK", "KAZOO_AUTHORIZATION_ERROR") != 1) { - xlog("L_INFO", "$ci|log|failed to send Kazoo query for authentication credentials for $Au $si:$sp\n"); - append_to_reply("Retry-After: 60\r\n"); - send_reply("503", "Retry Later"); - exit; + + if($sel(cfg_get.kazoo.registrar_send_100) == 1) { + sl_send_reply("100", "checking your credentials"); + } + + $var(amqp_payload_request) = $_s({"Event-Category" : "directory" , "Event-Name" : "authn_req", "Method" : "REGISTER", "Auth-Nonce" : "$adn", "Auth-Realm" : "$fd", "Auth-User" : "$fU", "From" : "$fu", "To" : "$tu", "Orig-IP" : "$si", "Orig-Port" : "$sp", "User-Agent" : "$(ua{s.escape.common}{s.replace,\','}{s.replace,$$,})", "Contact" : "$(ct{s.escape.common}{s.replace,\','}{s.replace,$$,})", "Call-ID" : "$ci" }); + $var(amqp_routing_key) = $_s($def(REGISTRAR_AMQP_RK_PREFIX)$(fd{kz.encode})); + $avp(kz_timeout) = $sel(cfg_get.kazoo.registrar_query_timeout_ms); + $xavp(deltas=>query) = $(TV(Sn){s.replace,.,}); + + xlog("L_DEBUG", "$ci|amqp|publishing to $def(REGISTRAR_AMQP_EXCHANGE) => $var(amqp_routing_key) : $def(REGISTRAR_AMQP_FLAGS) : $var(amqp_payload_request)\n"); + if(kazoo_async_query("$def(REGISTRAR_AMQP_EXCHANGE)", $var(amqp_routing_key), $var(amqp_payload_request), "KZ_AUTHORIZATION_REPLY", "KZ_AUTHORIZATION_TIMEOUT", "$def(REGISTRAR_AMQP_FLAGS)") != 1) { + xlog("L_INFO", "$ci|log|failed to send registrar query for authentication credentials for $Au $si:$sp\n"); + update_stat("registrar:amqp_async_error", "+1"); + t_drop(); } } -route[KAZOO_AUTHORIZATION_OK] +failure_route[KZ_AUTHORIZATION_TIMEOUT] { - $var(password) = $(kzR{kz.json,Auth-Password}); - $var(nonce) = $adn; - #!ifdef OPENBTS_AUTH_ROLE - if( $(kzR{kz.json,Auth-Nonce}) != "" && $var(nonce) != $(kzR{kz.json,Auth-Nonce})) { - xlog("L_INFO", "$ci|log|nonce replace $var(nonce) with $(kzR{kz.json,Auth-Nonce})\n"); - $var(nonce) = $(kzR{kz.json,Auth-Nonce}); - $sht(auth_cache=>$Au::nonce) = $var(nonce); - } - #!endif - if( $(kzR{kz.json,Event-Name}) == "authn_err" ) { - auth_challenge("$fd", "0"); - xlog("L_INFO", "$ci|end|issued auth challenge to registration attempt for $Au $si:$sp\n"); - exit; + if($(kzR{kz.json,Event-Name}) == "message_returned" ) { + xlog("L_WARNING", "$ci|amqp|message was returned by broker $(kzR{kz.json,Error-Code}) $(kzR{kz.json,Error-Reason})\n"); + update_stat("registrar:amqp_returned", "+1"); } else { - $xavp(ulattrs=>custom_channel_vars) = $(kzR{kz.json,Custom-Channel-Vars}); - xlog("L_INFO", "$ci|log|authenticating $Au via Kazoo query response\n"); - route(CHECK_AUTHORIZATION); + xlog("L_WARNING", "$ci|end|failed $T_reply_code $T_reply_reason [$T(id_index):$T(id_label)] querying directory for authentication credentials for $Au $si:$sp\n"); + update_stat("registrar:amqp_timeout", "+1"); } + update_stat("registrar:drops", "+1"); + t_drop(); } -route[KAZOO_AUTHORIZATION_ERROR] +onreply_route[KZ_AUTHORIZATION_REPLY] { - xlog("L_INFO", "$ci|log|failed to query Kazoo for authentication credentials for $Au $si:$sp\n"); - append_to_reply("Retry-After: 60\r\n"); - send_reply("503", "Retry Later"); - exit; + $var(StartRoute) = $(TV(Sn){s.replace,.,}); + $var(delta_to_start) = $var(StartRoute) - $(kzR{kz.json,AMQP-Received}); + $var(delta_from_query) = $(kzR{kz.json,AMQP-Received}) - $xavp(deltas=>query); + xlog("L_INFO", "$ci|log|received $(kzR{kz.json,Event-Category}) $(kzR{kz.json,Event-Name}) reply from $(kzR{kz.json,App-Name})-$(kzR{kz.json,App-Version}) (Δ1 $(kzR{kz.json,AMQP-Elapsed-Micro}) μs , Δ2 $var(delta_to_start) μs, Δ3 $var(delta_from_query) μs)\n"); + $var(password) = $(kzR{kz.json,Auth-Password}); + $var(nonce) = $adn; + if( $(kzR{kz.json,Event-Name}) == "authn_err" ) { + if($(kzR{kz.json,Permanent-Error}) == "true") { + $sht(auth_cache=>$Au) = "authn_perm_err"; + } + update_stat("registrar:authn_err", "+1"); + #!ifdef ANTIFLOOD_ROLE + route(ANITFLOOD_FAILED_AUTH); + #!endif + update_stat("registrar:challenge", "+1"); + auth_challenge("$fd", "4"); + xlog("L_INFO", "$ci|end|challenging $Au $si:$sp via $(kzR{kz.json,App-Name})-$(kzR{kz.json,App-Version}) response\n"); + exit; + } else if( $(kzR{kz.json,Event-Name}) == "authn_resp" ) { + update_stat("registrar:authn_resp", "+1"); + xlog("L_INFO", "$ci|log|authenticating $Au via $(kzR{kz.json,App-Name})-$(kzR{kz.json,App-Version}) response\n"); + route(CHECK_AUTHORIZATION); + } else { + update_stat("registrar:authn_unknown", "+1"); + update_stat("registrar:drops", "+1"); + xlog("L_INFO", "$ci|log|unhandle response from directory $Au via $(kzR{kz.json,App-Name})-$(kzR{kz.json,App-Version})\n"); + t_drop(); + } } route[CHECK_AUTHORIZATION] { - if($ua =~ "OpenBTS") { - xlog("L_INFO", "$ci|end|OPENBTS attempt for $Au $si:$sp\n"); - } else { - if($var(password) == $null || $var(password) == "") { - auth_challenge("$fd", "0"); - xlog("L_INFO", "$ci|end|issued auth challenge to registration attempt for $Au $si:$sp\n"); - exit; - } - - if (!pv_auth_check("$fd", "$var(password)", "0", "0")) { - #!ifdef ANTIFLOOD_ROLE - route(ANITFLOOD_FAILED_AUTH); - #!endif + if (!pv_auth_check("$fd", "$var(password)", "0", "0")) { + #!ifdef ANTIFLOOD_ROLE + route(ANITFLOOD_FAILED_AUTH); + #!endif - auth_challenge("$fd", "0"); - xlog("L_WARNING", "$ci|end|issued auth challenge to failed registration attempt for $Au from IP $si:$sp\n"); - exit; - } + xlog("L_WARNING", "$ci|end|issuing auth challenge to failed registration attempt for $Au from IP $si:$sp\n"); + update_stat("registrar:challenge", "+1"); + auth_challenge("$fd", "5"); + exit; } #!ifdef ANTIFLOOD_ROLE @@ -239,26 +326,27 @@ route[CHECK_AUTHORIZATION] # user authenticated - remove auth header consume_credentials(); + $xavp(ulattrs=>custom_channel_vars) = $(kzR{kz.json,Custom-Channel-Vars}); + $xavp(ulattrs[0]=>token) = $_s($(kzR{kz.json,Custom-Channel-Vars.Authorizing-ID})@$(kzR{kz.json,Custom-Channel-Vars.Account-ID})); + route(SAVE_LOCATION); } route[SAVE_LOCATION] { - if ($sht(auth_cache=>$Au) == $null) { + + if ($sht(auth_cache=>$Au) == $null && $var(password) != $null) { xlog("L_INFO", "$ci|log|caching sip credentials for $Au\n"); }; $sht(auth_cache=>$Au) = $var(password); - #!ifdef OPENBTS_AUTH_ROLE - if($ua =~ "OpenBTS") { - $sht(auth_cache=>$Au::nonce) = $var(nonce); - } - #!endif + + route(REGISTRAR_NAT_FLAGS); $var(save_result) = save("location", "0x04"); - if($var(save_result) == -1) { - auth_challenge("$fd", "0"); - xlog("L_INFO", "$ci|end|issued auth challenge after failed attempt to save contact for $Au $si:$sp\n"); + if($var(save_result) < 0) { + xlog("L_WARNING", "$ci|end|not expected result $var(save_result) when saving $Au registration from IP $si:$sp\n"); + update_stat("registrar:save_error", "+1"); exit; } else { if($var(save_result) == 1) { @@ -268,34 +356,17 @@ route[SAVE_LOCATION] } } - if((int)@contact.expires) { - $var(expires) = @contact.expires; + if($var(save_result) == 3) { + $var(expires) = 0; } else { - if(is_present_hf("Expires")) { - $var(expires) = $hdr(Expires); - } else { - $var(expires) = REGISTRAR_MIN_EXPIRES; - } + $var(expires) = $xavp(ulrcd=>expires); } -## this is what we should be using -## but ulrcd seems to have a weird leak -## if($var(save_result) == 3) { -## $var(expires) = 0; -## } else { -## $var(expires) = $xavp(ulrcd=>expires); -## } - if($var(expires) == 0) { xlog("L_INFO", "$ci|end|unregister request from $Au $si:$sp\n"); $var(Status) = "Unregistered"; } else { $var(Status) = "Registered"; - if($var(expires) < REGISTRAR_MIN_EXPIRES) { - $var(expires) = REGISTRAR_MIN_EXPIRES; - } else if($var(expires) > REGISTRAR_MAX_EXPIRES) { - $var(expires) = REGISTRAR_MAX_EXPIRES; - } } $var(ip) = $Ri; @@ -340,17 +411,20 @@ route[SAVE_LOCATION] $var(AdvIP) = "[" + $RAi + "]"; } - - $var(amqp_payload_request) = $_s({"Event-Category" : "directory", "Event-Name" : "reg_success", "Status" : "$var(Status)", "Event-Timestamp" : $TS, "Expires" : $(var(expires){s.int}), "First-Registration" : $var(new_reg), "Contact" : "$(ct{s.escape.common}{s.replace,\','}{s.replace,$$,})", "Call-ID" : "$ci", "Realm" : "$fd", "Username" : "$fU", "From-User" : "$fU", "From-Host" : "$fd", "To-User" : "$tU", "To-Host" : "$td", "User-Agent" : "$(ua{s.escape.common}{s.replace,\','}{s.replace,$$,})" , "Custom-Channel-Vars" : $xavp(ulattrs=>custom_channel_vars), "Proxy-Path" : "sip:$var(ip):$var(port)", "Proxy-Protocol" : "$proto", "Proxy-IP" : "$var(AdvIP)", "Proxy-Port" : "$RAp", "Source-IP": "$si", "Source-Port": "$sp" }); + $var(amqp_payload_request) = $_s({"Event-Category" : "directory", "Event-Name" : "reg_success", "Status" : "$var(Status)", "Event-Timestamp" : $TS, "Expires" : $(var(expires){s.int}), "First-Registration" : $var(new_reg), "Contact" : "$(ct{s.escape.common}{s.replace,\','}{s.replace,$$,})", "Call-ID" : "$ci", "Realm" : "$fd", "Username" : "$fU", "From-User" : "$fU", "From-Host" : "$fd", "To-User" : "$tU", "To-Host" : "$td", "User-Agent" : "$(ua{s.escape.common}{s.replace,\','}{s.replace,$$,})" , "Custom-Channel-Vars" : $xavp(ulattrs=>custom_channel_vars), "Proxy-Path" : "sip:MY_IP_ADDRESS:$var(port)", "Proxy-Protocol" : "$proto", "Proxy-IP" : "$var(AdvIP)", "Proxy-Port" : "$RAp", "Source-IP": "$si", "Source-Port": "$sp" }); $var(amqp_routing_key) = "registration.success." + $(fd{kz.encode}) + "." + $(fU{kz.encode}); kazoo_publish("registrar", $var(amqp_routing_key), $var(amqp_payload_request)); - xlog("L_INFO", "$ci|end|successful $(var(Status){s.tolower}) with contact : $ct\n"); + xlog("L_INFO", "$ci|end|successful $(var(Status){s.tolower}) with contact : $ct : $var(expires)\n"); #!ifdef PUSHER_ROLE route(PUSHER_ON_REGISTRATION); #!endif + #!ifdef KEEPALIVE_ROLE + route(KEEPALIVE_ON_REGISTRATION); + #!endif + exit; } @@ -364,6 +438,10 @@ event_route[kazoo:consumer-event-directory-reg-flush] $sht(auth_cache=>$var(user)) = $null; } + if( $(kzE{kz.json,Cache-Only}) == "true") { + return; + } + if(reg_fetch_contacts("location", "sip:$var(user)", "caller")) { $var(i) = 0; while($var(i) < $(ulc(caller=>count))) { @@ -379,8 +457,81 @@ event_route[kazoo:consumer-event-directory-reg-flush] #!endif } +route[REGISTRAR_BINDINGS] +{ + #!import_file "registrar-custom-bindings.cfg" + + #!ifndef REGISTRAR_CUSTOM_BINDINGS + + $var(payload) = $_s({"name": "registrar-api", "exchange": "registrar", "type": "topic", "queue": "registrar-flush-MY_HOSTNAME", "routing": "registration.flush.*", "federate": 1 }); + kazoo_subscribe("$var(payload)"); + + #!endif + + #!ifdef REGISTRAR_SYNC_ROLE + route(REGISTRAR_SYNC_BINDINGS); + #!endif + +} + +route[REGISTRAR_BOUNDS] +{ + if((int)@contact.expires) { + $var(expires) = @contact.expires; + } else if(is_present_hf("Expires")) { + $var(expires) = $hdr(Expires); + } else if($(sel(contact){tobody.params}{param.value,expires}) != "") { + $var(expires) = $(sel(contact){tobody.params}{param.value,expires}{s.int}); + } else { + if($sel(cfg_get.kazoo.registrar_error_missing_expires) == 1) { + xlog("L_WARNING", "$ci|end|missing expires registering $Au from IP $si:$sp\n"); + send_reply("400", "Missing Expires"); + update_stat("registrar:missing_expires", "+1"); + exit; + } else { + update_stat("registrar:missing_expires_allowed", "+1"); + xlog("L_WARNING", "$ci|end|allowing missing expires registering $Au from IP $si:$sp\n"); + } + } + + if($var(expires) != 0) { + if($var(expires) < REGISTRAR_MIN_EXPIRES) { + if($sel(cfg_get.kazoo.registrar_error_min_expires) == 1) { + xlog("L_WARNING", "$ci|end|expires $var(expires) too brief (configured $def(REGISTRAR_MIN_EXPIRES)) registering $Au from IP $si:$sp\n"); + append_to_reply("Min-Expires: $def(REGISTRAR_MIN_EXPIRES)\r\n"); + send_reply("423", "Interval Too Brief"); + update_stat("registrar:min_expires", "+1"); + exit; + } else { + update_stat("registrar:min_expires_allowed", "+1"); + xlog("L_WARNING", "$ci|end|allowing expires $var(expires) too brief (configured $def(REGISTRAR_MIN_EXPIRES)) registering $Au from IP $si:$sp\n"); + } + } + } + +} + +## +## this needs handling logic in ecallmgr +## because we will fire the unregister from this server +## after device registers in another proxy +## causing ecallmgr to delete the registration +## from the other server +## event_route[usrloc:contact-expired] { + #!ifdef PRESENCE_ROLE + route(PRESENCE_EXPIRED_REGISTRATION); + #!endif + + #!ifdef KEEPALIVE_ROLE + route(KEEPALIVE_ON_EXPIRED_REGISTRATION); + #!endif + + ## return until we handle this in ecallmr + xlog("L_INFO", "$ulc(exp=>callid)|expired|removed registration for $ulc(exp=>aor) with contact : $ulc(exp=>addr)\n"); + return; + $var(transport) = $(ulc(exp=>received){uri.transport}); $var(proto) = $(ulc(exp=>socket){re.subst,/^([^:]*):(.*)/\1/}); if($var(proto) == "tls" && $var(transport) == "ws") { @@ -391,25 +542,25 @@ event_route[usrloc:contact-expired] { case "ws": case "wss": - if(@cfg_get.kazoo.registrar_handle_expired_ws == 1) { + if($sel(cfg_get.kazoo.registrar_handle_expired_ws) == 1) { $var(handle) = 1; } break; case "tls": - if(@cfg_get.kazoo.registrar_handle_expired_tls == 1) { + if($sel(cfg_get.kazoo.registrar_handle_expired_tls) == 1) { $var(handle) = 1; } break; case "tcp": - if(@cfg_get.kazoo.registrar_handle_expired_tcp == 1) { + if($sel(cfg_get.kazoo.registrar_handle_expired_tcp) == 1) { $var(handle) = 1; } break; case "udp": - if(@cfg_get.kazoo.registrar_handle_expired_udp == 1) { + if($sel(cfg_get.kazoo.registrar_handle_expired_udp) == 1) { $var(handle) = 1; } break; @@ -425,27 +576,9 @@ event_route[usrloc:contact-expired] $var(amqp_payload_request) = $_s({"Event-Category" : "directory", "Event-Name" : "reg_success", "Status" : "Unregistered", "Event-Timestamp" : $TS, "Expires" : 0, "First-Registration" : false, "Contact" : "$(ulc(exp=>addr){s.escape.common}{s.replace,\','}{s.replace,$$,})", "Call-ID" : "$ulc(exp=>callid)", "Realm" : "$var(domain)", "Username" : "$var(username)", "From-User" : "$var(username)", "From-Host" : "$var(domain)", "To-User" : "$var(username)", "To-Host" : "$var(domain)", "Proxy-Path" : "$ulc(exp=>socket)", "User-Agent" : "$(ulc(exp=>user_agent){s.escape.common}{s.replace,\','}{s.replace,$$,})"}); $var(amqp_routing_key) = "registration.success." + $(var(domain){kz.encode}) + "." + $(var(username){kz.encode}); kazoo_publish("registrar", $var(amqp_routing_key), $var(amqp_payload_request)); - xlog("L_INFO", "$ulc(exp=>callid)|expired|notified kazoo about removed registration with contact : $ulc(exp=>addr)\n"); - } else { - xlog("L_INFO", "$ulc(exp=>callid)|expired|removed registration with contact : $ulc(exp=>addr)\n"); + xlog("L_INFO", "$ulc(exp=>callid)|expired|notified registration removal with contact : $ulc(exp=>addr)\n"); } -} - -route[REGISTRAR_BINDINGS] -{ - #!import_file "registrar-custom-bindings.cfg" - - #!ifndef REGISTRAR_CUSTOM_BINDINGS - - $var(payload) = "{ 'exchange' : 'registrar' , 'type' : 'topic', 'queue' : 'registrar-flush-MY_HOSTNAME', 'routing' : 'registration.flush.*', 'federate' : 1 }"; - kazoo_subscribe("$var(payload)"); - - #!endif - - #!ifdef REGISTRAR_SYNC_ROLE - route(REGISTRAR_SYNC_BINDINGS); - #!endif - + xlog("L_INFO", "$ulc(exp=>callid)|expired|removed registration for $ulc(exp=>aor) with contact : $ulc(exp=>addr)\n"); } #!ifdef REGISTRAR_SYNC_ROLE diff --git a/kamailio/sanity.cfg b/kamailio/sanity.cfg new file mode 100644 index 0000000..92ba2fc --- /dev/null +++ b/kamailio/sanity.cfg @@ -0,0 +1,67 @@ + +#!trydef SANITY_CHECK_USE_PORT 1 +#!trydef SANITY_DROPS_REQUEST 1 +#!trydef SANITY_DEFAULT_CHECK 17895 +#!trydef SANITY_URI_CHECKS 7 +#!trydef SANITY_TRACE_REQUEST 1 + +#!substdef "!SANITY_SUBST_CACHE_PERIOD!$def(SANITY_CACHE_PERIOD)!g" + +######## SIP message formatting sanity checks [requires sl] ######## +loadmodule "sanity.so" +modparam("sanity", "default_checks", SANITY_DEFAULT_CHECK) +modparam("sanity", "uri_checks", SANITY_URI_CHECKS) +modparam("sanity", "autodrop", 0) +modparam("sanity", "noreply", 1) + +modparam("debugger", "mod_level", "sanity=-3") + +kazoo.sanity_check_use_port = SANITY_CHECK_USE_PORT descr "should we keep track of ip and port for sanity failures" +kazoo.sanity_drops_request = SANITY_DROPS_REQUEST descr "should we drop the request or send error on sanity failure" +kazoo.sanity_trace_request = SANITY_TRACE_REQUEST descr "should we trace the request if sip trace role is enabled" + +route[SANITY_CHECK] +{ + ## CVE-2018-14767 + if($(hdr(To)[1]) != $null) { + xlog("second To header not null - dropping message"); + drop; + } + + $var(sanity_key) = ""; + if($sel(cfg_get.kazoo.sanity_check_use_port) == 1) { + $var(sanity_key) = $_s("$si::$sp"); + } else { + $var(sanity_key) = $_s("$si"); + } + + if (!sanity_check()) { + #!ifdef SIP_TRACE_ROLE + sip_trace(); + #!endif + if($sel(cfg_get.kazoo.sanity_drops_request) == 1) { + xlog("L_WARN", "$ci|end|dropping insane message from $si:$sp\n"); + drop; + } else { + xlog("L_WARN", "$ci|end|insane message from $si:$sp\n"); + send_reply("400", "Bad Request"); + exit; + } + } + + if (!mf_process_maxfwd_header("10")) { + xlog("L_WARN", "$ci|end|too much hops, not enough barley from $si:$sp\n"); + send_reply("483", "Too Many Hops"); + exit; + } + + if ($ua == "friendly-scanner" || + $ua == "sundayddr" || + $ua == "pplsip" || + $ua =~ "NiceGuy" || + $ua =~ "PortSIP" || + $ua =~ "sipcli" ) { + xlog("L_WARN", "$ci|end|dropping message with user-agent $ua from $si:$sp\n"); + drop; + } +} diff --git a/kamailio/sip_trace-role.cfg b/kamailio/sip_trace-role.cfg index 6fb847b..afae3c7 100644 --- a/kamailio/sip_trace-role.cfg +++ b/kamailio/sip_trace-role.cfg @@ -1,11 +1,26 @@ +################################# +## SIP_TRACE_ROLE Defs + +#!trydef KZ_TRACE 0 +#!trydef KZ_TRACE_INTERNAL 1 +#!trydef KZ_TRACE_EXTERNAL 1 +#!trydef KZ_TRACE_LOCAL 1 +#!trydef KZ_TRACE_INTERNAL_INCOMING 1 +#!trydef KZ_TRACE_INTERNAL_OUTGOING 1 +#!trydef KZ_TRACE_EXTERNAL_INCOMING 1 +#!trydef KZ_TRACE_EXTERNAL_OUTGOING 1 +#!trydef SIP_TRACE_URI "sip:127.0.0.1:9060" +#!trydef HEP_CAPTURE_ID 1 + ############################################################## -## Kamailio siptrace settings configration examples at runtime +## Kamailio siptrace settings configuration examples at runtime ## kamcmd siptrace.status on ## kamcmd cfg.seti kazoo trace_external 0 ## kamcmd cfg.seti kazoo trace_internal 0 kazoo.trace_external = KZ_TRACE_EXTERNAL descr "activates tracing from external sources" kazoo.trace_internal = KZ_TRACE_INTERNAL descr "activates tracing from internal sources" +kazoo.trace_local = KZ_TRACE_LOCAL descr "activates tracing for local requests" kazoo.trace_internal_incoming = KZ_TRACE_INTERNAL_INCOMING descr "traces the original request as received from internal sources" kazoo.trace_internal_outgoing = KZ_TRACE_INTERNAL_OUTGOING descr "traces the outgoing request to external sources after possible modification" @@ -15,7 +30,7 @@ kazoo.trace_external_outgoing = KZ_TRACE_EXTERNAL_OUTGOING descr "traces the out ####### Siptrace module ########## loadmodule "siptrace.so" -modparam("siptrace", "duplicate_uri", "SIP_TRACE_URI") +modparam("siptrace", "duplicate_uri", SIP_TRACE_URI) modparam("siptrace", "hep_mode_on", 1) modparam("siptrace", "hep_version", 3) modparam("siptrace", "hep_capture_id", HEP_CAPTURE_ID) @@ -26,33 +41,55 @@ modparam("siptrace", "trace_on", KZ_TRACE) route[SIP_TRACE_INTERNAL] { - if(@cfg_get.kazoo.trace_internal == 0) { + if($sel(cfg_get.kazoo.trace_internal) == 0) { return; } - if(@cfg_get.kazoo.trace_internal_incoming == 1) { + if($sel(cfg_get.kazoo.trace_internal_incoming) == 1) { sip_trace(); } - if(@cfg_get.kazoo.trace_internal_outgoing == 1) { + if($sel(cfg_get.kazoo.trace_internal_outgoing) == 1) { setflag(FLAG_SIP_TRACE); } } route[SIP_TRACE_EXTERNAL] { - if(@cfg_get.kazoo.trace_external == 0) { + if($sel(cfg_get.kazoo.trace_external) == 0) { return; } - if(@cfg_get.kazoo.trace_external_incoming == 1) { + if($sel(cfg_get.kazoo.trace_external_incoming) == 1) { sip_trace(); } - if(@cfg_get.kazoo.trace_external_outgoing == 1) { + if($sel(cfg_get.kazoo.trace_external_outgoing) == 1) { setflag(FLAG_SIP_TRACE); } } -route[SEND_SIP_TRACE] +route[SIP_TRACE_LOCAL] +{ + if($sel(cfg_get.kazoo.trace_local) == 0) { + return; + } + + if($hdr(X-TM-Local) != $null) { + return; + } + + sip_trace(); +} + +route[SIP_TRACE_LOCAL_ROUTE] +{ + setflag(FLAG_SIP_TRACE); +} + +route[SIP_TRACE] { - if (isflagset(FLAG_INTERNALLY_SOURCED)) { + if (isflagset(FLAG_LOCAL_ROUTE)) { + route(SIP_TRACE_LOCAL_ROUTE); + } else if (isflagset(FLAG_LOCAL_REQUEST)) { + route(SIP_TRACE_LOCAL); + } else if (isflagset(FLAG_INTERNALLY_SOURCED)) { route(SIP_TRACE_INTERNAL); } else { route(SIP_TRACE_EXTERNAL); diff --git a/kamailio/tls-role.cfg b/kamailio/tls-role.cfg index b1ea5ba..df6507e 100644 --- a/kamailio/tls-role.cfg +++ b/kamailio/tls-role.cfg @@ -6,4 +6,8 @@ listen=TLS_ALG_SIP ####### TLS Parameters ######### loadmodule "tls.so" modparam("tls", "config", "/etc/kazoo/kamailio/tls.cfg") -modparam("tls", "low_mem_threshold1", 0) +modparam("tls", "low_mem_threshold1", 1) +modparam("tls", "low_mem_threshold2", 1) + +modparam("debugger", "mod_level", "tls=1") + diff --git a/kamailio/websockets-role.cfg b/kamailio/websockets-role.cfg index 0f6ee8a..2c8b77b 100644 --- a/kamailio/websockets-role.cfg +++ b/kamailio/websockets-role.cfg @@ -1,10 +1,8 @@ tcp_accept_no_cl=yes listen=TCP_WS -listen=UDP_WS_SIP #!ifdef TLS_ROLE listen=TLS_WSS -listen=UDP_WSS_SIP #!endif ######## NAT Traversal module - signaling functions ######## @@ -14,53 +12,37 @@ loadmodule "nathelper.so" #!endif ######## Generic Hash Table container in shared memory ######## -modparam("htable", "htable", "websockets=>size=16;autoexpire=7200;initval=0") +modparam("htable", "htable", "websockets=>size=16;initval=0") ######## Basic HTTP request handling ######## loadmodule "xhttp.so" + +#!trydef WS_KEEPALIVE_MECHANISM 0 +#!trydef WS_KEEPALIVE_TIMEOUT 30 +#!trydef WS_KEEPALIVE_PROCESSES 3 +#!trydef WS_KEEPALIVE_INTERVAL 1 +#!trydef WS_KEEPALIVE_DATA "Kazoo encourages you to keep alive" +#!trydef WS_MAX_CONNECTIONS_PER_IP 50 +#!trydef WS_MAX_CONNECTIONS_PER_PROXY 0 +#!trydef WS_ALLOWED_PROXIES "0.0.0.0/0" +#!trydef WS_CONNECTIONS_FROM_PROXY_ONLY 0 + ######## Websocket module ######## loadmodule "websocket.so" -modparam("websocket", "keepalive_mechanism", 0) -modparam("websocket", "keepalive_timeout", 30) -modparam("websocket", "keepalive_processes", 1) -modparam("websocket", "keepalive_interval", 1) -modparam("websocket", "ping_application_data", "Kazoo encourages you to keep alive") +modparam("websocket", "keepalive_mechanism", WS_KEEPALIVE_MECHANISM) +modparam("websocket", "keepalive_timeout", WS_KEEPALIVE_TIMEOUT) +modparam("websocket", "keepalive_processes", WS_KEEPALIVE_PROCESSES) +modparam("websocket", "keepalive_interval", WS_KEEPALIVE_INTERVAL) +modparam("websocket", "ping_application_data", WS_KEEPALIVE_DATA) modparam("websocket", "sub_protocols", 1) ####### Websocket Logic ######## -route[HANDLE_WEBSOCKETS] -{ - # Do NAT traversal stuff for requests from a WebSocket - # connection - even if it is not behind a NAT! - # This won't be needed in the future if Kamailio and the - # WebSocket client support Outbound and Path. - if (nat_uac_test(64)) { - xlog("L_INFO", "$ci|log|this is a websocket request\n"); - force_rport(); - if (is_method("REGISTER")) { - fix_nated_register(); - } else { - if (!add_contact_alias()) { - xlog("L_INFO", "$ci|stop|error aliasing contact <$ct>\n"); - sl_send_reply("400", "Bad Request"); - exit; - } - } - } -} -route[NAT_WEBSOCKETS_CORRECT] -{ - # Do NAT traversal stuff for replies to a WebSocket connection - # - even if it is not behind a NAT! - # This won't be needed in the future if Kamailio and the - # WebSocket client support Outbound and Path. - if (nat_uac_test(64)) { - xlog("L_INFO", "$ci|log|this is a websocket request\n"); - add_contact_alias(); - } -} +kazoo.ws_allowed_proxies = WS_ALLOWED_PROXIES desc "comma separated list of allowed proxies in cidr notation" +kazoo.ws_max_connection_per_ip = WS_MAX_CONNECTIONS_PER_IP desc "max connections per ip" +kazoo.ws_max_connection_per_proxy = WS_MAX_CONNECTIONS_PER_PROXY desc "max connections per proxy" +kazoo.ws_connections_via_proxy_only = WS_CONNECTIONS_FROM_PROXY_ONLY desc "only allow connections via proxy" event_route[xhttp:request] { @@ -93,21 +75,54 @@ event_route[xhttp:request] #!ifdef MY_WEBSOCKET_DOMAIN if (!($hdr(Origin) =~ "MY_WEBSOCKET_DOMAIN")) { - xlog("L_INFO", "websocket|log|rejecting HTTP request with unknown origin $hdr(Origin) from $si:$sp\n"); + xlog("L_INFO", "websocket|log|rejecting HTTP request with unauthorized origin $hdr(Origin) from $si:$sp, allowed origin is MY_WEBSOCKET_DOMAIN\n"); xhttp_reply("400", "Bad Request", "", ""); exit; } #!endif - if($sht(websockets=>$si::count) > 50) { - xlog("L_WARN", "websocket|log|$si:$sp is at the maximum allowable sockets per IP, rejecting request for another websocket\n"); + if ($hdr(X-Forwarded-For) == $null) { + if($sel(cfg_get.kazoo.ws_connections_via_proxy_only) == 1) { + xlog("L_INFO", "websocket|log|request from $si without X-Forwarded-For Header and only allowed connections are via proxy\n"); + xhttp_reply("403", "Forbidden", "", ""); + exit; + } else { + $var(ws_orig_ip) = $si; + } + } else { + xlog("L_INFO", "websocket|log|request X-Forwarded-For $hdr(X-Forwarded-For) from $si\n"); + $var(ws_orig_ip) = $hdr(X-Forwarded-For); + } + + if($si != $var(ws_orig_ip)) { + if(!is_in_subnet($si, $sel(cfg_get.kazoo.ws_allowed_proxies))) { + xlog("L_WARNING", "websocket|log|request X-Forwarded-For $hdr(X-Forwarded-For) from invalid ip $si - allowed $sel(cfg_get.kazoo.ws_allowed_proxies)\n"); + xhttp_reply("403", "Forbidden", "", ""); + exit; + } + + if($sel(cfg_get.kazoo.ws_max_connection_per_proxy) > 0 && $sht(websockets=>$si::count) > $sel(cfg_get.kazoo.ws_max_connection_per_proxy)) { + xlog("L_WARN", "websocket|log|$si is at the maximum $sel(cfg_get.kazoo.ws_max_connection_per_proxy) allowable sockets per PROXY IP, rejecting request for another websocket\n"); + xhttp_reply("403", "Forbidden", "", ""); + exit; + } + } + + if($sel(cfg_get.kazoo.ws_max_connection_per_ip) > 0 && $sht(websockets=>$var(ws_orig_ip)::count) > $sel(cfg_get.kazoo.ws_max_connection_per_ip)) { + xlog("L_WARN", "websocket|log|$var(ws_orig_ip) is at the maximum $sel(cfg_get.kazoo.ws_max_connection_per_ip) allowable sockets per IP, rejecting request for another websocket\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } if (ws_handle_handshake()) { - $var(shtinc) = $shtinc(websockets=>$si::count); - xlog("L_INFO", "websocket|log|opened websocket $var(count) of 50 for $si:$sp\n"); + $var(count) = $shtinc(websockets=>$var(ws_orig_ip)::count); + $sht(websockets=>$ws_conid::ws_orig_ip) = $var(ws_orig_ip); + if($si != $var(ws_orig_ip)) { + $var(proxy_count) = $shtinc(websockets=>$si::count); + xlog("L_INFO", "websocket|log|opened proxied websocket $ws_conid from $si for $var(ws_orig_ip):$sp\n"); + } else { + xlog("L_INFO", "websocket|log|opened websocket $ws_conid from $var(ws_orig_ip):$sp\n"); + } exit; } @@ -115,12 +130,18 @@ event_route[xhttp:request] xhttp_reply("404", "Not Found", "", ""); } -event_route[websocket:closed] { - $var(shtdec) = $shtdec(websockets=>$si::count); - if ($sht(websockets=>$si::count) < 1) { - xlog("L_INFO", "websocket|log|$si:$sp closed last websocket to that IP\n"); - sht_rm_name_re("websockets=>$(si{re.subst,/\\./\\\\./g})::.*"); +event_route[websocket:closed] +{ + $var(ws_orig_ip) = $sht(websockets=>$ws_conid::ws_orig_ip); + $sht(websockets=>$ws_conid::ws_orig_ip) = $null; + + $var(count) = $shtdec(websockets=>$si::count); + if($var(ws_orig_ip) != $null && $si != $var(ws_orig_ip)) { + $var(countip) = $shtdec(websockets=>$var(ws_orig_ip)::count); + xlog("L_INFO", "websocket|log|$si closed proxied websocket $ws_conid for $var(ws_orig_ip):$sp\n"); + if ($var(countip) < 1) $sht(websockets=>$var(ws_orig_ip)::count) = $null; } else { - xlog("L_INFO", "websocket|log|closed websocket from $si:$sp, $var(count) remaining from that IP\n"); + xlog("L_INFO", "websocket|log|closed websocket $ws_conid from $var(ws_orig_ip):$sp\n"); } + if ($var(count) < 1) $sht(websockets=>$si::count) = $null; } diff --git a/system/sbin/kazoo-kamailio b/system/sbin/kazoo-kamailio index 9fa4e00..23321be 100755 --- a/system/sbin/kazoo-kamailio +++ b/system/sbin/kazoo-kamailio @@ -1,20 +1,60 @@ #!/bin/bash if [ -f /etc/default/kamailio ]; then - . /etc/default/kamailio + . /etc/default/kamailio fi if [ -f /etc/sysconfig/kamailio ]; then - . /etc/sysconfig/kamailio + . /etc/sysconfig/kamailio fi +if [ -f /etc/kazoo/kamailio/options ]; then + . /etc/kazoo/kamailio/options +fi + +is_ipaddress () +{ +if [ $(echo $1 | grep -o '\.' | wc -l) -ne 3 ]; then + return 1 +elif [ $(echo $1 | tr '.' ' ' | wc -w) -ne 4 ]; then + return 1 +else + for OCTET in $(echo $1 | tr '.' ' '); do + if ! [[ $OCTET =~ ^[0-9]+$ ]]; then + return 1 + elif [[ $OCTET -lt 0 || $OCTET -gt 255 ]]; then + return 1 + fi + done +fi +return 0 +} + RETVAL=1 USER=${KAMAILIO_USER:-kamailio} -GROUP=${KAMAILIO_GROUP:-kamailio} +GROUP=${KAMAILIO_GROUP:-daemon} BIN_FILE=${KAMAILIO_BIN:-/usr/sbin/kamailio} PID_FILE=${KAMAILIO_PID:-/var/run/kamailio/kazoo-kamailio.pid} CFG_FILE=${KAMAILIO_CONFIG:-/etc/kazoo/kamailio/kamailio.cfg} +CFG_DIR=$(dirname ${CFG_FILE}) +export DB_SCRIPT_DIR=${KAMAILIO_DB_SCRIPT_DIR:-${CFG_DIR}/db_scripts} +export DB_LOCATION=${KAMAILIO_DB_LOCATION:-/etc/kazoo/kamailio/db} +export KAMAILIO_SHARE_DIR=${KAMAILIO_SHARE_DIR:-/usr/share/kamailio} export HOME=${KAMAILIO_HOME:-/var/run/kamailio} +RAM_DISK_SIZE=${RAM_DISK_SIZE:-256m} +RAM_DISK_FREE_SIZE_PERC=${RAM_DISK_FREE_SIZE:-30} +RAM_DISK_ENABLED=${RAM_DISK_ENABLED:-false} +EXTRA_OPTIONS=${EXTRA_OPTIONS:-"-x tlsf -w /tmp"} + +MY_LOCAL_IP=$(ip route get 8.8.8.8 2> /dev/null | awk '{print ""$7""; exit}') +if is_ipaddress ${MY_LOCAL_IP}; then + EXTRA_OPTIONS+=" -A MY_LOCAL_IP=${MY_LOCAL_IP}" +fi + +MY_PUBLIC_IP=$(dig @ns1.google.com TXT o-o.myaddr.l.google.com +short -4 2> /dev/null | sed s/\"//g ) +if [[ ! -z ${MY_PUBLIC_IP} ]]; then + EXTRA_OPTIONS+=" -A MY_PUBLIC_IP=${MY_PUBLIC_IP}" +fi SHM_MEMORY=$((`echo $SHM_MEMORY | sed -e 's/[^0-9]//g'`)) PKG_MEMORY=$((`echo $PKG_MEMORY | sed -e 's/[^0-9]//g'`)) @@ -22,159 +62,244 @@ PKG_MEMORY=$((`echo $PKG_MEMORY | sed -e 's/[^0-9]//g'`)) [ $PKG_MEMORY -le 0 ] && PKG_MEMORY=8 if test "$DUMP_CORE" = "yes" ; then - ulimit -c unlimited + ulimit -c unlimited fi prepare() { - mkdir -p /var/run/kamailio - chown -R ${USER} /var/run/kamailio - if [ -e ${PID_FILE} ]; then - rm -rf ${PID_FILE} - fi - if ram_disk_enabled; then - if ! mount_point_exists; then - if [ -f /etc/kazoo/kamailio/kazoo.db ]; then - mv /etc/kazoo/kamailio/db /etc/kazoo/kamailio/db-backup - fi - mount -t tmpfs -o size=256m tmpfs /etc/kazoo/kamailio/db - if [ -f /etc/kazoo/kamailio/db-backup/kazoo.db ]; then - cp -a /etc/kazoo/kamailio/db-backup/* /etc/kazoo/kamailio/db/ - fi - fi - fi - if [ ! -f /etc/kazoo/kamailio/db/kazoo.db ]; then + mkdir -p /var/run/kamailio + chown -R ${USER} /var/run/kamailio + if [ -e ${PID_FILE} ]; then + rm -rf ${PID_FILE} + fi + if ram_disk_enabled; then + if ! mount_point_exists; then + if [ -d ${DB_LOCATION}/../db-backup-temp ]; then + rm -rf ${DB_LOCATION}/../db-backup-temp + fi + if [ -d ${DB_LOCATION} ]; then + mkdir -p ${DB_LOCATION}/../db-backup-temp + cp -f ${DB_LOCATION}/* ${DB_LOCATION}/../db-backup-temp/ + rm -rf ${DB_LOCATION} + fi + mkdir -p ${DB_LOCATION} + mount -t tmpfs -o size=${RAM_DISK_SIZE} tmpfs ${DB_LOCATION} + if [ -f ${DB_LOCATION}/../db-backup-temp/kazoo.db ]; then + cp -f ${DB_LOCATION}/../db-backup-temp/* ${DB_LOCATION}/ + fi + if [ -d ${DB_LOCATION}/../db-backup-temp ]; then + rm -rf ${DB_LOCATION}/../db-backup-temp + fi + else + ### check ramdisk size + mountcurrentsize=$(mount_point_size) + mountwantedsize=$(convert_size ${RAM_DISK_SIZE}) + if [ $mountcurrentsize -lt $mountwantedsize ]; then + echo "current size is $mountcurrentsize is below wanted size of $mountwantedsize, remounting" + rm -rf ${DB_LOCATION}/../db-backup-temp + mkdir -p ${DB_LOCATION}/../db-backup-temp + cp -f ${DB_LOCATION}/* ${DB_LOCATION}/../db-backup-temp/ + umount ${DB_LOCATION} + rm -rf ${DB_LOCATION} + mkdir -p ${DB_LOCATION} + mount -t tmpfs -o size=${RAM_DISK_SIZE} tmpfs ${DB_LOCATION} + cp -f ${DB_LOCATION}/../db-backup-temp/* ${DB_LOCATION}/ + rm -rf ${DB_LOCATION}/../db-backup-temp + elif [ $mountcurrentsize -gt $mountwantedsize ]; then + # check if it fits + echo "wanted size of $mountwantedsize is below current size of $mountcurrentsize , checking sizes" + mountusedsize=$(mount_point_used_size) + requiredsize=$(( mountusedsize * (100 + ${RAM_DISK_FREE_SIZE_PERC}) / 100)) + if [ $requiredsize -gt $mountwantedsize ]; then + echo "wanted size of $mountwantedsize doesn't have enough space for required size of $requiredsize" + mountwantedsize=$requiredsize + else + echo "resizing from $mountcurrentsize to $mountwantedsize" + rm -rf ${DB_LOCATION}/../db-backup-temp + mkdir -p ${DB_LOCATION}/../db-backup-temp + cp -f ${DB_LOCATION}/* ${DB_LOCATION}/../db-backup-temp/ + umount ${DB_LOCATION} + rm -rf ${DB_LOCATION} + mkdir -p ${DB_LOCATION} + mount -t tmpfs -o size=$mountwantedsize tmpfs ${DB_LOCATION} + cp -f ${DB_LOCATION}/../db-backup-temp/* ${DB_LOCATION}/ + rm -rf ${DB_LOCATION}/../db-backup-temp + fi + fi + fi + else + if mount_point_exists; then + if [ -d ${DB_LOCATION}/../db-backup-temp ]; then + rm -rf ${DB_LOCATION}/../db-backup-temp + fi + mkdir -p ${DB_LOCATION}/../db-backup-temp + cp -f ${DB_LOCATION}/* ${DB_LOCATION}/../db-backup-temp/ + umount ${DB_LOCATION} + rm -rf ${DB_LOCATION} + mkdir -p ${DB_LOCATION} + cp -f ${DB_LOCATION}/../db-backup-temp/* ${DB_LOCATION}/ + fi + fi + if [ ! -f ${DB_LOCATION}/kazoo.db ]; then init_database - fi - chown -R ${USER} /etc/kazoo/kamailio/db - RETVAL=$? + else + check_database + fi + chown -R ${USER} ${DB_LOCATION} + RETVAL=$? } ram_disk_enabled() { - grep -e "enable_ram_disk = true" /etc/kazoo/kamailio/local.cfg &> /dev/null - return $? + + grep -e "enable_ram_disk = true" ${CFG_DIR}/local.cfg &> /dev/null + config_enabled=$? + if [[ $RAM_DISK_ENABLED == "true" ]] || [[ $config_enabled -eq 0 ]]; then + return 0 + else + return 1 + fi } mount_point_exists() { - mount | grep /etc/kazoo/kamailio/db &> /dev/null - return $? + mount | grep $(readlink -f ${DB_LOCATION}) &> /dev/null + return $? +} + +mount_point_size() { + echo $(df --block-size=1 | grep $(readlink -f ${DB_LOCATION}) | tr -s ' ' | cut -d ' ' --fields=2) +} + +mount_point_used_size() { + echo $(df --block-size=1 | grep $(readlink -f ${DB_LOCATION}) | tr -s ' ' | cut -d ' ' --fields=3) +} + +mount_point_free_size() { + echo $(df --block-size=1 | grep $(readlink -f ${DB_LOCATION}) | tr -s ' ' | cut -d ' ' --fields=4) +} + +convert_size() { + echo "$1" | awk \ + 'BEGIN{IGNORECASE = 1} + function printpower(n,b,p) {printf "%u\n", n*b^p; next} + /[0-9]$/{print $1;next}; + /K(iB)?$/{printpower($1, 2, 10)}; + /M(iB)?$/{printpower($1, 2, 20)}; + /G(iB)?$/{printpower($1, 2, 30)}; + /T(iB)?$/{printpower($1, 2, 40)}; + /KB$/{ printpower($1, 10, 3)}; + /MB$/{ printpower($1, 10, 6)}; + /GB$/{ printpower($1, 10, 9)}; + /TB$/{ printpower($1, 10, 12)}' } init_database() { - /etc/kazoo/kamailio/db_scripts/create-kazoodb-sql.sh + ${DB_SCRIPT_DIR}/create-kazoodb-sql.sh + check_database } +check_database() { + ${DB_SCRIPT_DIR}/check-kazoodb-sql.sh +} start() { - cd ${HOME} + cd ${HOME} - check_config - check_fork + check_config + check_fork - if [ "$(whoami)" == "${USER}" ]; then - set -- ${BIN_FILE} -f ${CFG_FILE} -m ${SHM_MEMORY} -M ${PKG_MEMORY} ${EXTRA_OPTIONS} "$@" - exec "$@" - else - set -- ${BIN_FILE} -f ${CFG_FILE} -m ${SHM_MEMORY} -M ${PKG_MEMORY} -u ${USER} -g ${GROUP} ${EXTRA_OPTIONS} "$@" - runuser -s /bin/bash ${USER} -c "$*" - fi - RETVAL=$? + if [ "$(whoami)" == "${USER}" ]; then + set -- ${BIN_FILE} -f ${CFG_FILE} -m ${SHM_MEMORY} -M ${PKG_MEMORY} ${EXTRA_OPTIONS} "$@" + exec "$@" + else + set -- ${BIN_FILE} -f ${CFG_FILE} -m ${SHM_MEMORY} -M ${PKG_MEMORY} -u ${USER} -g ${GROUP} ${EXTRA_OPTIONS} "$@" + runuser -s /bin/bash ${USER} -c "$*" + fi + RETVAL=$? - if [ ${RETVAL} -ne 0 ]; then - echo "Failed to start Kamailio!" - RETVAL=1 - fi + if [ ${RETVAL} -ne 0 ]; then + echo "Failed to start Kamailio!" + RETVAL=1 + fi } stop() { - killall ${BIN_FILE} - RETVAL=$? - if mount_point_exists; then - if [ -f /etc/kazoo/kamailio/kazoo.db ]; then - mv /etc/kazoo/kamailio/db /etc/kazoo/kamailio/db-backup - fi - fi + kamcmd core.kill + RETVAL=$? + if mount_point_exists; then + mkdir -p ${DB_LOCATION}/../db-backup + cp -f ${DB_LOCATION}/* ${DB_LOCATION}/../db-backup/ + fi } restart() { - stop - start + stop + start } reset-restart() { - cd /etc/kazoo/kamailio/dbtext/ - stop - - head -n1 active_watchers > active_watchers.tmp - mv -f active_watchers.tmp active_watchers - - head -n1 watchers > watchers.tmp - mv -f watchers.tmp watchers + cd /etc/kazoo/kamailio/dbtext/ + stop - head -n1 presentity > presentity.tmp - mv -f presentity.tmp presentity + chown kamailio:daemon * - chown kamailio:daemon * - - start + start } status() { - kamcmd dispatcher.list - RETVAL=$? + kamcmd dispatcher.list + RETVAL=$? } # Do not start kamailio if fork=no is set in the config file # otherwise the boot process will just stop check_fork () { - if grep -q "^[[:space:]]*fork[[:space:]]*=[[:space:]]*no.*" $CFG_FILE; then - echo "WARNING: fork=no specified in config file" - exit 1 - fi + if grep -q "^[[:space:]]*fork[[:space:]]*=[[:space:]]*no.*" $CFG_FILE; then + echo "WARNING: fork=no specified in config file" + exit 1 + fi } check_config () { - local ERRORS=$($BIN_FILE -c -f ${CFG_FILE} 2>&1 > /dev/null) - RETVAL=$? - - if [ ${RETVAL} -ne 0 ]; then - echo "ERROR: Invalid configuration file ${CFG_FILE}!" - echo -e "\n${ERRORS}\n" - else - echo "No errors found in ${CFG_FILE}" - fi + ERRORS="$($BIN_FILE -c -f ${CFG_FILE} -x tlsf 2>&1 > /dev/null)" + RETVAL=$? + if [ ${RETVAL} -ne 0 ]; then + echo "ERROR: Invalid configuration file ${CFG_FILE}!" + echo -e "\n${ERRORS}\n" + else + echo "No errors found in ${CFG_FILE}" + fi } case "$1" in - prepare) - prepare - ;; - background) - shift - start $@ - ;; - start) - shift - start -DD $@ - ;; - stop) - stop - ;; - restart) - restart - ;; - reset-restart) - reset-restart - ;; - status) - status - ;; - check) - check_config - ;; - *) - echo $"Usage: $0 {prepare|start|background|stop|restart|reset-restart|status|check|pid}" + prepare) + prepare + ;; + background) + shift + start $@ + ;; + start) + shift + start -DD $@ + ;; + stop) + stop + ;; + restart) + restart + ;; + reset-restart) + reset-restart + ;; + status) + status + ;; + check) + check_config + ;; + *) + echo $"Usage: $0 {prepare|start|background|stop|restart|reset-restart|status|check|pid}" esac exit ${RETVAL}