| @ -0,0 +1,21 @@ | |||
| ####### Flags ####### | |||
| #!trydef FLAG_ACC 13 | |||
| #!trydef FLAG_ACCMISSED 14 | |||
| #!trydef FLAG_ACCFAILED 15 | |||
| ######## Accounting module ######## | |||
| loadmodule "acc.so" | |||
| ######## Accounting params ######## | |||
| modparam("acc", "early_media", 0) | |||
| modparam("acc", "report_ack", 0) | |||
| modparam("acc", "report_cancels", 1) | |||
| modparam("acc", "detect_direction", 0) | |||
| modparam("acc", "log_flag", 7) | |||
| modparam("acc", "log_level", 2) | |||
| modparam("acc", "log_missed_flag", 8) | |||
| modparam("acc", "failed_transaction_flag", 9) | |||
| modparam("acc", "log_extra", | |||
| "src_user=$fU;src_domain=$fd;src_ip=$si;" | |||
| "dst_ouser=$tU;dst_user=$rU;dst_domain=$rd") | |||
| modparam("acc", "log_facility", "LOG_LOCAL6") | |||
| @ -0,0 +1,273 @@ | |||
| ######## DoS prevention module ######## | |||
| # Default "order" is "deny,allow". | |||
| # So if there is no data from DB request will be permitted by default. | |||
| # | |||
| modparam("htable", "htable", "acl=>initval=-1;autoexpire=7200") | |||
| #!trydef ACL_MESSAGE_DENY "Rejected by ACL" | |||
| #!trydef ACL_CODE_DENY "603" | |||
| #!trydef ACL_ORDER_ALLOW_DENY "allow,deny" | |||
| #!trydef ACL_IP_ADDR_ANY "0.0.0.0/0" | |||
| #!trydef IP_REGEX "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}" | |||
| ## Route for ACL functionality | |||
| route[ACL_CHECK] { | |||
| # If packet came from platform or from 4 class MERA, do not check it | |||
| if (isflagset(FLAG_INTERNALLY_SOURCED) || isflagset(FLAG_TRUSTED_SOURCE) ) { | |||
| xlog("L_DEBUG", "$ci|ACL|Trusted source IP($si) ignoring\n"); | |||
| return; | |||
| } | |||
| if (isflagset(FLAG_IS_REPLY)) { | |||
| $var(sip-packet) = $rs; | |||
| } else { | |||
| $var(sip-packet) = $rm; | |||
| } | |||
| # FIX for BYE method with IP instead of REALM in From, take REALM from To header | |||
| if ($fd =~ IP_REGEX) { | |||
| xlog("L_WARNING","$ci|ACL-realm|Fix for $var(sip-packet) with IP in from URI: use to-domain\n"); | |||
| $var(realm) = $td; | |||
| } else { | |||
| $var(realm) = $fd; | |||
| } | |||
| $var(acl-realm-request) = "false"; | |||
| $var(acl-device-request) = "false"; | |||
| $var(realm-decision) = $sht(acl=>$var(realm)/$si); | |||
| if ($var(realm-decision) == -1) { # we do not have cached decision | |||
| $var(acl-realm-request) = "true"; | |||
| } else if ($var(realm-decision) == 1 ){ # We have cached decision, let's use it | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(realm)\n"); | |||
| } else { | |||
| if (!isflagset(FLAG_IS_REPLY)) { | |||
| sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); | |||
| } | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(realm)\n"); | |||
| exit; | |||
| } | |||
| if (not_empty("$fU")) { | |||
| if ($fd =~ IP_REGEX) { | |||
| xlog("L_WARNING","$ci|ACL-device|Fix for $var(sip-packet) with IP in from URI: use to-domain\n"); | |||
| $var(device) = $fU + "@" + $td; | |||
| } else { | |||
| $var(device) = $fU + "@" + $fd; | |||
| } | |||
| $var(device-decision) = $sht(acl=>$var(device)/$si); | |||
| if ($var(device-decision) == -1) { # we do not have cached decision | |||
| $var(acl-device-request) = "true"; | |||
| } else if ($var(device-decision) == 1 ){ # We have cached decision, let's use it | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); | |||
| } else { | |||
| if (!isflagset(FLAG_IS_REPLY)) { | |||
| sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); | |||
| } | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(device)\n"); | |||
| exit; | |||
| } | |||
| } | |||
| if ($var(acl-realm-request) == "true" || $var(acl-device-request) == "true") { | |||
| if (not_empty("$fU")) | |||
| $var(query) = "{'Event-Category': 'acl', 'Event-Name': 'query', 'Entity': '" + $var(device) + "', 'With-Realm': " + $var(acl-realm-request) + "}"; | |||
| else | |||
| $var(query) = "{'Event-Category': 'acl', 'Event-Name': 'query', 'Entity': '" + $var(realm) + "'}"; | |||
| xlog("L_DBG", "$ci|ACL log|Query: $var(query)\n"); | |||
| sl_send_reply("100", "Attempting K query"); | |||
| if (kazoo_query("frontier", "sbc_config", $var(query), "$var(acl-response)")) { | |||
| xlog("L_DBG", "$ci|ACL log|Response: $var(acl-response)\n"); | |||
| kazoo_json($var(acl-response), "Realm.Order", "$var(acl-realm-order)"); | |||
| kazoo_json($var(acl-response), "Realm.CIDR", "$var(acl-realm-cidr)"); | |||
| kazoo_json($var(acl-response), "Device.Order", "$var(acl-device-order)"); | |||
| kazoo_json($var(acl-response), "Device.CIDR", "$var(acl-device-cidr)"); | |||
| kazoo_json($var(acl-response), "Device.User-Agent", "$var(acl-device-ua)"); | |||
| } else { | |||
| xlog("L_ERROR","$ci|ACL log|DB is unreachable\n"); | |||
| $sht(acl=>$var(device)/$si) = 1; | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); | |||
| return; | |||
| } | |||
| route(ACL_CHECK_REALM); | |||
| if (not_empty("$fU")) { | |||
| route(ACL_CHECK_DEVICE); | |||
| } | |||
| } | |||
| } | |||
| # Check ORDER setting for REALM | |||
| route[ACL_CHECK_REALM] { | |||
| if (not_empty("$var(acl-realm-order)")) { | |||
| if ($var(acl-realm-order) == ACL_ORDER_ALLOW_DENY) { | |||
| route(ACL_CHECK_REALM_ALLOW); | |||
| } else { | |||
| route(ACL_CHECK_REALM_DENY); | |||
| } | |||
| } else { | |||
| xlog("L_INFO","$ci|ACL-realm|undefined Order in response for $var(realm)\n"); | |||
| $sht(acl=>$var(realm)/$si) = 1; | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(realm)\n"); | |||
| } | |||
| } | |||
| route[ACL_CHECK_REALM_ALLOW] { | |||
| if (not_empty("$var(acl-realm-cidr)")) { | |||
| $var(i) = 0; | |||
| kazoo_json($var(acl-response), "Realm.CIDR[$var(i)]", "$var(record)");; | |||
| while(not_empty("$var(record)")) { | |||
| xlog("L_INFO", "$ci|ACL-realm|checking if $si is in $var(record)\n"); | |||
| if (($var(record) == ACL_IP_ADDR_ANY) || is_in_subnet("$si", $var(record))) { | |||
| $sht(acl=>$var(realm)/$si) = 1; | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(realm)\n"); | |||
| return; | |||
| } | |||
| $var(i) = $var(i) + 1; | |||
| kazoo_json($var(acl-response), "Realm.CIDR[$var(i)]", "$var(record)");; | |||
| } | |||
| } else { | |||
| xlog("L_INFO", "$ci|ACL-realm|undefined CIDR in response for $var(realm)\n"); | |||
| } | |||
| # Remember in CACHE and DENY | |||
| $sht(acl=>$var(realm)/$si) = 0; | |||
| if (!isflagset(FLAG_IS_REPLY)) { | |||
| sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); | |||
| } | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(realm)\n"); | |||
| exit; | |||
| } | |||
| route[ACL_CHECK_REALM_DENY] { | |||
| $var(size) = $(kzR{kz.json,Realm.CIDR.length}); | |||
| if (not_empty("$var(acl-realm-cidr)")) { | |||
| $var(i) = 0; | |||
| kazoo_json($var(acl-response), "Realm.CIDR[$var(i)]", "$var(record)");; | |||
| while(not_empty("$var(record)")) { | |||
| xlog("L_INFO", "$ci|ACL-realm|checking if $si is in $var(record)\n"); | |||
| if (($var(record) == ACL_IP_ADDR_ANY) || is_in_subnet("$si", $var(record))) { | |||
| $sht(acl=>$var(realm)/$si) = 0; | |||
| if (!isflagset(FLAG_IS_REPLY)) { | |||
| sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); | |||
| } | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(realm)\n"); | |||
| exit; | |||
| } | |||
| $var(i) = $var(i) + 1; | |||
| kazoo_json($var(acl-response), "Realm.CIDR[$var(i)]", "$var(record)");; | |||
| } | |||
| } else { | |||
| xlog("L_INFO", "$ci|ACL-realm|undefined CIDR in response for $var(realm)\n"); | |||
| } | |||
| # Remember in CACHE and ALLOW | |||
| $sht(acl=>$var(realm)/$si) = 1; | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(realm)\n"); | |||
| return; | |||
| } | |||
| # Check ORDER setting for DEVICE | |||
| route[ACL_CHECK_DEVICE] { | |||
| if (not_empty("$var(acl-device-order)")) { | |||
| if ($var(acl-device-order) == ACL_ORDER_ALLOW_DENY) { | |||
| route(ACL_CHECK_DEVICE_ALLOW); | |||
| } else { | |||
| route(ACL_CHECK_DEVICE_DENY); | |||
| } | |||
| } else { | |||
| xlog("L_INFO","$ci|ACL-device|undefined Order in response for $var(device)\n"); | |||
| $sht(acl=>$var(device)/$si) = 1; | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); | |||
| } | |||
| } | |||
| route[ACL_CHECK_DEVICE_ALLOW] { | |||
| if (!not_empty("$var(acl-device-ua)") || (not_empty("$var(acl-device-ua)") && $ua =~ $var(acl-device-ua))) { | |||
| if (not_empty("$var(acl-device-cidr)")) { | |||
| $var(i) = 0; | |||
| kazoo_json($var(acl-response), "Device.CIDR[$var(i)]", "$var(record)");; | |||
| while(not_empty("$var(record)")) { | |||
| xlog("L_INFO", "$ci|ACL-realm|checking if $si is in $var(record)\n"); | |||
| if (($var(record) == ACL_IP_ADDR_ANY) || is_in_subnet("$si", $var(record))) { | |||
| $sht(acl=>$var(device)/$si) = 1; | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); | |||
| return; | |||
| } | |||
| $var(i) = $var(i) + 1; | |||
| kazoo_json($var(acl-response), "Device.CIDR[$var(i)]", "$var(record)");; | |||
| } | |||
| } else { | |||
| xlog("L_INFO", "$ci|ACL-realm|undefined CIDR in response for $var(device)\n"); | |||
| } | |||
| } | |||
| # Remember in CACHE and DENY | |||
| $sht(acl=>$var(device)/$si) = 0; | |||
| if (!isflagset(FLAG_IS_REPLY)) { | |||
| sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); | |||
| } | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(device)\n"); | |||
| exit; | |||
| } | |||
| route[ACL_CHECK_DEVICE_DENY] { | |||
| if (not_empty("$var(acl-device-ua)") && !($ua =~ $var(acl-device-ua))) { | |||
| $sht(acl=>$var(device)/$si) = 0; | |||
| if (!isflagset(FLAG_IS_REPLY)) { | |||
| sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); | |||
| } | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(device)\n"); | |||
| exit; | |||
| } | |||
| if (not_empty("$var(acl-device-cidr)")) { | |||
| $var(i) = 0; | |||
| kazoo_json($var(acl-response), "Device.CIDR[$var(i)]", "$var(record)");; | |||
| while(not_empty("$var(record)")) { | |||
| xlog("L_INFO", "$ci|ACL-device|checking if $si is in $var(record)\n"); | |||
| if (($var(record) == ACL_IP_ADDR_ANY) || is_in_subnet("$si", $var(record))) { | |||
| $sht(acl=>$var(device)/$si) = 0; | |||
| if (!isflagset(FLAG_IS_REPLY)) { | |||
| sl_send_reply(ACL_CODE_DENY, ACL_MESSAGE_DENY); | |||
| } | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is rejected by ACL for $var(device)\n"); | |||
| exit; | |||
| } | |||
| $var(i) = $var(i) + 1; | |||
| kazoo_json($var(acl-response), "Device.CIDR[$var(i)]", "$var(record)");; | |||
| } | |||
| } else { | |||
| xlog("L_INFO", "$ci|ACL-device|undefined CIDR in response for $var(device)\n"); | |||
| } | |||
| # Remember in CACHE and ALLOW | |||
| $sht(acl=>$var(device)/$si) = 1; | |||
| xlog("L_INFO", "$ci|ACL|$var(sip-packet) from $si is permitted by ACL for $var(device)\n"); | |||
| return; | |||
| } | |||
| event_route[kazoo:consumer-event-acl-acl-flush] | |||
| { | |||
| if( $(kzE{kz.json,Device}) != "" ) { | |||
| $var(device_regexp) = $(kzE{kz.json,Device}) + "@" + $(kzE{kz.json,Realm}) + "/.*" ; | |||
| xlog("L_INFO","|ACL-Flush| Flush ACL for Device. Regexp: $var(device_regexp)\n"); | |||
| sht_rm_name_re("acl=>$var(device_regexp)"); | |||
| } else { | |||
| $var(realm_regexp) = "^" + $(kzE{kz.json,Realm}) + "/.*" ; | |||
| xlog("L_INFO","|ACL-Flush| Flush ACL for Realm. Regexp: $var(realm_regexp)\n"); | |||
| sht_rm_name_re("acl=>$var(realm_regexp)"); | |||
| } | |||
| } | |||
| 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)"); | |||
| } | |||
| @ -0,0 +1,140 @@ | |||
| #!trydef ANTIFLOOD_RATE_WINDOW 2 | |||
| #!trydef ANTIFLOOD_RATE_DENSITY 50 | |||
| #!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_SUBST_CACHE_PERIOD;initval=0") | |||
| ######## Flood Prevention Module ######## | |||
| loadmodule "pike.so" | |||
| 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] | |||
| { | |||
| if (has_totag() | |||
| || isflagset(FLAG_TRUSTED_SOURCE) | |||
| || isflagset(FLAG_INTERNALLY_SOURCED)) { | |||
| return; | |||
| } | |||
| # use pike to control the rates | |||
| if (!pike_check_req()) { | |||
| 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[ANTIFLOOD_AUTH_LIMIT] | |||
| { | |||
| if (has_totag() | |||
| || isflagset(FLAG_TRUSTED_SOURCE) | |||
| || isflagset(FLAG_INTERNALLY_SOURCED)) { | |||
| return(1); | |||
| } | |||
| $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 | |||
| ) { | |||
| $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] | |||
| { | |||
| 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] | |||
| { | |||
| $var(user) = $(kzE{kz.json,Username}) + "@" + $(kzE{kz.json,Realm}); | |||
| sht_rm_name_re("antiflood=>$(var(user){re.subst,/\\./\\\\./g})::.*"); | |||
| } | |||
| route[ANITFLOOD_FAILED_AUTH] | |||
| { | |||
| if (isflagset(FLAG_TRUSTED_SOURCE)) { | |||
| return; | |||
| } | |||
| $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) { | |||
| $var(exp) = $Ts - ANTIFLOOD_FAILED_AUTH_WINDOW; | |||
| 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("403", "Forbidden"); | |||
| exit; | |||
| } | |||
| } | |||
| } | |||
| event_route[htable:expired:antiflood] | |||
| { | |||
| xlog("L_NOTICE", "antiflood expired record $shtrecord(key) => $shtrecord(value)\n"); | |||
| } | |||
| @ -0,0 +1,130 @@ | |||
| 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-"); | |||
| if (!is_method("INVITE|MESSAGE|REFER")) { | |||
| return; | |||
| } | |||
| 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[AUTH_HEADERS_JSON] | |||
| { | |||
| xavp_params_implode("hf", "$var(outx)"); | |||
| $var(out) = $(var(outx){re.subst,/^(.*);$$/\1/}); | |||
| $var(c) = $(var(out){param.count}); | |||
| $var(headers_json) = ""; | |||
| $var(sep) = ""; | |||
| 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(headers_json) = $_s($var(headers_json)$var(sep)"$(var(out){param.name,$var(idx)})" : "$(var(out){param.valueat,$var(idx)}{s.unescape.param})"); | |||
| $var(c) = $var(c) - 1; | |||
| $var(sep) = " , "; | |||
| } | |||
| $var(headers_json) = $_s({ $var(headers_json) }); | |||
| } | |||
| route[SETUP_AUTH_HEADERS] | |||
| { | |||
| $xavp(hf=>X-AUTH-IP) = $si; | |||
| $xavp(hf[0]=>X-AUTH-PORT) = $sp; | |||
| #!ifdef REGISTRAR_ROLE | |||
| $avp(is_registered) = "false"; | |||
| $xavp(regcfg=>match_received) = $su; | |||
| if (registered("location","$fu", 2, 1) != 1) return; | |||
| $avp(is_registered) = "true"; | |||
| #!ifdef WITH_AUTH_TOKEN | |||
| route(AUTH_TOKEN); | |||
| #!else | |||
| route(AUTH_CCVS); | |||
| #!endif | |||
| #!endif | |||
| } | |||
| #!ifdef REGISTRAR_ROLE | |||
| route[AUTH_TOKEN] | |||
| { | |||
| 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] | |||
| { | |||
| 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,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,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 | |||
| @ -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; | |||
| } | |||
| } | |||
| @ -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; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1 @@ | |||
| ensure certs directory gets created | |||
| @ -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" | |||
| @ -0,0 +1,4 @@ | |||
| #### db_mysql module ### | |||
| loadmodule "db_mysql.so" | |||
| include_file "db_queries_mysql.cfg" | |||
| @ -0,0 +1,4 @@ | |||
| #### db_postgres module ### | |||
| loadmodule "db_postgres.so" | |||
| include_file "db_queries_postgres.cfg" | |||
| @ -0,0 +1,33 @@ | |||
| ####### Database queries ######## | |||
| #!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 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 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" | |||
| #!substdef "!KZQ_DELETE_FROM_ACTIVE_WATCHERS_WHERE_EXPIRES!DELETE FROM active_watchers WHERE expires > 0 AND datetime(expires, 'unixepoch') < datetime('now', '-90 seconds')!g" | |||
| #!substdef "!KZQ_DELETE_FROM_ACTIVE_WATCHERS_WHERE_PRESENTITY!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)\"!g" | |||
| #!substdef "!KZQ_DELETE_FROM_PRESENTITY_WHERE_EXPIRES!DELETE FROM PRESENTITY WHERE expires > 0 AND datetime(expires, 'unixepoch') < datetime('now')!g" | |||
| #!substdef "!KZQ_DELETE_FROM_PRESENTITY_WHERE_DIALOG_TERMINATED!DELETE FROM PRESENTITY WHERE ID IN(select id from presentities where event = 'dialog' and state = 'terminated' and received < datetime('now', '-5 minutes'))!g" | |||
| @ -0,0 +1,15 @@ | |||
| ####### Database queries ######## | |||
| #!substdef "!KZQ_CHECK_MEDIA_SERVER_INSERT!insert into dispatcher (setid, destination) select \$var(SetId), \"\$var(MediaUrl)\" from DUAL 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_COUNT_PRESENTITIES!select event, (select count(*) from presentity b where username = \"\$(kzE{kz.json,From}{uri.user})\" and domain = \"\$(kzE{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_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_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" | |||
| @ -0,0 +1,23 @@ | |||
| ####### 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_COUNT_PRESENTITIES!select event, (select count(*) from presentity b where username = '\$(kzE{kz.json,From}{uri.user})' and domain = '\$(kzE{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_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_PRESENCE_RESET!delete from presentity where sender = '\$var(MediaUrl)'!g" | |||
| #!substdef "!KZQ_REPLACE_WATCHERS_LOG!INSERT 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.replace,\\\',''}{s.replace,\$\$,''})', '\$(notify_reply(\$mb){s.replace,\\\',''}{s.replace,\$\$,''})') ON CONFLICT (presentity_uri, watcher_username, watcher_domain, event) DO UPDATE SET presentity_uri = excluded.presentity_uri, watcher_username = excluded.watcher_username, watcher_domain = excluded.watcher_domain, event = excluded.event!g" | |||
| #!substdef "!KZQ_DELETE_FROM_ACTIVE_WATCHERS_WHERE_EXPIRES!DELETE FROM active_watchers WHERE expires > 0 AND to_timestamp(expires) < now() - interval '90 seconds'!g" | |||
| #!substdef "!KZQ_DELETE_FROM_ACTIVE_WATCHERS_WHERE_PRESENTITY!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)'!g" | |||
| #!substdef "!KZQ_DELETE_FROM_PRESENTITY_WHERE_EXPIRES!DELETE FROM PRESENTITY WHERE expires > 0 AND to_timestamp(expires) < now()!g" | |||
| #!substdef "!KZQ_DELETE_FROM_PRESENTITY_WHERE_DIALOG_TERMINATED!DELETE FROM PRESENTITY WHERE ID IN(select id from presentities where event = 'dialog' and state = 'terminated' and received < now() - interval '5 minutes'))!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" | |||
| @ -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 | |||
| @ -0,0 +1,9 @@ | |||
| #!/bin/sh -e | |||
| . $(dirname $0)/kazoodb-sql.sh --source-only | |||
| file=$(sql_db_prepare) | |||
| echo "setting up kazoo db from init script $file" | |||
| sql_setup $file | |||
| exit 0 | |||
| @ -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;" | |||
| } | |||
| @ -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; | |||
| @ -0,0 +1,152 @@ | |||
| #!/bin/sh | |||
| sql_db_pre_setup() { | |||
| 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=${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() { | |||
| cat << EOF | |||
| EOF | |||
| } | |||
| sql_extra_tables() { | |||
| cat << EOF | |||
| CREATE TABLE active_watchers_log ( | |||
| id INTEGER PRIMARY KEY NOT NULL, | |||
| presentity_uri VARCHAR(128) NOT NULL COLLATE NOCASE, | |||
| watcher_username VARCHAR(64) NOT NULL COLLATE NOCASE, | |||
| watcher_domain VARCHAR(64) NOT NULL COLLATE NOCASE, | |||
| to_user VARCHAR(64) NOT NULL COLLATE NOCASE, | |||
| to_domain VARCHAR(64) NOT NULL COLLATE NOCASE, | |||
| event VARCHAR(64) DEFAULT 'presence' NOT NULL, | |||
| callid VARCHAR(255) NOT NULL, | |||
| time INTEGER NOT NULL, | |||
| result INTEGER NOT NULL, | |||
| sent_msg BLOB NOT NULL, | |||
| received_msg BLOB NOT NULL, | |||
| 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,"<state>")+7, instr(body,"</state>") - instr(body,"<state>") - 7) | |||
| when event = "presence" | |||
| then case when instr(body,"<dm:note>") == 0 | |||
| then replace(substr(body, instr(body,"<note>")+6, instr(body,"</note>") - instr(body,"<note>") - 6), " ", "") | |||
| else replace(substr(body, instr(body,"<dm:note>")+9, instr(body,"</dm:note>") - instr(body,"<dm:note>") - 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 | |||
| } | |||
| sql_footer() { | |||
| cat << EOF | |||
| EOF | |||
| } | |||
| @ -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" | |||
| } | |||
| @ -0,0 +1,44 @@ | |||
| #!/bin/sh | |||
| sql_db_pre_setup() { | |||
| cat << EOF | |||
| START TRANSACTION; | |||
| EOF | |||
| } | |||
| sql_setup() { | |||
| cat << EOF | |||
| EOF | |||
| } | |||
| sql_header() { | |||
| cat << EOF | |||
| EOF | |||
| } | |||
| sql_extra_tables() { | |||
| cat << EOF | |||
| CREATE TABLE active_watchers_log ( | |||
| id SERIAL PRIMARY KEY NOT NULL, | |||
| presentity_uri VARCHAR(128) NOT NULL, | |||
| watcher_username VARCHAR(64) NOT NULL, | |||
| watcher_domain VARCHAR(64) NOT NULL, | |||
| to_user VARCHAR(64) NOT NULL, | |||
| to_domain VARCHAR(64) NOT NULL, | |||
| event VARCHAR(64) DEFAULT 'presence' NOT NULL, | |||
| callid VARCHAR(255) NOT NULL, | |||
| time INTEGER NOT NULL, | |||
| result INTEGER NOT NULL, | |||
| sent_msg BLOB NOT NULL, | |||
| received_msg BLOB NOT NULL, | |||
| user_agent VARCHAR(255) DEFAULT '' NOT NULL, | |||
| CONSTRAINT active_watchers_active_watchers_log_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) | |||
| ); | |||
| EOF | |||
| } | |||
| sql_footer() { | |||
| cat << EOF | |||
| ALTER TABLE location_attrs MODIFY avalue varchar(512); | |||
| EOF | |||
| } | |||
| @ -0,0 +1,44 @@ | |||
| #!/bin/sh | |||
| sql_db_pre_setup() { | |||
| cat << EOF | |||
| BEGIN TRANSACTION; | |||
| EOF | |||
| } | |||
| sql_setup() { | |||
| cat << EOF | |||
| EOF | |||
| } | |||
| sql_header() { | |||
| cat << EOF | |||
| EOF | |||
| } | |||
| sql_extra_tables() { | |||
| cat << EOF | |||
| CREATE TABLE active_watchers_log ( | |||
| id SERIAL PRIMARY KEY NOT NULL, | |||
| presentity_uri VARCHAR(128) NOT NULL, | |||
| watcher_username VARCHAR(64) NOT NULL, | |||
| watcher_domain VARCHAR(64) NOT NULL, | |||
| to_user VARCHAR(64) NOT NULL, | |||
| to_domain VARCHAR(64) NOT NULL, | |||
| event VARCHAR(64) DEFAULT 'presence' NOT NULL, | |||
| callid VARCHAR(255) NOT NULL, | |||
| time INTEGER NOT NULL, | |||
| result INTEGER NOT NULL, | |||
| sent_msg BYTEA NOT NULL, | |||
| received_msg BYTEA NOT NULL, | |||
| user_agent VARCHAR(255) DEFAULT '' NOT NULL, | |||
| CONSTRAINT active_watchers_active_watchers_log_idx UNIQUE (presentity_uri, watcher_username, watcher_domain, event) | |||
| ); | |||
| EOF | |||
| } | |||
| sql_footer() { | |||
| cat << EOF | |||
| ALTER TABLE location_attrs ALTER COLUMN avalue TYPE varchar(512); | |||
| EOF | |||
| } | |||
| @ -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,"<state>")+7, instr(body,"</state>") - instr(body,"<state>") - 7) | |||
| when event = "presence" | |||
| then case when instr(body,"<dm:note>") == 0 | |||
| then replace(substr(body, instr(body,"<note>")+6, instr(body,"</note>") - instr(body,"<note>") - 6), " ", "") | |||
| else replace(substr(body, instr(body,"<dm:note>")+9, instr(body,"</dm:note>") - instr(body,"<dm:note>") - 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 | |||
| @ -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 | |||
| @ -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 | |||
| @ -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 | |||
| @ -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, "duid=")+5, instr(attrs, ";node")-instr(attrs, "duid=")-5) as integer) idx, | |||
| cast(substr(attrs, instr(attrs, "node=")+5) as varchar(50)) node | |||
| from dispatcher | |||
| @ -0,0 +1,970 @@ | |||
| ## NOTE: DO NOT CHANGE THIS FILE, EDIT local.cfg ## | |||
| ####### Flags ####### | |||
| #!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 | |||
| #!trydef FLAG_NETWORK_CLASSIFIED 18 | |||
| ####### Global Parameters ######### | |||
| fork = yes | |||
| children = CHILDREN | |||
| server_signature = no | |||
| server_header = "Server: Kazoo" | |||
| user_agent_header = "User-Agent: Kazoo" | |||
| shm_force_alloc = yes | |||
| mlock_pages = yes | |||
| phone2tel = 1 | |||
| max_while_loops = MAX_WHILE_LOOPS | |||
| 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_BUG | |||
| corelog = L_ERR | |||
| mem_summary = KZ_MEM_SUMMARY | |||
| log_stderror = no | |||
| log_facility = LOG_LOCAL0 | |||
| log_name="kamailio" | |||
| ####### Alias Parameters ######### | |||
| auto_aliases = yes | |||
| ####### Binding Parameters ######### | |||
| tos = IPTOS_LOWDELAY | |||
| ####### TCP Parameters ######### | |||
| tcp_children = TCP_CHILDREN | |||
| disable_tcp = no | |||
| tcp_max_connections = TCP_MAX_CONNECTIONS | |||
| tcp_connection_lifetime = TCP_CONNECTION_LIFETIME | |||
| tcp_accept_aliases = no | |||
| tcp_async = yes | |||
| 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 = 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 = TCP_SEND_TIMEOUT | |||
| tcp_wq_blk_size = 2100 | |||
| tcp_wq_max = 10485760 | |||
| ####### UDP Parameters ######### | |||
| udp4_raw = 0 | |||
| #udp4_raw_mtu = 800 | |||
| # # pmtu_discovery = no | |||
| #udp_mtu = 800 | |||
| # #udp_mtu_try_proto = TCP | |||
| ####### DNS Parameters ######### | |||
| dns = no | |||
| rev_dns = no | |||
| dns_try_ipv6 = no | |||
| use_dns_cache = on | |||
| dns_cache_del_nonexp = yes | |||
| dns_cache_flags = 1 | |||
| dns_cache_gc_interval = 120 | |||
| dns_cache_init = 0 | |||
| dns_cache_mem = 1000 | |||
| dns_cache_negative_ttl = 60 | |||
| dns_try_naptr = no | |||
| use_dns_failover = off | |||
| dns_srv_lb = off | |||
| ####### SCTP Parameters ######### | |||
| disable_sctp = yes | |||
| ####### multi homed ######### | |||
| mhomed=KZ_MULTI_HOMED | |||
| ######## Kamailio mqueue module ######## | |||
| loadmodule "mqueue.so" | |||
| ######## Kamailio outbound module ######## | |||
| loadmodule "outbound.so" | |||
| ######## Kamailio stun module ######## | |||
| loadmodule "stun.so" | |||
| ######## Kamailio path module ######## | |||
| loadmodule "path.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 ######## | |||
| loadmodule "uuid.so" | |||
| ######## Kamailio core extensions module ######## | |||
| loadmodule "kex.so" | |||
| ######## Transaction (stateful) module ######## | |||
| loadmodule "tm.so" | |||
| loadmodule "tmx.so" | |||
| modparam("tm", "auto_inv_100", 1) | |||
| 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) | |||
| ######## Stateless replier module ######## | |||
| loadmodule "sl.so" | |||
| ######## Record-Route and Route module ######## | |||
| loadmodule "rr.so" | |||
| 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" | |||
| modparam("maxfwd", "max_limit", 50) | |||
| ######## SIP utilities [requires sl] ######## | |||
| loadmodule "siputils.so" | |||
| ######## Text operations module ######## | |||
| 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") | |||
| ####### 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" | |||
| ######## UAC ######## | |||
| loadmodule "uac.so" | |||
| ######## AVP's ######## | |||
| loadmodule "avp.so" | |||
| loadmodule "avpops.so" | |||
| ######## UAC Redirection module ######## | |||
| loadmodule "uac_redirect.so" | |||
| #### json rpc #### | |||
| loadmodule "jsonrpcs.so" | |||
| ####### SQL OPS module ########## | |||
| loadmodule "sqlops.so" | |||
| modparam("sqlops","sqlcon", "cb=>KAZOO_DB_URL") | |||
| modparam("sqlops","sqlcon", "exec=>KAZOO_DB_URL") | |||
| ####### 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-MAJOR.cfg" | |||
| #!endif | |||
| #!ifdef REGISTRAR_ROLE | |||
| include_file "registrar-role.cfg" | |||
| #!endif | |||
| #!ifdef PRESENCE_ROLE | |||
| include_file "presence-role.cfg" | |||
| #!endif | |||
| #!ifdef DMQ_ROLE | |||
| include_file "dmq-role.cfg" | |||
| #!endif | |||
| #!ifdef MESSAGE_ROLE | |||
| include_file "message-role.cfg" | |||
| #!endif | |||
| #!ifdef NAT_TRAVERSAL_ROLE | |||
| include_file "nat-traversal-role.cfg" | |||
| #!endif | |||
| #!ifdef WEBSOCKETS_ROLE | |||
| include_file "websockets-role.cfg" | |||
| #!endif | |||
| #!ifdef TLS_ROLE | |||
| include_file "tls-role.cfg" | |||
| #!endif | |||
| #!ifdef ACCOUNTING_ROLE | |||
| include_file "accounting-role.cfg" | |||
| #!endif | |||
| #!ifdef ANTIFLOOD_ROLE | |||
| include_file "antiflood-role.cfg" | |||
| #!endif | |||
| #!ifdef TRAFFIC_FILTER_ROLE | |||
| include_file "traffic-filter-role.cfg" | |||
| #!endif | |||
| #!ifdef ACL_ROLE | |||
| include_file "acl-role.cfg" | |||
| #!endif | |||
| #!ifdef RATE_LIMITER_ROLE | |||
| include_file "rate-limiter-role.cfg" | |||
| #!endif | |||
| #!ifdef PUSHER_ROLE | |||
| include_file "pusher-role.cfg" | |||
| #!endif | |||
| #!ifdef RESPONDER_ROLE | |||
| include_file "responder-role.cfg" | |||
| #!endif | |||
| #!ifdef NODES_ROLE | |||
| include_file "nodes-role.cfg" | |||
| #!endif | |||
| #!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) | |||
| ###### local route ###### | |||
| socket_workers=5 | |||
| listen=tcp:127.0.0.1:5090 | |||
| ####### Routing Logic ######## | |||
| route | |||
| { | |||
| route(LOCAL_REQUEST); | |||
| route(SANITY_CHECK); | |||
| route(CHECK_RETRANS); | |||
| #!ifdef ANTIFLOOD_ROLE | |||
| route(ANTIFLOOD_LIMIT); | |||
| #!endif | |||
| #!ifdef TRAFFIC_FILTER_ROLE | |||
| route(FILTER_REQUEST); | |||
| #!endif | |||
| #!ifdef ACL_ROLE | |||
| route(ACL_CHECK); | |||
| #!endif | |||
| #!ifdef RATE_LIMITER_ROLE | |||
| route(DOS_PREVENTION); | |||
| #!endif | |||
| 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_if_exists("CUSTOM_START_ROUTES"); | |||
| route(HANDLE_NOTIFY); | |||
| #!ifdef AUTHORIZATION_ROLE | |||
| route(AUTHORIZATION_CHECK); | |||
| #!endif | |||
| #!ifdef MESSAGE_ROLE | |||
| route(HANDLE_MESSAGE); | |||
| #!else | |||
| if (is_method("MESSAGE")) { | |||
| sl_send_reply("405", "Method Not Allowed"); | |||
| exit; | |||
| } | |||
| #!endif | |||
| #!ifdef PRESENCE_ROLE | |||
| route(HANDLE_SUBSCRIBE); | |||
| route(HANDLE_PUBLISH); | |||
| #!endif | |||
| #!ifdef REGISTRAR_ROLE | |||
| route(HANDLE_REGISTER); | |||
| #!endif | |||
| #!ifdef DMQ_ROLE | |||
| route(HANDLE_DMQ); | |||
| #!endif | |||
| route(HANDLE_REFER); | |||
| route(HANDLE_IN_DIALOG_REQUESTS); | |||
| route(PREPARE_INITIAL_REQUESTS); | |||
| #!ifdef PUSHER_ROLE | |||
| route(PUSHER_ROUTE); | |||
| #!endif | |||
| #!ifdef RESPONDER_ROLE | |||
| if (isflagset(FLAG_INTERNALLY_SOURCED)) { | |||
| route(HANDLE_RESPOND); | |||
| } | |||
| #!endif | |||
| 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; | |||
| } | |||
| # 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] | |||
| { | |||
| # handle retransmissions | |||
| if (!is_method("ACK")) { | |||
| if(t_precheck_trans()) { | |||
| t_check_trans(); | |||
| exit; | |||
| } | |||
| t_check_trans(); | |||
| } | |||
| } | |||
| route[CLASSIFY_SOURCE] | |||
| { | |||
| #!ifdef DISPATCHER_ROLE | |||
| route(DISPATCHER_CLASSIFY_SOURCE); | |||
| #!endif | |||
| 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")) { | |||
| return; | |||
| } | |||
| 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 | |||
| sl_send_reply("200", "Rawr!!"); | |||
| #!ifdef KEEPALIVE_ROLE | |||
| route(KEEPALIVE_ON_OPTIONS); | |||
| #!endif | |||
| } | |||
| exit; | |||
| } | |||
| route[HANDLE_NOTIFY] | |||
| { | |||
| if (!is_method("NOTIFY")) return; | |||
| if (has_totag()) return; | |||
| if (isflagset(FLAG_INTERNALLY_SOURCED)) { | |||
| if (loose_route()) { | |||
| xlog("L_INFO", "$ci|log|Able to loose-route. Cool beans!\n"); | |||
| } | |||
| #!ifdef REGISTRAR_ROLE | |||
| if (registered("location")) { | |||
| lookup("location"); | |||
| xlog("L_INFO", "$ci|log|routing to $ruid\n"); | |||
| } | |||
| #!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; | |||
| } | |||
| 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!!"); | |||
| } | |||
| #!ifdef KEEPALIVE_ROLE | |||
| route(KEEPALIVE_ON_NOTIFY); | |||
| #!endif | |||
| } | |||
| exit; | |||
| } | |||
| 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(<sip:$Au>;created=true); | |||
| } | |||
| route(AUTH); | |||
| if($avp(is_registered) = "true") { | |||
| $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()) return; | |||
| if (is_method("INVITE")) { | |||
| setflag(FLAG_SESSION_PROGRESS); | |||
| record_route(); | |||
| } | |||
| if (loose_route()) { | |||
| #!ifdef NAT_TRAVERSAL_ROLE | |||
| if(!isdsturiset()) { | |||
| handle_ruri_alias(); | |||
| } | |||
| 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 | |||
| 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 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] | |||
| { | |||
| if (is_method("CANCEL")) { | |||
| if (t_check_trans()) { | |||
| route(RELAY); | |||
| } else { | |||
| sl_send_reply("481", "Call leg/transaction does not exist"); | |||
| } | |||
| exit(); | |||
| } else if (is_method("ACK")) { | |||
| if (t_check_trans()) { | |||
| route(RELAY); | |||
| } | |||
| exit(); | |||
| } | |||
| if (is_method("UPDATE")) { | |||
| xlog("L_WARN", "$ci|end|update outside dialog not allowed\n"); | |||
| send_reply("403", "Dialog does not exist"); | |||
| break; | |||
| } | |||
| if (is_method("BYE|PRACK")) { | |||
| xlog("L_WARN", "$ci|end|originating subsequent requests outside dialog not allowed\n"); | |||
| send_reply("403", "Dialog does not exist"); | |||
| break; | |||
| } | |||
| ## if (loose_route()) { | |||
| ## #!ifdef REGISTRAR_ROLE | |||
| ## $xavp(regcfg=>match_received) = $su; | |||
| ## if(registered("location", "$rz:$Au", 2) == 1) { | |||
| ## xlog("L_INFO", "$ci|log|allowing initial route-set for $Au\n"); | |||
| ## } else { | |||
| ## #!endif | |||
| ## xlog("L_WARN", "$ci|end|dropping initial request with route-set\n"); | |||
| ## sl_send_reply("403", "No pre-loaded routes"); | |||
| ## exit(); | |||
| ## #!ifdef REGISTRAR_ROLE | |||
| ## } | |||
| ## #!endif | |||
| ## } | |||
| record_route(); | |||
| } | |||
| route[SETUP] | |||
| { | |||
| #!ifdef DISPATCHER_ROLE | |||
| if (!isflagset(FLAG_INTERNALLY_SOURCED)) { | |||
| route(DISPATCHER_FIND_ROUTES); | |||
| } | |||
| #!endif | |||
| if (isflagset(FLAG_INTERNALLY_SOURCED)) { | |||
| route(ROUTE_TO_AOR); | |||
| forward(); | |||
| } | |||
| 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_INFO", "$ci|branch|new branch [$T_branch_idx] to $ru => $du\n"); | |||
| #!ifdef NAT_TRAVERSAL_ROLE | |||
| route(NAT_MANAGE); | |||
| #!endif | |||
| ### route(BRANCH_HEADERS); | |||
| route_if_exists("CUSTOM_BRANCH_HEADERS"); | |||
| } | |||
| route[RELAY] | |||
| { | |||
| if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE|NOTIFY|REFER")) { | |||
| if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH"); | |||
| } | |||
| if (isflagset(FLAG_INTERNALLY_SOURCED)) { | |||
| route(INTERNAL_TO_EXTERNAL_RELAY); | |||
| } else { | |||
| route(EXTERNAL_TO_INTERNAL_RELAY); | |||
| } | |||
| exit(); | |||
| } | |||
| route[INTERNAL_TO_EXTERNAL_RELAY] | |||
| { | |||
| #!ifdef ACCOUNTING_ROLE | |||
| if (is_method("INVITE")) { | |||
| setflag(FLAG_ACC); | |||
| setflag(FLAG_ACCFAILED); | |||
| } | |||
| #!endif | |||
| t_on_reply("EXTERNAL_REPLY"); | |||
| t_set_fr(0, $sel(cfg_get.kazoo.to_external_no_response_timer)); | |||
| t_relay(); | |||
| } | |||
| route[EXTERNAL_TO_INTERNAL_RELAY] | |||
| { | |||
| #!ifdef ACCOUNTING_ROLE | |||
| if (is_method("INVITE") && is_present_hf("Proxy-Authorization")) { | |||
| setflag(FLAG_ACC); | |||
| setflag(FLAG_ACCFAILED); | |||
| } | |||
| #!endif | |||
| t_on_reply("INTERNAL_REPLY"); | |||
| t_on_failure("INTERNAL_FAULT"); | |||
| t_set_fr(0, $sel(cfg_get.kazoo.to_internal_no_response_timer)); | |||
| t_relay(); | |||
| } | |||
| onreply_route[EXTERNAL_REPLY] | |||
| { | |||
| xlog("L_INFO", "$ci|log|external reply $T_reply_code $T_reply_reason\n"); | |||
| #!ifdef NAT_TRAVERSAL_ROLE | |||
| if(status=~"[12][0-9][0-9]") { | |||
| route(NAT_MANAGE); | |||
| } | |||
| #!endif | |||
| #!ifdef ACL_ROLE | |||
| setflag(FLAG_IS_REPLY); | |||
| route(ACL_CHECK); | |||
| #!endif | |||
| #!ifdef RATE_LIMITER_ROLE | |||
| setflag(FLAG_IS_REPLY); | |||
| route(DOS_PREVENTION); | |||
| #!endif | |||
| } | |||
| onreply_route[INTERNAL_REPLY] | |||
| { | |||
| # this route handles replies that are comming from our media server | |||
| 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 NAT_TRAVERSAL_ROLE | |||
| if(status=~"[12][0-9][0-9]") { | |||
| route(NAT_MANAGE); | |||
| } | |||
| #!endif | |||
| #!ifdef ACL_ROLE | |||
| setflag(FLAG_IS_REPLY); | |||
| route(ACL_CHECK); | |||
| #!endif | |||
| #!ifdef RATE_LIMITER_ROLE | |||
| setflag(FLAG_IS_REPLY); | |||
| route(DOS_PREVENTION); | |||
| #!endif | |||
| if (is_method("INVITE") && | |||
| !isflagset(FLAG_SESSION_PROGRESS) && | |||
| t_check_status("(180)|(183)|(200)") | |||
| ) { | |||
| xlog("L_INFO", "$ci|log|call setup, now ignoring abnormal termination\n"); | |||
| setflag(FLAG_SESSION_PROGRESS); | |||
| # clear the redirect | |||
| if ($avp(AVP_REDIRECT_KEY) != $null && $sht(redirects=>$avp(AVP_REDIRECT_KEY)) != $null) { | |||
| xlog("L_INFO", "$ci|log|removing redirect mapping $avp(AVP_REDIRECT_KEY)\n"); | |||
| $sht(redirects=>$avp(AVP_REDIRECT_KEY)) = $null; | |||
| } | |||
| } | |||
| if ($rs < 300) { | |||
| xlog("L_INFO", "$ci|pass|$T_req($si):$T_req($sp)\n"); | |||
| } | |||
| } | |||
| failure_route[INTERNAL_FAULT] | |||
| { | |||
| # this branch handles failures (>=300) to our media servers, | |||
| # which we can sometimes overcome by routing to another server | |||
| # if the failure cause was due to the transaction being | |||
| # cancelled then we are complete | |||
| if (t_is_canceled()) { | |||
| xlog("L_INFO", "$ci|log|transaction was cancelled\n"); | |||
| exit; | |||
| } | |||
| if (!is_method("INVITE") || has_totag()) { | |||
| xlog("L_INFO", "$ci|failure|internal reply $T_reply_code $T_reply_reason\n"); | |||
| xlog("L_INFO", "$ci|pass|$si:$sp\n"); | |||
| return; | |||
| } | |||
| # Handle redirects | |||
| if (t_check_status("302") && $T_rpl($hdr(X-Redirect-Server)) != $null) { | |||
| route(INTERNAL_REDIRECT); | |||
| } | |||
| remove_hf_re("^X-.*"); | |||
| # handle challenges replies from media server, we want to route to same media server | |||
| if (t_check_status("407")) { | |||
| xlog("L_INFO", "$ci|log|media $xavp(ds_dst=>uri) challenged the invite, creating redirect\n"); | |||
| $var(redirect) = @from.uri.user + "@" + @from.uri.host + "->" + @ruri.user + "@" + @ruri.host; | |||
| $sht(redirects=>$var(redirect)) = $xavp(ds_dst=>uri); | |||
| # change 6xx to 4xx | |||
| } else 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|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 ($T_reply_reason =~ "call barred") { | |||
| xlog("L_INFO", "$ci|failure|ignoring call barred\n"); | |||
| } else if (isflagset(FLAG_SESSION_PROGRESS)) { | |||
| xlog("L_INFO", "$ci|failure|ignoring failure after session progress\n"); | |||
| if (t_check_status("480")) { | |||
| xlog("L_INFO", "$ci|failure|overriding reply code 480 with $sel(cfg_get.kazoo.override_media_reply_480)\n"); | |||
| send_reply("$(sel(cfg_get.kazoo.override_media_reply_480){s.int})", "Endpoint Not Available"); | |||
| } | |||
| } 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)|(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|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|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|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"); | |||
| } | |||
| route[INTERNAL_REDIRECT] | |||
| { | |||
| xlog("L_INFO", "$ci|log|redirect to $T_rpl($hdr(X-Redirect-Server))\n"); | |||
| $du = $T_rpl($hdr(X-Redirect-Server)); | |||
| $xavp(ds_dst=>uri) = $du; | |||
| t_on_branch("MANAGE_BRANCH"); | |||
| t_on_reply("INTERNAL_REPLY"); | |||
| t_on_failure("INTERNAL_FAULT"); | |||
| t_set_fr(0, 1000); | |||
| t_relay(); | |||
| exit(); | |||
| } | |||
| onsend_route { | |||
| if (isflagset(FLAG_ASSOCIATE_USER)) { | |||
| $var(user_source) = $(ct{tobody.user}) + "@" + $si + ":" + $sp; | |||
| xlog("L_INFO", "$ci|log|associate traffic from $var(user_source) with media server sip:$sndto(ip):$sndto(port)\n"); | |||
| $sht(associations=>$var(user_source))= "sip:" + $sndto(ip) + ":" + $sndto(port); | |||
| } | |||
| #!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"); | |||
| } | |||
| } | |||
| route[ROUTE_TO_AOR] | |||
| { | |||
| if ($hdr(X-KAZOO-AOR) == $null) { | |||
| return; | |||
| } | |||
| 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){ | |||
| ## handle_ruri_alias(); | |||
| reg_fetch_contacts("location", "$hdr(X-KAZOO-AOR)", "callee"); | |||
| $du = $(ulc(callee=>received)); | |||
| xlog("L_INFO", "$ci|end|routing to contact $ru via $du\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)); | |||
| $bf = $bf | $(ulc(callee=>cflags)); | |||
| 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; | |||
| } | |||
| } | |||
| event_route[tm:local-request] | |||
| { | |||
| 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 | |||
| } | |||
| 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; | |||
| } | |||
| #!import_file "custom-routes.cfg" | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -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 | |||
| @ -0,0 +1,95 @@ | |||
| ## NOTE: DO NOT CHANGE THIS FILE, EDIT local.cfg ## | |||
| ####### defs ######## | |||
| #!ifndef DEFAULTS_INCLUDED | |||
| #!define DEFAULTS_INCLUDED | |||
| #!substdef "!MAJOR!$(version(num){re.subst,/^(([^\.])*\.([^\.])*)\..*/\1/})!g" | |||
| #!trydef EXTERNAL_TO_INTERNAL_NO_RESPONSE_TIMER 3500 | |||
| #!trydef INTERNAL_TO_EXTERNAL_NO_RESPONSE_TIMER 3500 | |||
| #!trydef OVERRIDE_MEDIA_REPLY_480 603 | |||
| kazoo.to_internal_no_response_timer = EXTERNAL_TO_INTERNAL_NO_RESPONSE_TIMER descr "external to internal no response timer" | |||
| kazoo.to_external_no_response_timer = INTERNAL_TO_EXTERNAL_NO_RESPONSE_TIMER descr "internal to external no response timer" | |||
| kazoo.override_media_reply_480 = OVERRIDE_MEDIA_REPLY_480 descr "sip code to send upstream when media returns 480 with session in progress" | |||
| #!ifndef MY_HOSTNAME | |||
| #!substdef "!MY_HOSTNAME!$HN(f)!g" | |||
| #!endif | |||
| #!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 | |||
| #!endif | |||
| #!ifndef WEBSOCKET_NO_ORIGIN_RESTRICTION | |||
| #!ifndef MY_WEBSOCKET_DOMAIN | |||
| #!substdef "!MY_WEBSOCKET_DOMAIN!$HN(d)!g" | |||
| #!endif | |||
| #!endif | |||
| #!trydef KAZOO_LOG_LEVEL L_INFO | |||
| #!trydef PV_BUFFER_SIZE 16384 | |||
| #!trydef PV_BUFFER_SLOTS 30 | |||
| #!trydef KZ_MEM_SUMMARY 0 | |||
| #!trydef KZ_DB_MODULE kazoo | |||
| #!substdef "!KAMAILIO_DBMS!$def(KZ_DB_MODULE)!g" | |||
| #!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 | |||
| #### tcp parameters ## | |||
| #!trydef CHILDREN 25 | |||
| #!trydef TCP_CHILDREN 25 | |||
| #!trydef TCP_MAX_CONNECTIONS 4096 | |||
| #!trydef TCP_CONNECTION_LIFETIME 3605 | |||
| #!trydef TCP_CONNECTION_TIMEOUT 5 | |||
| #!trydef TCP_KEEP_ALIVE yes | |||
| #!trydef TCP_KEEP_COUNT 3 | |||
| #!trydef TCP_KEEP_IDLE 30 | |||
| #!trydef TCP_KEEP_INTERVAL 30 | |||
| #!trydef TCP_SEND_TIMEOUT 3 | |||
| #!include_file "defs-amqp.cfg" | |||
| #!ifndef MEDIA_SERVERS_HASH_SIZE | |||
| #!substdef "!MEDIA_SERVERS_HASH_SIZE!256!g" | |||
| #!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 | |||
| #!endif | |||
| #!ifndef KZ_DISABLE_TLS_REGISTRAR_PORT | |||
| #!trydef KZ_TLS_REGISTRAR_PORT 7000 | |||
| #!endif | |||
| #!trydef KZ_MULTI_HOMED 0 | |||
| #!endif | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -0,0 +1,306 @@ | |||
| ### DISPATCHER ROLE #### | |||
| #!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_HASH_SIZE 8 | |||
| #!trydef KZ_DISPATCHER_ADD_FLAGS 9 | |||
| #!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 | |||
| #!trydef KZ_DISPATCHER_MAX_RETRIES 2 | |||
| #!trydef KZ_DISPATCHER_ROUTE_ASSOCIATED_MEDIA 1 | |||
| #!trydef KZ_DISPATCHER_CLASSIFY_FLAGS 2 | |||
| 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" | |||
| kazoo.dispatcher_max_retries = KZ_DISPATCHER_MAX_RETRIES descr "max number of retries for media servers" | |||
| kazoo.dispatcher_route_to_associated_media = KZ_DISPATCHER_ROUTE_ASSOCIATED_MEDIA descr "routes to associated media for atxfer" | |||
| kazoo.dispatcher_classify_flags = KZ_DISPATCHER_CLASSIFY_FLAGS descr "dispatch classifier flags" | |||
| ####### Dispatcher module ######## | |||
| loadmodule "dispatcher.so" | |||
| modparam("dispatcher", "db_url", "KAZOO_DB_URL") | |||
| modparam("dispatcher", "flags", 2) | |||
| modparam("dispatcher", "use_default", 0) | |||
| modparam("dispatcher", "force_dst", 1) | |||
| modparam("dispatcher", "dst_avp", "$avp(ds_dst)") | |||
| modparam("dispatcher", "attrs_avp", "$avp(ds_attrs)") | |||
| modparam("dispatcher", "grp_avp", "$avp(ds_grp)") | |||
| modparam("dispatcher", "cnt_avp", "$avp(ds_cnt)") | |||
| modparam("dispatcher", "hash_pvar", "$avp(ds_grp)") | |||
| modparam("dispatcher", "ds_hash_size", KZ_DISPATCHER_HASH_SIZE) | |||
| modparam("dispatcher", "setid_pvname", "$var(setid)") | |||
| modparam("dispatcher", "attrs_pvname", "$var(attrs)") | |||
| modparam("dispatcher", "ds_ping_method", "OPTIONS") | |||
| modparam("dispatcher", "ds_ping_interval", 10) | |||
| modparam("dispatcher", "ds_probing_threshold", 3) | |||
| 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") | |||
| #!import_file "dispatcher-network-params.cfg" | |||
| ## Dispatcher Groups: | |||
| ## 1 - Primary media servers | |||
| ## 2 - Backup media servers | |||
| ## 3 - Alternate media server IPs (used only for classification) | |||
| ## 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("$var(log_request_level)", "$ci|log|original R-URI ($ou) is this proxy, treating as external sources\n"); | |||
| } else { | |||
| $var(classify_dispatcher_flag) = $(sel(cfg_get.kazoo.dispatcher_classify_flags){s.int}); | |||
| if (ds_is_from_list(KZ_DISPATCHER_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_CLASSIFY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET1_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET1_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET2_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET2_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP, "$var(classify_dispatcher_flag)")) { | |||
| xlog("$var(log_request_level)", "$ci|log|originated from internal sources\n"); | |||
| setflag(FLAG_INTERNALLY_SOURCED); | |||
| } else { | |||
| xlog("$var(log_request_level)", "$ci|log|originated from external sources\n"); | |||
| } | |||
| } | |||
| } | |||
| # Take the routes from dispatcher - hash over callid | |||
| # If prefered route defined, reorder the destionations | |||
| route[DISPATCHER_FIND_ROUTES] | |||
| { | |||
| $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) = 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) = KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP; | |||
| $var(ds_backup_group) = KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP; | |||
| add_path(); | |||
| } | |||
| #!endif | |||
| #!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)", "$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)", "$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; | |||
| } else { | |||
| $var(ds_group) = $var(ds_backup_group); | |||
| } | |||
| } | |||
| $var(user_source) = $(ct{tobody.user}) + "@" + $si + ":" + $sp; | |||
| if ($sht(associations=>$var(user_source)) != $null) { | |||
| if($sel(cfg_get.kazoo.dispatcher_route_to_associated_media) == 1) { | |||
| $var(prefered_route) = $sht(associations=>$var(user_source)); | |||
| xlog("L_INFO", "$ci|route|found association for contact uri $var(user_source)\n"); | |||
| route(DISPATCHER_PREFERRED_ROUTE); | |||
| } | |||
| $sht(associations=>$var(user_source)) = $null; | |||
| } | |||
| $avp(ds_retries) = 0; | |||
| } | |||
| route[DISPATCHER_PREFERRED_ROUTE] | |||
| { | |||
| ###### | |||
| # check if the preferred route is active | |||
| ###### | |||
| if(!ds_is_from_list(-1, 6, "$var(prefered_route)")) { | |||
| xlog("L_INFO", "$ci|log|associated media server $var(prefered_route) is inactive, moving to $avp(ds_dst)\n"); | |||
| return -1; | |||
| } | |||
| xlog("L_INFO", "$ci|log|re-ordering the dispatcher list to maintain association with $var(prefered_route)\n"); | |||
| ###### | |||
| # filters current list from prefered route | |||
| # * saves the current list to temp avp removing the preferred route if it exists | |||
| # * resets current list | |||
| # * copies the temp back to list | |||
| # sets the prefered at top | |||
| # sets $du (destination) to prefered | |||
| ###### | |||
| $var(i) = 0; | |||
| while($(avp(ds_dst)[$var(i)]) != $null) { | |||
| if($(avp(ds_dst)[$var(i)]) != $var(prefered_route)) { | |||
| $avp(tmp_ds_dst) = $(avp(ds_dst)[$var(i)]); | |||
| } | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| $(avp(ds_dst)[*]) = $null; | |||
| $var(i) = 0; | |||
| while($(avp(tmp_ds_dst)[$var(i)]) != $null) { | |||
| $avp(ds_dst) = $(avp(tmp_ds_dst)[$var(i)]); | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| $avp(ds_dst) = $var(prefered_route); | |||
| $du = $var(prefered_route); | |||
| $(avp(tmp_ds_dst)[*]) = $null; | |||
| return 1; | |||
| } | |||
| route[DISPATCHER_NEXT_ROUTE] | |||
| { | |||
| if($avp(ds_retries) >= $sel(cfg_get.kazoo.dispatcher_max_retries)) return; | |||
| $avp(ds_retries) = $avp(ds_retries) + 1; | |||
| $var(remaining) = $(sel(cfg_get.kazoo.dispatcher_max_retries){s.int}) - $avp(ds_retries); | |||
| if(ds_next_dst()) { | |||
| xlog("L_INFO", "$ci|log|remaining failed retry attempts: $var(remaining)\n"); | |||
| xlog("L_INFO", "$ci|log|routing call to next media server $du\n"); | |||
| setflag(FLAG_SKIP_NAT_CORRECTION); | |||
| # reset the final reply timer | |||
| $avp(final_reply_timer) = 3; | |||
| # relay the request to the new media server | |||
| route(RELAY); | |||
| exit(); | |||
| } | |||
| } | |||
| event_route[dispatcher:dst-down] | |||
| { | |||
| xlog("L_WARNING", "Destination down: $ru\n"); | |||
| } | |||
| event_route[dispatcher:dst-up] | |||
| { | |||
| xlog("L_NOTICE", "Destination up: $ru\n"); | |||
| } | |||
| route[DISPATCHER_CHECK_MEDIA_SERVER] | |||
| { | |||
| $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);duid=$(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) { | |||
| $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(attrs) = $_s(zone=$var(Zone);profile=$var(MediaProfile);duid=$(var(MediaUrl){s.corehash, MEDIA_SERVERS_HASH_SIZE});node=$var(MediaName)); | |||
| $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] | |||
| { | |||
| jsonrpc_exec('{"jsonrpc": "2.0", "method": "dispatcher.list", "id": 1}'); | |||
| $var(Sets) = $(jsonrpl(body){kz.json, result.NRSETS}); | |||
| $var(i) = 0; | |||
| $var(ds_groups_json)=""; | |||
| $var(Sep1) = ""; | |||
| while($var(i) < $var(Sets)) { | |||
| $var(Set) = $(jsonrpl(body){kz.json, result.RECORDS[$var(i)].SET}); | |||
| $var(SetCount) = $(var(Set){kz.json.count,TARGETS}); | |||
| $var(Sep2)=""; | |||
| $var(ds_group_json)=""; | |||
| $var(c) = 0; | |||
| while($var(c) < $var(SetCount)) { | |||
| $var(Dest) = $(var(Set){kz.json,TARGETS[$var(c)].DEST}); | |||
| $var(record) = $_s("$(var(Dest){kz.json,URI})" : {"destination" : "$(var(Dest){kz.json,URI})", "flags" : "$(var(Dest){kz.json,FLAGS})", "priority" : $(var(Dest){kz.json,PRIORITY}), "attrs" : "$(var(Dest){kz.json,ATTRS.BODY})"}); | |||
| $var(ds_group_json) = $var(ds_group_json) + $var(Sep2) + $var(record); | |||
| $var(Sep2) = ","; | |||
| $var(c) = $var(c) + 1; | |||
| } | |||
| $var(ds_groups_json) = $var(ds_groups_json) + $var(Sep1) + $_s("$(var(Set){kz.json,ID})" : { $var(ds_group_json) }); | |||
| $var(Sep1)=", "; | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| } | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -0,0 +1,309 @@ | |||
| ### DISPATCHER ROLE #### | |||
| #!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_HASH_SIZE 8 | |||
| #!trydef KZ_DISPATCHER_ADD_FLAGS 9 | |||
| #!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 | |||
| #!trydef KZ_DISPATCHER_MAX_RETRIES 2 | |||
| #!trydef KZ_DISPATCHER_ROUTE_ASSOCIATED_MEDIA 1 | |||
| #!trydef KZ_DISPATCHER_CLASSIFY_FLAGS 2 | |||
| #!trydef KZ_DISPATCHER_PRINT_ROUTES 1 | |||
| 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" | |||
| kazoo.dispatcher_max_retries = KZ_DISPATCHER_MAX_RETRIES descr "max number of retries for media servers" | |||
| kazoo.dispatcher_route_to_associated_media = KZ_DISPATCHER_ROUTE_ASSOCIATED_MEDIA descr "routes to associated media for atxfer" | |||
| kazoo.dispatcher_classify_flags = KZ_DISPATCHER_CLASSIFY_FLAGS descr "dispatch classifier flags" | |||
| kazoo.dispatcher_print_routes = KZ_DISPATCHER_PRINT_ROUTES descr "should we log the selected routes" | |||
| ####### Dispatcher module ######## | |||
| loadmodule "dispatcher.so" | |||
| modparam("dispatcher", "db_url", "KAZOO_DB_URL") | |||
| modparam("dispatcher", "flags", 2) | |||
| modparam("dispatcher", "use_default", 0) | |||
| modparam("dispatcher", "force_dst", 1) | |||
| modparam("dispatcher", "hash_pvar", "$avp(ds_grp)") | |||
| modparam("dispatcher", "setid_pvname", "$var(setid)") | |||
| modparam("dispatcher", "attrs_pvname", "$var(attrs)") | |||
| modparam("dispatcher", "ds_ping_method", "OPTIONS") | |||
| modparam("dispatcher", "ds_ping_interval", 10) | |||
| modparam("dispatcher", "ds_probing_threshold", 3) | |||
| 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") | |||
| modparam("dispatcher", "xavp_dst", "ds_dst") | |||
| modparam("dispatcher", "xavp_ctx", "ds_ctx") | |||
| modparam("dispatcher", "ds_hash_size", KZ_DISPATCHER_HASH_SIZE) | |||
| ## Dispatcher Groups: | |||
| ## 1 - Primary media servers | |||
| ## 2 - Backup media servers | |||
| ## 3 - Alternate media server IPs (used only for classification) | |||
| ## 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] | |||
| { | |||
| route_if_exists("DISPATCHER_CUSTOM_NETWORK_CLASSIFY"); | |||
| if (!isflagset(FLAG_NETWORK_CLASSIFIED)) { | |||
| if (is_myself("$ou")) { | |||
| xlog("$var(log_request_level)", "$ci|log|original R-URI ($ou) is this proxy, treating as external sources\n"); | |||
| } else { | |||
| $var(classify_dispatcher_flag) = $(sel(cfg_get.kazoo.dispatcher_classify_flags){s.int}); | |||
| if (ds_is_from_list(KZ_DISPATCHER_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_CLASSIFY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET1_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET1_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET2_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET2_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP, "$var(classify_dispatcher_flag)")) { | |||
| xlog("$var(log_request_level)", "$ci|log|originated from internal sources\n"); | |||
| setflag(FLAG_INTERNALLY_SOURCED); | |||
| } else { | |||
| xlog("$var(log_request_level)", "$ci|log|originated from external sources\n"); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| # Take the routes from dispatcher - hash over callid | |||
| # If prefered route defined, reorder the destionations | |||
| route[DISPATCHER_FIND_ROUTES] | |||
| { | |||
| $var(ds_primary_group) = $(sel(cfg_get.kazoo.dispatcher_primary_group){s.int}); | |||
| $var(ds_backup_group) = $(sel(cfg_get.kazoo.dispatcher_secondary_group){s.int}); | |||
| #!ifndef PRESENCE_ROLE | |||
| if (is_method("SUBSCRIBE")) { | |||
| $var(ds_primary_group) = KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP; | |||
| $var(ds_backup_group) = KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP; | |||
| } | |||
| #!endif | |||
| #!ifndef REGISTRAR_ROLE | |||
| if (is_method("REGISTER")) { | |||
| $var(ds_primary_group) = KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP; | |||
| $var(ds_backup_group) = KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP; | |||
| } | |||
| #!endif | |||
| #!ifdef PRESENCE_ROLE | |||
| route(PRESENCE_FAST_PICKUP_ATTEMPT); | |||
| #!endif | |||
| route_if_exists("DISPATCHER_CUSTOM_SET_GROUPS"); | |||
| $var(ds_group) = $var(ds_primary_group); | |||
| $var(ds_alg) = $sel(cfg_get.kazoo.dispatcher_algorithm); | |||
| if(ds_list_exists("$var(ds_backup_group)")) { | |||
| $var(ds_rule) = $_s($var(ds_primary_group)=$var(ds_alg);$var(ds_backup_group)=$var(ds_alg)); | |||
| } else { | |||
| $var(ds_rule) = $_s($var(ds_primary_group)=$var(ds_alg)); | |||
| } | |||
| ds_select_routes("$var(ds_rule)", "2"); | |||
| if ($xavp(ds_ctx=>cnt) == 0) { | |||
| xlog("L_WARN", "$ci|end|no servers available in primary or backup group\n"); | |||
| sl_send_reply("480", "All servers busy"); | |||
| exit; | |||
| } | |||
| route(PRINT_ROUTES); | |||
| $var(user_source) = $(ct{tobody.user}) + "@" + $si + ":" + $sp; | |||
| $var(redirect) = @from.uri.user + "@" + @from.uri.host + "->" | |||
| + @ruri.user + "@" + @ruri.host; | |||
| if ($sht(redirects=>$var(redirect)) != $null) { | |||
| $var(prefered_route) = $sht(redirects=>$var(redirect)); | |||
| xlog("L_INFO", "$ci|route|found redirect for $var(redirect) to $var(prefered_route)\n"); | |||
| $avp(AVP_REDIRECT_KEY) = $var(redirect); | |||
| route(DISPATCHER_PREFERRED_ROUTE); | |||
| } else if ($sht(associations=>$var(user_source)) != $null) { | |||
| if($sel(cfg_get.kazoo.dispatcher_route_to_associated_media) == 1) { | |||
| $var(prefered_route) = $sht(associations=>$var(user_source)); | |||
| xlog("L_INFO", "$ci|route|found association for contact uri $var(user_source)\n"); | |||
| route(DISPATCHER_PREFERRED_ROUTE); | |||
| } | |||
| $sht(associations=>$var(user_source)) = $null; | |||
| } | |||
| $avp(ds_group) = $xavp(ds_dst=>grp); | |||
| $avp(ds_retries) = 0; | |||
| ds_set_dst(); | |||
| } | |||
| route[DISPATCHER_PREFERRED_ROUTE] | |||
| { | |||
| if(!ds_is_from_list(-1, 6, "$var(prefered_route)")) { | |||
| xlog("L_INFO", "$ci|log|associated media server $var(prefered_route) is inactive, moving to $xavp(ds_dst=>uri)\n"); | |||
| return -1; | |||
| } | |||
| xlog("L_INFO", "$ci|log|re-ordering the dispatcher list to maintain association with $var(prefered_route)\n"); | |||
| $var(i) = $xavp(ds_ctx=>cnt) - 1; | |||
| while($var(i) >= 0) { | |||
| if($xavp(ds_dst[$var(i)]=>uri) == $var(prefered_route)) { | |||
| $xavp(ds_dst[$var(i)]) = $null; | |||
| } | |||
| $var(i) = $var(i) - 1; | |||
| } | |||
| $xavp(ds_dst=>uri) = $var(prefered_route); | |||
| $xavp(ds_dst[0]=>grp) = $var(setid); | |||
| $xavp(ds_dst[0]=>attrs) = $var(attrs); | |||
| return 1; | |||
| } | |||
| route[PRINT_ROUTES] | |||
| { | |||
| if($sel(cfg_get.kazoo.dispatcher_print_routes) == 0) return; | |||
| $var(i) = 0; | |||
| while($xavp(ds_dst[$var(i)]=>uri) != $null) { | |||
| xlog("L_INFO", "$ci|route|group $xavp(ds_dst[$var(i)]=>grp) => $xavp(ds_dst[$var(i)]=>uri) => zone $(xavp(ds_dst[$var(i)]=>attrs){param.value,zone})\n"); | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| } | |||
| #!import_file "dispatcher-next-route.cfg" | |||
| #!ifndef CUSTOM_DISPATCHER_NEXT_ROUTE | |||
| # Try next destinations in failure route | |||
| route[DISPATCHER_NEXT_ROUTE] | |||
| { | |||
| if($avp(ds_retries) >= $sel(cfg_get.kazoo.dispatcher_max_retries)) return; | |||
| $avp(ds_retries) = $avp(ds_retries) + 1; | |||
| if(ds_next_dst()) { | |||
| xlog("L_INFO", "$ci|log|routing call to next media server $du\n"); | |||
| setflag(FLAG_SKIP_NAT_CORRECTION); | |||
| # relay the request to the new media server | |||
| route(RELAY); | |||
| exit; | |||
| } | |||
| } | |||
| #!endif | |||
| event_route[dispatcher:dst-down] | |||
| { | |||
| xlog("L_WARNING", "Destination down: $ru , $xavp(ds_dst=>uri), $xavp(ds_dst=>grp), $xavp(ds_dst=>attrs)\n"); | |||
| } | |||
| event_route[dispatcher:dst-up] | |||
| { | |||
| xlog("L_WARNING", "Destination up: $ru , $xavp(ds_dst=>uri), $xavp(ds_dst=>grp), $xavp(ds_dst=>attrs)\n"); | |||
| } | |||
| route[DISPATCHER_CHECK_MEDIA_SERVER] | |||
| { | |||
| $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);duid=$(var(MediaUrl){s.corehash, MEDIA_SERVERS_HASH_SIZE});node=$var(MediaName)); | |||
| route_if_exists("DISPATCHER_CUSTOM_MEDIA_CHECK"); | |||
| sql_query("exec", "KZQ_CHECK_MEDIA_SERVER_INSERT"); | |||
| if($sqlrows(exec) > 0) { | |||
| $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(attrs) = $_s(zone=$var(Zone);profile=$var(MediaProfile);duid=$(var(MediaUrl){s.corehash, MEDIA_SERVERS_HASH_SIZE});node=$var(MediaName)); | |||
| $var(SetId) = $sel(cfg_get.kazoo.dispatcher_add_secondary_ip_group); | |||
| route_if_exists("DISPATCHER_CUSTOM_MEDIA_CHECK"); | |||
| 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_WARNING", "reloading dispatcher table\n"); | |||
| ds_reload(); | |||
| }; | |||
| $shv(dispatcher_reload) = 0; | |||
| } | |||
| route[DISPATCHER_STATUS] | |||
| { | |||
| jsonrpc_exec('{"jsonrpc": "2.0", "method": "dispatcher.list", "id": 1}'); | |||
| $var(Sets) = $(jsonrpl(body){kz.json, result.NRSETS}); | |||
| $var(i) = 0; | |||
| $var(ds_groups_json)=""; | |||
| $var(Sep1) = ""; | |||
| while($var(i) < $var(Sets)) { | |||
| $var(Set) = $(jsonrpl(body){kz.json, result.RECORDS[$var(i)].SET}); | |||
| $var(SetCount) = $(var(Set){kz.json.count,TARGETS}); | |||
| $var(Sep2)=""; | |||
| $var(ds_group_json)=""; | |||
| $var(c) = 0; | |||
| while($var(c) < $var(SetCount)) { | |||
| $var(Dest) = $(var(Set){kz.json,TARGETS[$var(c)].DEST}); | |||
| $var(record) = $_s("$(var(Dest){kz.json,URI})" : {"destination" : "$(var(Dest){kz.json,URI})", "flags" : "$(var(Dest){kz.json,FLAGS})", "priority" : $(var(Dest){kz.json,PRIORITY}), "attrs" : "$(var(Dest){kz.json,ATTRS.BODY})"}); | |||
| $var(ds_group_json) = $var(ds_group_json) + $var(Sep2) + $var(record); | |||
| $var(Sep2) = ","; | |||
| $var(c) = $var(c) + 1; | |||
| } | |||
| $var(ds_groups_json) = $var(ds_groups_json) + $var(Sep1) + $_s("$(var(Set){kz.json,ID})" : { $var(ds_group_json) }); | |||
| $var(Sep1)=", "; | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| } | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -0,0 +1,309 @@ | |||
| ### DISPATCHER ROLE #### | |||
| #!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_HASH_SIZE 8 | |||
| #!trydef KZ_DISPATCHER_ADD_FLAGS 9 | |||
| #!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 | |||
| #!trydef KZ_DISPATCHER_MAX_RETRIES 2 | |||
| #!trydef KZ_DISPATCHER_ROUTE_ASSOCIATED_MEDIA 1 | |||
| #!trydef KZ_DISPATCHER_CLASSIFY_FLAGS 2 | |||
| #!trydef KZ_DISPATCHER_PRINT_ROUTES 1 | |||
| 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" | |||
| kazoo.dispatcher_max_retries = KZ_DISPATCHER_MAX_RETRIES descr "max number of retries for media servers" | |||
| kazoo.dispatcher_route_to_associated_media = KZ_DISPATCHER_ROUTE_ASSOCIATED_MEDIA descr "routes to associated media for atxfer" | |||
| kazoo.dispatcher_classify_flags = KZ_DISPATCHER_CLASSIFY_FLAGS descr "dispatch classifier flags" | |||
| kazoo.dispatcher_print_routes = KZ_DISPATCHER_PRINT_ROUTES descr "should we log the selected routes" | |||
| ####### Dispatcher module ######## | |||
| loadmodule "dispatcher.so" | |||
| modparam("dispatcher", "db_url", "KAZOO_DB_URL") | |||
| modparam("dispatcher", "flags", 2) | |||
| modparam("dispatcher", "use_default", 0) | |||
| modparam("dispatcher", "force_dst", 1) | |||
| modparam("dispatcher", "hash_pvar", "$avp(ds_grp)") | |||
| modparam("dispatcher", "setid_pvname", "$var(setid)") | |||
| modparam("dispatcher", "attrs_pvname", "$var(attrs)") | |||
| modparam("dispatcher", "ds_ping_method", "OPTIONS") | |||
| modparam("dispatcher", "ds_ping_interval", 10) | |||
| modparam("dispatcher", "ds_probing_threshold", 3) | |||
| 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") | |||
| modparam("dispatcher", "xavp_dst", "ds_dst") | |||
| modparam("dispatcher", "xavp_ctx", "ds_ctx") | |||
| modparam("dispatcher", "ds_hash_size", KZ_DISPATCHER_HASH_SIZE) | |||
| ## Dispatcher Groups: | |||
| ## 1 - Primary media servers | |||
| ## 2 - Backup media servers | |||
| ## 3 - Alternate media server IPs (used only for classification) | |||
| ## 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] | |||
| { | |||
| route_if_exists("DISPATCHER_CUSTOM_NETWORK_CLASSIFY"); | |||
| if (!isflagset(FLAG_NETWORK_CLASSIFIED)) { | |||
| if (is_myself("$ou")) { | |||
| xlog("$var(log_request_level)", "$ci|log|original R-URI ($ou) is this proxy, treating as external sources\n"); | |||
| } else { | |||
| $var(classify_dispatcher_flag) = $(sel(cfg_get.kazoo.dispatcher_classify_flags){s.int}); | |||
| if (ds_is_from_list(KZ_DISPATCHER_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_CLASSIFY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET1_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET1_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET2_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET2_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP, "$var(classify_dispatcher_flag)")) { | |||
| xlog("$var(log_request_level)", "$ci|log|originated from internal sources\n"); | |||
| setflag(FLAG_INTERNALLY_SOURCED); | |||
| } else { | |||
| xlog("$var(log_request_level)", "$ci|log|originated from external sources\n"); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| # Take the routes from dispatcher - hash over callid | |||
| # If prefered route defined, reorder the destionations | |||
| route[DISPATCHER_FIND_ROUTES] | |||
| { | |||
| $var(ds_primary_group) = $(sel(cfg_get.kazoo.dispatcher_primary_group){s.int}); | |||
| $var(ds_backup_group) = $(sel(cfg_get.kazoo.dispatcher_secondary_group){s.int}); | |||
| #!ifndef PRESENCE_ROLE | |||
| if (is_method("SUBSCRIBE")) { | |||
| $var(ds_primary_group) = KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP; | |||
| $var(ds_backup_group) = KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP; | |||
| } | |||
| #!endif | |||
| #!ifndef REGISTRAR_ROLE | |||
| if (is_method("REGISTER")) { | |||
| $var(ds_primary_group) = KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP; | |||
| $var(ds_backup_group) = KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP; | |||
| } | |||
| #!endif | |||
| #!ifdef PRESENCE_ROLE | |||
| route(PRESENCE_FAST_PICKUP_ATTEMPT); | |||
| #!endif | |||
| route_if_exists("DISPATCHER_CUSTOM_SET_GROUPS"); | |||
| $var(ds_group) = $var(ds_primary_group); | |||
| $var(ds_alg) = $sel(cfg_get.kazoo.dispatcher_algorithm); | |||
| if(ds_list_exists("$var(ds_backup_group)")) { | |||
| $var(ds_rule) = $_s($var(ds_primary_group)=$var(ds_alg);$var(ds_backup_group)=$var(ds_alg)); | |||
| } else { | |||
| $var(ds_rule) = $_s($var(ds_primary_group)=$var(ds_alg)); | |||
| } | |||
| ds_select_routes("$var(ds_rule)", "2"); | |||
| if ($xavp(ds_ctx=>cnt) == 0) { | |||
| xlog("L_WARN", "$ci|end|no servers available in primary or backup group\n"); | |||
| sl_send_reply("480", "All servers busy"); | |||
| exit; | |||
| } | |||
| route(PRINT_ROUTES); | |||
| $var(user_source) = $(ct{tobody.user}) + "@" + $si + ":" + $sp; | |||
| $var(redirect) = @from.uri.user + "@" + @from.uri.host + "->" | |||
| + @ruri.user + "@" + @ruri.host; | |||
| if ($sht(redirects=>$var(redirect)) != $null) { | |||
| $var(prefered_route) = $sht(redirects=>$var(redirect)); | |||
| xlog("L_INFO", "$ci|route|found redirect for $var(redirect) to $var(prefered_route)\n"); | |||
| $avp(AVP_REDIRECT_KEY) = $var(redirect); | |||
| route(DISPATCHER_PREFERRED_ROUTE); | |||
| } else if ($sht(associations=>$var(user_source)) != $null) { | |||
| if($sel(cfg_get.kazoo.dispatcher_route_to_associated_media) == 1) { | |||
| $var(prefered_route) = $sht(associations=>$var(user_source)); | |||
| xlog("L_INFO", "$ci|route|found association for contact uri $var(user_source)\n"); | |||
| route(DISPATCHER_PREFERRED_ROUTE); | |||
| } | |||
| $sht(associations=>$var(user_source)) = $null; | |||
| } | |||
| $avp(ds_group) = $xavp(ds_dst=>grp); | |||
| $avp(ds_retries) = 0; | |||
| ds_set_dst(); | |||
| } | |||
| route[DISPATCHER_PREFERRED_ROUTE] | |||
| { | |||
| if(!ds_is_from_list(-1, 6, "$var(prefered_route)")) { | |||
| xlog("L_INFO", "$ci|log|associated media server $var(prefered_route) is inactive, moving to $xavp(ds_dst=>uri)\n"); | |||
| return -1; | |||
| } | |||
| xlog("L_INFO", "$ci|log|re-ordering the dispatcher list to maintain association with $var(prefered_route)\n"); | |||
| $var(i) = $xavp(ds_ctx=>cnt) - 1; | |||
| while($var(i) >= 0) { | |||
| if($xavp(ds_dst[$var(i)]=>uri) == $var(prefered_route)) { | |||
| $xavp(ds_dst[$var(i)]) = $null; | |||
| } | |||
| $var(i) = $var(i) - 1; | |||
| } | |||
| $xavp(ds_dst=>uri) = $var(prefered_route); | |||
| $xavp(ds_dst[0]=>grp) = $var(setid); | |||
| $xavp(ds_dst[0]=>attrs) = $var(attrs); | |||
| return 1; | |||
| } | |||
| route[PRINT_ROUTES] | |||
| { | |||
| if($sel(cfg_get.kazoo.dispatcher_print_routes) == 0) return; | |||
| $var(i) = 0; | |||
| while($xavp(ds_dst[$var(i)]=>uri) != $null) { | |||
| xlog("L_INFO", "$ci|route|group $xavp(ds_dst[$var(i)]=>grp) => $xavp(ds_dst[$var(i)]=>uri) => zone $(xavp(ds_dst[$var(i)]=>attrs){param.value,zone})\n"); | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| } | |||
| #!import_file "dispatcher-next-route.cfg" | |||
| #!ifndef CUSTOM_DISPATCHER_NEXT_ROUTE | |||
| # Try next destinations in failure route | |||
| route[DISPATCHER_NEXT_ROUTE] | |||
| { | |||
| if($avp(ds_retries) >= $sel(cfg_get.kazoo.dispatcher_max_retries)) return; | |||
| $avp(ds_retries) = $avp(ds_retries) + 1; | |||
| if(ds_next_dst()) { | |||
| xlog("L_INFO", "$ci|log|routing call to next media server $du\n"); | |||
| setflag(FLAG_SKIP_NAT_CORRECTION); | |||
| # relay the request to the new media server | |||
| route(RELAY); | |||
| exit; | |||
| } | |||
| } | |||
| #!endif | |||
| event_route[dispatcher:dst-down] | |||
| { | |||
| xlog("L_WARNING", "Destination down: $ru , $xavp(ds_dst=>uri), $xavp(ds_dst=>grp), $xavp(ds_dst=>attrs)\n"); | |||
| } | |||
| event_route[dispatcher:dst-up] | |||
| { | |||
| xlog("L_WARNING", "Destination up: $ru , $xavp(ds_dst=>uri), $xavp(ds_dst=>grp), $xavp(ds_dst=>attrs)\n"); | |||
| } | |||
| route[DISPATCHER_CHECK_MEDIA_SERVER] | |||
| { | |||
| $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);duid=$(var(MediaUrl){s.corehash, MEDIA_SERVERS_HASH_SIZE});node=$var(MediaName)); | |||
| route_if_exists("DISPATCHER_CUSTOM_MEDIA_CHECK"); | |||
| sql_query("exec", "KZQ_CHECK_MEDIA_SERVER_INSERT"); | |||
| if($sqlrows(exec) > 0) { | |||
| $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(attrs) = $_s(zone=$var(Zone);profile=$var(MediaProfile);duid=$(var(MediaUrl){s.corehash, MEDIA_SERVERS_HASH_SIZE});node=$var(MediaName)); | |||
| $var(SetId) = $sel(cfg_get.kazoo.dispatcher_add_secondary_ip_group); | |||
| route_if_exists("DISPATCHER_CUSTOM_MEDIA_CHECK"); | |||
| 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_WARNING", "reloading dispatcher table\n"); | |||
| ds_reload(); | |||
| }; | |||
| $shv(dispatcher_reload) = 0; | |||
| } | |||
| route[DISPATCHER_STATUS] | |||
| { | |||
| jsonrpc_exec('{"jsonrpc": "2.0", "method": "dispatcher.list", "id": 1}'); | |||
| $var(Sets) = $(jsonrpl(body){kz.json, result.NRSETS}); | |||
| $var(i) = 0; | |||
| $var(ds_groups_json)=""; | |||
| $var(Sep1) = ""; | |||
| while($var(i) < $var(Sets)) { | |||
| $var(Set) = $(jsonrpl(body){kz.json, result.RECORDS[$var(i)].SET}); | |||
| $var(SetCount) = $(var(Set){kz.json.count,TARGETS}); | |||
| $var(Sep2)=""; | |||
| $var(ds_group_json)=""; | |||
| $var(c) = 0; | |||
| while($var(c) < $var(SetCount)) { | |||
| $var(Dest) = $(var(Set){kz.json,TARGETS[$var(c)].DEST}); | |||
| $var(record) = $_s("$(var(Dest){kz.json,URI})" : {"destination" : "$(var(Dest){kz.json,URI})", "flags" : "$(var(Dest){kz.json,FLAGS})", "priority" : $(var(Dest){kz.json,PRIORITY}), "attrs" : "$(var(Dest){kz.json,ATTRS.BODY})"}); | |||
| $var(ds_group_json) = $var(ds_group_json) + $var(Sep2) + $var(record); | |||
| $var(Sep2) = ","; | |||
| $var(c) = $var(c) + 1; | |||
| } | |||
| $var(ds_groups_json) = $var(ds_groups_json) + $var(Sep1) + $_s("$(var(Set){kz.json,ID})" : { $var(ds_group_json) }); | |||
| $var(Sep1)=", "; | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| } | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -0,0 +1,309 @@ | |||
| ### DISPATCHER ROLE #### | |||
| #!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_HASH_SIZE 8 | |||
| #!trydef KZ_DISPATCHER_ADD_FLAGS 9 | |||
| #!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 | |||
| #!trydef KZ_DISPATCHER_MAX_RETRIES 2 | |||
| #!trydef KZ_DISPATCHER_ROUTE_ASSOCIATED_MEDIA 1 | |||
| #!trydef KZ_DISPATCHER_CLASSIFY_FLAGS 2 | |||
| #!trydef KZ_DISPATCHER_PRINT_ROUTES 1 | |||
| 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" | |||
| kazoo.dispatcher_max_retries = KZ_DISPATCHER_MAX_RETRIES descr "max number of retries for media servers" | |||
| kazoo.dispatcher_route_to_associated_media = KZ_DISPATCHER_ROUTE_ASSOCIATED_MEDIA descr "routes to associated media for atxfer" | |||
| kazoo.dispatcher_classify_flags = KZ_DISPATCHER_CLASSIFY_FLAGS descr "dispatch classifier flags" | |||
| kazoo.dispatcher_print_routes = KZ_DISPATCHER_PRINT_ROUTES descr "should we log the selected routes" | |||
| ####### Dispatcher module ######## | |||
| loadmodule "dispatcher.so" | |||
| modparam("dispatcher", "db_url", "KAZOO_DB_URL") | |||
| modparam("dispatcher", "flags", 2) | |||
| modparam("dispatcher", "use_default", 0) | |||
| modparam("dispatcher", "force_dst", 1) | |||
| modparam("dispatcher", "hash_pvar", "$avp(ds_grp)") | |||
| modparam("dispatcher", "setid_pvname", "$var(setid)") | |||
| modparam("dispatcher", "attrs_pvname", "$var(attrs)") | |||
| modparam("dispatcher", "ds_ping_method", "OPTIONS") | |||
| modparam("dispatcher", "ds_ping_interval", 10) | |||
| modparam("dispatcher", "ds_probing_threshold", 3) | |||
| 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") | |||
| modparam("dispatcher", "xavp_dst", "ds_dst") | |||
| modparam("dispatcher", "xavp_ctx", "ds_ctx") | |||
| modparam("dispatcher", "ds_hash_size", KZ_DISPATCHER_HASH_SIZE) | |||
| ## Dispatcher Groups: | |||
| ## 1 - Primary media servers | |||
| ## 2 - Backup media servers | |||
| ## 3 - Alternate media server IPs (used only for classification) | |||
| ## 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] | |||
| { | |||
| route_if_exists("DISPATCHER_CUSTOM_NETWORK_CLASSIFY"); | |||
| if (!isflagset(FLAG_NETWORK_CLASSIFIED)) { | |||
| if (is_myself("$ou")) { | |||
| xlog("$var(log_request_level)", "$ci|log|original R-URI ($ou) is this proxy, treating as external sources\n"); | |||
| } else { | |||
| $var(classify_dispatcher_flag) = $(sel(cfg_get.kazoo.dispatcher_classify_flags){s.int}); | |||
| if (ds_is_from_list(KZ_DISPATCHER_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_CLASSIFY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET1_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET1_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET2_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_ALTNET2_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP, "$var(classify_dispatcher_flag)") || | |||
| ds_is_from_list(KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP, "$var(classify_dispatcher_flag)")) { | |||
| xlog("$var(log_request_level)", "$ci|log|originated from internal sources\n"); | |||
| setflag(FLAG_INTERNALLY_SOURCED); | |||
| } else { | |||
| xlog("$var(log_request_level)", "$ci|log|originated from external sources\n"); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| # Take the routes from dispatcher - hash over callid | |||
| # If prefered route defined, reorder the destionations | |||
| route[DISPATCHER_FIND_ROUTES] | |||
| { | |||
| $var(ds_primary_group) = $(sel(cfg_get.kazoo.dispatcher_primary_group){s.int}); | |||
| $var(ds_backup_group) = $(sel(cfg_get.kazoo.dispatcher_secondary_group){s.int}); | |||
| #!ifndef PRESENCE_ROLE | |||
| if (is_method("SUBSCRIBE")) { | |||
| $var(ds_primary_group) = KZ_DISPATCHER_PRESENCE_PRIMARY_GROUP; | |||
| $var(ds_backup_group) = KZ_DISPATCHER_PRESENCE_SECONDARY_GROUP; | |||
| } | |||
| #!endif | |||
| #!ifndef REGISTRAR_ROLE | |||
| if (is_method("REGISTER")) { | |||
| $var(ds_primary_group) = KZ_DISPATCHER_REGISTRAR_PRIMARY_GROUP; | |||
| $var(ds_backup_group) = KZ_DISPATCHER_REGISTRAR_SECONDARY_GROUP; | |||
| } | |||
| #!endif | |||
| #!ifdef PRESENCE_ROLE | |||
| route(PRESENCE_FAST_PICKUP_ATTEMPT); | |||
| #!endif | |||
| route_if_exists("DISPATCHER_CUSTOM_SET_GROUPS"); | |||
| $var(ds_group) = $var(ds_primary_group); | |||
| $var(ds_alg) = $sel(cfg_get.kazoo.dispatcher_algorithm); | |||
| if(ds_list_exists("$var(ds_backup_group)")) { | |||
| $var(ds_rule) = $_s($var(ds_primary_group)=$var(ds_alg);$var(ds_backup_group)=$var(ds_alg)); | |||
| } else { | |||
| $var(ds_rule) = $_s($var(ds_primary_group)=$var(ds_alg)); | |||
| } | |||
| ds_select_routes("$var(ds_rule)", "2"); | |||
| if ($xavp(ds_ctx=>cnt) == 0) { | |||
| xlog("L_WARN", "$ci|end|no servers available in primary or backup group\n"); | |||
| sl_send_reply("480", "All servers busy"); | |||
| exit; | |||
| } | |||
| route(PRINT_ROUTES); | |||
| $var(user_source) = $(ct{tobody.user}) + "@" + $si + ":" + $sp; | |||
| $var(redirect) = @from.uri.user + "@" + @from.uri.host + "->" | |||
| + @ruri.user + "@" + @ruri.host; | |||
| if ($sht(redirects=>$var(redirect)) != $null) { | |||
| $var(prefered_route) = $sht(redirects=>$var(redirect)); | |||
| xlog("L_INFO", "$ci|route|found redirect for $var(redirect) to $var(prefered_route)\n"); | |||
| $avp(AVP_REDIRECT_KEY) = $var(redirect); | |||
| route(DISPATCHER_PREFERRED_ROUTE); | |||
| } else if ($sht(associations=>$var(user_source)) != $null) { | |||
| if($sel(cfg_get.kazoo.dispatcher_route_to_associated_media) == 1) { | |||
| $var(prefered_route) = $sht(associations=>$var(user_source)); | |||
| xlog("L_INFO", "$ci|route|found association for contact uri $var(user_source)\n"); | |||
| route(DISPATCHER_PREFERRED_ROUTE); | |||
| } | |||
| $sht(associations=>$var(user_source)) = $null; | |||
| } | |||
| $avp(ds_group) = $xavp(ds_dst=>grp); | |||
| $avp(ds_retries) = 0; | |||
| ds_set_dst(); | |||
| } | |||
| route[DISPATCHER_PREFERRED_ROUTE] | |||
| { | |||
| if(!ds_is_from_list(-1, 6, "$var(prefered_route)")) { | |||
| xlog("L_INFO", "$ci|log|associated media server $var(prefered_route) is inactive, moving to $xavp(ds_dst=>uri)\n"); | |||
| return -1; | |||
| } | |||
| xlog("L_INFO", "$ci|log|re-ordering the dispatcher list to maintain association with $var(prefered_route)\n"); | |||
| $var(i) = $xavp(ds_ctx=>cnt) - 1; | |||
| while($var(i) >= 0) { | |||
| if($xavp(ds_dst[$var(i)]=>uri) == $var(prefered_route)) { | |||
| $xavp(ds_dst[$var(i)]) = $null; | |||
| } | |||
| $var(i) = $var(i) - 1; | |||
| } | |||
| $xavp(ds_dst=>uri) = $var(prefered_route); | |||
| $xavp(ds_dst[0]=>grp) = $var(setid); | |||
| $xavp(ds_dst[0]=>attrs) = $var(attrs); | |||
| return 1; | |||
| } | |||
| route[PRINT_ROUTES] | |||
| { | |||
| if($sel(cfg_get.kazoo.dispatcher_print_routes) == 0) return; | |||
| $var(i) = 0; | |||
| while($xavp(ds_dst[$var(i)]=>uri) != $null) { | |||
| xlog("L_INFO", "$ci|route|group $xavp(ds_dst[$var(i)]=>grp) => $xavp(ds_dst[$var(i)]=>uri) => zone $(xavp(ds_dst[$var(i)]=>attrs){param.value,zone})\n"); | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| } | |||
| #!import_file "dispatcher-next-route.cfg" | |||
| #!ifndef CUSTOM_DISPATCHER_NEXT_ROUTE | |||
| # Try next destinations in failure route | |||
| route[DISPATCHER_NEXT_ROUTE] | |||
| { | |||
| if($avp(ds_retries) >= $sel(cfg_get.kazoo.dispatcher_max_retries)) return; | |||
| $avp(ds_retries) = $avp(ds_retries) + 1; | |||
| if(ds_next_dst()) { | |||
| xlog("L_INFO", "$ci|log|routing call to next media server $du\n"); | |||
| setflag(FLAG_SKIP_NAT_CORRECTION); | |||
| # relay the request to the new media server | |||
| route(RELAY); | |||
| exit; | |||
| } | |||
| } | |||
| #!endif | |||
| event_route[dispatcher:dst-down] | |||
| { | |||
| xlog("L_WARNING", "Destination down: $ru , $xavp(ds_dst=>uri), $xavp(ds_dst=>grp), $xavp(ds_dst=>attrs)\n"); | |||
| } | |||
| event_route[dispatcher:dst-up] | |||
| { | |||
| xlog("L_WARNING", "Destination up: $ru , $xavp(ds_dst=>uri), $xavp(ds_dst=>grp), $xavp(ds_dst=>attrs)\n"); | |||
| } | |||
| route[DISPATCHER_CHECK_MEDIA_SERVER] | |||
| { | |||
| $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);duid=$(var(MediaUrl){s.corehash, MEDIA_SERVERS_HASH_SIZE});node=$var(MediaName)); | |||
| route_if_exists("DISPATCHER_CUSTOM_MEDIA_CHECK"); | |||
| sql_query("exec", "KZQ_CHECK_MEDIA_SERVER_INSERT"); | |||
| if($sqlrows(exec) > 0) { | |||
| $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(attrs) = $_s(zone=$var(Zone);profile=$var(MediaProfile);duid=$(var(MediaUrl){s.corehash, MEDIA_SERVERS_HASH_SIZE});node=$var(MediaName)); | |||
| $var(SetId) = $sel(cfg_get.kazoo.dispatcher_add_secondary_ip_group); | |||
| route_if_exists("DISPATCHER_CUSTOM_MEDIA_CHECK"); | |||
| 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_WARNING", "reloading dispatcher table\n"); | |||
| ds_reload(); | |||
| }; | |||
| $shv(dispatcher_reload) = 0; | |||
| } | |||
| route[DISPATCHER_STATUS] | |||
| { | |||
| jsonrpc_exec('{"jsonrpc": "2.0", "method": "dispatcher.list", "id": 1}'); | |||
| $var(Sets) = $(jsonrpl(body){kz.json, result.NRSETS}); | |||
| $var(i) = 0; | |||
| $var(ds_groups_json)=""; | |||
| $var(Sep1) = ""; | |||
| while($var(i) < $var(Sets)) { | |||
| $var(Set) = $(jsonrpl(body){kz.json, result.RECORDS[$var(i)].SET}); | |||
| $var(SetCount) = $(var(Set){kz.json.count,TARGETS}); | |||
| $var(Sep2)=""; | |||
| $var(ds_group_json)=""; | |||
| $var(c) = 0; | |||
| while($var(c) < $var(SetCount)) { | |||
| $var(Dest) = $(var(Set){kz.json,TARGETS[$var(c)].DEST}); | |||
| $var(record) = $_s("$(var(Dest){kz.json,URI})" : {"destination" : "$(var(Dest){kz.json,URI})", "flags" : "$(var(Dest){kz.json,FLAGS})", "priority" : $(var(Dest){kz.json,PRIORITY}), "attrs" : "$(var(Dest){kz.json,ATTRS.BODY})"}); | |||
| $var(ds_group_json) = $var(ds_group_json) + $var(Sep2) + $var(record); | |||
| $var(Sep2) = ","; | |||
| $var(c) = $var(c) + 1; | |||
| } | |||
| $var(ds_groups_json) = $var(ds_groups_json) + $var(Sep1) + $_s("$(var(Set){kz.json,ID})" : { $var(ds_group_json) }); | |||
| $var(Sep1)=", "; | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| } | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -0,0 +1,17 @@ | |||
| loadmodule "dmq.so" | |||
| loadmodule "dmq_usrloc.so" | |||
| # ---- dmq params ---- | |||
| modparam("dmq", "server_address", "sip:MY_IP_ADDRESS:5090") | |||
| modparam("dmq", "multi_notify", 0) | |||
| modparam("dmq_usrloc", "enable", 1) | |||
| route[HANDLE_DMQ] | |||
| { | |||
| if (is_method("KDMQ") && $Rp == 5090) | |||
| { | |||
| dmq_handle_message(); | |||
| } | |||
| } | |||
| @ -0,0 +1,52 @@ | |||
| ## NOTE: DO NOT CHANGE THIS FILE, EDIT local.cfg ## | |||
| #### Preprocessor Directives ######### | |||
| #!define L_ALERT -5 | |||
| #!define L_BUG -4 | |||
| #!define L_CRIT2 -3 | |||
| #!define L_CRIT -2 | |||
| #!define L_ERR -1 | |||
| #!define L_WARN 0 | |||
| #!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" | |||
| #!define AVP_ROUTE_CNT "route_cnt" | |||
| #!define AVP_ASSOCIATED_SERVER "associated_server" | |||
| #!define AVP_ASSOCIATE_CONTACT "associate_contact" | |||
| #!define AVP_REDIRECT_KEY "redirect_key" | |||
| #!define FLB_NATB 1 | |||
| #!define FLB_NATSIPPING 2 | |||
| #!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) | |||
| # textops - apply regexp | |||
| # | |||
| # | |||
| ################ | |||
| loadmodule "ipops.so" | |||
| loadmodule "pv.so" | |||
| loadmodule "textops.so" | |||
| ####### Local Configuration ######## | |||
| include_file "local.cfg" | |||
| ####### defaults not configured in local ######## | |||
| include_file "defs.cfg" | |||
| ####### Default Configuration ###### | |||
| include_file "default.cfg" | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -0,0 +1,156 @@ | |||
| ######## kazoo bindings ######## | |||
| ### | |||
| ### | |||
| #!trydef KZ_PUA_PRESENCE_USE_FULL_ENTITY 1 | |||
| ####### 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) | |||
| modparam("kazoo", "presence_use_full_entity", KZ_PUA_PRESENCE_USE_FULL_ENTITY) | |||
| ## 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] | |||
| { | |||
| #!ifdef PRESENCE_ROLE | |||
| route(PRESENCE_BINDINGS); | |||
| #!endif | |||
| #!ifdef MESSAGE_ROLE | |||
| route(MESSAGE_BINDINGS); | |||
| #!endif | |||
| #!ifdef REGISTRAR_ROLE | |||
| route(REGISTRAR_BINDINGS); | |||
| #!endif | |||
| #!ifdef NODES_ROLE | |||
| route(NODES_BINDINGS); | |||
| #!endif | |||
| #!ifdef ACL_ROLE | |||
| route(ACL_BINDINGS); | |||
| #!endif | |||
| #!import_file "kazoo-custom-bindings.cfg" | |||
| } | |||
| event_route[kazoo:consumer-event] | |||
| { | |||
| xlog("L_DEBUG","unhandled AMQP event, payload: $kzE\n"); | |||
| } | |||
| event_route[kazoo:consumer-event-connection-open] | |||
| { | |||
| 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_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 | |||
| @ -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)"); | |||
| } | |||
| } | |||
| @ -0,0 +1,159 @@ | |||
| ################################################################################ | |||
| ## ROLES | |||
| ################################################################################ | |||
| ## Enabled Roles | |||
| #!trydef DISPATCHER_ROLE | |||
| #!trydef NAT_TRAVERSAL_ROLE | |||
| #!trydef REGISTRAR_ROLE | |||
| # # #!trydef PRESENCE_ROLE | |||
| #!trydef RESPONDER_ROLE | |||
| #!trydef NODES_ROLE | |||
| ## Disabled Roles - remove all but the last '#' to enable | |||
| # # #!trydef TRAFFIC_FILTER_ROLE | |||
| # # #!trydef WEBSOCKETS_ROLE | |||
| # # #!trydef TLS_ROLE | |||
| # # #!trydef ANTIFLOOD_ROLE | |||
| # # #!trydef RATE_LIMITER_ROLE | |||
| # # #!trydef ACL_ROLE | |||
| # # #!trydef MESSAGE_ROLE | |||
| # # #!trydef PUSHER_ROLE | |||
| # # #!trydef REGISTRAR_SYNC_ROLE | |||
| # # #!trydef PRESENCE_NOTIFY_SYNC_ROLE | |||
| # # #!trydef SIP_TRACE_ROLE | |||
| ################################################################################ | |||
| ## SERVER INFORMATION | |||
| ################################################################################ | |||
| ## UNCOMMENT & CHANGE "kamailio.2600hz.com" TO YOUR SERVERS HOSTNAME | |||
| # # #!substdef "!MY_HOSTNAME!kamailio.2600hz.com!g" | |||
| ## 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!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!amqp://guest:guest@127.0.0.1:5672!g" | |||
| ################################################################################ | |||
| ## 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. 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 | |||
| ################################################################################ | |||
| ## This parameter is OPTIONAL | |||
| ## If you would like to use an external database | |||
| ## remove all but the last '#' and configure the connector | |||
| ## accordingly. The example shows how to use postgres. | |||
| ## See the kamailio documentation for more details. | |||
| #!trydef KZ_DB_MODULE postgres | |||
| #!substdef "!KAMAILIO_DBMS!postgres!g" | |||
| #!substdef "!KAZOO_DB_URL!postgres://kamailio:kamailio@127.0.0.1/kamailio!g" | |||
| ################################################################################ | |||
| ## UDP PARAMETERS | |||
| ################################################################################ | |||
| ## This parameter is OPTIONAL | |||
| ## If large UDP packets are dropped by the | |||
| ## interface try uncommenting this option. | |||
| ## However, you MUST match this to your | |||
| ## network adapter! If they do not match, | |||
| ## all UDP packets over this limit WILL FAIL! | |||
| ## E.g.: Add MTU=1472 to the /etc/sysconfig/network-scripts/XXX | |||
| # udp4_raw_mtu = 1472 | |||
| ################################################################################ | |||
| ## 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. | |||
| ##!define KZ_MULTI_HOMED 1 | |||
| ################################################################################ | |||
| ## KZ_DISABLE_REGISTRAR_PORTS | |||
| ################################################################################ | |||
| ## This parameter is OPTIONAL. | |||
| ## It will disable publishing single proxy port on registrar success messages | |||
| ## By default single port proxy is send on registrar success messages | |||
| ## for websockets/tls protocols. the default port is 7000 | |||
| ## which enables config less in freeswitch for handling webrtc/tls | |||
| ## ie, no certs in freeswitch, no webrtc/tls listeners on freeswitch | |||
| ## by disabling single port proxy, ecallmgr needs to be started with | |||
| ## 'use_transport_for_fs_path' set to true in its environment configuration | |||
| ## | |||
| ###!define KZ_DISABLE_WEBSOCKETS_REGISTRAR_PORT 1 | |||
| ###!define KZ_DISABLE_TLS_REGISTRAR_PORT 1 | |||
| ## | |||
| ## you can also change the ports used for single port redirect | |||
| ##!define KZ_WEBSOCKETS_REGISTRAR_PORT 5060 | |||
| ##!define KZ_TLS_REGISTRAR_PORT 5060 | |||
| ## | |||
| ## also available for udp/tcp if it works for you | |||
| ##!define KZ_UDP_REGISTRAR_PORT 7000 | |||
| ##!define KZ_TCP_REGISTRAR_PORT 7000 | |||
| ################################################################################ | |||
| ## 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 | |||
| # # #!define SIP_TRACE_URI "sip:127.0.0.1:9060" | |||
| # # #!define HEP_CAPTURE_ID 1 | |||
| ## YOU SHOULD NOT HAVE TO CHANGE THESE! | |||
| ## By setting MY_IP_ADDRESS above these will resolve | |||
| ## to the proper bindings. These are here | |||
| ## for those with complex layouts who know | |||
| ## what they are doing :) | |||
| #!substdef "!UDP_SIP!udp:MY_IP_ADDRESS:5060!g" | |||
| #!substdef "!TCP_SIP!tcp:MY_IP_ADDRESS:5060!g" | |||
| #!substdef "!TLS_SIP!tls:MY_IP_ADDRESS:5061!g" | |||
| #!substdef "!UDP_ALG_SIP!udp:MY_IP_ADDRESS:7000!g" | |||
| #!substdef "!TCP_ALG_SIP!tcp:MY_IP_ADDRESS:7000!g" | |||
| #!substdef "!TLS_ALG_SIP!tls:MY_IP_ADDRESS:7001!g" | |||
| #!substdef "!TCP_WS!tcp:MY_IP_ADDRESS:5064!g" | |||
| #!substdef "!UDP_WS_SIP!udp:MY_IP_ADDRESS:5064!g" | |||
| #!substdef "!TLS_WSS!tls:MY_IP_ADDRESS:5065!g" | |||
| #!substdef "!UDP_WSS_SIP!udp:MY_IP_ADDRESS:5065!g" | |||
| ## YOU SHOULD NOT HAVE TO CHANGE THESE! | |||
| ## This will bind the default SIP listeners | |||
| ## as determined above. The tls-role and | |||
| ## websocket-role will use the appropriate | |||
| ## definitions if enabled. These are here | |||
| ## for those with complex layouts who know | |||
| ## what they are doing :) | |||
| listen=UDP_SIP | |||
| listen=TCP_SIP | |||
| listen=UDP_ALG_SIP | |||
| listen=TCP_ALG_SIP | |||
| +#!ifdef DMQ_ROLE | |||
| +listen=udp:MY_IP_ADDRESS:5090 | |||
| +#!endif | |||
| @ -0,0 +1,102 @@ | |||
| route[HANDLE_MESSAGE] | |||
| { | |||
| if (!is_method("MESSAGE")) return; | |||
| if (isflagset(FLAG_INTERNALLY_SOURCED) || src_ip == myself) return; | |||
| xlog("L_INFO", "$ci|log|MESSAGE from $fu to $tu\n"); | |||
| route(AUTH); | |||
| $xavp(regcfg=>match_received) = $su; | |||
| if($avp(is_registered) != "true") { | |||
| sl_send_reply("403", "Forbidden"); | |||
| exit; | |||
| } else { | |||
| if($hdr(Content-Type) =~ "text/plain" || | |||
| $hdr(Content-Type) =~ "text/html") { | |||
| route(MESSAGE_INBOUND); | |||
| } else { | |||
| xlog("L_WARN", "$ci|end|dropping MESSAGE $hdr(Content-Type)\n"); | |||
| sl_send_reply("200", "OK"); | |||
| exit; | |||
| } | |||
| } | |||
| } | |||
| route[MESSAGE_INBOUND] | |||
| { | |||
| $var(key) = "kamailio@MY_HOSTNAME"; | |||
| route(AUTH_HEADERS_JSON); | |||
| $var(Payload) = $_s({"Event-Category" : "sms", "Event-Name" : "inbound", "Call-ID" : "$ci", "Message-ID" : "$ci", "Route-Type" : "onnet", "Route-ID" : "$(var(key){kz.encode})", "From" : "$fU", "To" : "$tU", "Body" : "$rb", "Custom-SIP-Headers" : $var(headers_json), "Msg-ID" : "$uuid(g)"}); | |||
| $var(exchange) = "im"; | |||
| $var(RoutingKey) = $_s(sms.inbound.onnet.$(ci{kz.encode})); | |||
| xlog("L_INFO", "$ci|msg|sending inbound message $var(RoutingKey) => $var(Payload)\n"); | |||
| kazoo_publish($var(exchange), $var(RoutingKey), $var(Payload)); | |||
| sl_send_reply("200", "OK"); | |||
| exit; | |||
| } | |||
| event_route[kazoo:consumer-event-sms-outbound] | |||
| { | |||
| xlog("L_INFO", "$(kzE{kz.json,Message-ID})|log|received message outbound for $(kzE{kz.json,Endpoints[0].To-DID})\n"); | |||
| $var(from_uri) = $_s(sip:$(kzE{kz.json,From})@$(kzE{kz.json,Custom-Vars.Realm})); | |||
| $var(to_uri) = $_s(sip:$(kzE{kz.json,Endpoints[0].To-Username})@$(kzE{kz.json,Endpoints[0].To-Realm})); | |||
| $uac_req(method)="MESSAGE"; | |||
| $uac_req(body)= $kzE; | |||
| $uac_req(hdrs)="X-TM-Local: MESSAGE_ROUTE\r\nX-KAZOO-AOR: " + $var(to_uri)+ "\r\nContent-Type: text/plain\r\n"; | |||
| $uac_req(turi) = $var(to_uri); | |||
| $uac_req(ruri) = $var(to_uri); | |||
| $uac_req(furi) = $var(from_uri); | |||
| $uac_req(ouri) = "sip:127.0.0.1:5090;transport=tcp"; | |||
| $uac_req(callid) = $(kzE{kz.json,Message-ID}); | |||
| xlog("L_INFO", "$(kzE{kz.json,Message-ID})|log|sending message from $var(from_uri) to $var(to_uri) \n"); | |||
| uac_req_send(); | |||
| } | |||
| route[MESSAGE_ROUTE] | |||
| { | |||
| remove_hf_re("^X-"); | |||
| route(ROUTE_TO_AOR); | |||
| $var(JObj) = $rb; | |||
| set_body("$(var(JObj){kz.json,Body})", "text/plain"); | |||
| $avp(message_id) = $(var(JObj){kz.json,Message-ID}); | |||
| $avp(msg_id) = $(var(JObj){kz.json,Msg-ID}); | |||
| $avp(server_id) = $(var(JObj){kz.json,Server-ID}); | |||
| t_on_reply("MESSAGE_REPLY"); | |||
| t_on_failure("MESSAGE_FAULT"); | |||
| t_relay(); | |||
| } | |||
| onreply_route[MESSAGE_REPLY] | |||
| { | |||
| if($T_reply_code < 300) { | |||
| xlog("L_INFO", "$ci|log|sending success delivery message to $avp(server_id)\n"); | |||
| $var(Payload) = $_s({"Event-Category" : "message", "Event-Name" : "delivery", "Call-ID" : "$ci", "Message-ID" : "$avp(message_id)" , "Delivery-Result-Code" : "sip:$T_reply_code", "Msg-ID" : "$avp(msg_id)" , "Status" : "delivered"}); | |||
| $var(exchange) = "targeted"; | |||
| $var(RK) = $avp(server_id); | |||
| kazoo_publish($var(exchange), $var(RK), $var(Payload)); | |||
| } | |||
| } | |||
| failure_route[MESSAGE_FAULT] | |||
| { | |||
| xlog("L_INFO", "$ci|log|sending failure delivery message to $avp(server_id)\n"); | |||
| $var(Payload) = $_s({"Event-Category" : "message", "Event-Name" : "delivery", "Call-ID" : "$ci", "Message-ID" : "$avp(message_id)" , "Delivery-Result-Code" : "sip:$T_reply_code", "Msg-ID" : "$avp(msg_id)" , "Status" : "failure"}); | |||
| $var(exchange) = "targeted"; | |||
| $var(RK) = $avp(server_id); | |||
| kazoo_publish($var(exchange), $var(RK), $var(Payload)); | |||
| } | |||
| route[MESSAGE_BINDINGS] | |||
| { | |||
| $var(key) = "kamailio@MY_HOSTNAME"; | |||
| $var(payload) = $_s({"name": "sms", "exchange": "im", "type": "topic", "queue": "MSG-QUEUE-MY_HOSTNAME", "routing": "sms.outbound.$(var(key){kz.encode}).*", "no_ack": false, "federate": true }); | |||
| kazoo_subscribe("$var(payload)"); | |||
| } | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -0,0 +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: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_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_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 | |||
| @ -0,0 +1,281 @@ | |||
| ######## Nodes role - pushes info to kazoo ######## | |||
| #!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"); | |||
| 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;") | |||
| modparam("rtimer", "exec", "timer=ta;route=NODE_HEARTBEAT_ROUTE") | |||
| modparam("rtimer", "exec", "timer=retry;route=NODE_TRACK_ROUTE") | |||
| modparam("rtimer", "exec", "timer=pub;route=NODES_ADVERTISE_ROUTE") | |||
| modparam("mqueue","mqueue", "name=node_track") | |||
| modparam("mqueue","mqueue", "name=node_heartbeat") | |||
| ####### NODES Logic ######## | |||
| route[NODES_ADVERTISE_ROUTE] | |||
| { | |||
| route(LISTENER_STATUS); | |||
| #!ifdef DISPATCHER_ROLE | |||
| route(DISPATCHER_STATUS); | |||
| $var(Dispatcher) = $_s(, "Dispatcher" : {"Groups" : { $var(ds_groups_json) }}); | |||
| #!else | |||
| $var(Dispatcher) = ""; | |||
| #!endif | |||
| #!ifdef PRESENCE_ROLE | |||
| route(COUNT_ALL_SUBSCRIBERS); | |||
| $var(Subscriptions) = $_s("Subscriptions" : { $var(Subscriptions) }); | |||
| $var(Subscribers) = $_s("Subscribers" : { $var(Subscribers) }); | |||
| route(COUNT_ALL_PRESENTITIES); | |||
| $var(Presentities) = $_s("Presentities" : {"message-summary" : $xavp(watchers=>message-summary), "dialog" : $xavp(watchers=>dialog), "presence" : $xavp(watchers=>presence)}); | |||
| $var(Presence) = $_s(, "Presence" : {$var(Subscribers), $var(Subscriptions), $var(Presentities)}); | |||
| #!else | |||
| $var(Presence) = ""; | |||
| #!endif | |||
| #!ifdef REGISTRAR_ROLE | |||
| $var(Registrar) = $_s(, "Registrar" : {"Registrations" : $(stat(registered_users){s.int})}); | |||
| #!else | |||
| $var(Registrar) = ""; | |||
| #!endif | |||
| $var(Roles) = $_s("Roles" : {"Proxy" : $var(listeners) $var(Dispatcher) $var(Presence) $var(Registrar)}); | |||
| $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); | |||
| if($var(count) == 0) { | |||
| 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_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"); | |||
| } | |||
| event_route[htable:expired:nodes] | |||
| { | |||
| if($shtrecord(key) =~ "::count$$") { | |||
| if($shtrecord(value) == -1) { | |||
| xlog("L_WARNING", "htable|nodes|node $(shtrecord(key){s.rm,::count}) is still unreachable\n"); | |||
| } | |||
| mq_add("node_track", "$shtrecord(key)", ""); | |||
| return; | |||
| } | |||
| xlog("L_WARNING", "htable|nodes|heartbeat expired for node $shtrecord(key)\n"); | |||
| } | |||
| route[NODE_TRACK_ROUTE] | |||
| { | |||
| $var(runloop) = 1; | |||
| while(mq_fetch("node_track") == 1 && $var(runloop) < MAX_WHILE_LOOPS) { | |||
| $var(Key) = $mqk(node_track); | |||
| $sht(nodes=>$var(Key)) = -1; | |||
| $var(runloop) = $var(runloop) + 1; | |||
| } | |||
| } | |||
| route[NODE_HEARTBEAT_ROUTE] | |||
| { | |||
| $var(runloop) = 1; | |||
| while(mq_fetch("node_heartbeat") == 1 && $var(runloop) < MAX_WHILE_LOOPS) { | |||
| $var(Node) = $mqk(node_heartbeat); | |||
| $var(Payload) = $mqv(node_heartbeat); | |||
| 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_EXPIRE; | |||
| $var(runloop) = $var(runloop) + 1; | |||
| } | |||
| } | |||
| route[CHECK_MEDIA_SERVERS] | |||
| { | |||
| if($(var(Payload){kz.json,Media-Servers}) == "") { | |||
| return; | |||
| } | |||
| $var(Msg-ID) = $(var(Payload){kz.json,Msg-ID}); | |||
| $var(Media) = $(var(Payload){kz.json,Media-Servers}); | |||
| $var(Zone) = $(var(Payload){kz.json,AMQP-Broker-Zone}); | |||
| $var(Dot) = "."; | |||
| $var(Perc) = "%"; | |||
| avp_delete("$avp(MediaKeys)/g"); | |||
| if(kazoo_json_keys($var(Payload), "Media-Servers", "$avp(MediaKeys)") == 1) { | |||
| $var(Count) = $cnt($avp(MediaKeys)); | |||
| $var(Idx) = 0; | |||
| while( $var(Idx) < $var(Count) ) { | |||
| $var(MediaName) = $(avp(MediaKeys)[$var(Idx)]); | |||
| $var(MediaKey) = $(var(MediaName){s.replace,$var(Dot),$var(Perc)}); | |||
| avp_delete("$avp(ProfileKeys)/g"); | |||
| $var(MediaReconnected) = 0; | |||
| $var(MediaConnected) = $(var(Payload){kz.json,Media-Servers.$var(MediaKey).Startup}); | |||
| $var(ExistingMediaConnected) = $sht(media=>$var(MediaKey)::connected::$var(Node)); | |||
| xlog("L_DEBUG", "$var(Msg-ID)|nodes|$var(MediaKey) from $var(Node) => $var(MediaConnected) vs $var(ExistingMediaConnected)\n"); | |||
| if($var(ExistingMediaConnected) != $null && $var(MediaConnected) > $var(ExistingMediaConnected)) { | |||
| $var(MediaReconnected) = 1; | |||
| } | |||
| $sht(media=>$var(MediaKey)::connected::$var(Node)) = $var(MediaConnected); | |||
| $shtex(media=>$var(MediaKey)::connected::$var(Node)) = 0; | |||
| if(kazoo_json_keys($var(Payload), "Media-Servers.$var(MediaKey).Interfaces", "$avp(ProfileKeys)") == 1) { | |||
| $var(ProfileCount) = $cnt($avp(ProfileKeys)); | |||
| $var(ProfileIdx) = 0; | |||
| while( $var(ProfileIdx) < $var(ProfileCount) ) { | |||
| $var(MediaProfile) = $(avp(ProfileKeys)[$var(ProfileIdx)]); | |||
| $var(MediaRawUrl) = $(var(Payload){kz.json,Media-Servers.$var(MediaKey).Interfaces.$var(MediaProfile).info.url}); | |||
| if($var(MediaRawUrl) == "") { | |||
| $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).info.sip-ip}); | |||
| if($var(MediaIP) == "") { | |||
| $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); | |||
| } else if($var(MediaReconnected) == 1) { | |||
| route(MEDIA_SERVER_RECONNECTED); | |||
| }; | |||
| $var(MediaExpire) = ($(var(Payload){kz.json,Expires}{s.int}) / 1000) + NODES_FUDGE_EXPIRE; | |||
| xlog("L_DEBUG", "$var(Msg-ID)|nodes|$var(Node) media expiration $var(MediaExpire) for $var(MediaUrl)\n"); | |||
| $shtex(media=>$var(MediaUrl)::count) = $var(MediaExpire); | |||
| $var(ProfileIdx) = $var(ProfileIdx) + 1; | |||
| } | |||
| } | |||
| $var(Idx) = $var(Idx) + 1; | |||
| } | |||
| }; | |||
| } | |||
| event_route[htable:expired:media] | |||
| { | |||
| $var(MediaUrl) = $(shtrecord(key){re.subst,/(.*)::(.*)/\1/}); | |||
| $var(Zone) = $sht(media=>$var(MediaUrl)::zone); | |||
| route(MEDIA_SERVER_DOWN); | |||
| } | |||
| route[MEDIA_SERVER_UP] | |||
| { | |||
| xlog("L_NOTICE", "$var(Msg-ID)|nodes|$var(Node) reported new media server $var(MediaUrl) in zone $var(Zone)\n"); | |||
| #!ifdef DISPATCHER_ROLE | |||
| route(DISPATCHER_CHECK_MEDIA_SERVER); | |||
| #!endif | |||
| #!ifdef PRESENCE_ROLE | |||
| route(PRESENCE_FAST_PICKUP_START); | |||
| route(RESET_PUBLISHER); | |||
| #!endif | |||
| } | |||
| route[MEDIA_SERVER_DOWN] | |||
| { | |||
| xlog("L_WARNING", "expired|nodes|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", "$var(Msg-ID)|nodes|media server $var(MediaUrl) restarted in zone $var(Zone)\n"); | |||
| #!ifdef PRESENCE_ROLE | |||
| route(RESET_PUBLISHER); | |||
| #!endif | |||
| } | |||
| route[MEDIA_SERVER_RECONNECTED] | |||
| { | |||
| xlog("L_NOTICE", "$var(Msg-ID)|nodes|media server $var(MediaUrl) reconnected to $var(Node) in zone $var(Zone)\n"); | |||
| #!ifdef PRESENCE_ROLE | |||
| route(RESET_PUBLISHER); | |||
| #!endif | |||
| } | |||
| #!ifndef NODES_CUSTOM_BINDINGS | |||
| route[NODES_BINDINGS] | |||
| { | |||
| $var(payload) = $_s({"name": "nodes", "exchange" : "nodes" , "type" : "fanout", "queue" : "nodes-MY_HOSTNAME", "exclusive": false, "federate": true }); | |||
| kazoo_subscribe("$var(payload)"); | |||
| } | |||
| #!endif | |||
| route[LISTENER_STATUS] | |||
| { | |||
| jsonrpc_exec('{"jsonrpc": "2.0", "method": "corex.list_sockets", "id": 1}'); | |||
| $var(count) = $(jsonrpl(body){kz.json.count,result}); | |||
| $var(loop) = 0; | |||
| $var(sep) = ""; | |||
| $var(listeners) = ""; | |||
| while( $var(loop) < $var(count) ) { | |||
| $var(listener) = $(jsonrpl(body){kz.json,result[$var(loop)]}); | |||
| $var(proto) = $(var(listener){kz.json,PROTO}); | |||
| $var(address) = $(var(listener){kz.json,ADDRLIST.ADDR}); | |||
| 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(loop) = $var(loop) + 1; | |||
| } | |||
| $var(listeners) = $_s({"Listeners" : { $var(listeners) }}); | |||
| } | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -0,0 +1,197 @@ | |||
| ######## FAST PICKUP ROLE ######## | |||
| modparam("htable", "htable", "park=>size=16;autoexpire=600") | |||
| modparam("htable", "htable", "fp=>size=8"); | |||
| #!trydef KZ_PRESENCE_FAST_PICKUP_COOKIES 1 | |||
| #!trydef KZ_PRESENCE_FAST_PICKUP_REALTIME 1 | |||
| #!trydef KZ_PRESENCE_FAST_PICKUP_STAR_5 1 | |||
| 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[PRESENCE_FAST_PICKUP_LOAD] | |||
| { | |||
| sht_reset("fp"); | |||
| 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) { | |||
| $var(host) = $(xavp(ra=>destination){uri.host}); | |||
| $var(port) = $(xavp(ra=>destination){uri.port}); | |||
| $var(destination) = $xavp(ra=>destination); | |||
| $var(i) = 0; | |||
| if(!is_ip("$var(host)")) { | |||
| 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); | |||
| } | |||
| pv_unset("$xavp(ra)"); | |||
| } | |||
| } | |||
| } | |||
| route[PRESENCE_FAST_PICKUP_ATTEMPT] | |||
| { | |||
| if (!is_method("INVITE")) { | |||
| return; | |||
| } | |||
| $var(replaced_call_id) = "none"; | |||
| if($hdr(Replaces)!= $null) { | |||
| $var(replaced_call_id) = $(hdr(Replaces){s.select,0,;}); | |||
| } | |||
| if($var(replaced_call_id) =~ "kfp+") { | |||
| if($shtinc(fp=>count) == 1) { | |||
| 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(PRESENCE_FAST_PICKUP_OPTION); | |||
| $du = $sht(fp=>$var(md5)); | |||
| append_hf("Replaces: $var(replaced_call_id)$var(Pickup)\r\n"); | |||
| append_hf("k-cid: $var(replaced_call_id)\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(RELAY); | |||
| exit(); | |||
| } else { | |||
| xlog("L_INFO", "$ci|log|fast|shortcut $var(md5) invalid in this server, using standard routing\n"); | |||
| } | |||
| } | |||
| 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 }'; | |||
| $var(amqp_routing_key) = "call.status_req." + $(var(replaced_call_id){kz.encode}); | |||
| sl_send_reply("100", "locating your call"); | |||
| xlog("L_INFO", "$ci|log|querying cluster for the location of call-id $var(replaced_call_id)\n"); | |||
| if(kazoo_query("callevt", $var(amqp_routing_key), $var(amqp_payload_request))) { | |||
| $du = $(kzR{kz.json,Switch-URL}); | |||
| if($du != $null) { | |||
| if($(kzR{kz.json,Other-Leg-Call-ID}) == "") { | |||
| ## not bridged | |||
| $var(rep) = $_s($var(replaced_call_id);a-leg=true); | |||
| } else { | |||
| ## ensure early-only=true | |||
| $var(rep) = $_s($var(replaced_call_id);early-only=true); | |||
| } | |||
| remove_hf_re("^Replaces"); | |||
| append_hf("Replaces: $var(rep)\r\n"); | |||
| append_hf("k-cid: $var(replaced_call_id)\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(RELAY); | |||
| exit(); | |||
| } else { | |||
| xlog("L_WARN", "$ci|log|call-id $var(replaced_call_id) not found in cluster, proceeding with normal dispatch\n"); | |||
| } | |||
| } else { | |||
| xlog("L_WARN", "$ci|log|error querying cluster for call-id $var(replaced_call_id), proceeding with normal dispatch\n"); | |||
| } | |||
| } | |||
| } | |||
| ##### 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)); | |||
| append_hf("k-cid: $sht(park=>$var(park)::callid)\r\n"); | |||
| 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})); | |||
| append_hf("k-cid: $sht(park=>$(ru{uri.user})@$(ruri{uri.domain})::callid)\r\n"); | |||
| 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) && !is_ip($rd)) { | |||
| 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"); | |||
| append_hf("k-cid: $(kzR{kz.json,Channels[0].call_id})\r\n"); | |||
| route(RELAY); | |||
| exit(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| route[PRESENCE_FAST_PICKUP_OPTION] | |||
| { | |||
| $var(Pickup) = ""; | |||
| switch($var(PickupOptions)) | |||
| { | |||
| case 1: | |||
| $var(Pickup) = ";a-leg=true"; | |||
| break; | |||
| case 2: | |||
| $var(Pickup) = ";early-only=true"; | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| route[PRESENCE_FAST_PICKUP_INIT] | |||
| { | |||
| $var(AppName) = $(kzE{kz.json,App-Name}); | |||
| ## park redirects without replaces header | |||
| 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($sel(cfg_get.kazoo.presence_fast_pickup_cookies) == 1) { | |||
| if($var(AppName) == "park") { | |||
| $var(Pickup) = 1; #";a-leg=true"; | |||
| } else { | |||
| $var(Pickup) = 2; #";early-only=true"; | |||
| } | |||
| $var(Option) = $(var(Pickup){s.encode.hexa}); | |||
| $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_DEBUG", "$var(call_id)|fast|shortcut ($var(Pickup)) kfp+$var(Option)$var(Cookie)@$var(call_id)\n"); | |||
| } | |||
| } | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -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(KZQ_DELETE_FROM_ACTIVE_WATCHERS_WHERE_EXPIRES;); | |||
| mq_add("presence_last_notity", "$uuid(g)", "$var(Query)"); | |||
| $var(Query) = $_s(KZQ_DELETE_FROM_PRESENTITY_WHERE_EXPIRES;); | |||
| mq_add("presence_last_notity", "$uuid(g)", "$var(Query)"); | |||
| $var(Query) = $_s(KZQ_DELETE_FROM_PRESENTITY_WHERE_DIALOG_TERMINATED;); | |||
| 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)"); | |||
| } | |||
| } | |||
| @ -0,0 +1,113 @@ | |||
| ######## 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", "$(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}); | |||
| $var(Username) = $(kzE{kz.json,Username}); | |||
| $var(Now) = $TS; | |||
| $var(Items) = ""; | |||
| $var(Query) = $_s(KZQ_PRESENCE_SEARCH_SUMMARY); | |||
| if($var(Event) != "") { | |||
| $var(Query) = $var(Query) + $_s( and event = "$var(Event)"); | |||
| } | |||
| if($var(Username) != "") { | |||
| $var(Query) = $var(Query) + $_s( and watcher_username = "$var(Username)"); | |||
| } | |||
| $var(Query) = $var(Query) + " order by presentity_uri, event, watcher_username, callid"; | |||
| xlog("L_DEBUG", "$ci| QUERY $var(Query)\n"); | |||
| if (sql_xquery("cb", "$var(Query)", "ra") == 1) | |||
| { | |||
| $var(Subs) = ""; | |||
| $var(Sep1) = ""; | |||
| while($xavp(ra) != $null) { | |||
| $var(Username) = $(xavp(ra=>presentity_uri){s.tolower}); | |||
| $var(Sep2)=""; | |||
| $var(Evt)=""; | |||
| while($xavp(ra) != $null && $var(Username) == $(xavp(ra=>presentity_uri){s.tolower}) ) | |||
| { | |||
| $var(Event) = $xavp(ra=>event); | |||
| $var(Sep3)=""; | |||
| $var(Sub)=""; | |||
| $var(Count) = 0; | |||
| while($xavp(ra) != $null && $var(Username) == $(xavp(ra=>presentity_uri){s.tolower}) && $var(Event) == $xavp(ra=>event)) | |||
| { | |||
| $var(Count) = $var(Count) + 1; | |||
| pv_unset("$xavp(ra)"); | |||
| } | |||
| $var(Evt) = $var(Evt) + $var(Sep2) + $_s("$var(Event)" : $var(Count)); | |||
| $var(Sep2)=", "; | |||
| } | |||
| $var(Subs) = $var(Subs) + $var(Sep1) + $_s("$(var(Username){uri.user})") + " : { " + $var(Evt) + " }"; | |||
| $var(Sep1)=", "; | |||
| } | |||
| } | |||
| xlog("L_DEBUG", "$ci| RESULT \"Subscriptions\" : { $var(Subs) }\n"); | |||
| $var(amqp_payload_request) = $_s({"Event-Category" : "presence", "Event-Name" : "search_resp", "Msg-ID" : "$(kzE{kz.json,Msg-ID})", "Subscriptions" : { $var(Subs) } }); | |||
| kazoo_publish("targeted", "$var(Queue)", $var(amqp_payload_request)); | |||
| } | |||
| route[PRESENCE_SEARCH_DETAIL] | |||
| { | |||
| 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}); | |||
| $var(presentity_uri) = "sip:" + $var(Username) + "@" + $var(Domain); | |||
| $var(Now) = $TS; | |||
| $var(Items) = ""; | |||
| $var(Query) = $_s(KZQ_PRESENCE_SEARCH_DETAIL); | |||
| if($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"); | |||
| if (sql_xquery("cb", "$var(Query)", "ra") == 1) { | |||
| while($xavp(ra) != $null) { | |||
| $var(Event) = $xavp(ra=>event); | |||
| 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,$$,}{re.subst,/\\\\\\"/\\"/g})", "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" : "$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" : "$var(Msg-ID)" }'; | |||
| kazoo_publish("targeted", "$var(Queue)", $var(amqp_payload_request)); | |||
| } | |||
| event_route[kazoo:consumer-event-presence-search-req] | |||
| { | |||
| $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)) { | |||
| 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 | |||
| @ -0,0 +1,154 @@ | |||
| #!trydef PRESENCE_RESET_BLF_DEFER_UPDATE 0 | |||
| kazoo.presence_reset_blf_defer_update = PRESENCE_RESET_BLF_DEFER_UPDATE descr "defers blf reset" | |||
| ##### PRESENCE RESET ROUTINES | |||
| route[PRESENCE_RESET_ROUTE] | |||
| { | |||
| $var(runloop) = 1; | |||
| while(mq_fetch("blf_reset") == 1 && $var(runloop) < MAX_WHILE_LOOPS) { | |||
| $var(Key) = $mqk(blf_reset); | |||
| $sht(nodes=>$var(Key)) = 0; | |||
| $var(runloop) = $var(runloop) + 1; | |||
| } | |||
| } | |||
| route[RESET_PUBLISHER] | |||
| { | |||
| 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)"); | |||
| } | |||
| $var(Query) = $_s(KZQ_PRESENCE_RESET); | |||
| 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] | |||
| { | |||
| xlog("L_INFO", "$var(Msg-ID)|reset|received presence reset ALL\n"); | |||
| sql_query("exec", "delete from presentity"); | |||
| $var(presentities) = $sqlrows(exec); | |||
| xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|removed $var(presentities) presentities\n"); | |||
| } | |||
| route[RESET_ZONE] | |||
| { | |||
| xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|received presence reset for this zone (MY_AMQP_ZONE)\n"); | |||
| route(RESET_ALL); | |||
| } | |||
| route[RESET_SERVER] | |||
| { | |||
| xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|received presence reset for this server (MY_HOSTNAME)\n"); | |||
| route(RESET_ALL); | |||
| } | |||
| 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_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] | |||
| { | |||
| if($(kzE{kz.json,Realm}) == "*" && $(kzE{kz.json,Username}) == "*") { | |||
| xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|received presence reset for cluster\n"); | |||
| route(RESET_ALL); | |||
| } else { | |||
| if($(kzE{kz.json,Realm}) == "*") { | |||
| if($(kzE{kz.json,Username}) == "MY_AMQP_ZONE") { | |||
| route(RESET_ZONE); | |||
| } else { | |||
| if($(kzE{kz.json,Username}) == "MY_HOSTNAME") { | |||
| route(RESET_SERVER); | |||
| } else { | |||
| if($(kzE{kz.json,Username}) =~ "sip:") { | |||
| $var(Msg-ID) = $(kzE{kz.json,Msg-ID}); | |||
| $var(MediaUrl) = $(kzE{kz.json,Username}); | |||
| route(RESET_PUBLISHER); | |||
| } else { | |||
| xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|reset|received presence wildcard reset for $(kzE{kz.json,Username}). 'MY_AMQP_ZONE/MY_HOSTNAME' doesn't care\n"); | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| route(RESET_ACCOUNT); | |||
| } | |||
| } | |||
| } | |||
| event_route[kazoo:consumer-event-presence-reset] | |||
| { | |||
| if($(kzE{kz.json,Username}) == "*" || $(kzE{kz.json,Realm}) == "*") { | |||
| route(RESET_WILDCARD); | |||
| exit(); | |||
| } | |||
| 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'); | |||
| if($xavp(watchers=>message-summary) > 0) { | |||
| 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_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_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_DEBUG", "$(kzE{kz.json,Msg-ID})|reset|skip dialog subscriber notification for $var(presentity)\n"); | |||
| } | |||
| } | |||
| @ -0,0 +1,585 @@ | |||
| ######## Presence server module ######## | |||
| #!trydef PRESENCE_MIN_EXPIRES 300 | |||
| #!trydef PRESENCE_MIN_EXPIRES_ACTION 1 | |||
| #!trydef PRESENCE_MAX_EXPIRES 3600 | |||
| #!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 1 | |||
| #!trydef KZ_PRESENCE_KEEPALIVE_UDP_ONLY 1 | |||
| #!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" | |||
| loadmodule "presence_mwi.so" | |||
| loadmodule "presence_xml.so" | |||
| modparam("presence_dialoginfo", "force_dummy_dialog", 1) | |||
| modparam("presence_dialoginfo", "force_single_dialog", BLF_USE_SINGLE_DIALOG) | |||
| modparam("presence_xml", "force_dummy_presence", 1) | |||
| modparam("presence_xml", "force_active", 1) | |||
| modparam("presence_xml", "disable_winfo", 1) | |||
| modparam("presence_xml", "disable_bla", 1) | |||
| modparam("presence", "subs_db_mode", 3) | |||
| modparam("presence", "expires_offset", 0) | |||
| modparam("presence", "send_fast_notify", 0) | |||
| 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) | |||
| modparam("presence", "max_expires", PRESENCE_MAX_EXPIRES) | |||
| modparam("presence", "sip_uri_match", 1) | |||
| modparam("presence", "waitn_time", 1) | |||
| modparam("presence", "notifier_processes", 1) | |||
| modparam("presence", "db_url", "KAZOO_DB_URL") | |||
| modparam("presence", "xavp_cfg", "pres") | |||
| 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", KZ_PRESENCE_CSEQ_OFFSET) | |||
| modparam("kazoo", "db_url", "KAZOO_DB_URL") | |||
| modparam("kazoo", "pua_mode", 1) | |||
| 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" | |||
| 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 ######## | |||
| route[PRESENCE_NAT] | |||
| { | |||
| 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 | |||
| } | |||
| } | |||
| route[HANDLE_SUBSCRIBE] | |||
| { | |||
| if (!is_method("SUBSCRIBE")) { | |||
| return; | |||
| } | |||
| 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(); | |||
| } | |||
| record_route(); | |||
| if(has_totag()) { | |||
| route(HANDLE_RESUBSCRIBE); | |||
| } else { | |||
| route(HANDLE_NEW_SUBSCRIBE); | |||
| } | |||
| t_release(); | |||
| exit; | |||
| } | |||
| route[DELETE_DUPLICATED_SUBSCRIPTIONS] | |||
| { | |||
| sql_query("exec", 'KZQ_DELETE_FROM_ACTIVE_WATCHERS_WHERE_PRESENTITY'); | |||
| } | |||
| 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); | |||
| }; | |||
| } | |||
| route[HANDLE_NEW_SUBSCRIBE] | |||
| { | |||
| if ($hdr(Event) == "dialog" | |||
| || $hdr(Event) == "presence" | |||
| || $hdr(Event) == "message-summary") { | |||
| if ($tU == $null) { | |||
| xlog("L_INFO", "$ci|stop|ignoring subscribe with empty TO username from a $ua\n"); | |||
| send_reply(400, "Missing TO username"); | |||
| return; | |||
| } | |||
| if ($fU == $null) { | |||
| xlog("L_INFO", "$ci|stop|ignoring subscribe with empty FROM username from a $ua\n"); | |||
| send_reply(400, "Missing FROM username"); | |||
| return; | |||
| } | |||
| if($shtinc(first=>$ci) > 1) { | |||
| sql_query("exec", 'KZQ_HANDLE_NEW_SUBSCRIBE_DELETE1'); | |||
| xlog("L_INFO", "$ci|subscribe|resetting $hdr(Event) subscription from $fU to $tU in realm $fd : $sqlrows(exec)\n"); | |||
| } else { | |||
| $var(presentity_uri) = $ru; | |||
| if($(var(presentity_uri){uri.user}) == "") { | |||
| $var(presentity_uri) = $tu; | |||
| } | |||
| if($shtinc(first=>$fU::$var(presentity_uri)::$fd::$hdr(Event)) > 1) { | |||
| sql_query("exec", 'KZQ_HANDLE_NEW_SUBSCRIBE_DELETE2'); | |||
| xlog("L_INFO", "$ci|subscribe|resetting $hdr(Event) subscription from $fU to $var(presentity_uri) in realm $fd : $sqlrows(exec)\n"); | |||
| } | |||
| } | |||
| 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"); | |||
| } | |||
| } else { | |||
| xlog("L_INFO", "$ci|stop|unsupported subscription package $hdr(Event) from $fU to $tU in realm $fd\n"); | |||
| send_reply(489, "Bad Event"); | |||
| } | |||
| } | |||
| route[SUBSCRIBE_AMQP] | |||
| { | |||
| 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)); | |||
| } | |||
| } | |||
| route[REQUEST_PROBE] | |||
| { | |||
| 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); | |||
| } else { | |||
| if($tU =~ "\*98") { | |||
| $var(mwi) = $(tU{s.substr,3,0}); | |||
| route(REQUEST_MWI); | |||
| } else { | |||
| xlog("L_INFO", "$ci|sub|requesting $hdr(Event) probe for $subs(to_user) in realm $subs(to_domain)\n"); | |||
| $var(rk) = "probes." + $hdr(Event); | |||
| $var(amqp_payload_request) = $_s({"Event-Category" : "presence", "Event-Name" : "probe", "Event-Package" : "$hdr(event)", "Username" : "$tU", "Realm" : "$fd", "Call-ID" : "$ci"}); | |||
| kazoo_publish("presence", "$var(rk)", $var(amqp_payload_request)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| 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)); | |||
| } | |||
| route[HANDLE_PUBLISH] | |||
| { | |||
| 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] | |||
| { | |||
| $var(Query) = $_s(KZQ_HAS_PRESENTITY); | |||
| $var(res) = 0; | |||
| if (sql_xquery("cb", "$var(Query)", "subs") == 1) | |||
| { | |||
| if($xavp(subs) != $null) { | |||
| $var(res) = $xavp(subs=>count); | |||
| pv_unset("$xavp(subs)"); | |||
| } | |||
| } | |||
| return $var(res); | |||
| } | |||
| route[COUNT_PRESENTITIES] | |||
| { | |||
| $var(Query) = $_s(KZQ_COUNT_PRESENTITIES); | |||
| $var(p) = $_s(presence_id='$var(presentity)'); | |||
| if (sql_xquery("cb", "$var(Query)", "subs") == 1) | |||
| { | |||
| while($xavp(subs) != $null) { | |||
| $var(p) = $var(p) + ";" + $xavp(subs=>event) + "=" + $xavp(subs=>count); | |||
| pv_unset("$xavp(subs)"); | |||
| } | |||
| } | |||
| xavp_params_explode($var(p), "watchers"); | |||
| } | |||
| 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) = "presence_id=none"; | |||
| if (sql_xquery("cb", "$var(Query)", "subs") == 1) | |||
| { | |||
| while($xavp(subs) != $null) { | |||
| $var(p) = $var(p) + ";" + $xavp(subs=>event) + "=" + $xavp(subs=>count); | |||
| pv_unset("$xavp(subs)"); | |||
| } | |||
| } | |||
| xavp_params_explode($var(p), "watchers"); | |||
| } | |||
| route[COUNT_ALL_SUBSCRIBERS] | |||
| { | |||
| $var(Query) = $_s(KZQ_COUNT_ALL_SUBSCRIBERS); | |||
| if (sql_xquery("cb", "$var(Query)", "subs") == 1) | |||
| { | |||
| $var(sep) = ""; | |||
| $var(Subscribers) = ""; | |||
| $var(Subscriptions) = ""; | |||
| while($xavp(subs) != $null) { | |||
| $var(Subscribers) = $var(Subscribers) + $var(sep) + $_s("$xavp(subs=>event)" : $xavp(subs=>count_unique)); | |||
| $var(Subscriptions) = $var(Subscriptions) + $var(sep) + $_s("$xavp(subs=>event)" : $xavp(subs=>count)); | |||
| $var(sep) = " , "; | |||
| pv_unset("$xavp(subs)"); | |||
| } | |||
| } | |||
| } | |||
| route[COUNT_SUBSCRIBERS] | |||
| { | |||
| $var(Query) = $_s(KZQ_COUNT_SUBSCRIBERS); | |||
| $var(p) = $_s(presence_id='$var(presentity)'); | |||
| if (sql_xquery("cb", "$var(Query)", "subs") == 1) | |||
| { | |||
| while($xavp(subs) != $null) { | |||
| $var(p) = $var(p) + ";" + $xavp(subs=>event) + "=" + $xavp(subs=>count); | |||
| pv_unset("$xavp(subs)"); | |||
| } | |||
| } | |||
| xavp_params_explode($var(p), "watchers"); | |||
| } | |||
| event_route[kazoo:consumer-event-presence-dialog-update] | |||
| { | |||
| $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; | |||
| route(PRESENCE_FAST_PICKUP_INIT); | |||
| $var(presentity) = $(kzE{kz.json,From}); | |||
| $var(payload) = $kzE; | |||
| route(PRESENCE_UPDATE); | |||
| $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] | |||
| { | |||
| $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 { | |||
| #!ifdef PRESENCE_TRACK_ALL_PKG_MWI | |||
| $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); | |||
| $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] | |||
| { | |||
| $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); | |||
| $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=>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 { | |||
| 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) { | |||
| $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 | |||
| } | |||
| } | |||
| } | |||
| #!define MWI_PRESENCE_BODY $(kzE{re.subst,/"Messages-Waiting"\s*\:\s*"[^"]*"/"State" : "$var(State)"/} \ | |||
| {re.subst,/"From"\s*\:\s*"[^"]*"/"From" : "$var(presentity)"/} \ | |||
| {re.subst,/"From-User"\s*\:\s*"[^"]*"/"From-User" : "$var(user)"/} \ | |||
| {re.subst,/"To"\s*\:\s*"[^"]*"/"To" : "$var(presentity)"/} \ | |||
| {re.subst,/"To-User"\s*\:\s*"[^"]*"/"To-User" : "$var(user)"/} \ | |||
| {re.subst,/"Messages-New"\s*\:\s*[^,]*/"Direction" : "initiator"/} \ | |||
| {re.subst,/"Event-Name"\s*\:\s*"[^"]*"/"Event-Name" : "presence"/}); | |||
| route[MWI_AS_PRESENCE] | |||
| { | |||
| if( $(kzE{kz.json,Extended-Presence-ID}) == "" ) { | |||
| return; | |||
| } | |||
| $var(realm) = $(kzE{kz.json,From-Realm}); | |||
| $var(user) = $(kzE{kz.json,Extended-Presence-ID}); | |||
| $var(presentity) = $_s(sip:$var(user)@$var(realm)); | |||
| if( $(kzE{kz.json,Messages-Waiting}) == "yes" ) { | |||
| $var(State) = "confirmed"; | |||
| } else { | |||
| $var(State) = "terminated"; | |||
| } | |||
| $var(payload) = MWI_PRESENCE_BODY | |||
| $var(JObj) = $var(payload); | |||
| route(PRESENCE_UPDATE); | |||
| } | |||
| route[PRESENCE_BINDINGS] | |||
| { | |||
| #!import_file "presence-custom-bindings.cfg" | |||
| #!ifndef PRESENCE_CUSTOM_BINDINGS | |||
| $var(payload) = $_s({ "name" : "presence", "exchange" : "presence", "type" : "topic", "queue" : "presence-dialog-MY_HOSTNAME", "routing" : ["dialog.*.*", "update.*.*", "mwi_updates.*.*"], "exclusive" : false, "federate" : true }); | |||
| kazoo_subscribe("$var(payload)"); | |||
| #!endif | |||
| route(PRESENCE_API_BINDINGS); | |||
| route(PRESENCE_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": false, "federate": true }); | |||
| 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 | |||
| @ -0,0 +1,223 @@ | |||
| ## PUSHER ROLE | |||
| #!trydef KZ_PUSHER_LOG_CONTACT 0 | |||
| kazoo.pusher_log_contacts = KZ_PUSHER_LOG_CONTACT descr "logs contacts after registration" | |||
| ######## Generic Hash Table container in shared memory ######## | |||
| modparam("htable", "htable", "push_cache=>autoexpire=60;") | |||
| route[PUSHER_ROUTE] | |||
| { | |||
| if ( (!is_method("INVITE")) || (!isflagset(FLAG_INTERNALLY_SOURCED)) || $hdr(X-KAZOO-PUSHER-Token-ID) == $null) | |||
| return; | |||
| xlog("L_INFO", "$ci|pusher|start deliver call to $hdr(X-KAZOO-PUSHER-Token-ID)\n"); | |||
| if(route(PUSHER_PREPARE_PUSH)) { | |||
| if(reg_fetch_contacts("location", "$hdr(X-KAZOO-AOR)", "callee")) { | |||
| $du = $(ulc(callee=>received)); | |||
| $fs = $(ulc(callee=>socket)); | |||
| xlog("L_INFO", "$ci|pusher|routing $hdr(X-KAZOO-AOR) to contact $du\n"); | |||
| send_reply(100, "calling a push device"); | |||
| t_set_fr(0, 2000); | |||
| route(PUSHER_TO_EXTERNAL_RELAY); | |||
| } else { | |||
| send_reply(100, "waking the push device"); | |||
| route(PUSHER_SEND_PUSH_NOTIFICATION); | |||
| } | |||
| } | |||
| exit(); | |||
| } | |||
| route[PUSHER_TO_EXTERNAL_RELAY] | |||
| { | |||
| remove_hf_re("^X-.*"); | |||
| set_forward_no_connect(); | |||
| t_on_branch("MANAGE_BRANCH"); | |||
| t_on_reply("EXTERNAL_REPLY"); | |||
| t_on_failure("PUSHER_EXTERNAL_FAULT"); | |||
| t_relay(); | |||
| } | |||
| failure_route[PUSHER_EXTERNAL_FAULT] | |||
| { | |||
| if (!t_check_status("486|487|603") && $avp(push_sent) != 1) { | |||
| send_reply(182, "sending push notification"); | |||
| route(PUSHER_SEND_PUSH_NOTIFICATION); | |||
| } else if (t_check_status("487")) { | |||
| xlog("L_INFO", "$ci|pusher|push transaction canceled\n"); | |||
| t_reply("$T_reply_code", "pusher canceled"); | |||
| } else { | |||
| xlog("L_INFO", "$ci|pusher|push transaction result - $T_reply_code\n"); | |||
| t_reply("$T_reply_code", "pusher failed"); | |||
| } | |||
| } | |||
| route[PUSHER_PREPARE_PUSH] | |||
| { | |||
| if (t_newtran()) { | |||
| route(PUSHER_PREPARE_PUSH_PAYLOAD); | |||
| remove_hf_re("^X-.*"); | |||
| t_save_lumps(); | |||
| t_set_auto_inv_100(0); | |||
| return 1; | |||
| } else { | |||
| sl_send_reply(500, "error creating transaction for waking the dead guy"); | |||
| return 0; | |||
| } | |||
| } | |||
| 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); | |||
| $var(TokenProxy) = $hdr(X-KAZOO-PUSHER-Token-Proxy); | |||
| #!ifdef PUSHER_TOKEN_PROXY | |||
| $var(TokenProxy) = $_s(PUSHER_TOKEN_PROXY); | |||
| #!endif | |||
| ### token for fast reg ### | |||
| $var(TokenReg) = $uuid(g); | |||
| $sht(push_cache=>$var(TokenReg)) = 1; | |||
| ### caller-id ### | |||
| if($hdr(Remote-Party-ID) != $null) { | |||
| $var(from_user) = $(hdr(Remote-Party-ID){tobody.user}); | |||
| $var(from_name) = $(hdr(Remote-Party-ID){tobody.display}{re.subst,/"//g}); | |||
| } else if($hdr(P-Asserted-Identity) != $null) { | |||
| $var(from_user) = $(hdr(P-Asserted-Identity){tobody.user}); | |||
| $var(from_name) = $(hdr(P-Asserted-Identity){tobody.display}{re.subst,/"//g}); | |||
| } else if($hdr(P-Preferred-Identity) != $null) { | |||
| $var(from_user) = $(hdr(P-Preferred-Identity){tobody.user}); | |||
| $var(from_name) = $(hdr(P-Preferred-Identity){tobody.display}{re.subst,/"//g}); | |||
| } else { | |||
| $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); | |||
| } | |||
| route[PUSHER_SEND_PUSH_NOTIFICATION] | |||
| { | |||
| xlog("L_INFO", "$ci|pusher|sending push notification request\n"); | |||
| xlog("L_DEBUG", "$ci|pusher|pushing to $avp(push_routing_key) : $avp(push_payload)\n"); | |||
| t_set_fr(20000, 20000); | |||
| $avp(push_sent) = 1; | |||
| t_suspend(); | |||
| $sht(push_cache=>$(tu{s.tolower})) = $_s(a=0;index=$T(id_index);label=$T(id_label)); | |||
| kazoo_publish("pushes", $avp(push_routing_key), $avp(push_payload)); | |||
| } | |||
| route[PUSHER_ATTEMPT_REGISTRATION] | |||
| { | |||
| if (!is_method("REGISTER")) { | |||
| return; | |||
| } | |||
| if($hdr(X-Token-Reg) != $null) { | |||
| if($sht(push_cache=>$hdr(X-Token-Reg)) != $null) { | |||
| $var(password) = $null; | |||
| $sht(push_cache=>$hdr(X-Token-Reg)) = $null; | |||
| xlog("L_INFO", "$ci|pusher|registration with x-token-reg $hdr(X-Token-Reg)\n"); | |||
| $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"); | |||
| } | |||
| } | |||
| if($(sel(contact.uri){uri.param,x-token-reg}) != "") { | |||
| if($sht(push_cache=>$(sel(contact.uri){uri.param,x-token-reg})) != $null) { | |||
| $var(password) = $null; | |||
| $sht(push_cache=>$(sel(contact.uri){uri.param,x-token-reg})) = $null; | |||
| xlog("L_INFO", "$ci|pusher|registration with x-token-reg $(sel(contact.uri){uri.param,x-token-reg})\n"); | |||
| $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"); | |||
| } | |||
| } | |||
| if($(sel(contact){tobody.params}{param.value,x-token-reg}) != "") { | |||
| if($sht(push_cache=>$(sel(contact){tobody.params}{param.value,x-token-reg})) != $null) { | |||
| $var(password) = $null; | |||
| $sht(push_cache=>$(sel(contact){tobody.params}{param.value,x-token-reg})) = $null; | |||
| xlog("L_INFO", "$ci|pusher|registration with x-token-reg $(sel(contact){tobody.params}{param.value,x-token-reg})\n"); | |||
| $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"); | |||
| } | |||
| } | |||
| } | |||
| 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) && | |||
| $var(Status) == "Registered") { | |||
| if($sht(push_cache=>$(tu{s.tolower})) != $null) { | |||
| xlog("L_INFO", "$ci|pusher|device registered, delivering the call\n"); | |||
| $var(ref) = $sht(push_cache=>$(tu{s.tolower})); | |||
| $sht(push_cache=>$(tu{s.tolower})) = $null; | |||
| $var(t_index) = $(var(ref){param.value,index}{s.int}); | |||
| $var(t_label) = $(var(ref){param.value,label}{s.int}); | |||
| t_continue("$var(t_index)", "$var(t_label)", "PUSHER_DELIVER_CALL"); | |||
| } | |||
| } | |||
| } | |||
| route[PUSHER_DELIVER_CALL] | |||
| { | |||
| if(reg_fetch_contacts("location", "$hdr(X-KAZOO-AOR)", "callee")) { | |||
| if($sel(cfg_get.kazoo.pusher_log_contacts) == 1) { | |||
| xlog("L_NOTICE", "callee=>aor: $(ulc(callee=>aor))\n"); | |||
| xlog("L_NOTICE", "callee=>count: $(ulc(callee=>count))\n"); | |||
| xlog("L_NOTICE", "callee=>domain: $(ulc(callee=>domain))\n"); | |||
| xlog("L_NOTICE", "callee=>aorhash $(ulc(callee=>aorhash))\n"); | |||
| $var(i) = 0; | |||
| while($var(i) < $(ulc(callee=>count))) { | |||
| xlog("L_NOTICE", "--- contact [$var(i)]\n"); | |||
| xlog("L_NOTICE", "callee=>addr: $(ulc(callee=>addr)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>path: $(ulc(callee=>path)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>received: $(ulc(callee=>received)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>expires: $(ulc(callee=>expires)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>callid: $(ulc(callee=>callid)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>regid: $(ulc(callee=>regid)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>q: $(ulc(callee=>q)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>cseq: $(ulc(callee=>cseq)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>flags: $(ulc(callee=>flags)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>cflags: $(ulc(callee=>cflags)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>user_agent: $(ulc(callee=>user_agent)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>socket: $(ulc(callee=>socket)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>modified: $(ulc(callee=>modified)[$var(i)])\n"); | |||
| xlog("L_NOTICE", "callee=>methods: $(ulc(callee=>methods)[$var(i)])\n"); | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| } | |||
| $var(idx) = $ulc(callee=>count) - 1; | |||
| $du = $(ulc(callee=>received)[$var(idx)]); | |||
| $fs = $(ulc(callee=>socket)[$var(idx)]); | |||
| t_set_fr(30000, 30000); | |||
| route(PUSHER_TO_EXTERNAL_RELAY); | |||
| } else { | |||
| t_reply(486, "Failed to lookup after resume"); | |||
| } | |||
| } | |||
| @ -0,0 +1,254 @@ | |||
| ######## DoS prevention module ######## | |||
| modparam("htable", "timer_interval", 10) | |||
| modparam("htable", "htable", "rps=>size=8;initval=0;autoexpire=60") | |||
| modparam("htable", "htable", "rpm=>size=8;initval=0;autoexpire=180") | |||
| modparam("htable", "htable", "tps=>size=8;initval=0;autoexpire=60") | |||
| modparam("htable", "htable", "tpm=>size=8;initval=0;autoexpire=180") | |||
| modparam("htable", "htable", "rate_limits=>initval=-1;autoexpire=60") # initval = -1 means that record is expired and we need an update from DB | |||
| #!trydef RATE_LIMIT_MESSAGE "Over rate Limit" | |||
| #!trydef RATE_LIMIT_CODE "603" | |||
| #!trydef IP_REGEX "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}" | |||
| route[DOS_PREVENTION] { | |||
| # If packet came from platform or from 4 class MERA, do not check it | |||
| if (isflagset(FLAG_INTERNALLY_SOURCED) || isflagset(FLAG_TRUSTED_SOURCE) ) { | |||
| xlog("L_DEBUG", "$ci |RL| Trusted source IP($si) ignoring\n"); | |||
| return; | |||
| } | |||
| # Initially we do not want to get data | |||
| $var(with-realm-request) = "false"; | |||
| $var(with-realm-total) = "false"; | |||
| $var(with-device-request) = "false"; | |||
| $var(with-device-total) = "false"; | |||
| $var(method-key) = "Method"; | |||
| $var(method-value) = "\"TOTAL\""; | |||
| # SIP methods INVITE and REGISTER have personal counters | |||
| if ((is_method("INVITE") || is_method("REGISTER")) && (!isflagset(FLAG_IS_REPLY))) { | |||
| $var(lrpm_realm) = $fd+"/"+$rm+"/min"; | |||
| $var(lrps_realm) = $fd+"/"+$rm+"/sec"; | |||
| $var(lrpm_device) = $fU+"@"+$fd+"/"+$rm+"/min"; | |||
| $var(lrps_device) = $fU+"@"+$fd+"/"+$rm+"/sec"; | |||
| $var(method-value) = "\"" + $rm + "\""; | |||
| } | |||
| # For BYE method we use REALM from To SIP header | |||
| if ($fd =~ IP_REGEX) { | |||
| xlog("L_WARNING","$ci|RL-realm log| Fixup for $rm method with IP in from URI: use to-domain\n"); | |||
| $var(ltpm_realm) = $td+"/TOTAL/min"; | |||
| $var(ltps_realm) = $td+"/TOTAL/sec"; | |||
| $var(ltpm_device) = $fU+"@"+$td+"/TOTAL/min"; | |||
| $var(ltps_device) = $fU+"@"+$td+"/TOTAL/sec"; | |||
| $var(entity) = $td; | |||
| } else { | |||
| $var(ltpm_realm) = $fd+"/TOTAL/min"; | |||
| $var(ltps_realm) = $fd+"/TOTAL/sec"; | |||
| $var(ltpm_device) = $fU+"@"+$fd+"/TOTAL/min"; | |||
| $var(ltps_device) = $fU+"@"+$fd+"/TOTAL/sec"; | |||
| $var(entity) = $fd; | |||
| } | |||
| # REALM check | |||
| if ((is_method("INVITE") || is_method("REGISTER")) && (!isflagset(FLAG_IS_REPLY))) { | |||
| if ($sht(rate_limits=>$var(lrpm_realm)) == -1 | |||
| || $sht(rate_limits=>$var(lrps_realm)) == -1) { | |||
| xlog("L_INFO", "$ci|RL-realm log| Can't find HASHed rate for $var(entity) with $rm method\n"); | |||
| $var(with-realm-request) = "true"; | |||
| } | |||
| } | |||
| if ($sht(rate_limits=>$var(ltpm_realm)) == -1 | |||
| || $sht(rate_limits=>$var(ltps_realm)) == -1) { | |||
| xlog("L_INFO", "$ci|RL-realm log| Can't find HASHed rate for $var(entity) with $rm method\n"); | |||
| $var(with-realm-total) = "true"; | |||
| } | |||
| if (not_empty("$fU")) { | |||
| if ($fd =~ IP_REGEX) { | |||
| xlog("L_WARNING","$ci|RL-realm log| Fixup for $rm method with IP in from URI: use to-domain\n"); | |||
| $var(entity) = $fU+"@"+$td; | |||
| } else { | |||
| $var(entity) = $fU+"@"+$fd; | |||
| } | |||
| #DEVICE check | |||
| if ((is_method("INVITE") || is_method("REGISTER")) && (!isflagset(FLAG_IS_REPLY))) { | |||
| if ($sht(rate_limits=>$var(lrpm_device)) == -1 | |||
| || $sht(rate_limits=>$var(lrps_device)) == -1) { | |||
| xlog("L_INFO", "$ci|RL-device log| Can't find HASHed rate for $var(entity) with $rm method\n"); | |||
| $var(with-device-request) = "true"; | |||
| } | |||
| } | |||
| if ($sht(rate_limits=>$var(ltpm_device)) == -1 || $sht(rate_limits=>$var(ltps_device)) == -1) { | |||
| xlog("L_INFO", "$ci|RL-device log| Can't find HASHed rate for $var(entity) with $rm method\n"); | |||
| $var(with-device-total) = "true"; | |||
| } | |||
| } | |||
| if ((is_method("INVITE") || is_method("REGISTER")) | |||
| && (($var(with-device-request) == "true" && $var(with-device-total) == "true") | |||
| || ($var(with-realm-request) == "true" && $var(with-realm-total) == "true"))) { | |||
| $var(method-key) = "Method-List"; | |||
| $var(method-value) = "[\"" + $rm + "\", \"TOTAL\"]"; | |||
| } | |||
| if ( $var(with-device-request) == "true" | |||
| || $var(with-device-total) == "true" | |||
| || $var(with-realm-request) == "true" | |||
| || $var(with-realm-total) == "true" ) { | |||
| avp_printf("$avp(s:query-request)", "{\"Entity\" : \"$var(entity)\", \"$var(method-key)\" : $var(method-value), \"Event-Category\" : \"rate_limit\", \"Event-Name\" : \"query\", \"With-Realm\" : $var(with-realm-request)}"); | |||
| xlog("L_INFO", "$ci|RL log| Query: $avp(s:query-request)\n"); | |||
| sl_send_reply("100", "Attempting K query"); | |||
| if (kazoo_query("frontier", "sbc_config", $avp(s:query-request), "$var(amqp_result)")) { | |||
| xlog("L_INFO", "$ci|RL log| Response: $var(amqp_result)\n"); | |||
| kazoo_json($var(amqp_result), "Realm.Minute." + $rm, "$var(realm-min)"); | |||
| kazoo_json($var(amqp_result), "Realm.Second." + $rm, "$var(realm-sec)"); | |||
| kazoo_json($var(amqp_result), "Realm.Minute.TOTAL", "$var(realm-min-total)"); | |||
| kazoo_json($var(amqp_result), "Realm.Second.TOTAL", "$var(realm-sec-total)"); | |||
| kazoo_json($var(amqp_result), "Device.Minute." + $rm, "$var(device-min)"); | |||
| kazoo_json($var(amqp_result), "Device.Second." + $rm, "$var(device-sec)"); | |||
| kazoo_json($var(amqp_result), "Device.Minute.TOTAL", "$var(device-min-total)"); | |||
| kazoo_json($var(amqp_result), "Device.Second.TOTAL", "$var(device-sec-total)"); | |||
| if ( not_empty("$var(realm-min)") ) { | |||
| $sht(rate_limits=>$var(lrpm_realm)) = $(var(realm-min){s.int}); | |||
| xlog("L_INFO", "$ci|RL-realm log| $rm DB=>HASH for $var(lrpm_realm)=$sht(rate_limits=>$var(lrpm_realm))\n"); | |||
| } | |||
| if ( not_empty("$var(realm-sec)") ) { | |||
| $sht(rate_limits=>$var(lrps_realm)) = $(var(realm-sec){s.int}); | |||
| xlog("L_INFO", "$ci|RL-realm log| $rm DB=>HASH for $var(lrps_realm)=$sht(rate_limits=>$var(lrps_realm))\n"); | |||
| } | |||
| if ( not_empty("$var(realm-min-total)") ) { | |||
| $sht(rate_limits=>$var(ltpm_realm)) = $(var(realm-min-total){s.int}); | |||
| xlog("L_INFO", "$ci|RL-realm log| $rm DB=>HASH for $var(ltpm_realm)=$sht(rate_limits=>$var(ltpm_realm))\n"); | |||
| } | |||
| if ( not_empty("$var(realm-sec-total)") ) { | |||
| $sht(rate_limits=>$var(ltps_realm)) = $(var(realm-sec-total){s.int}); | |||
| xlog("L_INFO", "$ci|RL-realm log| $rm DB=>HASH for $var(ltps_realm)=$sht(rate_limits=>$var(ltps_realm))\n"); | |||
| } | |||
| if ( not_empty("$var(device-min)") ) { | |||
| $sht(rate_limits=>$var(lrpm_device)) = $(var(device-min){s.int}); | |||
| xlog("L_INFO", "$ci|RL-device log| $rm DB=>HASH for $var(lrpm_device)=$sht(rate_limits=>$var(lrpm_device))\n"); | |||
| } | |||
| if ( not_empty("$var(device-sec)") ) { | |||
| $sht(rate_limits=>$var(lrps_device)) = $(var(device-sec){s.int}); | |||
| xlog("L_INFO", "$ci|RL-device log| $rm DB=>HASH for $var(lrps_device)=$sht(rate_limits=>$var(lrps_device))\n"); | |||
| } | |||
| if ( not_empty("$var(device-min-total)") ) { | |||
| $sht(rate_limits=>$var(ltpm_device)) = $(var(device-min-total){s.int}); | |||
| xlog("L_INFO", "$ci|RL-device log| $rm DB=>HASH for $var(ltpm_device)=$sht(rate_limits=>$var(ltpm_device))\n"); | |||
| } | |||
| if ( not_empty("$var(device-sec-total)") ) { | |||
| $sht(rate_limits=>$var(ltps_device)) = $(var(device-sec-total){s.int}); | |||
| xlog("L_INFO", "$ci|RL-device log| $rm DB=>HASH for $var(ltps_device)=$sht(rate_limits=>$var(ltps_device))\n"); | |||
| } | |||
| } else { | |||
| xlog("L_ERROR", "$ci|RL log| $rm DB unreachable for entity: $var(entity)\n"); | |||
| return; | |||
| } | |||
| } | |||
| if ($fd =~ IP_REGEX) { | |||
| xlog("L_WARNING","$ci|RL-device log| Fixup for $rm method with IP in from URI: use to-domain\n"); | |||
| $var(entity) = $td; | |||
| } else { | |||
| $var(entity) = $fd; | |||
| } | |||
| $var(entity-type) = "realm"; | |||
| if ((is_method("INVITE") || is_method("REGISTER")) && (!isflagset(FLAG_IS_REPLY))) { | |||
| $var(lrpm) = $sht(rate_limits=>$var(lrpm_realm)); | |||
| $var(lrps) = $sht(rate_limits=>$var(lrps_realm)); | |||
| } | |||
| $var(ltpm) = $sht(rate_limits=>$var(ltpm_realm)); | |||
| $var(ltps) = $sht(rate_limits=>$var(ltps_realm)); | |||
| route(DO_DOS_PREVENTION); | |||
| if ( not_empty("$fU") ) { | |||
| if ($fd =~ IP_REGEX) { | |||
| $var(entity) = $fU+"@"+$td; | |||
| xlog("L_WARNING","$ci|RL-device log| Fixup for $rm method with IP in from URI: use to-domain\n"); | |||
| } else { | |||
| $var(entity) = $fU+"@"+$fd; | |||
| } | |||
| $var(entity-type) = "device"; | |||
| if ((is_method("INVITE") || is_method("REGISTER")) && (!isflagset(FLAG_IS_REPLY))) { | |||
| $var(lrpm) = $sht(rate_limits=>$var(lrpm_device)); | |||
| $var(lrps) = $sht(rate_limits=>$var(lrps_device)); | |||
| } | |||
| $var(ltpm) = $sht(rate_limits=>$var(ltpm_device)); | |||
| $var(ltps) = $sht(rate_limits=>$var(ltps_device)); | |||
| route(DO_DOS_PREVENTION); | |||
| } | |||
| } | |||
| # This route do counting and decide either to ACCEPT or DECLINE packet | |||
| route[DO_DOS_PREVENTION] { | |||
| # Personal counters for INVITE and REGISTER | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| $var(rpm) = $var(entity)+":"+$rm+":min:"+$timef(%Y/%m/%d_%H_%M_00); | |||
| $var(rps) = $var(entity)+":"+$rm+":sec:"+$timef(%Y/%m/%d_%H_%M_%S); | |||
| } | |||
| # Commmon counters for ALL packet including INVITE and REGISTER | |||
| $var(tpm) = $var(entity)+":TOTAL:min:"+$timef(%Y/%m/%d_%H_%M_00); | |||
| $var(tps) = $var(entity)+":TOTAL:sec:"+$timef(%Y/%m/%d_%H_%M_%S); | |||
| # Personal debug for INVITE and REGISTER | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| L/C for $var(rpm) = $var(lrpm)/$sht(rpm=>$var(rpm))\n"); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| L/C for $var(rps) = $var(lrps)/$sht(rps=>$var(rps))\n"); | |||
| } | |||
| # Commmon debug for ALL packet including INVITE and REGISTER | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| L/C for $var(tpm) = $var(ltpm)/$sht(tpm=>$var(tpm))\n"); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| L/C for $var(tps) = $var(ltps)/$sht(tps=>$var(tps))\n"); | |||
| # Personal increment just for INVITE and REGISTER | |||
| if ((is_method("INVITE") || is_method("REGISTER")) && (!isflagset(FLAG_IS_REPLY))) { | |||
| $sht(rpm=>$var(rpm)) = $shtinc(rpm=>$var(rpm)); | |||
| $sht(rps=>$var(rps)) = $shtinc(rps=>$var(rps)); | |||
| } | |||
| # Commmon increment for ALL packet including INVITE and REGISTER | |||
| $sht(tpm=>$var(tpm)) = $shtinc(tpm=>$var(tpm)); | |||
| $sht(tps=>$var(tps)) = $shtinc(tps=>$var(tps)); | |||
| # Personal checks for INVITE and REGISTER | |||
| if ((is_method("INVITE") || is_method("REGISTER")) && (!isflagset(FLAG_IS_REPLY))) { | |||
| if ($sht(rps=>$var(rps)) > $var(lrps)) { | |||
| sl_send_reply(RATE_LIMIT_CODE, RATE_LIMIT_MESSAGE); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of $rm $var(rps) rate limits: $sht(rps=>$var(rps)) > $var(lrps))\n"); | |||
| exit; | |||
| } | |||
| if ($sht(rpm=>$var(rpm)) > $var(lrpm)) { | |||
| sl_send_reply(RATE_LIMIT_CODE, RATE_LIMIT_MESSAGE); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of $rm $var(rpm) rate limits: $sht(rpm=>$var(rpm)) > $var(lrpm))\n"); | |||
| exit; | |||
| } | |||
| } | |||
| # Commmon checks for ALL packet including INVITE and REGISTER | |||
| if ($sht(tps=>$var(tps)) > $var(ltps)) { | |||
| if (isflagset(FLAG_IS_REPLY)) { | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of TOTAL($rm::$rs $rr) $var(tps) rate limits: $sht(tps=>$var(tps)) > $var(ltps))\n"); | |||
| } else { | |||
| sl_send_reply(RATE_LIMIT_CODE, RATE_LIMIT_MESSAGE); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of TOTAL($rm) $var(tps) rate limits: $sht(tps=>$var(tps)) > $var(ltps))\n"); | |||
| } | |||
| exit; | |||
| } | |||
| if ($sht(tpm=>$var(tpm)) > $var(ltpm)) { | |||
| if (isflagset(FLAG_IS_REPLY)) { | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of TOTAL($rm::$rs $rr) $var(tpm) rate limits: $sht(tpm=>$var(tpm)) > $var(ltpm))\n"); | |||
| } else { | |||
| sl_send_reply(RATE_LIMIT_CODE, RATE_LIMIT_MESSAGE); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of TOTAL($rm) $var(tpm) rate limits: $sht(tpm=>$var(tpm)) > $var(ltpm))\n"); | |||
| } | |||
| exit; | |||
| } | |||
| } | |||
| @ -0,0 +1,588 @@ | |||
| #### NAT PINGING PARAMS ### | |||
| #!trydef REGISTRAR_NAT_PING_INTERVAL 0 | |||
| ## 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_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 0 | |||
| #!trydef KZ_REGISTRAR_KEEPALIVE_UDP_ONLY 1 | |||
| #!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;REGISTRAR_S_WARM_CACHE") | |||
| ####### Authentication Interface module ########## | |||
| loadmodule "auth.so" | |||
| ####### 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", 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", "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) | |||
| ####### 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) | |||
| 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", "contact_max_size", REGISTRAR_CONTACT_MAX_SIZE) | |||
| ####### 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[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 =~ "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") | |||
| )) { | |||
| xlog("L_DEBUG", "$ci|log|NOT set nat pinging\n"); | |||
| setbflag(FLB_NATSIPPING); | |||
| } | |||
| } else { | |||
| $avp(AVP_RECV_PARAM) = $su; | |||
| } | |||
| } | |||
| route[HANDLE_REGISTER] | |||
| { | |||
| if (!is_method("REGISTER")) { | |||
| return; | |||
| } | |||
| #!ifdef PUSHER_ROLE | |||
| route(PUSHER_ATTEMPT_REGISTRATION); | |||
| #!endif | |||
| if($sel(cfg_get.kazoo.registrar_failover) == 1) { | |||
| xlog("L_INFO", "$ci|log|register|forcing failover\n"); | |||
| update_stat("registrar:force_failover", "+1"); | |||
| drop; | |||
| } | |||
| 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($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; | |||
| } | |||
| 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; | |||
| } | |||
| } | |||
| 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; | |||
| } | |||
| if (!t_newtran()) { | |||
| 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($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(); | |||
| } | |||
| } | |||
| failure_route[KZ_AUTHORIZATION_TIMEOUT] | |||
| { | |||
| 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 { | |||
| 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(); | |||
| } | |||
| onreply_route[KZ_AUTHORIZATION_REPLY] | |||
| { | |||
| $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 (!pv_auth_check("$fd", "$var(password)", "0", "0")) { | |||
| #!ifdef ANTIFLOOD_ROLE | |||
| route(ANITFLOOD_FAILED_AUTH); | |||
| #!endif | |||
| 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 | |||
| route(ANTIFLOOD_SUCCESSFUL_AUTH); | |||
| #!endif | |||
| # 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 && $var(password) != $null) { | |||
| xlog("L_INFO", "$ci|log|caching sip credentials for $Au\n"); | |||
| }; | |||
| $sht(auth_cache=>$Au) = $var(password); | |||
| route(REGISTRAR_NAT_FLAGS); | |||
| $var(save_result) = save("location", "0x04"); | |||
| 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) { | |||
| $var(new_reg) = "true"; | |||
| } else { | |||
| $var(new_reg) = "false"; | |||
| } | |||
| } | |||
| 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"; | |||
| } | |||
| $var(ip) = $Ri; | |||
| if(af==INET6) { | |||
| $var(ip) = "[" + $Ri + "]"; | |||
| } | |||
| # allow port redirection on registration | |||
| switch($proto) | |||
| { | |||
| #!ifdef KZ_WEBSOCKETS_REGISTRAR_PORT | |||
| case "ws": | |||
| case "wss": | |||
| $var(port) = KZ_WEBSOCKETS_REGISTRAR_PORT; | |||
| break; | |||
| #!endif | |||
| #!ifdef KZ_TLS_REGISTRAR_PORT | |||
| case "tls": | |||
| $var(port) = KZ_TLS_REGISTRAR_PORT; | |||
| break; | |||
| #!endif | |||
| #!ifdef KZ_UDP_REGISTRAR_PORT | |||
| case "udp": | |||
| $var(port) = KZ_UDP_REGISTRAR_PORT; | |||
| break; | |||
| #!endif | |||
| #!ifdef KZ_TCP_REGISTRAR_PORT | |||
| case "tcp": | |||
| $var(port) = KZ_TCP_REGISTRAR_PORT; | |||
| break; | |||
| #!endif | |||
| default: | |||
| $var(port) = $Rp; | |||
| } | |||
| $var(AdvIP) = $RAi; | |||
| if(af==INET6) { | |||
| $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: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 : $var(expires)\n"); | |||
| #!ifdef PUSHER_ROLE | |||
| route(PUSHER_ON_REGISTRATION); | |||
| #!endif | |||
| #!ifdef KEEPALIVE_ROLE | |||
| route(KEEPALIVE_ON_REGISTRATION); | |||
| #!endif | |||
| exit; | |||
| } | |||
| ## kazoo event route , {"directory", "reg_flush") => reg-flush by kamailio limitations | |||
| ## when a Event-Category or Event-Name has a underscore (_) we need to declare it with a dash (-) | |||
| event_route[kazoo:consumer-event-directory-reg-flush] | |||
| { | |||
| $var(user) = $(kzE{kz.json,Username}) + "@" + $(kzE{kz.json,Realm}); | |||
| xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|log|received directory flush for $var(user)\n"); | |||
| if ($sht(auth_cache=>$var(user)) != $null) { | |||
| $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))) { | |||
| unregister("location", "sip:$(ulc(caller=>aor))", "$(ulc(caller=>ruid)[$var(i)])"); | |||
| $var(i) = $var(i) + 1; | |||
| } | |||
| reg_free_contacts("caller"); | |||
| } | |||
| #!ifdef ANTIFLOOD_ROLE | |||
| route(ANTIFLOOD_RESET_AUTH); | |||
| #!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") { | |||
| $var(proto) = "wss"; | |||
| } | |||
| $var(handle) = 0; | |||
| switch($var(proto)) | |||
| { | |||
| case "ws": | |||
| case "wss": | |||
| if($sel(cfg_get.kazoo.registrar_handle_expired_ws) == 1) { | |||
| $var(handle) = 1; | |||
| } | |||
| break; | |||
| case "tls": | |||
| if($sel(cfg_get.kazoo.registrar_handle_expired_tls) == 1) { | |||
| $var(handle) = 1; | |||
| } | |||
| break; | |||
| case "tcp": | |||
| if($sel(cfg_get.kazoo.registrar_handle_expired_tcp) == 1) { | |||
| $var(handle) = 1; | |||
| } | |||
| break; | |||
| case "udp": | |||
| if($sel(cfg_get.kazoo.registrar_handle_expired_udp) == 1) { | |||
| $var(handle) = 1; | |||
| } | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| if($var(handle) == 1) { | |||
| $var(aor) = $_s(sip:$ulc(exp=>aor)); | |||
| $var(username) = $(var(aor){uri.user}); | |||
| $var(domain) = $(var(aor){uri.host}); | |||
| $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 registration removal with contact : $ulc(exp=>addr)\n"); | |||
| } | |||
| xlog("L_INFO", "$ulc(exp=>callid)|expired|removed registration for $ulc(exp=>aor) with contact : $ulc(exp=>addr)\n"); | |||
| } | |||
| #!ifdef REGISTRAR_SYNC_ROLE | |||
| #!include_file "registrar-sync-role.cfg" | |||
| #!endif | |||
| # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab | |||
| @ -0,0 +1,31 @@ | |||
| ######## Presence sync server module ######## | |||
| event_route[kazoo:consumer-event-directory-reg-sync] | |||
| { | |||
| $var(Server) = $(kzE{kz.json,Server-ID}); | |||
| xlog("L_INFO", "received registrar sync from $var(Server) : $kzE\n"); | |||
| if (sql_xquery("cb", "select * from location", "ra") == 1) | |||
| { | |||
| while($xavp(ra) != $null) { | |||
| $var(runloop) = 1; | |||
| while($xavp(ra) != $null && $var(runloop) < MAX_WHILE_LOOPS ) { | |||
| if(registered("location", "sip:$xavp(ra=>username)@$xavp(ra=>domain)", 0, 1)) { | |||
| xlog("L_INFO", "[id, username, domain] = [$xavp(ra=>id), $xavp(ra=>username), $xavp(ra=>domain)]\n"); | |||
| $var(Expires) = $xavp(ra=>expires) - $TS; | |||
| $var(amqp_payload_request) = '{"Event-Category" : "directory", "Event-Name" : "reg_success", "Status" : "Success", "Event-Timestamp" : $TS, "Expires" : $(var(Expires){s.int}), "First-Registration" : false, "Contact" : "$(xavp(ra=>contact){s.escape.common})", "Call-ID" : "$xavp(ra=>callid)", "Realm" : "$xavp(ra=>domain)", "Username" : "$xavp(ra=>username)", "From-User" : "$xavp(ra=>username)", "From-Host" : "$xavp(ra=>domain)", "To-User" : "$xavp(ra=>username)", "To-Host" : "$xavp(ra=>domain)", "User-Agent" : "$(xavp(ra=>user_agent){s.escape.common})" , "Custom-Channel-Vars" : $xavp(ulattrs=>custom_channel_vars), "Proxy-Path" : "sip:$(xavp(ra=>socket){s.substr,4,0})" }'; | |||
| kazoo_publish("targeted", "$var(Server)", $var(amqp_payload_request)); | |||
| } | |||
| pv_unset("$xavp(ra)"); | |||
| $var(runloop) = $var(runloop) + 1; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| route[REGISTRAR_SYNC_BINDINGS] | |||
| { | |||
| $var(payload) = $_s({"exchange": "registrar", "type": "topic", "queue": "registrar-sync-MY_HOSTNAME", "routing": "registration.sync"}); | |||
| kazoo_subscribe("$var(payload)"); | |||
| } | |||
| @ -0,0 +1,12 @@ | |||
| ####### Responder Logic ######## | |||
| route[HANDLE_RESPOND] | |||
| { | |||
| $var(header) = $hdr(X-KAZOO-Respond-With); | |||
| if (not_empty("$var(header)")) { | |||
| $var(code) = $(var(header){s.substr,0,3}{s.int}); | |||
| $var(msg) = $(var(header){s.substr,4,0}); | |||
| xlog("L_INFO", "$ci|end|Responding with [$var(code)/$var(msg)]\n"); | |||
| sl_send_reply("$var(code)", "$var(msg)"); | |||
| exit(); | |||
| } | |||
| } | |||
| @ -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; | |||
| } | |||
| } | |||
| @ -0,0 +1,97 @@ | |||
| ################################# | |||
| ## 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 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" | |||
| kazoo.trace_external_incoming = KZ_TRACE_EXTERNAL_INCOMING descr "traces the original request as received from external sources" | |||
| kazoo.trace_external_outgoing = KZ_TRACE_EXTERNAL_OUTGOING descr "traces the outgoing request to internal sources after possible modification" | |||
| ####### Siptrace module ########## | |||
| loadmodule "siptrace.so" | |||
| 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) | |||
| modparam("siptrace", "trace_to_database", 0) | |||
| ## `trace_flag` value must be equal to FLAG_SIP_TRACE value at head of "default.cfg" file | |||
| modparam("siptrace", "trace_flag", 8) | |||
| modparam("siptrace", "trace_on", KZ_TRACE) | |||
| route[SIP_TRACE_INTERNAL] | |||
| { | |||
| if($sel(cfg_get.kazoo.trace_internal) == 0) { | |||
| return; | |||
| } | |||
| if($sel(cfg_get.kazoo.trace_internal_incoming) == 1) { | |||
| sip_trace(); | |||
| } | |||
| if($sel(cfg_get.kazoo.trace_internal_outgoing) == 1) { | |||
| setflag(FLAG_SIP_TRACE); | |||
| } | |||
| } | |||
| route[SIP_TRACE_EXTERNAL] | |||
| { | |||
| if($sel(cfg_get.kazoo.trace_external) == 0) { | |||
| return; | |||
| } | |||
| if($sel(cfg_get.kazoo.trace_external_incoming) == 1) { | |||
| sip_trace(); | |||
| } | |||
| if($sel(cfg_get.kazoo.trace_external_outgoing) == 1) { | |||
| setflag(FLAG_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_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); | |||
| } | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| enable_tls = yes | |||
| listen=TLS_SIP | |||
| listen=TLS_ALG_SIP | |||
| ####### TLS Parameters ######### | |||
| loadmodule "tls.so" | |||
| modparam("tls", "config", "/etc/kazoo/kamailio/tls.cfg") | |||
| modparam("tls", "low_mem_threshold1", 1) | |||
| modparam("tls", "low_mem_threshold2", 1) | |||
| modparam("debugger", "mod_level", "tls=1") | |||
| @ -0,0 +1,52 @@ | |||
| # | |||
| # $Id$ | |||
| # | |||
| # Example Kamailio TLS Configuration File | |||
| # | |||
| # This is the default server domain, settings | |||
| # in this domain will be used for all incoming | |||
| # connections that do not match any other server | |||
| # domain in this configuration file. | |||
| # | |||
| # We do not enable anything else than TLSv1 | |||
| # over the public internet. Clients do not have | |||
| # to present client certificates by default. | |||
| # | |||
| [server:default] | |||
| method = SSLv23 | |||
| verify_certificate = no | |||
| require_certificate = no | |||
| #crl = /etc/kazoo/kamailio/certs/crl.pem | |||
| certificate = /etc/kazoo/kamailio/certs/cert.pem | |||
| private_key = /etc/kazoo/kamailio/certs/key.pem | |||
| #ca_list = /etc/kazoo/kamailio/certs/ca.pem | |||
| # This is the default client domain, settings | |||
| # in this domain will be used for all outgoing | |||
| # TLS connections that do not match any other | |||
| # client domain in this configuration file. | |||
| # We require that servers present valid certificate. | |||
| # | |||
| [client:default] | |||
| verify_certificate = no | |||
| require_certificate = no | |||
| # This is an example server domain for TLS connections | |||
| # received from the loopback interface. We allow | |||
| # the use of SSLv2 and SSLv3 protocols here, we do | |||
| # not require that clients present client certificates | |||
| # but if they present it it must be valid. We also use | |||
| # a special certificate and CA list for loopback | |||
| # interface. | |||
| # | |||
| #[server:127.0.0.1:5061] | |||
| #method = SSLv23 | |||
| #verify_certificate = yes | |||
| #require_certificate = no | |||
| #private_key = /etc/kazoo/kamailio/certs/local_key.pem | |||
| #certificate = /etc/kazoo/kamailio/certs/local_cert.pem | |||
| #verify_depth = 3 | |||
| #ca_list = /etc/kazoo/kamailio/certs/local_ca.pem | |||
| #crl = /etc/kazoo/kamailio/certs/local_crl.pem | |||
| @ -0,0 +1,57 @@ | |||
| route[FILTER_REQUEST] | |||
| { | |||
| # allow request from internal network or from whitelist | |||
| if (isflagset(FLAG_TRUSTED_SOURCE)) { | |||
| return; | |||
| } | |||
| # drop requests with no To domain or IP To domain (friendly-scanner) | |||
| if (is_method("REGISTER|SUBSCRIBE")) { | |||
| route(FILTER_TO_DOMAIN); | |||
| route(FILTER_FROM_DOMAIN); | |||
| } | |||
| # drop Invite with IP auth realm | |||
| if (is_method("INVITE")) { | |||
| route(FILTER_REQUEST_DOMAIN); | |||
| route(FILTER_AUTHORIZATION_DOMAIN); | |||
| } | |||
| } | |||
| route[FILTER_REQUEST_DOMAIN] | |||
| { | |||
| if ($rd =~ "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}") { | |||
| xlog("L_WARN", "$ci|end|dropping $rm request with IP domain\n"); | |||
| drop(); | |||
| exit(); | |||
| } | |||
| } | |||
| route[FILTER_AUTHORIZATION_DOMAIN] | |||
| { | |||
| if (is_present_hf("Proxy-Authorization") && | |||
| $ar =~ "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}" ) { | |||
| xlog("L_WARN", "$ci|end|dropping request with IP domain in Proxy-Authorization header\n"); | |||
| drop(); | |||
| exit; | |||
| } | |||
| } | |||
| route[FILTER_FROM_DOMAIN] | |||
| { | |||
| if ($fd =~ "([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})") { | |||
| xlog("L_WARN", "$ci|end|dropping request with IP domain in From header\n"); | |||
| drop(); | |||
| exit; | |||
| } | |||
| } | |||
| route[FILTER_TO_DOMAIN] | |||
| { | |||
| if ($td =~ "([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})") { | |||
| xlog("L_WARN", "$ci|end|dropping request with IP domain in To header\n"); | |||
| drop(); | |||
| exit; | |||
| } | |||
| } | |||
| @ -0,0 +1,147 @@ | |||
| tcp_accept_no_cl=yes | |||
| listen=TCP_WS | |||
| #!ifdef TLS_ROLE | |||
| listen=TLS_WSS | |||
| #!endif | |||
| ######## NAT Traversal module - signaling functions ######## | |||
| #!ifndef NATHELPER_LOADED | |||
| #!trydef NATHELPER_LOADED | |||
| loadmodule "nathelper.so" | |||
| #!endif | |||
| ######## Generic Hash Table container in shared memory ######## | |||
| modparam("htable", "htable", "websockets=>size=16;initval=0") | |||
| ######## Basic HTTP request handling ######## | |||
| loadmodule "xhttp.so" | |||
| #!trydef WS_KEEPALIVE_MECHANISM 3 | |||
| #!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", 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 ######## | |||
| 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] | |||
| { | |||
| set_reply_close(); | |||
| set_reply_no_connect(); | |||
| if (!($rm =~ "GET")) { | |||
| xlog("L_INFO", "websocket|log|rejecting HTTP request $rm from $si:$sp\n"); | |||
| xhttp_reply("405", "Method Not Allowed", "", ""); | |||
| exit; | |||
| } | |||
| if (!($hdr(Connection) =~ "Upgrade")) { | |||
| xlog("L_INFO", "websocket|log|rejecting HTTP connection $hdr(Connection) request from $si:$sp\n"); | |||
| xhttp_reply("400", "Bad Request", "", ""); | |||
| exit; | |||
| } | |||
| if (!($hdr(Upgrade) =~ "websocket")) { | |||
| xlog("L_INFO", "websocket|log|rejecting HTTP upgrade $hdr(Upgrade) request from $si:$sp\n"); | |||
| xhttp_reply("400", "Bad Request", "", ""); | |||
| exit; | |||
| } | |||
| if (!($hdr(Sec-WebSocket-Protocol) =~ "sip")) { | |||
| xlog("L_INFO", "websocket|log|rejecting request for websocket protocol $hdr(Sec-WebSocket-Protocol) from $si:$sp\n"); | |||
| xhttp_reply("400", "Bad Request", "", ""); | |||
| exit; | |||
| } | |||
| #!ifdef MY_WEBSOCKET_DOMAIN | |||
| if (!($hdr(Origin) =~ "MY_WEBSOCKET_DOMAIN")) { | |||
| 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 ($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(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; | |||
| } | |||
| xlog("L_INFO", "websocket|log|unhandled HTTP request $rm from $si:$sp\n"); | |||
| xhttp_reply("404", "Not Found", "", ""); | |||
| } | |||
| 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 $ws_conid from $var(ws_orig_ip):$sp\n"); | |||
| } | |||
| if ($var(count) < 1) $sht(websockets=>$si::count) = $null; | |||
| } | |||