Skip to content

Instantly share code, notes, and snippets.

@joshelson
Forked from PBXForums/vn-kamailio.cfg
Created January 20, 2020 22:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshelson/5fd5671c53cea64d9dbc6264ddc7dfb4 to your computer and use it in GitHub Desktop.
Save joshelson/5fd5671c53cea64d9dbc6264ddc7dfb4 to your computer and use it in GitHub Desktop.
#!KAMAILIO
# Copyright (c) 2007-2010 by 4PSA, Inc
# All rights reserved
# This file is used to define the interaction between Kamailio and VoipNow
# DO NOT MAKE ANY CHANGES TO THIS FILE!
# ------------------------------------------ global configuration parameters -----------------------
debug=-1 # debug level (cmd line: -dddddddddd)
fork=yes # run in the background
log_stderror=no # (cmd line: -E)
children=4
log_engine_type="zlog"
log_engine_data="/etc/kamailio/zlog.conf"
log_facility=LOG_LOCAL0
disable_tls = 0
facility_sql=no
facility_sipmsg=no
facility_hr=no
# Enable/Disable presence support (default enabled)
#!define ENABLE_PRESENCE
# Enable/Disable application level firewall (default disabled)
##!define ENABLE_PIKE
# Enable/Disable notifications (default disabled)
##!define ENABLE_NOTIFICATION
# Enable/Disable homer sip tracing (default disabled)
##!define ENABLE_SIP_TRACING
# All the branch/contact flags used throughout the script
#!define FMWI_BFLAG 0
#!define SIPPING_BFLAG 7
#!define NAT_BFLAG 8
# Use this settings in order to set the log level of the memory allocators logs.
# In order to enable memory allocation logs you have to compile with "make MEMDBG=1 prefix= all"
# Current values are ERROR=-1, WARN=0, NOTICE=1, INFO=2, DEBUG=3
memdbg=50
# Enable dumping allocator statistics on exit in kamailio.log (need both pkg_status and shm_status flags)
# This option is a bit flag values: 1 - pkg status; 2 - shm status; 4 - pkg summary; 8 - shm summary
# You will need to set above memdbg option to debug+1 to see them as well. For example if debug=2 then set memdbg=3
mem_summary=3
# Voipnow uses raw sockets for sending packages
udp4_raw = -1 # 0 - disabled (default), 1 - enabled, -1 auto. We use auto to fallback to the a normal UDP socket if this fails
udp4_raw_mtu = 1500 # Maximum Transmission Unit, the largest physical packet size, measured in bytes, that a network can transmit. Any messages larger than the MTU are divided into smaller packets before being sent.
#udp4_raw_ttl = -1 # TTL value used for UDP IPv4 packets when udp4_raw is enabled. It is set to auto mode (-1), meaning that the same TTL will be used as for normal UDP sockets.
# Uncomment these lines to enter debugging mode
# fork=no
# log_stderror=yes
listen=udp:192.168.1.23:5060
listen=tcp:192.168.1.23:5060
listen=tls:192.168.1.23:5061
listen=ws:192.168.1.23:5065
listen=wss:192.168.1.23:5066
tcp_connection_lifetime=1300
tcp_connect_timeout=1
tcp_send_timeout=1
tcp_keepalive = yes
tcp_keepcnt = 10
tcp_keepidle = 90
tcp_keepintvl = 2
tcp_accept_no_cl=yes
tcp_rd_buf_size=8000
# ------------------------------------------- DNS settings -------------------------------------------
dns=yes
# We set rev_dns because it is slow and issue VNP-48821 occured on a loaded client.
# rev_dns is used in the following cases:
# When snd_ip, src_ip, dst_ip, to_ip are used in cfg http://www.kamailio.org/wiki/cookbooks/3.2.x/core#snd_ip to be equal to an ip address as shown in the link. It will do a rev_dns for the IP address that is logical used (eq for src_
ip it will do a rev_dns for the source ip) and compare it to the right side of the equality (the user supplied value).
# When a REQUEST is forwarded or a reply is constructed for the REQUEST, it will do a rev_dns for the REQUEST's source ip and compare it with the host value in Via1
rev_dns=no
use_dns_failover=on # next SRV record is tried in case of a timeout or specific negative reply
dns_try_ipv6=yes # if a DNS lookup fails, it will retry it for IPV6(AAAA record)
dns_try_naptr=yes
# Use this to disable appending the localhost name to the domain name set to be reslved
# For example if your hostname is me.4psa.com and the domain name is toresolve.domain.com the resolver
# will try to add the the cache toresolve.domain.com.4psa.com . Use this to disable CNAME cycles such as 4psa.com.4psa.com
dns_ignore_localhost=no
# DNS cache setup
use_dns_cache=yes
dns_cache_del_nonexp=yes # deletes entries if the memory is becoming full
dns_cache_flags=0
dns_cache_gc_interval=120
dns_cache_init=on
dns_cache_max_ttl=3600
dns_cache_negative_ttl=60 # gc entries that have not been found by the resolver. Time to try DNS again
# DNS blacklist setup
use_dst_blacklist=off
dst_blacklist_mem=500
dst_blacklist_expire=45
dst_blacklist_gc_interval=30
dst_blacklist_init=off
# ------------------------------------------ module loading ------------------------------------------
# set module path
mpath="/usr/lib64/kamailio/modules/:/usr/lib64/kamailio/modules_k/"
loadmodule "zlog.so"
loadmodule "tm.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tmx.so"
loadmodule "tls.so"
loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "cfgutils.so"
loadmodule "db_mysql.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
#!ifdef ENABLE_NOTIFICATION
loadmodule "notification.so"
#!endif
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "tsilo.so"
loadmodule "textops.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "xlog.so"
loadmodule "avpops.so"
loadmodule "dialog.so"
#!ifdef ENABLE_PRESENCE
loadmodule "pua.so"
loadmodule "presence.so"
loadmodule "presence_xml.so"
loadmodule "presence_mwi.so"
loadmodule "pua_dialoginfo.so"
loadmodule "presence_dialoginfo.so"
#!endif
loadmodule "uac.so"
loadmodule "siputils.so"
loadmodule "domain.so"
loadmodule "htable.so"
loadmodule "mi_fifo.so"
loadmodule "ctl.so"
loadmodule "load_balancer.so"
loadmodule "xhttp.so"
loadmodule "websocket.so"
loadmodule "permissions.so"
#!ifdef ENABLE_PIKE
loadmodule "pike.so"
#!endif
#!ifdef ENABLE_SIP_TRACING
loadmodule "siptrace.so"
#!endif
# ------------------------------------------ global variables ------------------------------------------
# Kamailio Node ID
# These values are automatically set by astsync
modparam("pv", "cntset", "KAMAILIO_NODEID=s:519ee6a9b4")
# Kamailio IP/port. If server is behind NAT, this is the address set as public.
# These values are automatically set by astsync
modparam("pv", "cntset", "KAMAILIO_IP=s:192.168.1.23")
modparam("pv", "cntset", "KAMAILIO_UDP_PORT=i:5060")
modparam("pv", "cntset", "KAMAILIO_TCP_PORT=i:5060")
modparam("pv", "cntset", "KAMAILIO_TLS_PORT=i:5061")
modparam("pv", "cntset", "KAMAILIO_WS_PORT=i:5065")
modparam("pv", "cntset", "KAMAILIO_WSS_PORT=i:5066")
# Kamailio logic IP/port. This value is used when DEPLOYMENTINF > 0 (see DOCS, Section 2), to define the IP and port that we set
# in the Via header for requests sent to asterisk
# These values are automatically set by astsync
modparam("pv", "cntset", "LOGIC_IP=s:192.168.1.23")
modparam("pv", "cntset", "LOGIC_UDP_PORT=i:5060")
modparam("pv", "cntset", "LOGIC_TCP_PORT=i:5060")
modparam("pv", "cntset", "LOGIC_TLS_PORT=i:5061")
modparam("pv", "cntset", "LOGIC_WS_PORT=i:5065")
modparam("pv", "cntset", "LOGIC_WSS_PORT=i:5066")
# Extension length
# This value is automatically set by astsync
modparam("pv", "cntset", "EXTLEN=i:3")
modparam("pv", "cntset", "EXTSEP=s:*")
# If server is behind NAT, this is the address set as private
# These values are automatically set by astsync
modparam("pv", "cntset", "LOCAL_IP=s:")
modparam("pv", "cntset", "LOCAL_UDP_PORT=s:")
modparam("pv", "cntset", "LOCAL_TCP_PORT=s:")
modparam("pv", "cntset", "LOCAL_TLS_PORT=s:")
modparam("pv", "cntset", "LOCAL_WS_PORT=s:")
modparam("pv", "cntset", "LOCAL_WSS_PORT=s:")
# If server is behind NAT, this is the NAT nermask
# This value is automatically set by astsync
modparam("pv", "cntset", "LOCAL_NETMASK=s:")
# Number of UDP LOAD BALANCERS
modparam("pv", "cntset", "UDP_LB_ADDRESSES=i:0")
modparam("pv", "cntset", "UDP_LB_START_PORT=i:5070")
# (See '2. Advanced deployment implementation' in the DOCS section)
# This value is automatically set by astsync
modparam("pv", "cntset", "DEPLOYMENTINF=i:0")
# Database connection details
modparam("pv", "cntset", "DB_URL=s:mysql://main")
# Reply code/text that will be sent when the maximum number of concurrent calls has been reached
modparam("pv", "cntset", "MAXCALLS_REPLYCODE=i:486")
modparam("pv", "cntset", "MAXCALLS_REPLYREASON=s:Busy Here")
# Reply code that will be sent when the extension failed to register because of bad username/password
modparam("pv", "cntset", "EXPLICIT_REASON_FOR_REGISTRATION_FAILURE_ENABLED=i:0")
# Constant that marks that we force the usage of a token for ws/wss connection
# Value greater than 1 means that we also try to auth with ser, subscriber credentials on wss
modparam("pv", "cntset", "WSS_FORCE_TOKEN=i:2")
# Regular expression used for matching late forking UAs
modparam("pv", "cntset", "MOBILE_UA_REGEX=s:Hubgets iOS .*");
# In the VoipNow architecture, all SIP packages from asterisk go through kamailio. Kamailio replaces the Contact header in order to make
# sure all messages return back through kamailio. If HIDE_ASTERISK_VIA_HEADER_ENABLED is set to 1, kamailio also removes the Via header which
# had been added by asterisk when requests are sent to the outside
modparam("pv", "cntset", "HIDE_ASTERISK_VIA_HEADER_ENABLED=i:1")
# Disable Globally Routable User Agent (UA) URIs (GRUU) - Aastra phones compatibility issue
modparam("registrar", "gruu_enabled", 0)
# RFC-compliance in handling TCP.
# If TCP_STANDARD_COMPLIANT is 1, kamailio will use the IP/port in the Contact header when sending requests to an extension on TCP
# If TCP_STANDARD_COMPLIANT is 0, kamailio will use the IP/port received on the socket at register time when sending requests to an extension on TCP
modparam("pv", "cntset", "TCP_STANDARD_COMPLIANT=i:1")
# Add the Record-Route header for better and faster dialog recognition
# modparam("pv", "shvset", "ADD_RECORD_ROUTE=i:1")
modparam("websocket", "keepalive_mechanism", 3)
modparam("websocket", "keepalive_timeout", 10)
modparam("websocket", "clientalive_ping", 4)
modparam("websocket", "trusted_proxies","http")
disdbase_credentials="/etc/voipnow/sip.conf"
sqldbase_credentials="/etc/voipnow/sip.conf"
disdbase_layout="/etc/voipnow/disdbase.conf"
disdbase_options="/etc/voipnow/local.conf"
sqldbase_layout="/etc/voipnow/sqldbase.conf"
# ------------------------------------------- setting module-specific parameters -------------------------------
# We must set aliases to the avp names in order to have prettier avp names ( instead of $avp(i:1) or $avp(s:test) we will have avp names of type $avp(test) )
modparam("pv","avp_aliases","
is_authenticated=i:1;
destination_did=i:2;
destination_did_header=i:3;
authentication_username=i:4;
authentication_password=i:5;
socket=i:6;
socket_ip=i:7;
socket_port=i:8;
destination_extension=i:9;
watcher_parentid=i:10;
watched_parentid=i:11;
is_allowed_access=i:12;
cflags=i:13;
error_reason=i:14;
error_text=i:15;
destination_uri=i:16;
timeout=i:17;
natping=i:18;
can_register=i:19;
lookedup_extension=i:20;
channel_call_limit=i:21;
channel_call_count=i:22;
user_client_id=i:23;
user_call_limit=i:24;
call_waiting=i:25;
user_call_count=i:26;
client_client_id=i:27;
client_call_count=i:28;
client_call_limit=i:29;
reseller_client_id=i:30;
reseller_call_count=i:31;
reseller_call_limit=i:32;
persistent_user_client_id=i:33;
from=i:34;
force_mwi=i:35;
server_ip=i:36;
server_port=i:37;
new_callid=i:38;
can_publish=i:39;
new_contact=i:40;
asterisk_ip=i:41;
asterisk_port=i:42;
extension_to_check=i:43;
channel_id=i:44;
authentication_realm=i:45;
caller_username=i:46;
internal_call_count=i:47;
external_call_count=i:48;
contact=i:50;
lb_tried_avp=i:51;
redirect_cause=s:52;
lb_extension=s:53;
calltype=s:54;
custom_header_did=s:55;
getbase=s:56;
remote_extension=s:57;
extid=i:58;
targetpbxnodeid=s:60;
extension_client_id=i:61;
extension_call_count=i:62;
device=s:63;
tk_visitor=s:64;
tk_ext=s:65;
tk_vid=s:66;
token_expires=i:67;
caller_device=i:69;
fmwi_subs=i:70;
client_subscription=i:71;
visitor_phone=i:72
")
# end modparam("pv","avp_aliases","..")
# Database url
modparam("auth_db", "db_url", "$cnt(DB_URL)")
modparam("usrloc", "db_url", "$cnt(DB_URL)")
modparam("avpops", "db_url", "$cnt(DB_URL)")
#!ifdef ENABLE_PRESENCE
modparam("presence", "db_url", "$cnt(DB_URL)")
modparam("presence_xml", "db_url", "$cnt(DB_URL)")
modparam("pua", "db_url", "$cnt(DB_URL)")
modparam("pua_dialoginfo", "db_url", "$cnt(DB_URL)")
#!endif
modparam("dialog", "db_url", "$cnt(DB_URL)")
modparam("domain", "db_url", "$cnt(DB_URL)")
modparam("load_balancer", "db_url", "$cnt(DB_URL)")
modparam("permissions", "db_url", "$cnt(DB_URL)")
modparam("websocket", "db_url", "$cnt(DB_URL)")
# #!ifdef ENABLE_PRESENCE
# modparam("presence|presence_xml|pua|pua_dialoginfo", "db_url", "$cnt(DB_URL)")
# #!endif
# modparam("auth_db|usrloc|avpops|dialog|domain", "db_url", "$cnt(DB_URL)")
# ------------------------------------------ Transaction module settings - start -------------------------------
# The name of the avp used to set the final response timeout value which hits if no final reply for a request or ACK for a negative INVITE reply arrives (in seconds).
# We will use this avp later in the script to set timeout to 5 seconds at requests to providers
# modparam("tm", "fr_timer_avp", "$avp(timeout)") is deprecated in 3.1.x. Will use t_set_fr(miliseconds) for setting the timeout value
# The following 5 parameters are needed by tm to know what IP:port to put in the Via headers of NOTIFY messages,
# depending on the destination IP and on whether the server is behind NAT or not. B18498
modparam("tm", "server_ip", "$cnt(KAMAILIO_IP)")
modparam("tm", "server_port", "$cnt(KAMAILIO_UDP_PORT)")
modparam("tm", "server_tcp_port", "$cnt(KAMAILIO_TCP_PORT)")
modparam("tm", "server_tls_port", "$cnt(KAMAILIO_TLS_PORT)")
# If server is behind NAT, the local IP/port is entered here. Else, kamailio logic address is entered
modparam("tm", "local_ip","$cnt(LOCAL_IP)")
modparam("tm", "local_port", "$cnt(LOCAL_UDP_PORT)")
modparam("tm", "local_tcp_port", "$cnt(LOCAL_TCP_PORT)")
modparam("tm", "local_tls_port", "$cnt(LOCAL_TLS_PORT)")
# If server is behind NAT, the local network mask is entered here. Else, "255.255.0.0" is entered
# This value is automatically set by astsync
modparam("tm", "local_mask","$cnt(LOCAL_MASK)")
# This tm module parameter enables DNS fallback to work as in kamailio 1.5.x functionality
# Enables transaction module to reuse the initial REQUEST (sip INVITE) to construct a new branch (UAC) for the
# new SRV record that was used in the destination. Enables the correct contruction of the VIA header and other parameters
# for the new used address. The send interface should be updated for the new to send ip address.
modparam("tm", "reparse_on_dns_failover", 0)
# Global switch which decides if kamailio will send mobile notifications for invites (only if there ia a mobile subscription)
modparam("tm", "offband",0)
# ------------------------------------------ Transaction module settings - end -------------------------------
# ------------------------------------------ Load balancer module settings - start -------------------------------
# The name of the avp used to keep the list of already tried nodes
modparam("load_balancer", "tried_avp", "$avp(lb_tried_avp)")
# The time it tries a node before going to the next one
modparam("load_balancer", "dista", 10)
# The number of nodes with the lowest load that are retrieved
modparam("load_balancer", "toppbxn", 5)
# The interval at which a fresh list of top nodes is retrieved
modparam("load_balancer", "update_nodes_interval", 60)
# The timeout after which a node is reset to up state after being found down
modparam("load_balancer", "reset_down_timeout", 5)
# Interval where a request will be sent to the same server as the prev one for a client
modparam("load_balancer", "redistribution_interval",7210)
# The interval at which the list of pbx addresses is retrieved
# This list will be used by the is_from_pbx() function
modparam("load_balancer", "update_pbx_list_interval", 60)
# When several nodes have the same load pick random among them
modparam("load_balancer", "random_on_equal_load", 1)
# If this option is enabled(1) on a 408 Request Timeout it will not mark the node down, and possibly that node will be tried again
# for a maximum number of toppbxn times. Also other nodes may also be tried, depending on current load
modparam("load_balancer", "ignore_nodedown_ontimeout",1)
# Sets the chosen node identifier in the variable
modparam("load_balancer", "targetpbxnodeid", "$avp(targetpbxnodeid)")
# Extension identifier length
modparam("load_balancer", "extlen",3)
# ------------------------------------------ Load balancer module settings - end -------------------------------
# ------------------------------------------ Notification module settings - start ------------------------------
#!ifdef ENABLE_NOTIFICATION
modparam("notification", "sip_proclist", "hg:sip:proclist");
modparam("notification", "hammer_proclist", "hg:hammer:proclist");
modparam("notification", "hammer_public_key", "/etc/voipnow/certs/hammerpub_sip.key");
modparam("notification", "dc_proclist", "hg:im:proclist");
modparam("notification", "dc_public_key", "/etc/voipnow/certs/chatintpub_sip.key");
#!endif
# ------------------------------------------ Notification module settings - end ---------------------------------
# ---------------------------------------------- TLS module settings - start -----------------------------------
# Set up the TLS protocol method.
modparam("tls", "tls_method", "TLSv1")
# Set up the list of CA
modparam("tls", "private_key", "/etc/voipnow/certs/kamailio.pem")
# Set up the public key certificate
modparam("tls", "certificate", "/etc/voipnow/certs/kamailio.pem")
# Set up the private key
modparam("tls", "ca_list", "/etc/voipnow/certs/kamailio.pem")
# Setup strong ciphers
modparam("tls", "cipher_list", "EECDH+CHACHA20:EECDH+CHACHA20-draft:ECDH+AESGCM:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:!MD5")
# Set up the authentification option
# Check an user certificate to be correctly signed by a trusted CA
modparam("tls", "verify_certificate", 0)
# Sets that a client does not require to provide a signed certificate
modparam("tls", "require_certificate", 0)
# Start kamailio even if a TLS problem was detected
modparam("tls", "tls_force_run", 1)
# ---------------------------------------------- TLS module settings - end -----------------------------------
# ------------------------------------------ DIALOG settings - start -------------------------------------------
# Name of the table where we keep the dialogs (currently not used)
modparam("dialog", "table_name", "ser_dialog")
# Dialog profiles used for call limitation (See '1. Call limitation' in the DOCS section)
modparam("dialog", "profiles_with_value", "resellerinternal ; resellerexternal ; clientinternal ; clientexternal ; userinternal ; userexternal ; extension; channel")
# Flag to be used for marking (by setflag()) if a dialog should be constructed for the current request (make sense only for initial requests).
modparam("dialog", "dlg_flag", 4)
# How the sequential requests should be matched against the known dialogs. The modes are a combination between matching based on a cookie (DID) stored as cookie in
# Record-Route header and the matching based on SIP elements (as in RFC3261).
# Mode 1 means the match is first tried based on DID and if not present, it will fallback to SIP matching
modparam("dialog", "dlg_match_mode", 1)
# Dialog timeout (seconds). This value is automatically set by astsync
modparam("dialog", "default_timeout",7200)
# ------------------------------------------ DIALOG settings - end -------------------------------------------
#!ifdef ENABLE_PRESENCE
# ------------------------------------------ PRESENCE settings - start -----------------------------------------
# Name of the table where we keep the contents of received PUBLISH messages (for presence or MWI)
modparam("presence", "presentity_table", "ser_presentity")
# Tables used to store the presence watchers (for presence or MWI)
modparam("presence", "active_watchers_table", "ser_active_watchers")
modparam("presence", "watchers_table", "ser_watchers")
# The period at which to synchronize cached subscriber info with the database.
modparam("presence", "db_update_period", 15)
# The presence server address which will become the value of Contact header filed for 200OK replies to Subscribe and Publish and in Notify messages.
modparam("presence", "server_address", "sip:$cnt(KAMAILIO_IP):$cnt(KAMAILIO_UDP_PORT)")
modparam("presence", "server_tcp_address", "sip:$cnt(KAMAILIO_IP):$cnt(KAMAILIO_TCP_PORT)")
modparam("presence", "server_tls_address", "sip:$cnt(KAMAILIO_IP):$cnt(KAMAILIO_TLS_PORT)")
# If server is behind NAT, this is the address set as private
modparam("presence", "local_ip", "$cnt(LOCAL_IP)")
modparam("presence", "local_port", "$cnt(LOCAL_UDP_PORT)")
modparam("presence", "local_tcp_port", "$cnt(LOCAL_TCP_PORT)")
modparam("presence", "local_tls_port", "$cnt(LOCAL_TLS_PORT)")
modparam("presence", "local_mask", "$cnt(LOCAL_NETMASK)")
# IP/host used when generating NOTIFY bodies. It is also used as the address to which presence data is stored. All presence subscritions will be stored as extension@default_domain
modparam("presence", "default_domain", "$cnt(KAMAILIO_IP)")
# The timer period that Kamailio will use to check if publish_servers event changed, meaning that publisher information should be reloaded for is_publisher() test
modparam("presence", "update_publishers_period", 60)
# The value in seconds that should be subtracted from the expires value when sending a 200OK for a publish.
# It is used for forcing the client to send an update before the old publish expires.
modparam("presence", "expires_offset", 10)
# The period in seconds between checks if there are expired messages stored in database.
modparam("presence", "clean_period", 60)
# The the maximum admissible expires value for PUBLISH/SUBSCRIBE message (in seconds)
modparam("presence", "max_expires",3600)
# This variable should be set to 1 if you want Kamailio to always set an extension's Presence State in Redis
# Now the scenario when Kamailio does not set the state is when an extension PUBLISHes its state
# AND Kamialio does not have to convert the event/content type between the PUBLISH and the sent NOTIFY
modparam("presence", "publish_presence_state_in_hr", 1)
# The name of the database table where XCAP documents are stored. (currently not used)
modparam("presence_xml", "xcap_table", "ser_xcap")
# This parameter must be set to 1 because we are not using an XCAP server
modparam("presence_xml", "force_active", 1)
# Name of the pua table (currently not used)
modparam("pua", "db_table", "ser_pua")
# The address used to send the self-PUBLISH message. It is also used in the R-URI, To:, From: headers of the PUBLISH message
modparam("pua_dialoginfo", "default_domain", "$cnt(KAMAILIO_IP)");
# The UDP port used to send the self-PUBLISH message. It is also used in the R-URI, To:, From: headers of the PUBLISH message
modparam("pua_dialoginfo", "default_domain_port", "$cnt(KAMAILIO_UDP_PORT)");
# Usually the dialog-info of the caller will be "trying -> early -> confirmed" and the dialog-info of the callee will be "early -> confirmed".
# On some phones the function LED will start blinking if the state is early, regardless if is is the caller or the callee (indicated with the "direction" parameter).
# To avoid blinking LEDs for the caller, you can enable this parameter. Then the state of the caller will be signaled as "confirmed" even in "early" state.
# This is a workaround for the buggy Linksys SPA962 phones. SNOM phones work well with the default setting.
modparam("pua_dialoginfo", "caller_confirmed", 1)
# If this parameter is set, the optional local and remote elements will be put into the dialog element. This is needed for call-pickup features.
modparam("pua_dialoginfo", "include_localremote", 0)
# Flag used to mark (by setflag()) that presence state must change for the caller
modparam("pua_dialoginfo", "caller_flag", 3)
# Flag used to mark (by setflag()) that presence state must change for the callee
modparam("pua_dialoginfo", "callee_flag", 2)
# Do not allow received PUBLISH watcher info events (presence.winfo)
modparam("presence_xml", "disable_winfo", 1)
# Do not allow received PUBLISH bla type events (dialog;sla)
modparam("presence_xml", "disable_bla", 1)
# Use VoipNow shortcircuit. Introduced in B16354
presence_short_circuit = 0
# ------------------------------------------ PRESENCE settings - end -------------------------------------------
#!endif
# ENABLE_PRESENCE endif
# ------------------------------------------ NATHELPER and OPTIONS settings - start ------------------------------
# This flag will be set (by setbflag()) if we want to send Options pings
modparam("nathelper", "sipping_bflag", SIPPING_BFLAG)
# We don't use this parameter, but we must set it in order we use "fix_nated_register". In such case we must set the parameter with same name of "registrar" module to same value.
modparam("nathelper", "received_avp", "$avp(i:200)")
# We don't use this parameter, but we must set it in order we use "fix_nated_register". In such case we must set the parameter with same name of "nathelper" module to same value.
modparam("registrar", "received_avp", "$avp(i:200)")
# Ping all users, not just the ones behind NAT
modparam("nathelper", "ping_nated_only", 0)
# Ping using the OPTIONS message
modparam("nathelper", "sipping_method", "OPTIONS")
# Query the database every natping_db_query_interval seconds
modparam("nathelper", "natping_db_query_interval", 50)
# Send a chunk of options every natping_interval seconds
modparam("nathelper", "natping_interval", 1)
# What the pinged client will see in the From header of the OPTIONS message
modparam("nathelper", "sipping_from", "sip:voipnow@192.168.1.23")
# Flag that marks (by setbflag()) the fact that the extension is behind NAT
modparam("usrloc","nat_bflag", NAT_BFLAG)
# ------------------------------------------ NATHELPER and OPTIONS settings - end -------------------------------
# ------------------------------------------ AUTHENTICATION settings - start --------------------------------------
# The definition of an AVP that might contain the username to be used to perform authentication
modparam("uac","auth_username_avp","$avp(authentication_username)")
# The definition of an AVP that might contain the password to be used to perform authentication
modparam("uac","auth_password_avp","$avp(authentication_password)")
# The definition of an AVP that might contain the realm to be used to perform authentication
modparam("uac","auth_realm_avp","$avp(authentication_realm)")
# Timer interval (in seconds) at which registrations are managed, e.g. renewed as needed.
modparam("uac", "reg_timer_interval",60)
# Failed registration attempts will be retried after this interval (in seconds)
modparam("uac", "reg_retry_interval",120)
# Contains a multiple definition of credentials used to perform authentication.
modparam("uac", "credential","username:domain:password")
# Address to be used to build contact address. Must be at least host part, can have port and parameters. Must not include 'sip:'. The username part of the Contact: URI will be the L_UUID field in the database.
modparam("uac", "reg_contact_addr", "$cnt(KAMAILIO_IP):$cnt(KAMAILIO_UDP_PORT)")
# DB table name to fetch user profiles for registration.
modparam("uac", "reg_db_url", "$cnt(DB_URL)")
# DB table name to fetch user profiles for registration.
modparam("uac", "reg_db_table", "trunk_reg")
# This is the name of the column in ser_subscriber table which holds the extension passwords
modparam("auth_db", "password_column", "password")
# This parameter tells the server whether it should use a pre-calculated HA1 string or plaintext passwords for authentification
modparam("auth_db", "calculate_ha1", yes)
# ------------------------------------------ AUTHENTICATION settings - end --------------------------------------
# --------------------------------------------- HASH TABLES - start -----------------------------------------------
# See '4. PBX did replacement' in DOCS section
modparam("htable", "htable", "pbx_replacement_did=>size=11;autoexpire=7200;")
# See '4. PBX did replacement' in DOCS section
modparam("htable", "htable", "pbx_extension_number=>size=11;autoexpire=7200;")
# Hash table that keeps the channel ID of an external call
modparam("htable", "htable", "ch=>size=11;autoexpire=7200;")
# Hash table that keeps the DID of an external call
modparam("htable", "htable", "did=>size=11;autoexpire=7200;")
# Hash table that keeps the value of the Via header sent from
# Kamailio removes the Via header added by asterisk at every outgoing request and adds is back at replies.
# The expires value should be equal to the one from tm.fr_inv_timer(default 120s).
modparam("htable", "htable", "via=>size=11;autoexpire=120;")
# Hash table that keeps the value of the asterisk ip and port that sent a request
# Kamailio changes the asterisk ip and port in the contact header when sending requests to outside. this is used to add IP and port back at replies.
modparam("htable", "htable", "cnt=>size=11;autoexpire=7200;")
# Hash table that keeps the value of the incoming contact of a request from outside that come on tcp
# Kamailio comunicates on UDP with Asterisk; for changes that come from external on tcp, the contact is saved in a hash;
# We will use the transport and other params to edit the R-URI of the subsequent requests sent to that contact.
modparam("htable", "htable", "cnttcp=>size=11;autoexpire=7200;")
# Hash table that keeps the number of the extension connected to particular call. This is used if DEPLOYMENTINF > 0 (see above for explanation of DEPLOYMENTINF),
# to know for which extension we must take the socket value from ser_location. If DEPLOYMENTINF = 0 then server is on a single IP so we don't need socket settings per extension
modparam("htable", "htable", "remote_extension=>size=11;autoexpire=7200;")
# Hash table used to keep the source IP/port when the request was sent from an extension behind NAT.
# If nat_uri hash table value is not null for current dialog, we replace the destination uri with the uri we find in the nat_uri hash table
modparam("htable", "htable", "nat_uri=>size=8;autoexpire=7200;")
# Hash table used to keep the source nodeid of the pbx we use.
# We use it in order to set SDP on later media changes(reinvites)
modparam("htable", "htable", "pbx_nodeid=>size=20;autoexpire=7200;")
# After how much time are all the hash table deleted after a BYE is received
modparam("htable", "dlg_key_expire_on_bye", 30)
# Redirect source PBX IP:Port
modparam("htable", "htable", "pbx_redirect_src=>size=20;autoexpire=7200;")
# Redirect destination PBX IP:Port
modparam("htable", "htable", "pbx_redirect_dst=>size=11;autoexpire=7200;")
# Hash table that keeps the virtualized extension extended number of caller
modparam("htable", "htable", "virt_caller=>size=11;autoexpire=7200;")
# Hash table that keeps the base extension extended number of a virtualized caller
modparam("htable", "htable", "base_caller=>size=11;autoexpire=7200;")
# Hash table that keeps the virtualized extension extended number of callee
modparam("htable", "htable", "virt_callee=>size=11;autoexpire=7200;")
# Hash table that keeps the base extension extended number of a virtualized callee
modparam("htable", "htable", "base_callee=>size=11;autoexpire=7200;")
# Hash table that keeps the base extension extended number (rU) of a virtualized callee
modparam("htable", "htable", "base_callee_rU=>size=11;autoexpire=7200;")
# Hash table that keeps user subscriptions
modparam("htable", "htable", "user_subscription=>size=11;autoexpire=600;")
# Hash table that keeps extension subscriptions
modparam("htable", "htable", "ext_subscription=>size=11;autoexpire=600;")
# --------------------------------------------- HASH TABLES - end ----------------------------------------------
# Name of the table where we keep kamailio's aliases
modparam("domain", "domain_table", "ser_domain")
# Name of the table where to keep domain attributes
modparam("domain", "domain_attrs_table", "ser_domain_attrs")
# The timer value that kamailio will use to check is to_sip_allow event changed - meaning if ser_domain table changed and to reload the accepted domains for myself test
modparam("domain", "sipallow_period", 60)
# The timer value that kamailio will use to check is role_exposure and to reload the accepted domains for myself test
modparam("domain", "sipexposure_period", 14400)
# Register the list of domains to match 'myself' check
modparam("domain", "register_myself", 1)
# The avpops module requires this parameter, but it does not use it
modparam("avpops","avp_table","ser_location")
# When loading the contact list fetch 50 rows at a time (use mhmget with a maximum of 50 hashes)
modparam("usrloc", "fetch_rows", 50)
# Nonce values expire after 30 seconds
modparam("auth", "nonce_expire", 30)
# Add parameter ";lr=on" instead of just ";lr" to the Record-Route header. This is to overcome problems with broken UAs which
# strip ";lr" parameter when generating Route header fields from Record-Route (";lr=on" seems to help)
modparam("rr", "enable_full_lr", 0)
# Set the upper limit for the Max-Forwards header value.
modparam("maxfwd", "max_limit", 70)
# If the user's contacts should be kept timestamp ordered; otherwise the contact will be ordered based on q value. Non 0 value means true.
modparam("usrloc", "desc_time_order", 0)
# The parameter controls how lookup function processes multiple contacts. If there are multiple contacts for the given username in usrloc and this parameter is set to 1,
# Request-URI will be overwritten with the highest-q rated contact and the rest will be appended to sip_msg structure and can be later used by tm for forking.
# If the parameter is set to 0, only Request-URI will be overwritten with the highest-q rated contact and the rest will be left unprocessed.
# HOWEVER, here we have set "desc_time_order" to 1, which means the lines in "location" will be ordered by registered time. As a result of this,
# the last registered terminal will always be the one who gets called.
modparam("registrar", "append_branches", 1)
# If the processed message contains neither Expires headers nor expires contact parameters, this value will be used for newly created records in ser_location.
# The parameter contains number of second to expire (for example use 3600 for one hour).
# This value is automatically set by astsync
modparam("registrar", "default_expires",3600)
# The maximum expires value of a contact (seconds); values higher than this maximum will be automatically set to the maximum
# This value is automatically set by astsync
modparam("registrar", "max_expires",3600)
# Do not modify this parameter!!!
modparam("registrar", "max_expires_tcp", 3600)
modparam("registrar", "min_expires", 120)
modparam("registrar", "max_contacts", 20 )
modparam("auth_db", "load_credentials", "$avp(natping)=natping;$avp(can_register)=can_register")
# The siputils modules offers some functions related to handling ringing. In a parallel forking scenario you get several 183s with SDP.
# You don't want that your customers hear more than one ringtone or answer machine in parallel on the phone. So it's necessary to drop the 183 when one has already been sent and send a 180 instead
# The "ring_timeout" is the timeout value in seconds, defines how long the call-id is stored in the internal list.
modparam("siputils", "ring_timeout", 30)
# The permissions module is used to whitelist a set of IPs in order to skip pike checks.
# Name of the address table. Contains the IPs, subnets and DNS domain names used by the `allow_address` functions family.
modparam("permissions", "address_table", "ser_address");
# Name of the trusted table (currently not used)
modparam("permissions", "trusted_table", "ser_trusted");
#!ifdef ENABLE_PIKE
# Level 4 pike ip_tree used to detect more than 20 failed auth per 5 minutes sampling unit.
modparam("pike", "ip_tree", "l4_tree=>sampling_time_unit=300;reqs_density_per_unit=20;remove_latency=1200")
# Level 3 pike ip_tree used to detect more than 30 failed auth per 10 minutes sampling unit.
modparam("pike", "ip_tree", "l3_tree=>sampling_time_unit=600;reqs_density_per_unit=30;remove_latency=1800")
# Level 2 pike ip_tree used to detect more than 5 failed auth per 30 seconds
modparam("pike", "ip_tree", "l2_tree=>sampling_time_unit=30;reqs_density_per_unit=5;remove_latency=240")
# Level 1 pike ip_tree used to detect more than 300 auth requests per 10 seconds sampling unit.
modparam("pike", "ip_tree", "l1_tree=>sampling_time_unit=10;reqs_density_per_unit=300;remove_latency=120")
modparam("pike", "pike_log_path", "/var/log/kamailio/abuse.log")
#!endif
#!ifdef ENABLE_SIP_TRACING
# The address in form of a SIP URI where to send a duplicate of traced messages. It uses UDP all the time.
modparam("siptrace", "duplicate_uri", "CHANGEME");
# Mirror sip trafic using the Homer Encapsulation Protocol.
modparam("siptrace", "hep_mode_on", 1);
# Don't insert traced messages into a database. We only need to mirror the trafic to another kamailio instance, which will handle capturing.
modparam("siptrace", "trace_to_database", 0);
# Use core events for tracing. This way we don't need to call 'sip_trace()' or set the 'trace_flag' inside script routes, all the messages will be automatically mirrored.
modparam("siptrace", "trace_mode", 1);
# Tracing disabled by default. Use the rpc command interface to dinamically enable/disable tracing: 'kamcmd siptrace.status [on|off|check]'.
modparam("siptrace", "trace_on", 0);
#!endif
# The name of the FIFO file to be created for listening and reading external commands
modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
# The UNIX stream socket used for binrpc commands
modparam("ctl", "binrpc", "unix:/tmp/kamailio_ctl");
modparam("notification", "identity_update_ivl",3600)
user_agent_header="User-Agent: VoipNow PBX"
server_header="Server: VoipNow PBX"
# ------------------------- request routing logic -------------------
route{
# ----------- Variable initializations - start -----------
# This variable is used to keep the value that will be entered in the remote_extension hash table before we can actually write it in the hash
$var(sht_remote_extension) = "";
# This variable is used to keep the value that will be entered in the nat_uri hash table before we can actually write it in the hash
$var(sht_nat_uri) = "";
# AVPs used to set error reply codes
$avp(error_reason)= $null;
$avp(error_text)= $null;
# Variable that marks the fact that the extension for which we check call limitation is a callee.
# (In this case, we will need to check the call waiting settings)
$var(check_callee) = 0;
# ------------ Variable initializations - end ------------
# initial sanity checks -- messages with max_forwards==0, or excessively long request
if (!mf_process_maxfwd_header("10")) {
sl_send_reply("483","Too Many Hops");
exit;
};
if (msg:len >= 6000 ) {
sl_send_reply("513", "Message too big");
exit;
};
# SIP presence is not supported on websockets
if (($proto == "ws" || $proto == "wss") && is_method("PUBLISH|SUBSCRIBE|NOTIFY")
&& ($(hdr(Event)) == "dialog" || $(hdr(Event)) == "presence")) {
sl_send_reply("405", "Method Not Allowed");
exit;
}
# Absorb retransmissions as early as possible
t_check_trans();
# Setup variable request_is_for_me if uri == myself
# The "uri==myself" test verifies if the host part of the request uri is in the list of local IP addresses, hostnames and aliases of the kamailio server
if (uri==myself){
$var(request_is_for_me) = 1;
} else {
$var(request_is_for_me) = 0;
}
# Reply with 200OK to OPTIONS and NOTIFY pings which are destined to kamailio
if ( (method=="OPTIONS") || (method=="NOTIFY") ){
if ($var(request_is_for_me) == 1){
route(16);
sl_send_reply("200","OK");
exit;
}
} else {
# Handle PUBLISH requests from asterisk
if (method=="PUBLISH") {
route(15);
if($rc<0){
exit;
}
}
}
# Check if the request is coming from asterisk.
$var(req_from_pbx) = 0;
$var(check_source_port) = 0;
if ($proto == "udp") {
$var(check_source_port) = $sp;
}
if (($proto == "udp" || $proto == "tcp") && is_from_pbx("$si", "$var(check_source_port)")) {
$var(req_from_pbx) = 1;
}
# Handle extensions behind NAT. Change the Contact header, set what route to be used for replies, and fill $var(nat_uri) with the source address on the socket
# Skip NAT handling if request comes from asterisk. $si = IP source address of the message; $sp - source port of the message
if ( ($var(req_from_pbx) == 0) ) {
route(16);
}
# From this point we will start using values from the SIP message, so we need to get their escaped values
if ($tU!=$null) $var(to_username)=$(tU{s.escape.common}); # $tU - reference to username in URI of 'To' header
if ($rU!=$null) $var(request_username)=$(rU{s.escape.common}); # $rU - reference to username in request's URI
if ($au!=$null) $var(authentication_username)=$(au{s.escape.common}); # $au - username from Authorization header
if (($var(request_is_for_me) == 1) && (method=="REGISTER")) {
#Handle REGISTERs from extensions. Check if they are authenticated/allowed to register, force subscription to MWI if needed, and save to ser_location
route(22);
exit;
}
if (($var(request_is_for_me) ==1) && (src_ip==myself) && (method == "PUBLISH")) {
# Handle a self publish
#!ifdef ENABLE_PRESENCE
# Create a transaction to absorb retransmissions
if (! t_newtran()){
sl_reply_error();
return;
};
handle_publish();
t_release();
#!endif
exit;
}
# If request is from asterisk
if ( ($var(req_from_pbx) == 1) ) {
dlg_setflag("1");
# Fax loop between asterisk and kamailio. See '5. FAX loop' in DOCS section
if (uri=~"^sip:faxsend-[0-9,*]+-[0-9,*]+@"){
route(24);#Handle FAX loop, initial INVITE
exit;
} else if (is_method("ACK|BYE|INVITE") && ($(hdr(Route))!=$null) && ($(hdr(Route))=~".*;fax=1") ) {
route(25);#Handle FAX loop, subsequent requests
exit;
}
# xlog("L_NOTICE", "NOTICE: Before infrastructure redirect check.\n");
if ( ((method == "ACK") || (method == "BYE") || (method == "INVITE") ) && (($(hdr(Route))!=$null) && ($(hdr(Route))=~".*;vnr=1") )){
xlog("L_NOTICE", "NOTICE: Subsequent request for local redirects .\n");
route(50);
} else if (is_present_hf("X-voipnow-redir") && is_present_hf("X-voipnow-nodeid")){
# save the value of the initial request username, escaped
route(47);
exit;
}
# xlog("L_NOTICE", "NOTICE: After infrastructure redirect check.\n");
# This variable is used to mark the fact that the destination of the call is an extension, and the call comes from asterisk.
# We will use it to know if we need to replace asterisk's IP with kamailio's IP in the From header
# It will be set if lookup is successful
$var(destination_is_extension) = 0;
# If nat_uri hash table value is not null for current dialog, it means the extension to which this request is destined is behind NAT.
# In this case, we replace the destination uri with the uri we find in the nat_uri hash table
if ($sht(nat_uri=>$hdr(Call-ID))!=$null){
# Dialog identification and loose routing
route(43);
avp_pushto("$du","$sht(nat_uri=>$hdr(Call-ID))");
} else {
# If the username in the request's URI is not null, we can try lookup
if ($rU!=$null){
# save the value of the initial request username, escaped, tu use further in the processing logic for call limitation
$avp(extension_to_check) = $var(request_username);
if ((!has_totag()) && (method=="INVITE")){
# Check for virtualization if we have virtualization avp(extension_to_check) will be set to the target extension(the one we need to look for).
route(51);
} else {
if ($avp(getbase) != "" ){
$avp(extension_to_check)=$avp(getbase);
}
}
# save the value of the initial request username, in the original form (unescaped). This will be used for loose routing if lookup fails
$avp(lookedup_extension) = $rU;
# escape the request username to avoid sql injection during lookup
$rU = $avp(extension_to_check);
$var(callee_is_visitor) = 0;
if ($(var(request_username){s.substr,0,3}) == "44*") {
$var(callee_is_visitor) = 1;
}
# Do user location lookups only for initial INVITE requests destined to extensions or visitors.
if (is_method("INVITE") && !has_totag() && (!is_present_hf("X-voipnow-channel") || $var(callee_is_visitor))) {
$var(destination_is_extension) = 1;
lookup("ser_location");
if ($rc == -1) {
# No callee contact registered. If at the relay step we find out that
# the extension is activated on at least one mobile device then we
# will create the transaction and suspend it instead of forwarding it.
$avp(s:maybe_suspend_transaction) = 1;
}
# The process_rr_callbacks function is used for dialog identification
process_rr_callbacks();
# If ($cnt(DEPLOYMENTINF)>1) (See explanation of DEPLOYMENTINF above), we will have to set the remote_extension htable. But we don't write the value just yet,
# to avoid filling unnecessary data too soon. We will keep it in a variable and write it in the hash table when we get closer to the relay command,
# so we know for sure that we have no reason to reject the request
if ($cnt(DEPLOYMENTINF)>1){
$var(sht_remote_extension) = $avp(extension_to_check);
}
# Mark that presence state must change for the callee
setflag(2);
# This dialog flag is used to mark the fact that the destination of the call is an extension.
# It will be used in future requests to determine whether we replace the From header or not (From header us replaced only in requests to extensions)
dlg_setflag("5");
} else {
# We didn't need lookup or it returned no result. Restore the original request uri
$rU = $avp(lookedup_extension);
# If we have a virtualized target extension we set Request URI to the contat of the base
route(67);
# Dialog identification and loose routing
route(43);
}
} else {
# If request username is null, the best chance we've got is to try loose_route. This is done in route(43)
route(43);
}
}
if (method=="INVITE"){
# Reject calls from intercom/paging to an extension involved in a call. Introduced by B13264
route(26);
if ($retcode < 0) exit;
# If "X-asterisk-Info" header contains "SIP re-invite", than this is a re-invite sent from asterisk. No call limitation check is needed, and
# we can go directly to the route that forwards the request
if (is_present_hf("X-asterisk-Info") && $(hdr(X-asterisk-Info)) =~ "^SIP re-invite.*"){
# Final route for requests from asterisk to an outside destination
# Edit necessary headers, set reply/failure routes and send the request
route(1);
exit;
}
# Call limitation: Usually we don't count the caller in requests that come from asterisk (we assume the caller was already counted in the first leg of the call).
# But if the "X-voipnow-user" header is present, it means the original caller was changed by asterisk, and we must count the caller.
# Call limitation algorithm was introduced in B12240
if (is_present_hf("X-voipnow-user")){
route(27);
if ($retcode < 0) exit;
}
#If the "X-voipnow-channel" header is present, then the destination is a channel
if (is_present_hf("X-voipnow-channel")){
# Handle requests from asterisk when the destination is a channel. Check call limitation, edit necessary headers, set reply/failure routes and send the request
route(28);
exit;
} else {
# If this is not a susequent request and "X-voipnow-channel" header is not present, and destination is not a registered extension
# we reject the call because we have no way of knowing where to send the call
if ((!has_totag()) && ($var(destination_is_extension) != 1)) {
xlog("L_NOTICE", "NOTICE: INVITE cam from asterisk without X-voipnow-channel header set fro an extenion not found in ser_location . Rejecting request.\n");
sl_send_reply("404","Not found");
return;
}
# If the "X-voipnow-channel" header is not present, then the destination is an extension
# Handle requests from asterisk when the destination is an extension. Check call limitation, edit necessary headers, set reply/failure routes and send the request
route(29);
exit;
}
} else {
# If there is anything else to write to hash tables, we can write it now because all acceptance tests have been passed
route(30);
# Final route for requests from asterisk to an outside destination
# Edit necessary headers, set reply/failure routes and send the request
route(1);
exit;
}
} else {
# Write the destination IP into $var(destIP) for further use. If $du is set, then the destination IP is found in it. If $du is null, the destination IP is found in $rd
# $du = destination uri; $rd = domain in request's URI
if ($var(request_is_for_me) == 0) {
if ($du!=$null) {
$var(destIP)=$(du{s.select,1,:});
$var(destIP)=$(var(destIP){s.select,0,;});
} else {
$var(destIP)=$rd;
}
xlog("L_NOTICE","DEBUG: Incoming request is not destined to the register server.\n");
xlog("L_NOTICE","DEBUG: Register server is $cnt(KAMAILIO_IP), destination IP is $var(destIP).\n");
route(3); #Send negative reply
exit;
}
# If request does not come from an Asterisk we remove some unwanted x-voipnow-*headers for security reasons
route(49);
# Authentication for non-REGISTER requests which are not from asterisk
route(31);
if ($retcode < 0) exit;
if (($avp(is_authenticated) != "0") && $avp(s:is_extension) && !$avp(s:is_visitor)){
switch ($rm) {
case "PUBLISH":
route(32);#Handle PUBLISH from extensions
exit;
break;
case "SUBSCRIBE":
route(33);#Handle SUBSCRIBE from extensions
exit;
break;
case "INVITE":
route(34);#Handle INVITE from extensions
exit;
break;
default: #Other requests from extensions
# If we have a virtual callee that sends subsequent requests we check it here
route(68);
# Try to identify this dialog with one that already exists in memory
route(42);
# Set advertised address to LOGIC_IP for other subsequent messages(ex: BYE)
route(72);
# If there is anything to write to the nat_uri htable, we can write it now because all acceptance tests have been passed
if ($var(sht_nat_uri) != "")
$sht(nat_uri=>$hdr(Call-ID)) = $var(sht_nat_uri);
# Add X-voipnow-user header containing caller extension to help asterisk identify it
route(4);
# Final route for requests from an outside destination to asterisk
# Edit necessary headers, set reply/failure routes and send the request
route(2);
exit;
}
} else {
# Try to identify this dialog with one that already exists in memory
route(42);
# Authentication for requests from non-extensions (we try to authenticate the sender as channel)
route(35);
if ($retcode < 0) exit;
# If a channel id was found in route(35) then the request is from a valid provider and we we can handle it
if ( $avp(channel_id) != "-1") {
if (method=="INVITE"){
route(36);# Handle INVITEs from provider
if ($retcode < 0) exit;
}
# Add VoipNow channel header containing the incoming channel to help asterisk identify it
route(5);
# Remove X-voipnow-rl header if not received from an extension
remove_hf("X-voipnow-rl");
# Remove X-voipnow-trunk header if not received from an extension
remove_hf("X-voipnow-trunk");
# Final route for requests from an outside destination to asterisk
# Edit necessary headers, set reply/failure routes and send the request
route(2);
exit;
} else {
xlog("L_NOTICE", "NOTICE: IP address $si is not allowed to call did number $avp(destination_did). Unauthorized request.\n");
# Send an authorization challenge (401 Unauthorized)
www_challenge("$fd", "4");
exit;
}
}
}
xlog("L_NOTICE", "NOTICE: Request $rm did not match any of the call routing rules. Rejecting.\n");
route(3); #Send negative reply
exit;
}
route[outbound_branch] {
xlog("L_NOTICE", "outbound branch processing\n");
# Server behind NAT header setups for requests from asterisk to outside
route(12);
# If destination is an extension, the "X-voipnow-did" is present, and the extension has 'A PBX is connected to this extension' set, we replace
# request username and To with did. See '4. PBX did replacement' in DOCS section
# If destination is an extension, the target extension is virtualized we replace request username and To with the base extension
route(39);
}
branch_route[1] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing thoug BRANCH ROUTE 1 message from $si : $sp .\n");
route(outbound_branch);
}
# Final route for requests from asterisk to an outside destination
# Edit necessary headers, set reply/failure routes and send the request
route[1]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 1 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Final route for requests from asterisk to an outside destination.Edit necessary headers, set reply/failure routes and send the request\n");
# The IP:port that must be written in the Contact header will be placed in $var(contact)
$var(contact)=$cnt(KAMAILIO_IP) + ":" + $cnt(KAMAILIO_UDP_PORT);
if ($ct != $null) { # $ct - reference to body of contact header
# Replace asterisk's contact with kamailio's contact. The IP:port that must be written in the Contact header has already been placed in $var(contact) by route(12)
# Now we must call route(38) which does the actual replacement
route(38);
}
# If the "X-voipnow-followme" header is present, then this call has been made as a result of followme.
# We remove the header as it is no longer needed and then we call ring_insert_callid. This inserts the call-id in the internal list,
# which is checked when further replies arrive. Any 183 reply that is received during the timeout value will be converted to a 180 message,
# the SDP body is removed as well. More details in B16528
if (is_present_hf("X-voipnow-followme")){
remove_hf("X-voipnow-followme");
ring_insert_callid();
}
# If the destination is an extension, when replies to this request will arrive, they will go through onreply_route(2)
if ($var(destination_is_extension) == 1) {
t_on_reply("2");
}
else {
# If the destination is not an extension , we asume it is a channel,
# and we route replies through a different reply route
t_on_reply("4");
}
t_on_branch(1);
# Replace From
if ( (($var(destination_is_extension) == 1) || (dlg_isflagset("5")) || method =="ACK" ) && ($fU != $null) && ($sht(ch=>$hdr(Call-ID))==$null) ){
remove_hf("From");
if ($fn != $null) insert_hf("From: $fn<sip:$fU@$(var(contact){s.select,0,:})>;tag=$ft\r\n","To");
else insert_hf("From: <sip:$fU@$(var(contact){s.select,0,:})>;tag=$ft\r\n","To");
# Special failure root for replies from extensions. When negative replies to this request will arrive, they will go through failure_route(2)
# This route removes the call from the call counters and
# handles the 401 reply, for which the request must be resent with an authentication header
t_on_failure("2");
}
# Find out if 'A PBX is connected to this extension' for:
# - branch DID replacement if the destination is an extension and the "X-voipnow-did" header is present
# - filtering out custom INFO requests
if ((($var(destination_is_extension) == 1 || is_method("ACK|BYE|CANCEL")) && ($hdr(X-voipnow-did)!=$null)) || is_method("INFO")){
$avp(s:pbx_connected) = "0";
avp_hr_query_set("slave", "<res:po:ext:$avp(lookedup_extension) | pbx | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM extension_prefs, extension WHERE extension.extended_number='$avp(lookedup_extension)' AND extension_id = extension.id AND param='pbx_connected'",
"$avp(s:pbx_connected)", "0");
}
# Filter out custom INFO requests towards connected trunks
if ($avp(s:pbx_connected) == "1" && is_method("INFO") && ($cT == "application/hold" || $cT == "application/orientation" || $cT == "application/recording"))
drop();
if ( $cnt(HIDE_ASTERISK_VIA_HEADER_ENABLED)==1 && !( search("Via:(.*);rport") )) {
$sht(via=>$hdr(Call-ID))=$hdr(Via);
remove_hf("Via");
}
# When Kamailio is on single IP and first INVITE of a call comes from Asterisk, we add a Record-Route for strict routing cases ;
# (Asterisk acts as an user agent when we have Record Route in 200OK to Invite
# When Asterisk sends the ACK , it sets Request URI(if strict), deletes the first Route and sends the invite to Kamailio
# Kamailio sends the ACK to the next top route which is the second route from the initial set)
# To resolve this issue we add a Record-Route in the Invites that come from Asterisk and are Forwarded by Kamailio so that Asterisk will no longer delete the first proxy.
# Only add Record-Route header if the feature has been enabled
if ( !is_method("REGISTER|MESSAGE|ACK") ) {
# This will only anchor message lumps. The actual headers will be set for each
# branch, using it's specific ip, port and protocol.
record_advertised_route();
}
if (!is_method("INVITE|BYE")) {
# INVITE and BYE are treated in branch_route
route(outbound_branch);
}
#!ifdef ENABLE_NOTIFICATION
# Check if the callee extension is activated on mobile devices.
$avp(s:ndev_count) = 0;
if ($var(destination_is_extension) == 1) {
$var(hr_key) = "hg:" + $var(non_padded_client_client_id) + ":" + $var(non_padded_user_client_id) + ":dev:native:hp";
avp_hr_zcountto("$var(hr_key)", "$avp(s:ndev_count)", "0", "1");
}
$var(relay_status) = 1;
if ($avp(s:maybe_suspend_transaction) != $null && $avp(s:ndev_count) > 0) {
xlog("L_NOTICE", "No UACs registered. Suspending transaction.\n");
# Set the final response timer to 5s. If the called extension registers, then a new
# branch will be dinamically added to the transaction and the timer will be reset.
t_set_fr(0, 5000);
if (!t_suspend()) {
sl_reply_error();
exit;
}
} else $var(relay_status) = t_relay();
if (@cfg_get.tm.offband == "1" && is_method("INVITE") && isflagset(4) && $avp(s:ndev_count) > 0) {
xlog("L_NOTICE", "Initial INVITE from mobile enabled extension. Storing transaction for late forking\n");
# Store the transaction for "late" forking
ts_store();
# and also notify the device.
if ( $(var(request_username){s.len}) == $cnt(EXTLEN) ){
$var(len) = $(avp(caller_username){s.len}) - $(var(request_username){s.len});
$var(callee) = $(avp(caller_username){s.substr,0,$var(len)}) + $var(request_username);
} else {
$var(callee) = $var(request_username);
}
xlog("L_NOTICE", "Sending offband dialog notification towards $var(callee)\n");
dialog_notification("$var(callee)", "$hdr(Call-ID)");
} else if (!$var(relay_status)) {
sl_reply_error();
exit;
}
#!else
# send the request to its destination
if (!t_relay()) {
sl_reply_error();
exit;
}
#!endif
}
# Final route for requests from an outside destination to asterisk
# Edit necessary headers, set reply/failure routes and send the request
route[2]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 2 message fom $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Final route for requests from an outside destination to asterisk.\n");
if ( $sht(virt_caller=>$hdr(Call-ID))!=$null ){
$avp(from)=$hdr(From);
remove_hf("From");
avp_subst("$avp(from)","/(\".*\"){0,1}<sip:(.*)@/<sip:$sht(virt_caller=>$hdr(Call-ID))@/g");
insert_hf("From: $avp(from)\r\n","To");
$avp(contact)=$hdr(Contact);
if($avp(contact)!=$null){
remove_hf("Contact");
avp_subst("$avp(contact)","/sip:(.*)@/sip:$sht(virt_caller=>$hdr(Call-ID))@/g");
insert_hf("Contact: $avp(contact)\r\n","To");
}
}
# If in the initial To: header, the destination extension number has been replaced with a did (See '4. PBX did replacement' in DOCS section),
# that header has now become the From: header, and the old extension number must be restored
if ( $sht(pbx_extension_number=>$hdr(Call-ID))!=$null ){
remove_hf("From");
insert_hf("From: $sht(pbx_extension_number=>$hdr(Call-ID));tag=$ft\r\n","From");
}
# If DEPLOYMENTINF is advanced (>0) a special reply route must be used. If DEPLOYMENTINF is simple (=0), we use the standard reply route for replies from asterisk to outside
if ($cnt(DEPLOYMENTINF) > 0){
# When replies to this request will arrive, they will go through onreply_route(5)
t_on_reply("5");
} else {
# When replies to this request will arrive, they will go through onreply_route(1)
t_on_reply("1");
}
if (($dlg(to_contact) != $null) && ($dlg(from_contact) != $null)) {
if (dlg_isflagset("1")){
avp_pushto("$ruri","$dlg(from_contact)");
}else {
avp_pushto("$ruri","$dlg(to_contact)");
}
# we disable dns failover for the selected route. Even for stateless requests (setflag usage) like ACKs
t_set_disable_failover(1);
setflag(9);
# we disable blacklisting for the selected route. Even for stateless requests (setflag usage) like ACKs
t_set_disable_blst(1);
setflag(12);
t_relay_to_tcp();
exit;
}
if (($du == $null) || (($du != $null) && (! is_from_pbx("$(du{uri.host})", "$(du{uri.port})"))) ){
$var(pbx_node)="";
$avp(calltype)="";
if (is_method("BYE|CANCEL") && $sht(pbx_nodeid=>$hdr(Call-ID)) != $null) {
# if set, use the pbx_nodeid for balancing CANCEL and BYE reqs
$var(pbx_node) = $sht(pbx_nodeid=>$hdr(Call-ID));
}
# Determine the extension to use for balancing (target or source)
route(48);
if (!load_balance("$avp(lb_extension)","$var(pbx_node)","$avp(calltype)")) {
sl_send_reply("500", "Service full");
xlog("L_NOTICE", "[LB] Could not select a node (all full or other error) \n");
exit;
}else{
xlog("L_DEBUG", "[LB] Selected destination is: $du .\n");
if( $avp(targetpbxnodeid)!= $null ){
xlog("L_NOTICE", "ROUTE 2 new pbx chosen, if we have avp(targetpbxnodeid) $avp(targetpbxnodeid) we also set hash table pbx_nodeid .\n");
$sht(pbx_nodeid=>$hdr(Call-ID))=$avp(targetpbxnodeid);
}
}
}
if ($du != $null) {
$var(temp) = "sip:" + $rU + "@" + $(du{uri.host}) + ":" + $(du{uri.port});
avp_pushto("$ruri","$var(temp)");
if(($avp(custom_header_did) != $null ) && ($avp(custom_header_did) != "0") ) {
avp_pushto("$ru/username","$avp(custom_header_did)");
}
xlog("L_DEBUG", "[LB] Ruri se to: $ru .\n");
}
remove_hf("X-voipnow-caller");
if($proto=="ws"||$proto=="wss") {
append_hf("X-voipnow-caller: WebRTC\r\n");
}
# Add X-voipnow-transport header to notify asterisk of the caller's connection information
remove_hf("X-voipnow-transport");
if ($x_real_ip != $null) {
append_hf("X-voipnow-transport: $x_real_ip;$proto\r\n");
} else {
append_hf("X-voipnow-transport: $src_ip;$proto\r\n");
}
#Server behind NAT header setups for requests from outside to asterisk
route(13);
# Send 100 Trying even if transaction exists. t_relay will send it if no transaction is present.
if ( (is_method("INVITE")) && (t_check_trans()) ) {
sl_send_reply("100","Trying");
}
xlog("L_DEBUG", "[LB] Ruri se to: $ru .\n");
# we disable dns failover for LB selected route. Even for stateless requests (setflag usage) like ACKs
t_set_disable_failover(1);
setflag(9);
# we disable blacklisting for LB selected route. Even for stateless requests (setflag usage) like ACKs
t_set_disable_blst(1);
setflag(12);
# Send the request to its destination
if (!t_relay_to_tcp()) {
sl_reply_error();
};
}
# Send negative reply
route[3]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 3 message fom $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Send negative reply.\n");
if ($avp(error_reason) == $null) {
$avp(error_reason)= 603;
}
if ($avp(error_text) == $null) {
$avp(error_text)= "Declined";
}
sl_send_reply("$avp(error_reason)","$avp(error_text)");
}
# Add X-voipnow-user header to help asterisk identify the caller extension
route[4]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 4 message fom $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Add X-voipnow-user header to help asterisk identify the caller extension.\n");
if ($au == $null) { # $au - username from Authorization header
if ($avp(caller_username) != '') {
$var(voipnowuser) = $avp(caller_username);
} else {
$var(voipnowuser) = $fu;
}
} else {
$var(voipnowuser) = $au;
}
$var(testuser)=$(var(voipnowuser){s.select,1,:});
if ($var(testuser) != '') {
$var(voipnowuser) = $var(testuser);
}
$var(testuser)=$(var(voipnowuser){s.select,0,@});
if ($var(testuser) != '') {
$var(voipnowuser) = $var(testuser);
}
append_hf("X-voipnow-orgext: $var(voipnowuser)\r\n");
if ( $sht(virt_caller=>$hdr(Call-ID))!=$null ){
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 2 WE HAVE virt caller $sht(virt_caller=>$hdr(Call-ID)) .\n");
$var(voipnowuser)=$sht(virt_caller=>$hdr(Call-ID));
}
append_hf("X-voipnow-user: $var(voipnowuser)\r\n");
}
# Add VoipNow channel header to help asterisk identify the incoming channel
route[5]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 5 message fom $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Add VoipNow channel header to help asterisk identify the incoming channel.\n");
remove_hf("X-voipnow-channel");
append_hf("X-voipnow-channel: $avp(channel_id)\r\n");
}
# Helper route used to lazy load the webrtc channel id into $var(webrtc_channel).
route["lazyload:webrtc_channel"] {
if ($var(webrtc_channel) == 0) {
avp_db_query("slave", "SELECT id from channel where paid ='2' ORDER BY id LIMIT 1", "$avp(s:result)");
$var(webrtc_channel) = $avp(s:result);
}
}
# Check call limits for channel. If call limit is OK, increase call count for channel
route[6]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 6 message fom $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Check call limits for channel. If call limit is OK, increase call count for channel.\n");
# Don't check count for retransmissions or requests within a dialog (if the request is within a dialog $DLG_status is NULL)
if ( !(t_check_trans()) && ($DLG_status==$null) && (!is_in_profile("channel","$avp(channel_id)")) ) {
$avp(channel_call_limit)="-1";
route("lazyload:webrtc_channel");
if($var(webrtc_channel) == $avp(channel_id)){
xlog("L_NOTICE", "ROUTE 6 WebRTC channel we consider unlimited.\n");
}else{
avp_hr_query_set("slave", "<res:inf:cha:$avp(channel_id) | cc | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM channel_prefs WHERE channel_id='$avp(channel_id)' AND param='concurent_calls'",
"$avp(channel_call_limit)", "0");
}
get_profile_size("channel","$avp(channel_id)","$avp(channel_call_count)");
if ( ($avp(channel_call_limit) != "-1") && ($avp(channel_call_count) >= $(avp(channel_call_limit){s.int})) ){
xlog("L_ERR", "Rejecting call because channel $avp(channel_id) has reached its concurrent calls " +
"limit of $(avp(channel_call_limit){s.int})");
route(3); #Send negative reply
return(-1);
}
# Increase call counter for channel
set_dlg_profile("channel","$avp(channel_id)");
# When negative replies to this request will arrive, they will go through failure_route(2). This route which will decrease the call count
t_on_failure("1");
}
}
#Check call limits for extension/client/reseller, internal calls. If call limits are OK, increase call counts
route[7]{
xlog("L_NOTICE", "[ROUTE 7] Passing though ROUTE 7 message fom $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 7] Check call limits for extension/client/reseller, internal calls.\n");
# Don't check count for retransmissions or requests within a dialog (if the request is within a dialog $DLG_status is NULL)
if ( !(t_check_trans()) && ($DLG_status==$null) ) {
# Set error code and text according to the cnt value set at the beginning of the config file
$avp(error_reason)= $cnt(MAXCALLS_REPLYCODE);
$avp(error_text)= $cnt(MAXCALLS_REPLYREASON);
# Get the extension's user, organization and provider client ids
route(14);
if ( !is_in_profile("userinternal","$var(non_padded_user_client_id)") ) {
$avp(call_waiting) = "1";
$avp(user_call_limit) = "-1";
$avp(client_call_limit) = "-1";
$avp(reseller_call_limit) = "-1";
if ( $var(check_callee) == 1 ) {
$avp(call_waiting) = "0";
avp_hr_query_set("slave", "<res:po:ext:$avp(extension_to_check) | cw | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM extension_prefs, extension WHERE extension.extended_number='$avp(extension_to_check)' AND extension_id = extension.id AND param='call_waiting'",
"$avp(call_waiting)", "0");
}
if ($avp(call_waiting) != "1") {
xlog("L_NOTICE", "[ROUTE 7] Call waiting disabled for extension $avp(extension_to_check).\n");
#In case call waiting is disabled, only one call is allowed.
get_profile_size("extension","$var(non_padded_extension_client_id)","$avp(extension_call_count)");
if ($avp(extension_call_count) > 0){
xlog("L_ERR", "Rejecting call because extension $avp(extension_to_check) has call waiting disabled and is already in a call\n");
route(3); #Send negative reply
return(-1);
}
}
if ($avp(client_subscription) == $null) {
if ($sht(user_subscription=>$var(non_padded_user_client_id)) != $null) {
$avp(client_subscription) = $sht(user_subscription=>$var(non_padded_user_client_id));
} else {
avp_hr_query_set("slave",
"<res:inf:ext:$avp(extension_to_check) | sub | 0 | DB_STRING | 0 | hash>",
"SELECT subscription FROM extension, client WHERE client_id=client.id AND extended_number='$avp(extension_to_check)'",
"$avp(client_subscription)", "0");
if ($avp(client_subscription) == $null) {
$sht(user_subscription=>$var(non_padded_user_client_id)) = "";
} else {
$sht(user_subscription=>$var(non_padded_user_client_id)) = $avp(client_subscription);
}
}
}
if ($avp(client_subscription) == $null || $avp(client_subscription) == "") {
avp_hr_query_set("slave",
"<res:inf:mng:$var(non_padded_user_client_id) | icc | 0 | DB_STRING | 0 | hash>"
"<res:inf:mng:$var(non_padded_client_client_id) | icc | 1 | DB_STRING | 0 | hash>"
"<res:inf:mng:$var(non_padded_reseller_client_id) | icc | 2 | DB_STRING | 0 | hash>"
,
"SELECT get_client_pref_no_sub('$var(non_padded_user_client_id)', 'max_concurentint'),
get_client_pref_no_sub('$var(non_padded_client_client_id)', 'max_concurentint'),
get_client_pref_no_sub('$var(non_padded_reseller_client_id)', 'max_concurentint')"
,
"$avp(user_call_limit);$avp(client_call_limit);$avp(reseller_call_limit)", "0");
} else {
avp_hr_query_set("slave",
"<res:inf:mng:$var(non_padded_reseller_client_id) | icc | 0 | DB_STRING | 0 | hash>",
"SELECT get_client_pref_no_sub('$var(non_padded_reseller_client_id)', 'max_concurentint')",
"$avp(reseller_call_limit)", "0");
avp_hr_query_set("slave",
"<res:inf:sub:$avp(client_subscription) | max_concurentint | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM client_template_data WHERE param='max_concurentint' and client_template_id = '$avp(client_subscription)'",
"$avp(user_call_limit)", "0");
$avp(client_call_limit) = $avp(user_call_limit);
}
get_profile_size("userinternal","$var(non_padded_user_client_id)","$avp(user_call_count)");
if ( ($avp(user_call_limit) != "-1") && ($avp(user_call_count) >= $(avp(user_call_limit){s.int})) ){
xlog("L_ERR", "Rejecting call because user $var(non_padded_user_client_id) of extension $avp(extension_to_check) " +
"has reached its concurrent calls limit of $(avp(user_call_limit){s.int})");
route(3); #Send negative reply
return(-1);
}
$var(check_callee) = 0;
get_profile_size("clientinternal","$var(non_padded_client_client_id)","$avp(client_call_count)");
if ( ($avp(client_call_limit) != "-1") && ($avp(client_call_count) >= $(avp(client_call_limit){s.int})) ){
xlog("L_ERR", "Rejecting call because client $var(non_padded_client_client_id) has reached its concurrent " +
"calls limit of $(avp(client_call_limit){s.int})");
route(3); #Send negative reply
return(-1);
}
get_profile_size("resellerinternal","$var(non_padded_reseller_client_id)","$avp(reseller_call_count)");
if ( ($avp(reseller_call_limit) != "-1") && ($avp(reseller_call_count) >= $(avp(reseller_call_limit){s.int})) ){
xlog("L_ERR", "Rejecting call because reseller $var(non_padded_reseller_client_id) has reached its concurrent " +
"calls limit of $(avp(reseller_call_limit){s.int})");
route(3); #Send negative reply
return(-1);
}
#Increase call counters for user, client, reseller
set_dlg_profile("extension","$var(non_padded_extension_client_id)");
set_dlg_profile("userinternal","$var(non_padded_user_client_id)");
set_dlg_profile("clientinternal","$var(non_padded_client_client_id)");
set_dlg_profile("resellerinternal","$var(non_padded_reseller_client_id)");
$avp(persistent_user_client_id) = $var(non_padded_user_client_id);
}
}
$avp(user_client_id) = -1;
$avp(extension_client_id) = -1;
}
# Reply-route for replies from asterisk if DEPLOYMENTINF is simple (=0)
onreply_route[1]
{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though REPLY ROUTE 1 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Reply-route for replies from asterisk if DEPLOYMENTINF is simple (=0).\n");
if ($ct != $null) { # $ct - reference to body of contact header
# Replace asterisk's contact with kamailio's contact. Set the IP:port that must be written in the Contact header to $var(contact), and then call route(38) which does the actual replacement
$var(contact) = $cnt(KAMAILIO_IP) + ":" + $cnt(KAMAILIO_UDP_PORT);
route(38);
}
# If in the initial To: header, the destination extension number has been replaced with a did (See '4. PBX did replacement' in DOCS section),
# that header has now become the From: header, and the old extension number must be restored
if ($sht(pbx_replacement_did=>$hdr(Call-ID)) != $null){
$avp(from)=$hdr(From);
remove_hf("From");
avp_subst("$avp(from)","/sip:(.*)@/sip:$sht(pbx_replacement_did=>$hdr(Call-ID))@/g");
insert_hf("From: $avp(from)\r\n","To");
}
# Restore the original From: header
if ($sht(base_caller=>$hdr(Call-ID)) != $null){
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG contact is:$sht(base_caller=>$hdr(Call-ID))\n");
$var(from_restore)=$sht(base_caller=>$hdr(Call-ID)) ;
$var(test)=$(var(from_restore){s.select,1,:});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG restore 00 is: $var(from_restore) . \n");
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG test 00 is: $var(test) . \n");
if (not_empty("$var(test)")) {
$var(from_restore) = $var(test);
}
$var(test)=$(var(from_restore){s.select,0,@});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG restore 01 is:$var(from_restore)\n");
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG test 01 is: $var(test) . \n");
if (not_empty("$var(test)")){
$var(from_restore) = $var(test);
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG restore is:$var(from_restore)\n");
$avp(from)=$hdr(From);
remove_hf("From");
avp_subst("$avp(from)","/sip:(.*)@/sip:$var(from_restore)@/g");
insert_hf("From: $avp(from)\r\n","To");
}
if( ($sht(base_callee=>$hdr(Call-ID)) != $null) && ($fU == $sht(virt_callee=>$hdr(Call-ID)))){
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM is:$sht(virt_callee=>$hdr(Call-ID)) . \n");
$var(from_restore)=$sht(base_callee=>$hdr(Call-ID)) ;
$var(test)=$(var(from_restore){s.select,1,:});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG restore 00 is: $var(from_restore) . \n");
if (not_empty("$var(test)")) {
$var(from_restore) = $var(test);
}
$var(test)=$(var(from_restore){s.select,0,@});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG restore 01 is:$var(from_restore) . \n");
if (not_empty("$var(test)")){
$var(from_restore) = $var(test);
}
$avp(from)=$hdr(From);
avp_subst("$avp(from)","/sip:(.*)@/sip:$sht(base_callee=>$hdr(Call-ID))@/g");
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[2] new from is: $avp(from) .\n");
remove_hf("From");
insert_hf("From: $avp(from)\r\n","To");
}
}
# Reply-route for replies from outside
onreply_route[2]
{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though REPLY ROUTE 2 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Reply-route for replies from outside.\n");
# If we have tcp comunication we keep contact to later use in subsequent requests sent to this contact.
if(($proto=="tcp" || $proto=="tls")&&($hdr(Contact)!=$null)){
$var(contact_tcp)='';
$var(contact_tcp)= $hdr(Contact);
$var(contact_tcp)= $(var(contact_tcp){nameaddr.uri});
$var(contact_tcp)= $(var(contact_tcp){uri.params});
if (not_empty("$var(contact_tcp)")) {
$sht(cnttcp=>$hdr(Call-ID))=$var(contact_tcp);
xlog("L_NOTICE", "ROUTE Reply-route for replies from outside sht(cnttcp=>$hdr(Call-ID)) is $sht(cnttcp=>$hdr(Call-ID)).\n");
}
}
# If source extension is behind NAT (i.e. "isbflagset(NAT_BFLAG)"), we call force_rport(). This function adds the rport parameter to the first Via header.
# Thus, kamailio will add the received IP port to the top most via header in the SIP message, even if the client does not indicate support for rport.
# This enables subsequent SIP messages to return to the proper port later on in a SIP transaction.
# We don't change anything if the reply is of type 3xx, because the reply must get to asterisk intact
if (!(t_check_status("3[0-9][0-9]"))){
if ( (is_method("INVITE")) && (isbflagset(NAT_BFLAG)) ){
force_rport();
}
}
if (t_check_status("2[0-9][0-9]") && (is_method("INVITE"))) {
$var(req_rU) = $T_req($rU);
xlog("L_NOTICE", "REPLY ROUTE 2: req_rU is $var(req_rU), callid is $hdr(Call-ID) \n");
if ($sht(base_callee_rU=>$hdr(Call-ID)) != $null) {
$var(req_rU) = $sht(base_callee_rU=>$hdr(Call-ID));
xlog("L_NOTICE", "On 200 OK we find that INVITE UserName was: $var(req_rU) \n");
}
if (reg_fetch_contacts("ser_location", "$var(req_rU)", "callee")) {
$var(i_counter) = 0;
$var(brec)= "0";
xlog("L_NOTICE", "[REPLY ROUTE 2] Contact count: $(ulc(callee=>count))\n");
while($var(i_counter) < $(ulc(callee=>count))) {
if (isbflagset(NAT_BFLAG)) {
$var(brec) = $(ulc(callee=>received)[$var(i_counter)]);
} else {
$var(brec) = $(ulc(callee=>addr)[$var(i_counter)]);
}
if (($(var(brec){s.len}) >= 6) && ($(var(brec){uri.host}) == $si ) && ($(var(brec){uri.port}) == $sp)) {
$sht(nat_uri=>$hdr(Call-ID)) = $var(brec);
if ($sht(base_callee_rU=>$hdr(Call-ID)) != $null) {
$sht(base_callee=>$hdr(Call-ID)) = $(ulc(callee=>addr)[$var(i_counter)]);
xlog("L_NOTICE", "Setting base_calle on callid: $hdr(Call-ID) to $(ulc(callee=>addr)[$var(i_counter)]) \n");
}
break;
}
$var(i_counter) = $var(i_counter) + 1;
}
reg_free_contacts("callee");
}
}
# If extension is on fixed IP we set contact IP to the sorce IP to avoid problems with extensions behind NAT
$avp(s:extension_hash) = $null;
avp_hr_query_set("slave", "<node:sip:auth:nregext | $si | 0 | DB_STRING| 5 | hash>",
"SELECT EXTENSION_AUTH('$si','$sp')",
"$avp(s:extension_hash)", "0");
# If we have a virtualized target extension we set Request URI to the contact of the base
if ($sht(virt_callee=>$hdr(Call-ID))!= $null){
# Replace username in contact
$avp(contact)=$hdr(Contact);
if($avp(contact)!=$null){
remove_hf("Contact");
avp_subst("$avp(contact)","/sip:(.*)@/sip:$sht(virt_callee=>$hdr(Call-ID))@/g");
insert_hf("Contact: $avp(contact)\r\n","From");
}
# Replace To username
$avp(s:to)=$hdr(To);
avp_subst("$avp(s:to)","/sip:(.*)@/sip:$sht(virt_callee=>$hdr(Call-ID))@/g");
remove_hf("To");
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though REPLY ROUTE 2 CHAGING BACK TO VIRTUAL new To is: $avp(s:to) .\n");
insert_hf("To: $avp(s:to)\r\n","From");
} else {
if ($avp(s:extension_hash) != $null) {
$avp(contact) = $hdr(Contact);
if($avp(contact) != $null){
avp_subst("$avp(contact)","/sip:(.*)@(.*)>/sip:\1@$si:$sp>/");
remove_hf("Contact");
insert_hf("Contact: $avp(contact)\r\n","To");
}
}
}
# Restore the original To: header (See '4. PBX did replacement' in DOCS section)
if ($sht(pbx_extension_number=>$hdr(Call-ID)) != $null){
remove_hf("To");
insert_hf("To: $sht(pbx_extension_number=>$hdr(Call-ID));tag=$tt\r\n","From");
}
if ($sht(via=>$hdr(Call-ID))!=$null) {
insert_hf("Via: $sht(via=>$hdr(Call-ID))\r\n");
if (t_check_status("1[0-9][0-9]")) {
# Update the via htable entry expires for each provisional reply received
$sht(via=>$hdr(Call-ID)) = $sht(via=>$hdr(Call-ID));
}
}
}
# Reply-route for FAX loop handling. See '5. FAX loop' in DOCS section
onreply_route[3]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though REPLY ROUTE 3 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Reply-route for FAX loop handling.\n");
$var(callid) = $(hdr(Call-ID));
if ($(var(callid){s.substr,0,3}) == "fax"){
remove_hf("Call-ID");
insert_hf("Call-ID: $(var(callid){s.substr,3,0})\r\n","CSeq");
}
else {
remove_hf("Call-ID");
insert_hf("Call-ID: fax$var(callid)\r\n","CSeq");
}
}
onreply_route[4]{
# xlog("L_ERR", "ROUTE NOTICE: Passing though REPLY ROUTE 4 message from $si : $sp .\n");
# xlog("L_ERR", "ROUTE Reply-route for replies from channels.\n");
if (is_method("REGISTER") && $ct != $null && $sht(cnt=>$hdr(Call-ID)) != $null){
$avp(new_contact) = $ct;
avp_subst("$avp(new_contact)","/@.*>/@$sht(cnt=>$hdr(Call-ID))>/g");
remove_hf("Contact");
insert_hf("Contact: $avp(new_contact)\r\n", "CSeq");
}
# Restore the original To: header (See '4. PBX did replacement' in DOCS section)
if ($sht(pbx_extension_number=>$hdr(Call-ID)) != $null){
remove_hf("To");
insert_hf("To: $sht(pbx_extension_number=>$hdr(Call-ID));tag=$tt\r\n","From");
}
if ($sht(via=>$hdr(Call-ID)) != $null){
insert_hf("Via: $sht(via=>$hdr(Call-ID))\r\n");
if (t_check_status("1[0-9][0-9]")) {
# Update the via htable entry expires for each provisional reply received
$sht(via=>$hdr(Call-ID)) = $sht(via=>$hdr(Call-ID));
}
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[4] TO BUG $sht(virt_caller=>$hdr(Call-ID)) . \n");
if( ($sht(base_caller=>$hdr(Call-ID)) != $null)){
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[4] TO is:$sht(base_caller=>$hdr(Call-ID)) . \n");
$var(to_restore)=$sht(base_caller=>$hdr(Call-ID)) ;
$var(test)=$(var(to_restore){s.select,1,:});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[4] TO BUG restore 00 is: $var(to_restore) . \n");
if (not_empty("$var(test)")) {
$var(to_restore) = $var(test);
}
$var(test)=$(var(to_restore){s.select,0,@});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[4] TO BUG restore 01 is:$var(to_restore) . \n");
if (not_empty("$var(test)")){
$var(to_restore) = $var(test);
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[4] TO BUG restore is:$var(to_restore) . \n");
if($tU == $var(to_restore)) {
$avp(s:to)=$hdr(To);
remove_hf("To");
avp_subst("$avp(s:to)","/sip:(.*)@/sip:$sht(virt_caller=>$hdr(Call-ID))@/g");
insert_hf("To: $avp(s:to)\r\n","To");
$avp(contact)=$hdr(Contact);
if($avp(contact)!=$null){
remove_hf("Contact");
avp_subst("$avp(contact)","/sip:(.*)@/sip:$sht(virt_caller=>$hdr(Call-ID))@/g");
insert_hf("Contact: $avp(contact)\r\n","To");
}
}
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[4] sht(base_callee=>hdr(Call-ID)) is:$sht(base_callee=>$hdr(Call-ID)) . \n");
if( ($sht(base_callee=>$hdr(Call-ID)) != $null)){
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[4] TO is:$sht(base_callee=>$hdr(Call-ID)) . \n");
$var(to_restore)=$sht(base_callee=>$hdr(Call-ID)) ;
$var(test)=$(var(to_restore){s.select,1,:});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[4] FROM BUG restore 00 is: $var(to_restore) . \n");
if (not_empty("$var(test)")) {
$var(to_restore) = $var(test);
}
$var(test)=$(var(to_restore){s.select,0,@});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[4] FROM BUG restore 01 is:$var(to_restore) . \n");
if (not_empty("$var(test)")){
$var(to_restore) = $var(test);
}
# If we have a virtualized target extension we set Request URI to the contact of the base
if($tU == $var(to_restore) && $sht(virt_callee=>$hdr(Call-ID))!= $null){
# Replace username in contact
$avp(contact)=$hdr(Contact);
if($avp(contact)!=$null){
remove_hf("Contact");
avp_subst("$avp(contact)","/sip:(.*)@/sip:$sht(virt_callee=>$hdr(Call-ID))@/g");
insert_hf("Contact: $avp(contact)\r\n","From");
}
# Replace To username
$avp(s:to)=$hdr(To);
avp_subst("$avp(s:to)","/sip:(.*)@/sip:$sht(virt_callee=>$hdr(Call-ID))@/g");
remove_hf("To");
xlog("L_NOTICE", "ROUTE NOTICE: Passing though REPLY ROUTE 2 CHAGING BACK TO VIRTUAL new To is: $avp(s:to) . \n");
insert_hf("To: $avp(s:to)\r\n","From");
}
}
}
# Reply-route for replies from asterisk if DEPLOYMENTINF is advanced (>0)
onreply_route[5]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing through REPLY ROUTE 5 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Reply-route for replies from asterisk if DEPLOYMENTINF is advanced (>0).\n");
$var(req_real_ip) = $T_req($si);
if ($T_req($x_real_ip) != $null) {
$var(req_real_ip) = $T_req($x_real_ip);
}
# Replace asterisk's contact with kamailio's contact. Set the IP:port that must be written in the Contact header to $var(contact), and then call route(38) which does the actual replacement
if ($ct != $null) { # $ct - reference to body of contact header
if ( $sht(remote_extension=>$hdr(Call-ID)) != $null ){
xlog("L_NOTICE", " Reply-route 5 message from $si : $sp update basesd on socket var(contact) $var(contact) .\n");
# set $var(contact) based on remote_extension sk from hash rtl:sipses:ext:$avp(remote_extension)
route(62);
} else {
if ($cnt(LOCAL_IP)!=''){
if (avp_checklocal("$cnt(LOCAL_IP)", "$var(req_real_ip)", "$cnt(LOCAL_NETMASK)")){
# Set var(contact) based on (LOCAL_IP) and transport
route(60);
} else {
# Set var(contact) based on Direct Route and transport and fix Direct Route SDP
route(65);
}
} else {
# Set var(contact) based on Direct Route and transport and fix Direct Route SDP
route(65);
}
}
} else {
$var(contact) = $cnt(KAMAILIO_IP) + ":" + $cnt(KAMAILIO_UDP_PORT);
}
# Fix SDP contact
if (has_body("application/sdp")) {
if ($cnt(LOCAL_IP)!=''){
if (avp_checklocal("$cnt(LOCAL_IP)", "$var(req_real_ip)", "$cnt(LOCAL_NETMASK)")){
# set local SDP
$avp(advertised_sdp)=$si;
xlog("L_NOTICE", " Reply-route 5 message from $si : $sp Local SDP .\n");
} else {
# set var(contact) based on Direct Route and transport and fix Direct Route SDP
xlog("L_NOTICE", " Reply-route 5 message from $si : $sp cnt(DEPLOYMENTINF)== $cnt(DEPLOYMENTINF) SDP .\n");
route(54);
}
} else {
# set var(contact) based on Direct Route and transport and fix Direct Route SDP
xlog("L_NOTICE", " Reply-route 5 message from $si : $sp cnt(DEPLOYMENTINF)== $cnt(DEPLOYMENTINF) SDP .\n");
route(54);
}
if($avp(advertised_sdp) != $null ){
fix_nated_sdp("10","$avp(advertised_sdp)");
}
}
# Replace asterisk's contact with kamailio's contact
route(38);
# If in the initial To: header, the destination extension number has been replaced with a did (See '4. PBX did replacement' in DOCS section),
# that header has now become the From: header, and the old extension number must be restored
if ($sht(pbx_replacement_did=>$hdr(Call-ID)) != $null){
$avp(from)=$hdr(From);
remove_hf("From");
avp_subst("$avp(from)","/sip:(.*)@/sip:$sht(pbx_replacement_did=>$hdr(Call-ID))@/g");
insert_hf("From: $avp(from)\r\n","To");
}
# Restore the original From: header
if ($sht(base_caller=>$hdr(Call-ID)) != $null){
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[5] FROM BUG contact is:$sht(base_caller=>$hdr(Call-ID))\n");
$var(from_restore)=$sht(base_caller=>$hdr(Call-ID)) ;
$var(test)=$(var(from_restore){s.select,1,:});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[5] FROM BUG restore 00 is: $var(from_restore) . \n");
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[5] FROM BUG test 00 is: $var(test) . \n");
if (not_empty("$var(test)")) {
$var(from_restore) = $var(test);
}
$var(test)=$(var(from_restore){s.select,0,@});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[5] FROM BUG restore 01 is:$var(from_restore)\n");
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[5] FROM BUG test 01 is: $var(test) . \n");
if (not_empty("$var(test)")){
$var(from_restore) = $var(test);
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[5] FROM BUG restore is:$var(from_restore)\n");
$avp(from)=$hdr(From);
remove_hf("From");
avp_subst("$avp(from)","/sip:(.*)@/sip:$var(from_restore)@/g");
insert_hf("From: $avp(from)\r\n","To");
}
if( ($sht(base_callee=>$hdr(Call-ID)) != $null) && ($fU == $sht(virt_callee=>$hdr(Call-ID)))){
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM is:$sht(virt_callee=>$hdr(Call-ID)) . \n");
$var(from_restore)=$sht(base_callee=>$hdr(Call-ID)) ;
$var(test)=$(var(from_restore){s.select,1,:});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG restore 00 is: $var(from_restore) . \n");
if (not_empty("$var(test)")) {
$var(from_restore) = $var(test);
}
$var(test)=$(var(from_restore){s.select,0,@});
xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[1] FROM BUG restore 01 is:$var(from_restore) . \n");
if (not_empty("$var(test)")){
$var(from_restore) = $var(test);
}
$avp(from)=$hdr(From);
avp_subst("$avp(from)","/sip:(.*)@/sip:$sht(base_callee=>$hdr(Call-ID))@/g");
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though onreply_route[2] new from is: $avp(from) .\n");
remove_hf("From");
insert_hf("From: $avp(from)\r\n","To");
}
}
#Failure route for calls going to asterisk.
failure_route[3]{
xlog("L_NOTICE", "[FAILURE ROUTE 3] Passing though message from $si : $sp .\n");
xlog("L_NOTICE", "[FAILURE ROUTE 3] ROUTE Failure route for calls going to asterisk.\n");
if ( t_check_status("(408)") ){
xlog("L_NOTICE", "[FAILURE ROUTE 3] 408.\n");
# In case the extension is connected to a PBX, the extension username is not in request uri
# because it had been replaced by X-voipnow-did(See '4. PBX did replacement' in DOCS section).
# But we saved it earlier in the pbx_replacement_did htable
if ( $sht(pbx_extension_number=>$hdr(Call-ID)) != $null){
$var(request_username)=$(sht(pbx_extension_number=>$hdr(Call-ID)){tobody.user});
}
else{
if ($rU != $null){
$var(request_username) = $(rU{s.escape.common});
}
else {
xlog("L_NOTICE","ERROR: No Request uri in failure route \n");
return;
}
}
# In case of timeout for requests distributed to a pbx, we try to redistribute the call to another pbx.
route(46);
}
# Call failed, so all call limitation counters for this call must be decreased. This is done by removing the call from the dialog profiles. (CVN210RM52)
if (($avp(channel_id) != $null ) && (is_in_profile("channel","$avp(channel_id)"))) unset_dlg_profile("channel","$avp(channel_id)");
if ($avp(persistent_user_client_id) != $null && (is_in_profile("userinternal","$avp(persistent_user_client_id)") ||
is_in_profile("userexternal","$avp(persistent_user_client_id)") )){
unset_dlg_profile("userinternal","$avp(persistent_user_client_id)");
unset_dlg_profile("userexternal","$avp(persistent_user_client_id)");
if ($avp(client_client_id)!= $null ) {
unset_dlg_profile("clientinternal","$avp(client_client_id)");
unset_dlg_profile("clientexternal","$avp(client_client_id)");
}
if ($avp(reseller_client_id)!= $null ) {
unset_dlg_profile("resellerinternal","$avp(reseller_client_id)");
unset_dlg_profile("resellerexternal","$avp(reseller_client_id)");
}
if ($avp(extension_client_id)!= $null ) {
unset_dlg_profile("extension","$avp(extension_client_id)");
}
}
if ($sht(via=>$hdr(Call-ID))!=$null) insert_hf("Via: $sht(via=>$hdr(Call-ID))\r\n");
exit;
}
#Failure route for outgoing external calls
failure_route[1]{
xlog("L_NOTICE", "[FAILURE ROUTE 1] Passing though FAILURE ROUTE 1 message from $si : $sp .\n");
xlog("L_NOTICE", "[FAILURE ROUTE 1] ROUTE Failure route for outgoing external calls.\n");
# Call failed, so all call limitation counters for this call must be decreased. This is done by removing the call from the dialog profiles. (CVN210RM52)
if (($avp(channel_id) != $null ) && (is_in_profile("channel","$avp(channel_id)"))) unset_dlg_profile("channel","$avp(channel_id)");
if ($avp(persistent_user_client_id) != $null && (is_in_profile("userinternal","$avp(persistent_user_client_id)") ||
is_in_profile("userexternal","$avp(persistent_user_client_id)") )){
unset_dlg_profile("userinternal","$avp(persistent_user_client_id)");
unset_dlg_profile("userexternal","$avp(persistent_user_client_id)");
if ($avp(client_client_id)!= $null ) {
unset_dlg_profile("clientinternal","$avp(client_client_id)");
unset_dlg_profile("clientexternal","$avp(client_client_id)");
}
if ($avp(reseller_client_id)!= $null ) {
unset_dlg_profile("resellerinternal","$avp(reseller_client_id)");
unset_dlg_profile("resellerexternal","$avp(reseller_client_id)");
}
if ($avp(extension_client_id)!= $null ) {
unset_dlg_profile("extension","$avp(extension_client_id)");
}
}
if ($sht(via=>$hdr(Call-ID))!=$null) insert_hf("Via: $sht(via=>$hdr(Call-ID))\r\n");
exit;
}
#Failure route for authenticating calls to phone terminals
failure_route[2]{
xlog("L_NOTICE", "[FAILURE ROUTE 2] Passing though FAILURE ROUTE 2 message from $si : $sp .\n");
xlog("L_NOTICE", "[FAILURE ROUTE 2] Failure route for authenticating calls to phone terminals.\n");
if ( t_check_status("(401)") ){
$avp(authentication_password) = "";
# In case the extension is connected to a PBX, the extension username is not in request uri
# because it had been replaced by X-voipnow-did(See '4. PBX did replacement' in DOCS section).
# But we saved it earlier in the pbx_replacement_did htable
if ( $sht(pbx_extension_number=>$hdr(Call-ID)) != $null){
$var(request_username)=$(sht(pbx_extension_number=>$hdr(Call-ID)){tobody.user});
}
else{
if ($rU != $null){
$var(request_username) = $(rU{s.escape.common});
}
else {
xlog("L_NOTICE","ERROR: No Request uri in failure route \n");
return;
}
}
$avp(authentication_username) = $var(request_username);
avp_hr_query_set("slave", "<res:sip:ext:$var(request_username) | spass | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM extension,extension_prefs WHERE param='ast_passwd' AND extension_prefs.extension_id=extension.id AND extended_number='$var(request_username)'",
"$avp(authentication_password)", "0");
if ($avp(authentication_password) == "") {
xlog("L_NOTICE","NOTICE: Tried to add authentication for call to extension $rU, but no password found. \n");
return;
}
# Lookup again to fix NATed extensions
lookup("ser_location");
# Add a new branch to send the INVITE
append_branch();
# When replies to this request will arrive, they will go through onreply_route(2)
t_on_reply("2");
# Set authentication realm to special value so that uac_auth will use the received realm and not check the two realms for equality.
# This is used to allow realms that are hostnames
$avp(authentication_realm) = "0";
# Add the authentication header
uac_auth();
# Send the request
t_relay();
exit;
}
# Initial call failed, so all call limitation counters for this call must be decreased. This is done by removing the call from the dialog profiles. (CVN210RM52)
if ($avp(persistent_user_client_id) != $null && (is_in_profile("userinternal","$avp(persistent_user_client_id)") ||
is_in_profile("userexternal","$avp(persistent_user_client_id)") )){
unset_dlg_profile("userinternal","$avp(persistent_user_client_id)");
unset_dlg_profile("userexternal","$avp(persistent_user_client_id)");
if ($avp(client_client_id)!= $null ) {
unset_dlg_profile("clientinternal","$avp(client_client_id)");
unset_dlg_profile("clientexternal","$avp(client_client_id)");
}
if ($avp(reseller_client_id)!= $null ) {
unset_dlg_profile("resellerinternal","$avp(reseller_client_id)");
unset_dlg_profile("resellerexternal","$avp(reseller_client_id)");
}
if ($avp(extension_client_id)!= $null ) {
unset_dlg_profile("extension","$avp(extension_client_id)");
}
}
if ($sht(via=>$hdr(Call-ID))!=$null) insert_hf("Via: $sht(via=>$hdr(Call-ID))\r\n");
exit;
}
# Advanced deployment headers setup for DEPLOYMENTINF = 1
# In this route we set the variables $var(advertised_ip):$var(advertised_port) which will then be used to set the Via, Record-Route, Contact and SDP Contact headers
route[10]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 10 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE In this route we set the variables $var(advertised_ip):$var(advertised_port) which will then be used to set the headers\n");
# $cnt(LOCAL_IP) should be non-empty because DEPLOYMENTINF = 1
if ($cnt(LOCAL_IP)!=''){
# Write the destination IP into $var(destIP) for further use. If $du is set, then the destination IP is found in it. If $du is null, the destination IP is found in $rd
# $du = destination uri; $rd = domain in request's URI
if ($du!=$null) {
$var(destIP)=$(du{s.select,1,:});
$var(destIP)=$(var(destIP){s.select,0,;});
} else {
$var(destIP)=$rd;
}
# If destination IP is local and not one of our proxies, use local IP. Else, use the public IP
if (avp_checklocal("$cnt(LOCAL_IP)", "$var(destIP)", "$cnt(LOCAL_NETMASK)") && !is_proxy_ip("$var(destIP)")) {
# set advertised_address based on $cnt(LOCAL_IP) and $cnt(LOCAL_*_PORT)
route(57);
# destination is also behind NAT we set sdp address to $si
$avp(advertised_sdp)=$si;
} else {
# Set advertised_address based on Direct Routing mathch
# If no Direct Route is matched we set advertised_address to Kamailio PUBLIC IP;
route(55);
# Set advertised_sdp based on Direct Routing of the PBX that sends the rtp.
# If no Direct Route is matched we set SDP address to the public IP of the PBX that sends the rtp.
route(61);
}
} else {
# $cnt(LOCAL_IP) is empty, something must be wrong in the advanced SIP setup. Use the public because it's the most common case
# Set advertised_address based on Direct Routing mathch
# If no Direct Route is matched we set advertised_address to Kamailio PUBLIC IP;
route(55);
# Set advertised_sdp based on Direct Routing of the PBX that sends the rtp.
# If no Direct Route is matched we set SDP address to the public IP of the PBX that sends the rtp.
route(61);
}
# Set the Via, Record-Route, Contact and SDP Contact to the values in $var(advertised_ip):$var(advertised_port)
route(40);
}
# Advanced deployment headers setup for DEPLOYMENTINF = 2 or DEPLOYMENTINF = 3
route[11]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 11 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Advanced deployment headers setup for DEPLOYMENTINF = 2 or DEPLOYMENTINF = 3.\n");
# Write the destination IP into $var(destIP) for further use. If $du is set, then the destination IP is found in it. If $du is null, the destination IP is found in $rd
# $du = destination uri; $rd = domain in request's URI
if ($du!=$null) {
$var(destIP)=$(du{s.select,1,:});
$var(destIP)=$(var(destIP){s.select,0,;});
} else {
$var(destIP)=$rd;
}
# If target is an extension, we get the socket to usit it to set advertised address.
if ( $sht(remote_extension=>$hdr(Call-ID)) != $null ){
$avp(socket)=$null;
$avp(remote_extension)=$sht(remote_extension=>$hdr(Call-ID));
if (reg_fetch_contacts("ser_location", "$avp(remote_extension)", "caller")) {
$var(i_counter) = 0;
$var(bcontact)= "0";
xlog("L_NOTICE", "[ROUTE 51] SIPLOC HR Contact count: $(ulc(caller=>count))\n");
while($var(i_counter) < $(ulc(caller=>count))) {
$var(bcontact) = $(ulc(caller=>received)[$var(i_counter)]);
$avp(socket) = $(ulc(caller=>socket)[$var(i_counter)]);
if ($(var(bcontact){s.len})<6) {
$var(bcontact) = $(ulc(caller=>addr)[$var(i_counter)]);
}
if (($(var(bcontact){s.len}) >= 6) && ($(var(bcontact){uri.host}) == $si ) && ($(var(bcontact){uri.port}) == $sp)) {
if ($avp(socket)!=$null){
$avp(socket_ip)=$(avp(socket){s.select,1,:});
$avp(socket_port)=$(avp(socket){s.select,2,:});
}
break;
}
$var(i_counter) = $var(i_counter) + 1;
}
reg_free_contacts("caller");
}
}
# Try to use the ser_location.socket information to set the Via, Record-Route, Contact.
if ( ($avp(socket_ip)!=$null) && ($avp(socket_ip)!="") && ($avp(socket_port)!=$null) && ($avp(socket_port)!="")) {
$var(advertised_ip) = $avp(socket_ip);
$var(advertised_port) = $avp(socket_port);
# If socket was not found in ser_location (this probably means the destination is a provider)
# Set addresses based on destination location (private, direct route or public).
} else {
# If destination IP is local and not one of our proxies, use local IP. Else, use the public IP
if (!avp_checklocal("$cnt(LOCAL_IP)", "$var(destIP)", "$cnt(LOCAL_NETMASK)") || is_proxy_ip("$var(destIP)")) {
# Set advertised_address based on Direct Routing mathch
# If no Direct Route is matched we set advertised_address to Kamailio PUBLIC IP;
route(55);
} else {
# set advertised_address based on $cnt(LOCAL_IP) and $cnt(LOCAL_*_PORT)
# we are local no need to fix SDP
route(57);
}
}
# We only set sdp if call is not local or it's one of our proxies
if (!avp_checklocal("$cnt(LOCAL_IP)", "$var(destIP)", "$cnt(LOCAL_NETMASK)") || is_proxy_ip("$var(destIP)")) {
# Set advertised_sdp based on Direct Routing of the PBX that sends the rtp.
# If no Direct Route is matched we set SDP address to the public IP of the PBX that sends the rtp.
route(61);
}
# Set the Via, Record-Route, Contact and SDP Contact to the values in $var(advertised_ip):$var(advertised_port)
route(40);
}
# Helper used to fix the R-URI's protocol parameter(PV $rP) based on the force socket.
# \output $var(rP) lowercase string representation of the protocol
route[fix_rP] {
if (not_empty("$fs")) {
$var(rP) = $(fs{s.select,0,:});
} else if (not_empty("$rP")) {
$var(rP) = $(rP{s.tolower});
} else {
$var(rP) = "udp";
}
}
# Server behind NAT header setups for requests from asterisk to outside
route[12]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 12 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Server behind NAT header setups for requests from asterisk to outside.\n");
route(fix_rP);
switch ($cnt(DEPLOYMENTINF)) {
case 0:
# set advertised_address based on $cnt(KAMAILIO_IP) and $cnt(KAMAILIO_*_PORT)
route(56);
break;
case 1:
# Advanced deployment headers setup for DEPLOYMENTINF = 1
route(10);
break;
case 2:
case 3:
# Advanced deployment headers setup for DEPLOYMENTINF = 2 or DEPLOYMENTINF = 3
route(11);
break;
}
}
# Server behind NAT header setups for requests from outside to asterisk
route[13]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 13 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Server behind NAT header setups for requests from outside to asterisk.\n");
if ($cnt(LOCAL_IP)!='') {
if (!is_method("REGISTER|MESSAGE")){
$var(real_ip) = $si;
if ($x_real_ip != $null) {
$var(real_ip) = $x_real_ip;
}
# If source IP is not in the local network and server is behind NAT, we must set 2 Record-Route headers, one with local IP and one with public IP
if ( ($cnt(DEPLOYMENTINF)==1) && !avp_checklocal("$cnt(LOCAL_IP)", "$var(real_ip)", "$cnt(LOCAL_NETMASK)"))
record_advertised_route("$cnt(LOCAL_IP):$cnt(LOCAL_UDP_PORT);$cnt(KAMAILIO_IP):$Rp");
else record_advertised_route();
}
} else{
if (!is_method("REGISTER|MESSAGE")) record_advertised_route();
}
# We set Via header to the LOGIC_IP when sending requests to asterisk
# Set advertised address based on LOGIC_IP, LOGIC_*_PORT.
route(72);
}
# Retrieve an extension's user, organization and provider client ids and store
# them and their non-padded versions in avps and vars, respectively.
# The queries will be based on $avp(extension_to_check).
route[14]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 14 message from $si : $sp .\n");
xlog("L_NOTICE", "Route used for retrieving the user, organization and provider client ids.\n");
if ( ($avp(user_client_id) == -1) || (!is_avp_set("$avp(user_client_id)"))
|| ($avp(extension_client_id) == -1) || (!is_avp_set("$avp(extension_client_id)"))
|| ($avp(client_client_id) == -1) || (!is_avp_set("$avp(client_client_id)"))) {
$avp(user_client_id) = -1;
$avp(extension_client_id) = -1;
$avp(client_client_id) = -1;
$avp(reseller_client_id) = -1;
avp_hr_query_set("slave",
"<res:inf:ext:$avp(extension_to_check) | usrid | 0 | DB_STRING | 0 | hash>"
"<res:inf:ext:$avp(extension_to_check) | extid | 1 | DB_STRING | 0 | hash>"
"<res:inf:ext:$avp(extension_to_check) | orgid | 2 | DB_STRING | 0 | hash>"
"<res:inf:ext:$avp(extension_to_check) | spid | 3 | DB_STRING | 0 | hash>"
,
"SELECT concat(cl.id,''), concat(ext.id,''), concat(org.id,''), concat(org.parent_client_id,'') "
"FROM extension ext, client cl, client org "
"WHERE ext.extended_number='$avp(extension_to_check)' AND "
" cl.id = ext.client_id AND cl.parent_client_id = org.id"
,
"$avp(user_client_id);$avp(extension_client_id);$avp(client_client_id);$avp(reseller_client_id)", "0");
}
$var(non_padded_user_client_id) = $(avp(user_client_id){s.int});
$var(non_padded_extension_client_id) = $(avp(extension_client_id){s.int});
$var(non_padded_client_client_id) = $(avp(client_client_id){s.int});
$var(non_padded_reseller_client_id) = $(avp(reseller_client_id){s.int});
}
#Route used for handling mwi PUBLISH messages from asterisk
route[15]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 15 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Route used for handling mwi PUBLISH messages from asterisk.\n");
#!ifdef ENABLE_PRESENCE
if ( (is_present_hf("Event")) && ( $(hdr(Event)) == "message-summary" ) ){
# Check if source IP and source port is in the list of IPs and port ranges that are allowed to send us mwi PUBLISHes (node_component.publisher_server)
if ( is_publisher("$si", "$sp") ) {
xlog("L_DBG", "Authorized MWI PUBLISH received.\n");
# Create a transaction to absorb retransmissions
if (! t_newtran()){
sl_reply_error();
return;
};
handle_publish();
t_release();
} else {
xlog("L_NOTICE", "NOTICE: Unauthorized MWI PUBLISH received from IP address $si . Rejecting request.\n");
route(3); #Send negative reply
return(-1);
}
return(-1);
}
#!else
xlog("L_NOTICE", "ROUTE[15] Presence disabled.\n");
return;
#!endif
}
#Route used for handling extensions behind NAT
route[16]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 16 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Route used for handling extensions behind NAT.\n");
# nat_uac_test(flags) tries to guess if client's request originated behind a nat. Meaning of the flags is as follows:
# 2 - the "received" test is used: address in Via is compared against source IP address of signaling
# 16 - test if the source port is different from the port in Via
if (nat_uac_test("2"))
$var(check_rcv_ip) = 1;
else
$var(check_rcv_ip) = 0;
if ( ($var(check_rcv_ip)==1) || nat_uac_test("16") ){
xlog("L_NOTICE", "Request passed a nat uac test(ip test result was $var(check_rcv_ip))\n");
# On TCP usually the port is choosed randomly when a TCP connection is created, so
# we don't have NAT in this case.
if ( ($proto=="tcp") && ($cnt(TCP_STANDARD_COMPLIANT)==1) && ($var(check_rcv_ip)==0) ) {
xlog("L_DEBUG", "Skipping nat fixup for tcp standard compliant request\n");
return(-1);
}
route(17);
return(1);
}
#!ifdef ENABLE_NOTIFICATION
else if (@cfg_get.tm.offband == "1" && $ua =~ $cnt(MOBILE_UA_REGEX)) {
# Fake nat handling for mobile devices
xlog("L_NOTICE", "Doing nat fixup for mobile request\n");
route(17);
return(1);
}
#!endif
return(-1);
}
# Route used to fix nated requests
route[17] {
xlog("L_NOTICE", "Passing through ROUTE 16 message from $si:$sp\n");
xlog("L_NOTICE", "Route used to fix NATed requests\n");
# Flag that marks the fact that the extension is behind NAT
setbflag(NAT_BFLAG);
# If source extension is behind NAT we call force_rport(). This function adds the rport parameter to the first Via header.
# Thus, kamailio will add the received IP port to the top most via header in the SIP message, even if the client does not indicate support for rport.
# This enables subsequent SIP messages to return to the proper port later on in a SIP transaction.
force_rport();
if (is_method("SUBSCRIBE|PUBLISH")){
# Rewrite Contact header to contain request's source address:port
fix_nated_contact();
} else {
if (is_method("REGISTER")) {
# Create a URI consisting of the source IP, port, and protocol and stores the URI in an Attribute-Value-Pair.
# The URI will be appended as "received" parameter to Contact in 200 OK and registrar will store it in the received column in the location table.
fix_nated_register();
} else {
# $var(sht_nat_uri) is used to keep the value that will be entered in the nat_uri hash table before we can actually write it in the hash
if ($proto=="udp") {
$var(sht_nat_uri) = "sip:" + $si + ":" + $sp;
} else {
$var(sht_nat_uri) = "sip:" + $si + ":" + $sp + ";";
if ($proto=="wss") {
# For secure websocket transport should be set to ws not wss
# See draft-ietf-sipcore-sip-websocket section 5.2.2
$var(sht_nat_uri) = $var(sht_nat_uri) + "transport=ws";
} else {
$var(sht_nat_uri) = $var(sht_nat_uri) + "transport=" + $proto;
}
if ($var(sht_nat_uri) != "")
$sht(nat_uri=>$hdr(Call-ID)) = $var(sht_nat_uri);
}
}
#uncomment this if you need the tcp connection to stay alive for as long as possible
#if (is_method("REGISTER")) keep_tcp_connection_alive();
}
}
# Helper used to clamp $var(expires) values between the registrar's min_expires and max_expires.
# \param $var(expires) - [in|out] expires integer value
route["registrar:clamp_expires"] {
if ($var(expires) > $(sel(cfg_get.registrar.max_expires){s.int}))
$var(expires) = @cfg_get.registrar.max_expires;
else if ($var(expires) != 0 && $var(expires) < $(sel(cfg_get.registrar.min_expires){s.int}))
$var(expires) = @cfg_get.registrar.min_expires;
$var(expires) = $(var(expires){s.int});
}
#Route used for handling REGISTERs from extensions
route[22]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 22 message from $si : $sp . \n");
xlog("L_NOTICE", "[ROUTE 22] Route used for handling REGISTERs from extensions. \n");
$avp(is_authenticated) = "0";
# L1 pike check
#!ifdef ENABLE_PIKE
route(69);
#!endif
if ($tU != $null){
xlog("L_NOTICE", "[ROUTE 22] We check if the extensions has IP limitations . \n");
# We check if the extensions has IP limitations (Has "Allow extension SIP connection only from IP" setup with at least value) .
# If NULL is returned ==> Hubring is stopped or the set ext.$var(to_username).sa does not exist because the extension has not set any limitations.
# If 1 is returned ==> set res:sa:po:ext:$var(to_username) exists and there is alt least one record that matches $si => extension is allowed to register from this IP
# If -5 is returned ==> set res:sa:po:ext:$var(to_username) exists and no record matches $si => extension is not allowed to register from this IP
avp_hr_query_set("slave", "<res:sa:po:ext:$var(to_username) | $si | 0 | DB_INT | 5 >",
"SELECT verify_extension_access('$var(to_username)','$si')",
"$avp(is_authenticated)", "0");
$var(res) = $rc;
if ( $var(res) == -5 ) {
xlog("L_NOTICE", "[ROUTE 22] Extension $var(to_username) is not allowed to register from $si due to IP limitations. \n");
route(3); #Send negative reply
return(-1);
}
if ( ($au != $null) && ($au != $var(to_username)) ) {
xlog("L_NOTICE", "[ROUTE 22] The authentication username ($au ) is not null and different from the To username ($var(to_username)). Rejecting request. \n");
$avp(error_reason) = 404;
$avp(error_text) = "Not found";
route(3); #Send negative reply;
return(-1);
}
if (! t_newtran()) {
sl_reply_error();
return;
}
# If the value was returned (or NULL) continue with the auth.
# This avp will mark the fact that the extension is allowed to register (ser_subscriber.can_register)
$avp(can_register) = $null;
# This avp will mark the fact that the extension has enabled pinging (ser_subscriber.natping)
$avp(natping) = "1";
if($proto=="ws"||$proto=="wss"||$proto=="tcp" || $proto=="tls"){
xlog("L_NOTICE", "[ROUTE 22] We have $proto protocol \n");
xlog("L_NOTICE", "[ROUTE 22] We have cnt(WSS_FORCE_TOKEN) $cnt(WSS_FORCE_TOKEN) \n");
if ($hdr(X-User-device)==$null){
$var(res)="-10";
} else {
xlog("L_NOTICE", "[ROUTE 22] We have $proto protocol and X-User-Device; we go to route 37 to check if the extensions has a generated auth token. \n");
$var(auth_exten)=$var(to_username);
$var(auth_device)=$(hdr(X-user-device){s.escape.common});
route(37);
xlog("L_NOTICE", "[ROUTE 22] Authentication token found. We try to authorize based on token. \n");
# xlog("L_NOTICE", "[ROUTE 22] Authentication token found. We try to authorize based on token $avp(authentication_password) . \n");
if (!pv_www_authorize("$td", "$avp(authentication_password)",0)) {
$var(res) = $rc;
} else {
$var(res) = "1";
}
}
if(($cnt(WSS_FORCE_TOKEN)>1)&& ($avp(authentication_password)==$null)){
xlog("L_NOTICE", "[ROUTE 22] On $proto but force_token is not 1. we try to authenticate based on ser_subsriber credentials. \n");
if (!www_authorize("$td", "ser_subscriber")) {
$var(res) = $rc;
} else {
$var(res) = "1";
}
}
} else {
xlog("L_NOTICE", "[ROUTE 22] Protocol is not ws/wss/tcp/tls; we try to authenticate based on ser_subsriber credentials. \n");
if (!www_authorize("$td", "ser_subscriber")) {
$var(res) = $rc;
} else {
$var(res) = "1";
}
}
xlog("L_NOTICE", "[ROUTE 22] Return Code is: $var(res) . \n");
if ($var(res) == -10){
xlog("L_NOTICE", "[ROUTE 22] we reject the registration because it does not have a device specified. \n");
$avp(error_reason)= 403;
$avp(error_text)= "No device specified";
route(3); #Send negative reply
return(-1);
}
#!ifdef ENABLE_PIKE
if ($var(res) == -2) {
# L2 and L3 pike checks
route(70);
}
#!endif
if ( (($var(res) == -2) || ($var(res) == -3))&& ($cnt(EXPLICIT_REASON_FOR_REGISTRATION_FAILURE_ENABLED) == 1) ) {
xlog("L_NOTICE", "[ROUTE 22] If return code is -3(invalid user) or -2(invalid password) and cnt(EXPLICIT_REASON_FOR_REGISTRATION_FAILURE_ENABLED) is set to 1 we reject the registration instead of resending a chall
enge. \n");
$avp(error_reason)= 403;
$avp(error_text)= "Wrong username or password";
route(3); #Send negative reply
return(-1);
}
if( $var(res) <= 0 ){
# We chalenge the register only if www_authorize failed to find the user in ser_subscriber and we do not have $au.
# Otherwise we consider that the registration failed
www_challenge("$td", "4");
return;
}
avp_hr_query_set("slave", "<res:sip:ext:$var(to_username) | creg | 0 | DB_STRING | 1 | hash> <res:sip:ext:$var(to_username) | natpg | 1 | DB_STRING | 1 | hash>",
"SELECT can_register, natping FROM ser_subscriber WHERE username='$var(to_username)'",
"$avp(can_register);$avp(natping)", "0");
if ($avp(can_register) == "0"){
xlog("L_ERR", "[ROUTE 22] Extension $au has a static IP address and is not allowed to register. Rejecting register.\n");
route(3); #Send negative reply
return(-1);
}
if ($avp(natping) == "1")
setbflag(SIPPING_BFLAG); # send pings to this extension
#!ifdef ENABLE_PRESENCE
# If the extension has the force MWI setting enabled, we add/remove a subscription
# manually, depending on the current request(initial REGISTER or un-REGISTER). \see bellow
$avp(force_mwi) = "0";
avp_hr_query_set("slave", "<res:po:ext:$var(authentication_username) | fmwi | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM extension,extension_prefs WHERE param='force_mwi'"
" AND extension_prefs.extension_id = extension.id"
" AND extended_number='$var(authentication_username)'", "$avp(force_mwi)", "0");
if ($avp(force_mwi) == "1")
setbflag(FMWI_BFLAG);
#!endif
# Save contact in ser_location
save("ser_location", "0");
$var(save_rc) = $rc;
if ($var(save_rc) <= 0){
xlog("L_ERR", "[ROUTE 22] Failed to process REGISTER for $tU \n");
return;
}
$avp(s:pbx_connected) = "0";
avp_hr_query_set("slave", "<res:po:ext:$var(to_username) | pbx | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM extension_prefs, extension WHERE extension.extended_number='$var(to_username)'"
" AND extension_id = extension.id AND param='pbx_connected'",
"$avp(s:pbx_connected)", "0");
if ($avp(s:pbx_connected) == "1") {
sha1_b64("$si", "$var(si_hash)");
$var(memkey) = "rtl:trreg:" + $var(si_hash);
if ($var(save_rc) == 3) # contact deleted
avp_hr_delete("$var(memkey)");
else { # contact inserted/updated
$var(expires) = @cfg_get.registrar.default_expires;
if (not_empty("$hdr(Expires)"))
$var(expires) = $hdr(Expires);
route("registrar:clamp_expires");
avp_hr_direct_query("<$var(memkey) | 0 | DB_STRING | 0 | string>", "$var(to_username)", "", "$var(expires)");
}
}
#!ifdef ENABLE_NOTIFICATION
# Do "late" forking only for mobile devices if their contacts were updated/inserted
if (@cfg_get.tm.offband == "1" && $var(save_rc) != 3 && $ua =~ $cnt(MOBILE_UA_REGEX)) {
xlog("L_NOTICE", "Registration from mobile device. Append branches, if needed\n");
ts_append("ser_location", "$tu");
}
if ($hdr(Contact) == "*") {
# Special case: Don't send notifications for this type of unregister
if ($hdr(Expires) != "0") {
xlog("L_ERR", "Star contact REGISTER request with non zero expires\n");
return;
}
} else if ($var(save_rc) == 1 || $var(save_rc) == 2 || $var(save_rc) == 3) {
# Execute this only when contacts have been inserted, updated or deleted
$var(idx) = 0;
while ($(hdr(Contact)[$var(idx)]) != $null) {
$var(contact) = $(hdr(Contact)[$var(idx)]);
$var(contact_uri) = $(var(contact){nameaddr.uri});
$var(contact_params) = $(hdr(Contact){tobody.params});
# clamp expires
$var(expires) = "";
if (not_empty("$var(contact_params)")) {
$var(expires) = $(var(contact_params){param.value,expires});
}
if ($var(expires) == "") {
if (not_empty("$hdr(Expires)")) {
$var(expires) = $hdr(Expires);
} else if ($var(save_rc) == 1 || $var(save_rc) == 2) {
# Special case for insert and update
$var(expires) = @cfg_get.registrar.default_expires;
} else {
$var(idx) = $var(idx) + 1;
continue;
}
}
route("registrar:clamp_expires");
if ($proto=="ws" || $proto=="wss" || $proto=="tcp" || $proto=="tls"){
$var(device_ip) = $si + ":" + $sp;
} else if ($avp(i:200)) {
$var(device_ip) = $(avp(i:200){uri.host}) + ":" + $(avp(i:200){uri.port});
} else {
$var(device_ip) = $(var(contact_uri){uri.host}) + ":" + $(var(contact_uri){uri.port});
}
$var(device_id) = "";
if (not_empty("$var(auth_device)")) {
$var(device_id) = $var(auth_device);
}
register_notification("$au", "$var(contact_uri)", "$var(expires)", "$var(device_ip)", "$var(device_id)");
$var(idx) = $var(idx) + 1;
}
}
#!endif
#!ifdef ENABLE_PRESENCE
if ($avp(force_mwi) == "1"){
if ($var(save_rc) == 1) {
# Force subscription to MWI once for the initial REGISTER
route(23);
} else if ($var(save_rc) == 3) {
# Manual unsubscribe for extensions using force_mwi on un-REGISTER
pres_remove_active_watcher(
"sip:$var(authentication_username)@$cnt(KAMAILIO_IP)",
"message-summary", "$hdr(Call-ID)", "", "$hdr(Call-ID)1");
}
}
#!endif
} else{
xlog("L_ERR", "NOTICE: The To username ($tU) is null . Rejecting request.\n");
route(3); #Send negative reply
return(-1);
}
}
#We force MWI subscription by entering a manual entry in the ser_active_watchers table
#!ifdef ENABLE_PRESENCE
route[23]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 23 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE We force MWI subscription by entering a manual entry in the ser_active_watchers table.\n");
#get extension details from the ser_location table
$avp(s:expires)="0";
$avp(s:contact)="0";
$avp(s:received)="0";
$avp(s:socket)="0";
if (reg_fetch_contacts("ser_location", "$var(authentication_username)", "caller")) {
$var(i_counter) = 0;
xlog("L_NOTICE", "[ROUTE 23] SIPLOC Contact count: $(ulc(caller=>count))\n");
while($var(i_counter) < $(ulc(caller=>count))) {
$avp(s:received) = $(ulc(caller=>received)[$var(i_counter)]);
$avp(s:contact) = $(ulc(caller=>addr)[$var(i_counter)]);
$avp(s:expires) = $(ulc(caller=>expires)[$var(i_counter)]);
$avp(s:socket) = $(ulc(caller=>socket)[$var(i_counter)]);
# In case the extension is behind NAT, we use the received column as its contact address. Else, we use the contact column
if ($(avp(s:received){s.len})>=6) {
$avp(s:insert_contact) = $avp(s:received);
} else {
$avp(s:insert_contact) = $avp(s:contact);
}
if (($(avp(s:insert_contact){s.len}) >= 6) && ($(avp(s:insert_contact){uri.host}) == $si ) && ($(avp(s:insert_contact){uri.port}) == $sp)) {
if ($avp(s:expires)=="0") {
avp_db_query("master", "DELETE FROM ser_active_watchers where watcher_username='$var(authentication_username)' and event='message-summary'");
}
# In case the extension is in the local network, we must use the local IP as source address when we send NOTIFYs to this extension
if ( ($cnt(LOCAL_IP)!='') && (avp_checklocal("$cnt(LOCAL_IP)", "$si", "$cnt(LOCAL_NETMASK)")) ){
$avp(server_port)=$cnt(LOCAL_UDP_PORT);
$avp(server_ip)=$cnt(LOCAL_IP);
} else {
$avp(server_port)=$cnt(KAMAILIO_UDP_PORT);
$avp(server_ip)=$cnt(KAMAILIO_IP);
}
$var(presentity_uri) = "sip:" + $var(authentication_username) + "@" + $cnt(KAMAILIO_IP);
$var(subs_contact) = "sip:" + $var(authentication_username) + "@" + $(avp(s:insert_contact){uri.host}) + ":" + $(avp(s:insert_contact){uri.port});
$var(event_name) = "message-summary";
$var(from_user) = $fU;
$var(from_domain) = $(fu{uri.host});
if ($(fu{uri.port}) != $null) {
$var(from_domain) = $var(from_domain) + ":" + $(fu{uri.port});
}
# Push all the subscription fields
$avp(fmwi_subs) = $var(from_domain); # from_domain
$avp(fmwi_subs) = $var(from_user); # from_user
$avp(fmwi_subs) = "sip:" + $avp(server_ip) + ":" + $avp(server_port); # local_contact
$avp(fmwi_subs) = $avp(s:socket); # sockinfo_str
$avp(fmwi_subs) = 1; # version
$avp(fmwi_subs) = ""; # reason
$avp(fmwi_subs) = 1; # status
$avp(fmwi_subs) = $TV(s) + 31536000; # expires (one year from now)
$avp(fmwi_subs) = ""; # record_route
$avp(fmwi_subs) = $var(subs_contact); # contact
$avp(fmwi_subs) = 1; # remote_cseq
$avp(fmwi_subs) = 1; # local_cseq
$avp(fmwi_subs) = $hdr(Call-ID) + "1"; # callid
$avp(fmwi_subs) = ""; # from_tag
$avp(fmwi_subs) = $hdr(Call-ID); # to_tag
$avp(fmwi_subs) = ""; # event_id
$avp(fmwi_subs) = $var(event_name); # event_name
$avp(fmwi_subs) = $avp(server_ip); # to_domain
$avp(fmwi_subs) = $var(authentication_username); # to_user
$avp(fmwi_subs) = $avp(server_ip); # watcher_domain
$avp(fmwi_subs) = $var(authentication_username); # watcher_user
$avp(fmwi_subs) = $var(presentity_uri); # presentity_uri
pres_sync_watcher("$avp(fmwi_subs)");
break;
}
$var(i_counter) = $var(i_counter) + 1;
}
reg_free_contacts("caller");
}
}
#!endif
#Handle FAX loop, initial INVITE. See '5. FAX loop' in DOCS section
route[24]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 24 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handle FAX loop, initial INVITE.\n");
$var(extended_extension) = $(rU{s.select,1,-});
if (method=="INVITE") {
remove_hf("X-voipnow-user");
append_hf("X-voipnow-user: $var(extended_extension)\r\n");
}
append_hf("X-voipnow-faxsend: 1\r\n");
$rU = $(rU{s.select,2,-});
xlog("L_DBG","This is a fax message which will be sent back to the asterisk server that sent it.\n");
add_rr_param(";fax=1");
# Headers replacements for FAX loop handling
$avp(destination_uri) = "sip:" + $rU + "@" + $si + ":" + $sp;
$var(from) = $(hdr(From));
$var(from) = $(var(from){s.select,1,@});
remove_hf("From");
if ($ft != $null) insert_hf("From: <sip:$var(extended_extension)@$cnt(KAMAILIO_IP):$cnt(KAMAILIO_UDP_PORT)>;tag=$ft\r\n","Call-ID");
else insert_hf("From: <sip:$var(extended_extension)@$cnt(KAMAILIO_IP):$cnt(KAMAILIO_UDP_PORT)>\r\n","Call-ID");
remove_hf("To");
if ($tt != $null)
insert_hf("To: <sip:$rU@$si:$sp;tag=$tt>\r\n","Call-ID");
else insert_hf("To: <sip:$rU@$si:$sp>\r\n","Call-ID");
if($hdr(Contact)!=$null){
remove_hf("Contact");
insert_hf("Contact: <sip:$var(extended_extension)@$cnt(KAMAILIO_IP):$cnt(KAMAILIO_UDP_PORT)>\r\n","Call-ID");
}
$var(callid) = $(hdr(Call-ID));
remove_hf("Call-ID");
insert_hf("Call-ID: fax$var(callid)\r\n", "CSeq");
avp_pushto("$ruri","$avp(destination_uri)");
# When replies to this request will arrive, they will go through onreply_route(3)
t_on_reply("3");
# For non single ip deployments we the the source ip, Via, RR headers to the LOGIC IP
if ($cnt(DEPLOYMENTINF) > 0) {
route(fix_rP);
route(58);
}
# Add Record-Route header
if (!is_method("REGISTER|MESSAGE")) record_advertised_route();
# send the request to its destination
if (!t_relay_to_tcp()) {
sl_reply_error();
};
}
# Handle FAX loop, subsequent requests. See '5. FAX loop' in DOCS section
route[25]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 25 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handle FAX loop, subsequent requests..\n");
add_rr_param(";fax=1");
$var(callid) = $(hdr(Call-ID));
if ($(var(callid){s.substr,0,3}) == "fax"){
remove_hf("Call-ID");
$avp(new_callid)=$(var(callid){s.substr,3,0});
insert_hf("Call-ID: $avp(new_callid)\r\n", "CSeq");
} else {
remove_hf("Call-ID");
$avp(new_callid)="fax" + $var(callid);
insert_hf("Call-ID: $avp(new_callid)\r\n", "CSeq");
}
$avp(destination_uri) = "sip:" + $rU + "@" + $si + ":" + $sp;
avp_pushto("$ruri","$avp(destination_uri)");
if (method=="INVITE") {
route(4);# Add X-voipnow-user header to help asterisk identify the caller extension
}
# When replies to this request will arrive, they will go through onreply_route(3)
t_on_reply("3");
# For non single ip deployments we the the source ip, Via, RR headers to the LOGIC IP
if ($cnt(DEPLOYMENTINF) > 0) {
route(fix_rP);
route(58);
}
# Add Record-Route header
if (!is_method("REGISTER|MESSAGE")) {
record_advertised_route();
}
# Send the request to its destination
if (!t_relay_to_tcp()) {
sl_reply_error();
};
}
# Reject calls from intercom/paging to an extension involved in a call. Introduced by B13264
route[26]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 26 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Reject calls from intercom/paging to an extension involved in a call. Introduced by B13264.\n");
if (is_present_hf("Alert-Info") || is_present_hf("Call-Info")){
$avp(destination_extension) = $null;
if ($rU != $null) {
avp_hr_query_set("slave", "<res:inf:ext:$var(request_username) | extid | 0 | DB_STRING | 0 | hash>",
"SELECT concat(cl.id,\"\") FROM extension ext, client cl WHERE ext.extended_number='$var(request_username)' and cl.id=ext.client_id",
"$avp(destination_extension)", "0");
}
if ($avp(destination_extension) == $null) {
xlog("L_NOTICE","NOTICE: Intercom call to non-existent extension $rU . Rejecting request.\n");
route(3); #Send negative reply
return(-1);
}
$avp(extension_call_count)=0;
get_profile_size("extension","$avp(destination_extension)","$avp(extension_call_count)");
if ($avp(extension_call_count)>0) {
sl_send_reply("486","Busy Here");
return(-1);
}
}
}
# Call limitation: If the "X-voipnow-user" header is present, then we must count the caller (the original caller was changed by asterisk).
route[27]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 27 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Call limitation: If the X-voipnow-user header is present, then we must count the caller (the original caller was changed by asterisk)\n");
# $avp(extension_to_check) is the caller extension number. It will be used in route(7) or route(44) to check call limitation
$avp(extension_to_check) = $(hdr(X-voipnow-user));
remove_hf("X-voipnow-user");
# $var(destination_is_extension) is set to 1 in case lookup was successfull, which means the call is destined to an extension. In this case we
# increase the internal call limitation counters. If the call is not destined to an extension, then it is destined to an outside number so we
# must increase the external call limitation counters
if ($var(destination_is_extension) == 1){
# Check internal call limits for caller extension. Increase internal call count for caller extension
route(7);
if ($retcode < 0) return(-1);
# When negative replies to this request will arrive, they will go through Failure Route 2. This route will decrease the call count.
t_on_failure("1");
} else {
# Check external call limits for caller extension. Increase external call count for caller extension
route(44);
if ($retcode < 0) return(-1);
# When negative replies to this request will arrive, they will go through Failure Route 1. This route will decrease the call count.
t_on_failure("1");
}
}
# Handles requests from asterisk when the destination is a channel. Check call limitation, edit necessary headers, set reply/failure routes and send the request
route[28]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 28 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handles requests from asterisk when the destination is a channel. Check call limitation, edit necessary headers, set reply/failure routes and send the request.\n");
# Get channel id, which will be used in route(6) to check call limitation
$avp(channel_id) = $(hdr(X-voipnow-channel));
# Remove "X-voipnow-channel" header because we no longer need it
remove_hf("X-voipnow-channel");
# In case call limit has been reached, we must reply with error code 603 so that asterisk will try a different route
$avp(error_reason)= 603;
$avp(error_text)= "Declined";
# Check call limits for channel callee. Increase call count for channel callee
route(6);
if ($retcode < 0) return(-1);
# Write the channel id to the ch hash table for future use
$sht(ch=>$hdr(Call-ID))=$avp(channel_id);
# Setting timeout to 5 seconds, only in case of the first INVITE.
# this timeout referes to the tibe between the invite and the first provisional answer.
# if provider does not answer in 5 sec
if((method=="INVITE") && (!has_totag())){
t_set_fr(0,5000);
}
# Build a dialog for this call
setflag(4);
# If there is anything else to write to hash tables, we can write it now because all acceptance tests have been passed
route(30);
# Final route for requests from asterisk to an outside destination
# Edit necessary headers, set reply/failure routes and send the request
route(1);
}
#Handles requests for asterisk when the destination is an extension. Check call limitation, edit necessary headers, set reply/failure routes and send the request
route[29]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 29 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handles requests for asterisk when the destination is an extension. Check call limitation, edit necessary headers, set reply/failure routes and send the request.\n");
if ( $var(destination_is_extension) == 1 ){
# $var(check_callee) marks the fact that the extension for which we check call limitation is a callee.
# (In this case, we will need to check the call waiting settings)
$var(check_callee) = 1;
# Mark the extension for which we will be checking call limits (the callee extension)
$avp(extension_to_check) = $var(request_username);
# if we alredy checked for virtualization
if($avp(getbase) != "" ) {
$avp(extension_to_check)=$avp(getbase);
}
# In case the call comes from an external number, asterisk will add the X-voipnow-external header so that kamailio
# will know which call limitation counters to check and increase. (CVN210RM52)
if (is_present_hf("X-voipnow-external")){
# Check external call limits for callee extension. Increase external call count for callee extension
route(44);
if ($retcode < 0) {
return(-1);
}
remove_hf("X-voipnow-external");
# When negative replies to this request will arrive, they will go through Failure Route 1. This route will decrease the call count.
t_on_failure("1");
} else {
# Check internal call limits for callee extension. Increase internal call count for callee extension
route(7);
if ($retcode < 0) return(-1);
# When negative replies to this request will arrive, they will go through Failure Route 2. This route will decrease the call count.
t_on_failure("2");
}
if ($retcode < 0) return(-1);
} else {
xlog("L_DEBUG","DEBUG: Request URI $avp(extension_to_check) is not phone terminal extension and X-voipnow-channel header missing. \n");
}
# Build a dialog for this call
setflag(4);
# If there is anything to write to hash tables, we can write it now because all acceptance tests have been passed
route(30);
# Final route for requests from asterisk to an outside destination
# Edit necessary headers, set reply/failure routes and send the request
route(1);
}
#Write hash table values based on variables
route[30]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 30 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Write hash table values based on variables.\n");
if ($var(sht_remote_extension) != "")
$sht(remote_extension=>$hdr(Call-ID)) = $var(sht_remote_extension);
if ($var(sht_nat_uri) != "") {
$sht(nat_uri=>$hdr(Call-ID)) = $var(sht_nat_uri);
}
}
# Authentication for non-REGISTER requests from outside
# \output $avp(is_authenticated)
# \output $avp(s:is_extension)
# \output $avp(s:is_visitor)
# \output $avp(caller_username)
route[31]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 31 message from $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 31] Authentication for non-REGISTER requests from outside.\n");
$avp(is_authenticated) = "0";
$avp(s:is_extension) = $null;
$avp(s:is_visitor) = $null;
$var(res) = "0";
# We must get the number of the caller extension. It is usually found in the From header
# But in case the From username has been replaced with a did in the past(See 4. PBX did replacement in DOCS section),
# we must take the old From value, which gives us the extension number
if ($sht(pbx_extension_number=>$hdr(Call-ID))!=$null) {
$avp(caller_username) = $(sht(pbx_extension_number=>$hdr(Call-ID)){tobody.user});
} else {
if ($au != $null) { # $au - username from Authorization header
$avp(caller_username) = $(au{s.escape.common});
} else if ($fU != $null) { # $fU - username in URI of 'From' header
$avp(caller_username) = $(fU{s.escape.common});
} else {
$avp(caller_username) = $null;
}
}
# L1 pike check
#!ifdef ENABLE_PIKE
if ($au != $null) {
route(69);
}
#!endif
xlog("L_NOTICE", "[ROUTE 31] Check for fixed IP extensions\n");
$avp(s:extension_hash) = "";
avp_hr_query_set("slave", "<node:sip:auth:nregext | $si | 0 | DB_STRING | 5 | hash>",
"SELECT EXTENSION_AUTH('$si','$sp')", "$avp(s:extension_hash)", "0");
if ($avp(s:extension_hash) != "") {
# extract variables
$var(must_auth) = $(avp(s:extension_hash){s.select,0,,});
$var(extension) = $(avp(s:extension_hash){s.select,1,,});
$var(port) = $(avp(s:extension_hash){s.select,2,,});
# if extension has to be authorized we challenge
if ($var(port) == "" || $var(port) == $sp) {
$avp(s:is_extension) = "1";
$avp(caller_username) = $var(extension);
if ($var(must_auth) == "0") {
$avp(is_authenticated) = "1";
} else if (is_method("INVITE")){
if ($au == $null || !www_authorize("$fd", "ser_subscriber")) {
xlog("L_NOTICE", "[ROUTE 31] Fixed IP extension authorization failed. We challenge. \n");
# Send an authorization challenge (401 Unauthorized)
www_challenge("$fd", "4");
return(-1);
} else {
$avp(is_authenticated) = "1";
}
}
}
}
if ($avp(caller_username) != $null && $avp(is_authenticated) == "0") {
# Look for the caller username in rtl:sipses:ext:$avp(caller_username):<contact_hash>, to get its corresponding IP
# address, where <contact_hash> represents an entry in the rtl:sipses:ext:$avp(caller_username) ZSET.
# This is found in the received column for extensions behind NAT, or in the contact column for the other extensions
if ($(avp(caller_username){s.substr,0,3}) == "44*") {
$avp(s:is_visitor) = "1";
}
if (reg_fetch_contacts("ser_location", "$avp(caller_username)", "caller")) {
$avp(s:is_extension) = "1";
$var(i_counter) = 0;
xlog("L_NOTICE", "[ROUTE 31] SIPLOC HR Contact count: $(ulc(caller=>count))\n");
while($var(i_counter) < $(ulc(caller=>count))) {
$var(origin) = "";
$avp(s:rec) = $(ulc(caller=>received)[$var(i_counter)]);
$avp(s:caller_contact) = $(ulc(caller=>addr)[$var(i_counter)]);
$avp(s:expires) = $(ulc(caller=>expires)[$var(i_counter)]);
xlog("L_NOTICE", "[ROUTE 31] SIPLOC HR QUERY returned avp(s:rec) is: $avp(s:rec) - avp(s:caller_contact) is: $avp(s:caller_contact) - avp(s:expires) is: $avp(s:expires) TV(sn) is: $TV(sn) \n");
# Check if the extension has not expired
if (($avp(s:expires) == $null) || ($avp(s:expires) < $TV(sn))) {
xlog("L_NOTICE", "[ROUTE 31] SIPLOC HR Extension expired at $avp(s:expires)\n");
} else {
if (($avp(s:rec) == $null) || ($avp(s:rec) == "0") || ($avp(s:rec) == "") ) {
$var(origin) = $avp(s:caller_contact);
} else{
$var(origin) = $avp(s:rec);
}
xlog("L_NOTICE", "[ROUTE 31] SIPLOC HR var(origin) is: $var(origin)\n");
}
if (($(var(origin){s.len}) >= 6) && ($(var(origin){uri.host}) == $si) && ($(var(origin){uri.port}) == $sp)) {
$avp(is_authenticated) = "1";
$avp(caller_device) = $(ulc(caller=>device)[$var(i_counter)]);
break;
}
$var(i_counter) = $var(i_counter) + 1;
}
reg_free_contacts("caller");
} else {
# The extension was not found in usrloc (possibly because there are no registered contacts, thus no contact zset).
# By this point we don't really know if we're dealing with an extension, we must query HR for that.
avp_hr_query_set("slave", "<res:inf:ext:$avp(caller_username) | extid | 0 | DB_STRING | 0 | hash>",
"SELECT extension_id FROM ser_subscriber WHERE username='$avp(caller_username)'", "$avp(s:is_extension)", "0");
}
}
if ($avp(is_authenticated) == "1") {
xlog("L_NOTICE", "[ROUTE 31] Contact found in rtl:sipses:ext:$avp(caller_username) . \n");
if ( method == "ACK" ){
xlog("L_NOTICE", "[ROUTE 31] Received ACK from extension $avp(caller_username) found in hubring. There is no need to authenticate further. \n");
route(66);
exit;
}
# Check if the source ip is allowed.
xlog("L_NOTICE", "[ROUTE 31] Check ip limitation.\n");
$avp(is_allowed_access) = 0;
avp_hr_query_set("slave", "<res:sa:po:ext:$avp(caller_username) | $si | 0 | DB_INT | 5>",
"SELECT verify_extension_access('$avp(caller_username)','$si')",
"$avp(is_allowed_access)", "0");
if ( $rc == -5 ) {
xlog("L_NOTICE", "[ROUTE 31] Extension $avp(caller_username) is not allowed to make calls from $si due to IP limitations.\n");
route(3); #Send negative reply
return(-1);
}
# Handle websocket authentication
if(($proto == "ws" || $proto == "wss") && ($hdr(X-user-device) != $null)){
$var(auth_exten) = $avp(caller_username);
$var(auth_device) = $avp(caller_device);
xlog("L_NOTICE", "[ROUTE 31] We are on ws/wss; we go to route 37 to check if $var(auth_device) is authenticated. \n");
route(37);
}
} else {
xlog("L_NOTICE", "[ROUTE 31] We did not found the extension $avp(caller_username) in ser_location or the source IP /source port was wrong ( $si : $sp ), we try to authorize the extension.\n");
$avp(can_register) = $null;
# Beside trying to match the username/password from the authorization header,
# www_authorize also loads ser_subscriber.can_register and ser_subscriber.natping from the database into $avp(can_register) and $avp(natping)
if($proto=="ws"||$proto=="wss"){
if(($avp(authentication_password)!="0") && ($avp(authentication_password)!= $null)){
xlog("L_NOTICE", "[ROUTE31] Authentication token found; we auth based on token. \n");
if (!pv_www_authorize("$fd", "$avp(authentication_password)",0)) {
$var(res) = $rc;
} else {
$var(res) = "1";
}
}
} else{
xlog("L_NOTICE", "[ROUTE31] No token found; we auth based on ser_subsriber credentials. \n");
if (!www_authorize("$fd", "ser_subscriber")) {
$var(res) = $rc;
} else {
$var(res) = "1";
}
}
if ( $var(res) == "1") {
# If ser_subscriber.can_register is 0 then the extension has static IP.
# But it was not found in ser_location, or it was found with a different IP/port. We reject the call
if (($avp(can_register) == "0") && ($avp(is_authenticated) != "0")){
xlog("L_NOTICE", "[ROUTE 31] Extension ($au) is trying to send a call from another IP then its assigned static IP. Rejecting request.\n");
route(3); #Send negative reply
return(-1);
}
#Check if X-user-device header is present
if ($hdr(X-user-device) != $null) {
$avp(caller_device)=$(hdr(X-user-device){s.escape.common});
}
# ACKs cannot be challenged for authentication. We handle them separately.
if ( method == "ACK" ){
xlog("L_NOTICE", "[ROUTE 31] Received ACK from extension $avp(caller_username) found in hubring. There is no need to authenticate further. \n");
route(66);
exit;
}
# CANCELs are not authenticated
if (method != "CANCEL") {
# Check if source IP is in the list of allowed IPs for the caller extension.
$avp(is_allowed_access) = 0;
avp_hr_query_set("slave", "<res:sa:po:ext:$avp(caller_username) | $si | 0 | DB_INT | 5>",
"SELECT verify_extension_access('$avp(caller_username)','$si')",
"$avp(is_allowed_access)", "0");
if ( $rc == -5 ) {
xlog("L_NOTICE", "[ROUTE 31] Extension $avp(caller_username) is not allowed to send Request from $si due to IP limitations. \n");
route(3); #Send negative reply
return(-1);
}
$avp(is_authenticated) = "1";
} else {
xlog("L_NOTICE", "[ROUTE 31] CANCEL request. \n");
$avp(is_authenticated) = "1";
if($avp(caller_username) != $null) {
xlog("L_NOTICE", "[ROUTE 31] CANCEL FROM caller_username: ($avp(caller_username)) . \n");
} else {
xlog("L_NOTICE", "[ROUTE 31] CANCEL FROM empty caller_username Check if target number is a DID. \n");
route(45);
xlog("L_NOTICE", "[ROUTE 31] CANCEL for DID ( $avp(destination_did) ). \n");
}
}
} else {
xlog("L_NOTICE", "[ROUTE 31] FROM SER_SUBSCRIBER: authorize return code is: $var(res) . \n");
if(($var(res)==-1) || ($var(res)==-4) || ($var(res)==-8) || ($var(res)==-5 && $avp(s:is_extension))){
xlog("L_NOTICE", "[ROUTE 31] www_authorize returnes error with code -1 -4 -8 or -5(and caller is extension) so we challenge the request. \n");
# Send an authorization challenge (401 Unauthorized)
www_challenge("$fd", "4");
return(-1);
}
if(($au==$null) || ($var(res)==-5) ){
xlog("L_NOTICE", "[ROUTE 31] No credentials we try to search DIDs on $si : $sp and then fixed IP extensions. \n");
route(45); # Check if target number is a DID;
if( $avp(is_authenticated) == "0" ){
$avp(s:trunk_extension) = $null;
sha1_b64("$si", "$var(si_hash)");
avp_hr_direct_query("<rtl:trreg:$var(si_hash) | 0 | DB_STRING | 0 | string>", "", "$avp(s:trunk_extension)", "0");
if ($avp(s:trunk_extension) != $null) {
xlog("L_NOTICE", "[ROUTE 31] Challenging unauthenticated INVITE coming from trunk extension.\n");
www_challenge("$fd", "4");
return(-1);
} else {
xlog("L_NOTICE", "[ROUTE 31] INVITE from caller $avp(caller_username): no credentials, invalid DID or missing auth headers.\n");
route(3); # Send negative reply
return(-1);
}
}
}
#!ifdef ENABLE_PIKE
if ($var(res) == -2) {
# L2 and L3 pike checks
route(70);
}
#!endif
if((($var(res)==-2) || ($var(res)==-3) ) && ($avp(is_authenticated) == "0")) {
if(($sht(did=>$hdr(Call-ID))!= $null)) {
$avp(destination_did)=$sht(did=>$hdr(Call-ID));
}
xlog("L_NOTICE", "[ROUTE 31] BAD credentials and callee is not a DID and caller is not a static IP or a registered extension.\n");
xlog("L_NOTICE", "[ROUTE 31] Source is : $si : $sp . DID is: $avp(destination_did). From: $fu Rejecting request.\n");
route(3); #Send negative reply
return(-1);
}
}
}
remove_hf("X-voipnow-trunk");
if (method=="INVITE" && $avp(is_authenticated) == "1" && $avp(s:is_extension)) {
$avp(s:pbx_connected) = "0";
avp_hr_query_set("slave", "<res:po:ext:$avp(caller_username) | pbx | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM extension_prefs, extension WHERE extension.extended_number='$avp(caller_username)' AND extension_id = extension.id AND param='pbx_connected'",
"$avp(s:pbx_connected)", "0");
if ($avp(s:pbx_connected) == "1") {
append_hf("X-voipnow-trunk: $avp(caller_username)\r\n");
}
}
}
#Handle PUBLISH from extensions
route[32]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 32 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handle PUBLISH from extensions.\n");
#!ifdef ENABLE_PRESENCE
# Check if extension is allowed to PUBLISH its state
$avp(can_publish) = "0";
avp_hr_query_set("slave", "<res:po:ext:$var(request_username) | epub | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM extension, extension_prefs WHERE extended_number='$var(request_username)' AND extension.id=extension_prefs.extension_id AND param='extension_publish'",
"$avp(can_publish)", "0");
if ( $avp(can_publish) == "1"){
# Create a transaction to absorb retransmissions
if (! t_newtran()){
sl_reply_error();
return;
};
handle_publish();
t_release();
return;
} else {
xlog("L_NOTICE", "NOTICE: Extension $rU is trying to publish its presence but it is not allowed to do so. Rejecting request.\n");
route(3); #Send negative reply
return;
}
#!else
xlog("L_NOTICE", "ROUTE[32] Presence disabled.\n");
sl_send_reply("200", "OK");
return;
#!endif
}
#Handle SUBSCRIBE from extensions
route[33]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 33 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handle SUBSCRIBE from extensions.\n");
#!ifdef ENABLE_PRESENCE
# Some phones (Linksys, Snom) send empty request username. We must set it by hand
if ($rU == $null){
$var(rd)=$(rd{s.escape.common}); # $rd - domain in request's URI
$var(presence_extension) = $avp(caller_username);
# If we coulnd't find the presence extension using caller_username avp, we try to take it from the To username
if (!not_empty("$var(presence_extension)")) {
$var(presence_extension) = $var(to_username);
}
# If we sustract presence_extension out of caller_username or to_username
if($(var(presence_extension){s.select,1,:})!= ''){
$var(presence_extension) = $(var(presence_extension){s.select,1,:});
}
if($(var(presence_extension){s.select,0,@})!= ''){
$var(presence_extension) = $(var(presence_extension){s.select,0,@});
}
# We put the value of $var(presence_extension) in the request username and in $var(request_username) (we need $var(request_username) later in this route)
if (not_empty("$var(presence_extension)")) {
$avp(destination_uri) = "sip:" + $var(presence_extension) + "@" + $var(rd);
$var(request_username)=$var(presence_extension);
}
avp_pushto("$ruri","$avp(destination_uri)");
}
# We handle MWI separately, even though kamailio sees it as just another presence case
if (is_present_hf("Event")) {
if ( $(hdr(Event)) == "message-summary" ) {
# We only accept MWI requests from the same extension (an extension can only watch itself)
if ($avp(caller_username) == $var(request_username)) {
# Create a transaction to absorb retransmissions
if (! t_newtran()) {
sl_reply_error();
return;
};
handle_subscribe();
t_release();
return;
} else {
xlog("L_NOTICE","NOTICE: Cannot allow message-summary subscription for extension $rU sent by extension $avp(caller_username) . Rejecting.\n");
route(3); #Send negative reply
return;
}
} else {
if (( $(hdr(Event)) == "dialog" ) || ( $(hdr(Event)) == "presence" )) {
# Check if the sender is multiuseraware. Unless an extension is multiuseraware, it is not entitled to send SUBSCRIBEs for presence events
$avp(s:multiperm) = -1;
if ($avp(client_subscription) == $null) {
if ($sht(ext_subscription=>$avp(caller_username)) != $null) {
$avp(client_subscription) = $sht(ext_subscription=>$avp(caller_username));
} else {
avp_hr_query_set("slave",
"<res:inf:ext:$var(caller_username) | sub | 0 | DB_STRING | 0 | hash>",
"SELECT subscription FROM extension, client WHERE client_id=client.id AND extended_number='$var(caller_username)'",
"$avp(client_subscription)", "0");
if ($avp(client_subscription) == $null) {
$sht(ext_subscription=>$avp(caller_username)) = "";
} else {
$sht(ext_subscription=>$avp(caller_username)) = $avp(client_subscription);
}
}
}
if ($avp(client_subscription) == $null || $avp(client_subscription) == "") {
avp_hr_query_set("slave", "<res:po:ext:$avp(caller_username) | mu | 0 | DB_INT | 0 | hash>",
"SELECT CAST(get_client_extension_pref('$avp(caller_username)', 'multi_user') as SIGNED)",
"$avp(s:multiperm)", "0");
} else {
avp_hr_query_set("slave", "<res:inf:sub:$avp(client_subscription) | multi_user | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM client_template_data WHERE param='multi_user' and client_template_id = '$avp(client_subscription)'",
"$avp(s:multiperm)", "0");
}
if ( $avp(s:multiperm) == 1) {
if ($avp(caller_username) == $var(request_username)) { # The extension is allowed to watch itself, no further checkups needed
# Create a transaction to absorb retransmissions
if (! t_newtran()) {
sl_reply_error();
return;
};
handle_subscribe();
t_release();
return;
} else { # An extension is only allowed to watch other extension from the same client. Check that the parent clients of watcher and watched extensions are equal
$avp(watcher_parentid) = -1;
$avp(watched_parentid) = -2;
avp_hr_query_set("slave", "<res:inf:ext:$avp(caller_username) | orgid | 0 | DB_STRING | 0 | hash>",
"SELECT concat(client.parent_client_id,\"\") from client, extension WHERE client.id=extension.client_id AND extension.extended_number='$avp(caller_username)'",
"$avp(watcher_parentid)", "0");
avp_hr_query_set("slave", "<res:inf:ext:$var(request_username) | orgid | 0 | DB_STRING | 0 | hash>",
"SELECT concat(client.parent_client_id,\"\") from client, extension WHERE client.id=extension.client_id AND extension.extended_number='$var(request_username)'",
"$avp(watched_parentid)","0");
$var(len_org) = $(avp(caller_username){s.len}) - $cnt(EXTLEN) - 1;
$var(len_org1) = $var(len_org) + 1;
if ( ( $avp(watcher_parentid) == $avp(watched_parentid) ) || (starts_with("$rU", "*") && $(rU{s.substr,1,$var(len_org)}) == $(avp(caller_username){s.substr,0,$var(len_org)}) &&
$(rU{s.substr,$var(len_org1), 2}) == "*2" ) ) {
# Create a transaction to absorb retransmissions
if (! t_newtran()) {
sl_reply_error();
return;
};
handle_subscribe();
t_release();
return;
} else {
xlog("L_NOTICE","NOTICE: Extension $avp(caller_username) sent SUBSCRIBE for $rU but the extensions are not valid or they belong to different clients. Rejecting.\n");
route(3); #Send negative reply
return;
}
}
} else {
xlog("L_NOTICE","NOTICE: Extension $avp(caller_username) tried to send SUBSCRIBE but it has no multiuseraware permissions. Rejecting.\n");
route(3); #Send negative reply
return;
}
} else {
xlog("L_NOTICE","NOTICE: Extension $avp(caller_username) tried to send SUBSCRIBE with an unsupported event type. Rejecting.\n");
route(3); #Send negative reply
return;
}
}
} else {
xlog("L_NOTICE","NOTICE: Extension $avp(caller_username) tried to send SUBSCRIBE with no event type. Rejecting.\n");
$avp(error_text) = "Bad Event";
$avp(error_reason)= 483;
route(3); #Send negative reply
return;
}
#!else
xlog("L_NOTICE", "ROUTE[33] Presence disabled.\n");
sl_send_reply("200", "OK");
return;
#!endif
}
#Handle authenticated INVITE from extensions
route[34]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 34 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handle authenticated INVITE from extensions.\n");
# Try to identify this dialog with one that already exists in memory
route(42);
# If a dialog has been identified, we can go directly to the forwarding route
if (t_check_trans() || ($DLG_status!=$null)){
# Add X-voipnow-user header containing the caller extension to help asterisk identify it
route(4);
# Set advertised address to LOGIC_IP for subsequent INVITE messages
route(72);
# Final route for requests from an outside destination to asterisk
# Edit necessary headers, set reply/failure routes and send the request
route(2);
return;
}
# If ($cnt(DEPLOYMENTINF)>1) (See explanation of DEPLOYMENTINF above), we will have to set the remote_extension htable. But we don't write the value just yet,
# to avoid filling unnecessary data too soon. We will keep it in a variable and write it in the hash table when we get closer to the t_relay command,
# so we know for sure that we have no reason to reject the request
if ($cnt(DEPLOYMENTINF)>1) {
$var(sht_remote_extension) = $avp(caller_username);
}
# $avp(extension_to_check) is the extension number for which call limitation will be checked the next time we call the call limitation routes: route(7) or route(44)
# At the moment, we must check the callee extension, so if destination is short extension no, get full extension no
if ( $(var(request_username){s.len}) == $cnt(EXTLEN) ){
$var(len) = $(avp(caller_username){s.len}) - $(var(request_username){s.len});
$avp(extension_to_check) = $(avp(caller_username){s.substr,0,$var(len)}) + $var(request_username);
} else {
$avp(extension_to_check) = $var(request_username);
}
# Get client id and extension type for the callee extension
route(53);
if ($avp(user_client_id) != -1){
# We only check call limitation for callee if it is NOT a phone terminal. Because if it is a phone terminal extension,
# then we will check call limitation for it during the second call leg (CVN210RM52)
if ($avp(s:ce_exttype) != 'term') {
#Check internal call limits for callee extension. Increase internal call count for callee extension
route(7);
if ($retcode < 0) return(-1);
} else {
$avp(user_client_id) = -1;
}
# $avp(extension_to_check) is the extension number for which call limitation will be checked the next time we call the call limitation routes: route(7) or route(44)
# At the moment, we are interested in checking the caller extension
$avp(extension_to_check) = $avp(caller_username);
# Before we check call limitation check if caller is not base for a virtualized extension
route(52);
# Check internal call limits for caller extension. Increase internal call count for caller extension
route(7);
if ($retcode < 0) return(-1);
}
else {
# $avp(extension_to_check) is the extension number for which call limitation will be checked the next time we call the call limitation routes: route(7) or route(44)
# At the moment, we are interested in checking the caller extension
$avp(extension_to_check) = $avp(caller_username);
# Before we check call limitation check if caller is not base for a virtualized extension
route(52);
# Check external call limits for caller extension. Increase external call count for caller extension
route(44);
if ($retcode < 0) return(-1);
}
# When negative replies to this request will arrive, they will go through Failure Route 3. This route which will decrease the call count
t_on_failure("3");
# Mark that presence state must change for the caller
setflag(3);
#Build a dialog for this call;
setflag(4);
# If request username is empty, we first try to fill it with To username
if (!not_empty("$var(request_username)")) {
xlog("L_NOTICE", "NOTICE: Null request URI. Trying to replace request username with To username.\n");
if ($tU != $null) {
$rU = $var(to_username);
$var(request_username) = $var(to_username);
} else { # To username is also empty, so we must tell this to the user
$avp(s:erreason) = 603;
$avp(s:ertext) = "Null request username";
xlog("L_NOTICE", "NOTICE: Null request username and To username. Rejecting request.\n");
route(3);
return(-1);
}
}
# If there is anything to write to hash tables, we can write it now because all acceptance tests have been passed
route(30);
# Add X-voipnow-user header containing the caller extension to help asterisk identify it
route(4);
# Final route for requests from an outside destination to asterisk
# Edit necessary headers, set reply/failure routes and send the request
route(2);
}
# Authentication for requests from non-extensions (we try to authenticate the sender as channel)
# \output $avp(channel_id)
# \output $avp(destination_did)
route[35]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 35 message from $si : $sp .\n");
if ($avp(s:is_visitor) && $avp(is_authenticated) != "0"){
xlog("L_NOTICE", "ROUTE 35: We are on wss and we have a valid device $avp(caller_device) on caller: $avp(caller_username)\n");
route(41);
} else {
# lookup the channel id, if needed
if ($avp(channel_id) == $null) {
$avp(channel_id) = $sht(ch=>$hdr(Call-ID));
}
# set the destination did based on the local cache(sht did), request username or to username
if ($avp(destination_did) == $null || $avp(destination_did) == "0") {
$avp(destination_did)=$sht(did=>$hdr(Call-ID));
# query the database if we couldn't find the destination did in the local cache(sht did)
if ($avp(destination_did) == $null && $avp(channel_id) != $null) {
$avp(s:enabled) = "0";
if ($rU != $null) {
$avp(destination_did) = $var(request_username);
avp_hr_query_set("slave", "<res:ind:did | $var(request_username) | 0 | DB_INT | 1 >",
"SELECT status FROM channel,channel_did WHERE channel_id=channel.id and channel.id='$avp(channel_id)' and did='$var(request_username)'",
"$avp(s:enabled)", "0");
}
# if $rU is NULL or $var(request_username) was not found in channel.dids we check for to_username
if($avp(s:enabled) != "1"){
$avp(destination_did) = $var(to_username);
avp_hr_query_set("slave", "<res:ind:did | $var(to_username) | 0 | DB_INT | 1 >",
"SELECT status FROM channel,channel_did WHERE channel_id=channel.id and channel.id='$avp(channel_id)'and did='$var(to_username)'",
"$avp(s:enabled)", "0");
}
}
}
}
}
#Handles INVITEs from providers
route[36]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 36 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handles INVITEs from providers.\n");
# Set error code and text according to the cnt value set at the beginning of the config file
$avp(error_reason)= $cnt(MAXCALLS_REPLYCODE);
$avp(error_text)= $cnt(MAXCALLS_REPLYREASON);
#Check call limits for caller channel. Increase call count for caller channel
route(6);
if ($retcode < 0) return(-1);
#Build a dialog for this call;
setflag(4);
# $avp(extension_to_check) is the extension number for which call limitation will be checked the next time we call the call limitation routes: route(7) or route(44)
# At the moment, we must check the callee extension, but we only know the destination did, so we must get the extension number from database
if($avp(extension_to_check)==$null){
$avp(extension_to_check) = "0";
avp_hr_query_set("slave", "<res:inf:did:$avp(destination_did) | ext | 0 | DB_STRING | 0 | hash>",
"CALL get_channel_did_mapping('$avp(destination_did)')",
"$avp(extension_to_check)", "0");
}
if ( $avp(extension_to_check) != "0" ){
# Get client id and extension type for the callee extension
route(53);
# We only check call limitation for callee if it is NOT a phone terminal. Because if it is a phone terminal extension,
# then we will check call limitation for it during the second call leg (see call limitation algorithm, CVN210RM52)
if (($avp(s:ce_exttype) != 'term') && ($avp(user_client_id) != -1)) {
route(44);#Check external call limits for callee extension. Increase external call count for callee extension
if ($retcode < 0) return(-1);
} else {
$avp(user_client_id) = -1;
}
$sht(did=>$hdr(Call-ID))=$avp(destination_did);
# When negative replies to this request will arrive, they will go through Failure Route 3. This route will decrease the call count
t_on_failure("3");
} else {
xlog("L_NOTICE", "NOTICE: Did no. $avp(destination_did) is not assigned to any extension. Rejecting request.\n");
route(3); #Send negative reply
return(-1);
}
$sht(ch=>$hdr(Call-ID))=$avp(channel_id);
}
# Handle ws/wss request authentication
route[37]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 37 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handles Auth for ws.\n");
$avp(authentication_password)="0";
if ($hdr(X-visitor-id) != $null && $(var(auth_exten){s.substr,0,3}) == "44*" ) {
$var(visitor_id) = $(hdr(X-visitor-id){s.escape.common});
xlog("L_DBG", "[ROUTE 37] Visitor wsauth token lookup for (id=$var(visitor_id), ext=$var(auth_exten), devid=$var(auth_device)).\n");
avp_hr_direct_query_set("<hg:wsauth:visitor:$var(visitor_id):$var(auth_device) | tk | 0 | DB_STRING | 5 | hash>"
"<hg:wsauth:visitor:$var(visitor_id):$var(auth_device) | exp | 1 | DB_STRING | 5 | hash>"
"<hg:wsauth:visitor:$var(visitor_id):$var(auth_device) | pi | 2 | DB_STRING | 5 | hash>",
"","$avp(authentication_password);$avp(token_expires);$avp(visitor_phone)", "600");
if ($avp(visitor_phone) != $(var(auth_exten){s.substr,3,0})) {
xlog("L_NOTICE", "[ROUTE 37] Visitor(id=$var(visitor_id), pi=$avp(visitor_phone)) using an invalid authentication extension: $var(auth_exten).\n");
$avp(authentication_password) = "0";
}
} else {
xlog("L_DBG", "[ROUTE 37] wsauth token lookup for (ext=$var(auth_exten), devid=$var(auth_device)).\n");
avp_hr_direct_query_set("<hg:wsauth:ext:$var(auth_exten):$var(auth_device) | tk | 0 | DB_STRING | 5 | hash>"
"<hg:wsauth:ext:$var(auth_exten):$var(auth_device) | exp | 1 | DB_STRING | 5 | hash>",
"","$avp(authentication_password);$avp(token_expires)", "600");
}
$var(res) = $rc;
if ($avp(authentication_password) != $null && $avp(authentication_password) != "0" && $avp(token_expires) != $null) {
# BYE, CANCEL and un-REGISTER requests with an expired token will be accepted only
# if they pass the pv_www_authorize verification.
if ($avp(token_expires) >= $TV(sn))
xlog("L_DBG", "[ROUTE 37] Valid authentication token found for $rm request.\n");
else if ((is_method("REGISTER") && $hdr(Expires) == "0") || is_method("BYE|CANCEL"))
xlog("L_DBG", "[ROUTE 37] Accepting $rm request from (ext=$var(auth_exten), devid=$var(auth_device)), using an expired wsauth token.\n");
else {
xlog("L_ERR", "[ROUTE 37] Rejecting $rm request from (ext=$var(auth_exten), devid=$var(auth_device)), using an expired wsauth token.\n");
sl_send_reply("402", "Payment Required");
exit;
}
} else {
xlog("L_ERR", "[ROUTE 37] Couldn't find a wsauth token for (ext=$var(auth_exten), devid=$var(auth_device)).\n");
sl_send_reply("402", "Payment Required");
exit;
}
xlog("L_NOTICE", "[ROUTE 37] wsauth token found for (ext=$var(auth_exten), devid=$var(auth_device)): "
"(tk=$avp(authentication_password), exp=$avp(token_expires), pi=$avp(visitor_phone)).\n");
}
# Replace asterisk's contact with kamailio contact
route[38]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 38 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Replace asterisk's contact with kamailio contact.\n");
# We keep source IP and port from wich this request came so that we will have what to use on replies.
# On replies we use this to reconstruct the contact header foe asterisk.
$sht(cnt=>$hdr(Call-ID))=$si+":"+$sp ;
# xlog("L_ERR", " Replace asterisk's contact with kamailio contact $var(contact) .\n");
$avp(new_contact)=$ct;
avp_subst("$avp(new_contact)","/@.*>/@$var(contact)>/g");
if($hdr(Contact)!=$null){
remove_hf("Contact");
insert_hf("Contact: $avp(new_contact)\r\n", "CSeq");
}
avp_delete("$avp(new_contact)");
}
# If destination is an extension, the "X-voipnow-did" is present, and the extension has 'A PBX is connected to this extension' set,
# we replace destination with did (See '4. PBX did replacement' in DOCS section)
route[39]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 39 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE If destination is an extension, the X-voipnow-did is present, and the extension has 'A PBX is connected to this extension' set,we replace destination with did .\n");
# If the last reply came from on tcp we set transport and other parameters to Request URI
if (($sht(cnttcp=>$hdr(Call-ID)) != $null) && ($rU != $null)) {
$var(request_params) = $sht(cnttcp=>$hdr(Call-ID));
xlog("L_NOTICE", "ROUTE NOTICE: Request params are: $var(request_params) .\n");
$avp(new_ru) = $ru + ";" + $var(request_params);
avp_pushto("$ru", "$avp(new_ru)");
}
# If we have a virtualized target extension we set Request URI to the contat of the base
if ($var(destination_is_extension) == 1 || is_method("ACK|BYE|CANCEL")) {
if ($sht(base_callee=>$hdr(Call-ID)) != $null) {
$avp(new_ru) = $sht(base_callee=>$hdr(Call-ID));
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 39 new ru is: $avp(new_ru) .\n");
avp_pushto("$ru", "$avp(new_ru)");
$var(params) = '';
if($tt != $null){
$var(params) = ";tag=" + $tt;
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 39 adding var(params) $var(params) .\n");
}
# Replace To username
$avp(s:to) = "<sip:" + $(avp(new_ru){uri.user}) + "@" + $(avp(new_ru){uri.host}) + ":" + $(avp(new_ru){uri.port}) + ">" + $var(params);
remove_hf("To");
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 39 new to is: $avp(s:to) .\n");
insert_hf("To: $avp(s:to)\r\n", "From");
} else if ($sht(base_callee_rU=>$hdr(Call-ID)) != $null) {
$avp(s:to) = $hdr(To);
avp_subst("$avp(s:to)", "/sip:(.*)@/sip:$rU@/g");
xlog("L_NOTICE", "ROUTE NOTICE: Passing though BRANCH_ROUTE 1 new to is: $avp(s:to) .\n");
remove_hf("To");
insert_hf("To: $avp(s:to)\r\n","From");
}
}
if (($var(destination_is_extension) == 1 || is_method("ACK|BYE|CANCEL")) && ($hdr(X-voipnow-did) != $null) ){
if ($avp(s:pbx_connected) == "1") {
if ($T_branch_idx == 0) { # update once, for the main branch
# Keep the old destination in username in the 'pbx_extension_number' hash table
$sht(pbx_extension_number=>$hdr(Call-ID))=$hdr(To);
# Keep the value of the did in the 'direpl' hash table
$sht(pbx_replacement_did=>$hdr(Call-ID))=$hdr(X-voipnow-did);
}
# Replace request username
avp_pushto("$ru/username","$hdr(X-voipnow-did)");
# Replace To username
$avp(s:to) = $hdr(To);
remove_hf("To");
avp_subst("$avp(s:to)", "/sip:(.*)@/sip:$hdr(X-voipnow-did)@/g");
insert_hf("To: $avp(s:to)\r\n", "From");
}
} else {
# In case this is a subsequent request and direpl sht has a non-empty value, we only need to replace the To username
if ($sht(pbx_replacement_did=>$hdr(Call-ID)) != $null){
$avp(s:to) = $hdr(To);
remove_hf("To");
avp_subst("$avp(s:to)", "/sip:(.*)@/sip:$sht(pbx_replacement_did=>$hdr(Call-ID))@/g");
insert_hf("To: $avp(s:to)\r\n", "From");
}
}
}
# Set the Via, Record-Route, Contact and SDP Contact to the values in $var(advertised_ip):$var(advertised_port)
route[40]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 40 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set the Via, Record-Route, Contact and SDP Contact to the values $var(advertised_ip):$var(advertised_port).\n");
# Set Via and Record-Route address/port
set_advertised_address("$var(advertised_ip)");
set_advertised_port("$var(advertised_port)");
# The IP:port that must be written in the Contact header must be placed in $var(contact) and another route will use it to do the actual replacement later
$var(contact)=$var(advertised_ip) + ":" + $var(advertised_port);
# If we have a public IP for out PBX se set it otherwise we s
if(has_body("application/sdp") && $avp(advertised_sdp) != $null ){
xlog("L_NOTICE", "ROUTE 40 FIXING SDP WITH ADDRESS: $avp(advertised_sdp) .\n");
fix_nated_sdp("10","$avp(advertised_sdp)");
}
}
route[41]{
xlog("L_NOTICE", "[ROUTE 41] NOTICE: Passing though ROUTE 41 message from $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 41] Check WebRTC athorization to call from $avp(caller_username) to $var(to_username)\n");
#!ifdef ENABLE_PIKE
route(71);
#!endif
$avp(tk_ext)="0";
$avp(channel_id) = "-1";
if (is_method("BYE")) {
avp_hr_query_set("slave", "<res:visitor:$avp(caller_device):$var(request_username) | int | 0 | DB_STRING | 0 | hash>",
"SELECT local_id from hg_connect_invitation where id = '$avp(caller_device)' and phone_exposed = '$var(request_username)'", "$avp(tk_ext)", "60");
} else {
avp_hr_query_set("slave", "<res:visitor:$avp(caller_device):$var(request_username) | int | 0 | DB_STRING | 0 | hash>",
"SELECT local_id from hg_connect_invitation where status = 'open' and id = '$avp(caller_device)' and phone_exposed = '$var(request_username)'", "$avp(tk_ext)", "60");
}
if ($avp(tk_ext) != "0") {
$avp(tk_ext) = "43*0" + $avp(tk_ext);
}
xlog("L_NOTICE", "[ROUTE 41]: tk_ext is $avp(tk_ext) , caller_username is $avp(caller_username), to: $var(to_username) \n");
if($avp(tk_ext) != $null && $avp(tk_ext) != 0) {
xlog("L_NOTICE", "[ROUTE 41]: Authorized call from $avp(caller_username) to $avp(tk_ext) \n");
route("lazyload:webrtc_channel");
$avp(channel_id) = $var(webrtc_channel);
$avp(s:enabled)="1";
remove_hf("X-user-device");
append_hf("X-Resource-Context: $avp(caller_device)\r\n");
$avp(extension_to_check)=$avp(tk_ext);
# Set advertised address to LOGIC_IP for other subsequent messages(ex: BYE)
route(72);
} else{
exit;
}
}
# Dialog identification without loose routing
route[42]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 42 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Dialog identification without loose routing.\n");
# Identify dialog based on the 'did' parameter Route headers
process_rr_callbacks();
# Dialog could not be identified based on the 'did' parameter, try to identify it based on callid, from-tag and to-tag
if (has_totag()) {
if ($DLG_status==$null) {
dlg_manage();
}
} else if (is_method("BYE")) {
# if a BYE does not have a to tag reject it
route(3);
exit;
}
}
# Dialog identification with loose routing
route[43]{
xlog("L_NOTICE", "[ROUTE 43] Passing though ROUTE 43 message from $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 43] Dialog identification with loose routing.\n");
# Identify dialog based on the 'did' parameter Route headers, and perform loose routing
loose_route();
# Dialog could not be identified based on the 'did' parameter, try to identify it based on callid, from-tag and to-tag
if (has_totag()) {
if ( ($DLG_status==$null) && is_method("INVITE|ACK|BYE|CANCEL|PRACK|UPDATE|REFER") ) {
dlg_manage();
}
} else if (is_method("BYE")) {
# if a BYE does not have a to tag reject it
route(3);
exit;
}
}
# Check call limits for extension/client/reseller, external calls. If call limits are OK, increase call counts
route[44]{
xlog("L_NOTICE", "[ROUTE 44] Passing though ROUTE 44 message from $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 44] Check call limits for extension/client/reseller, external calls..\n");
# Don't check count for retransmissions or requests within a dialog (if the request is within a dialog $DLG_status is NULL)
if ( !(t_check_trans()) && ($DLG_status==$null) ) {
# Set error code and text according to the cnt value set at the beginning of the config file
$avp(error_reason)= $cnt(MAXCALLS_REPLYCODE);
$avp(error_text)= $cnt(MAXCALLS_REPLYREASON);
# Get the extension's user, organization and provider client ids
route(14);
if ( !is_in_profile("userexternal","$var(non_padded_user_client_id)") ) {
$avp(call_waiting) = "1";
$avp(user_call_limit) = "-1";
$avp(client_call_limit) = "-1";
$avp(reseller_call_limit) = "-1";
if ( $var(check_callee) == 1 ) {
$avp(call_waiting) = "0";
avp_hr_query_set("slave", "<res:po:ext:$avp(extension_to_check) | cw | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM extension_prefs, extension WHERE extension.extended_number='$avp(extension_to_check)' AND extension_id = extension.id AND param='call_waiting'",
"$avp(call_waiting)", "0");
}
if ($avp(call_waiting) != "1") {
xlog("L_NOTICE", "NOTICE: call waiting disabled for extension $avp(extension_to_check). Maximum number of concurrent incoming calls is considered 1.\n");
#In case call waiting is disabled, only one call is allowed. We will check the sum of external calls + internal calls
$avp(internal_call_count) = 0;
get_profile_size("extension","$var(non_padded_extension_client_id)","$avp(extension_call_count)");
if ($avp(extension_call_count) > 0){
xlog("L_ERR", "Rejecting call because extension $avp(extension_to_check) has call waiting disabled and is already in a call\n");
route(3); #Send negative reply
return(-1);
}
}
if ($avp(client_subscription) == $null) {
if ($sht(user_subscription=>$var(non_padded_user_client_id)) != $null) {
$avp(client_subscription) = $sht(user_subscription=>$var(non_padded_user_client_id));
} else {
avp_hr_query_set("slave",
"<res:inf:ext:$avp(extension_to_check) | sub | 0 | DB_STRING | 0 | hash>",
"SELECT subscription FROM extension, client WHERE client_id=client.id AND extended_number='$avp(extension_to_check)'",
"$avp(client_subscription)", "0");
if ($avp(client_subscription) == $null) {
$sht(user_subscription=>$var(non_padded_user_client_id)) = "";
} else {
$sht(user_subscription=>$var(non_padded_user_client_id)) = $avp(client_subscription);
}
}
}
if ($avp(client_subscription) == $null || $avp(client_subscription) == "") {
avp_hr_query_set("slave",
"<res:inf:mng:$var(non_padded_user_client_id) | ecc | 0 | DB_STRING | 0 | hash>"
"<res:inf:mng:$var(non_padded_client_client_id) | ecc | 1 | DB_STRING | 0 | hash>"
"<res:inf:mng:$var(non_padded_reseller_client_id) | ecc | 2 | DB_STRING | 0 | hash>"
,
"SELECT get_client_pref_no_sub('$var(non_padded_user_client_id)', 'max_concurent'),
get_client_pref_no_sub('$var(non_padded_client_client_id)', 'max_concurent'),
get_client_pref_no_sub('$var(non_padded_reseller_client_id)', 'max_concurent')"
,
"$avp(user_call_limit);$avp(client_call_limit);$avp(reseller_call_limit)", "0");
} else {
avp_hr_query_set("slave",
"<res:inf:mng:$var(non_padded_reseller_client_id) | ecc | 0 | DB_STRING | 0 | hash>",
"SELECT get_client_pref_no_sub('$var(non_padded_reseller_client_id)', 'max_concurent')",
"$avp(reseller_call_limit)", "0");
avp_hr_query_set("slave",
"<res:inf:sub:$avp(client_subscription) | max_concurent | 0 | DB_STRING | 0 | hash>",
"SELECT value FROM client_template_data WHERE param='max_concurent' and client_template_id = '$avp(client_subscription)'",
"$avp(user_call_limit)", "0");
$avp(client_call_limit) = $avp(user_call_limit);
}
get_profile_size("userexternal","$var(non_padded_user_client_id)","$avp(user_call_count)");
if ( ($avp(user_call_limit) != "-1") && ($avp(user_call_count) >= $(avp(user_call_limit){s.int})) ){
xlog("L_ERR", "Rejecting call because user $var(non_padded_user_client_id) of extension $avp(extension_to_check) " +
"has reached its concurrent calls limit of $(avp(user_call_limit){s.int})");
route(3); #Send negative reply
return(-1);
}
$var(check_callee) = 0;
get_profile_size("clientexternal","$var(non_padded_client_client_id)","$avp(client_call_count)");
if ( ($avp(client_call_limit) != "-1") && ($avp(client_call_count) >= $(avp(client_call_limit){s.int})) ){
xlog("L_ERR", "Rejecting call because client $var(non_padded_client_client_id) has reached its concurrent " +
"calls limit of $(avp(client_call_limit){s.int})");
route(3); #Send negative reply
return(-1);
}
get_profile_size("resellerexternal","$var(non_padded_reseller_client_id)","$avp(reseller_call_count)");
if ( ($avp(reseller_call_limit) != "-1") && ($avp(reseller_call_count) >= $(avp(reseller_call_limit){s.int})) ){
xlog("L_ERR", "Rejecting call because reseller $var(non_padded_reseller_client_id) has reached its " +
"concurrent calls limit of $(avp(reseller_call_limit){s.int})");
route(3); #Send negative reply
return(-1);
}
#Increase call counters for user, client, reseller
set_dlg_profile("extension","$var(non_padded_extension_client_id)");
set_dlg_profile("userexternal","$var(non_padded_user_client_id)");
set_dlg_profile("clientexternal","$var(non_padded_client_client_id)");
set_dlg_profile("resellerexternal","$var(non_padded_reseller_client_id)");
$avp(persistent_user_client_id) = $var(non_padded_client_client_id);
}
}
$avp(user_client_id) = -1;
}
route[45]{
xlog("L_NOTICE", "[ROUTE 45] Passing though ROUTE 45 message from $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 45] Authentication for DIDs from outside.\n");
# For subsequent requests we no longer try to authorize based on channel_id saved in sht(ch=$hdr(Call-ID)). we only check that the channel is caled from an authorized IP.
if (has_totag() && ($sht(ch=>$hdr(Call-ID))!= $null)) {
$avp(channel_id)=$sht(ch=>$hdr(Call-ID));
$avp(s:allowed)="0";
avp_hr_query_set("slave", "<res:ipa:cha:$avp(channel_id) | $si | 0 | DB_INT | 4 >",
"SELECT id FROM channel_ippermission WHERE INET_ATON('$si')&INET_ATON(mask)=INET_ATON(ip)&INET_ATON(mask) AND channel_id='$avp(channel_id)'",
"$avp(s:allowed)", "0");
if ($avp(s:allowed)>=1){
# This is an allowed IP, we can continue to forwarding the request
$avp(is_authenticated)="1";
# For subsequent requests from outside identify dialog before relay-ing.
route(42);
# Final route for requests from an outside destination to asterisk
# Edit necessary headers, set reply/failure routes and send the request
route(2);
exit;
}
}
$avp(s:enabled)="0";
$avp(destination_did)="0";
$avp(channel_id)="-1";
if ($rU != $null) {
$avp(destination_did)=$var(request_username);
avp_hr_query_set("slave", "<res:ind:did | $var(request_username) | 0 | DB_INT | 1 >",
"SELECT status FROM channel,channel_did WHERE channel_id=channel.id and did='$var(request_username)'",
"$avp(s:enabled)", "0");
}
# If $rU is NULL or $var(request_username) was not found in channel.dids we check for to_username there.
if($avp(s:enabled)!="1"){
$avp(destination_did)=$var(to_username);
avp_hr_query_set("slave", "<res:ind:did | $var(to_username) | 0 | DB_INT | 1 >",
"SELECT status FROM channel,channel_did WHERE channel_id=channel.id and did='$var(to_username)'",
"$avp(s:enabled)", "0");
}
# If neither $var(request_username) nor $var(to_username) were found in channel.dids it means this is not a DID.
if(($avp(s:enabled)=="0") || ($avp(s:enabled) == $null) ){
# DID not enabled or missing
$avp(destination_did)="0";
$avp(is_authenticated)="0";
}
if($avp(s:enabled)== "1"){
# If something was found in channel.dids we check ip limitations
$avp(is_authenticated)="0";
# Found DID enabled in channel.dids key
if( $avp(channel_id) == "-1" ){
avp_hr_query_set("slave", "<res:ip:did:$avp(destination_did) | $si | 0 | DB_INT| 4 >",
"CALL verify_channel_access('$avp(destination_did)','$si')",
"$avp(channel_id)", "84400");
}
if( ($avp(channel_id) != $null ) && ( $avp(channel_id) != "-1") ){
# channel $avp(channel_id) of DID = $avp(destination_did allows calls from $si checking if we have custom headers
$avp(is_authenticated)="1";
$avp(destination_did_header) = "0";
$avp(custom_header_did)="0";
avp_hr_query_set("slave", "<res:ind:didinv | $avp(channel_id) | 0 | DB_STRING | 1 >",
"SELECT value FROM channel_prefs WHERE param ='didfrominvite' and channel_id='$avp(channel_id)'",
"$avp(destination_did_header)", "0");
if(($avp(destination_did_header) !="0" )&&($(hdr($avp(destination_did_header))) != $null )){
# We have a custon header and a DID in it , we try to use the caller in that header.
xlog("L_NOTICE", "ROUTE 45 for CUSTOM HEADER IS $avp(destination_did_header) .\n");
$avp(custom_header_did) = $(hdr($avp(destination_did_header)));
$avp(custom_header_did) = $(avp(custom_header_did){s.escape.common});
$avp(s:custom_header_did_channel_id)='0';
# We found a did, now let's get the corresponding channel id
avp_hr_query_set("slave", "<res:ip:did:$avp(custom_header_did) | $si | 0 | DB_INT| 4 >",
"CALL verify_channel_access('$avp(custom_header_did)','$si')",
"$avp(s:custom_header_did_channel_id)", "0");
if ($avp(s:custom_header_did_channel_id)==$avp(channel_id)){
$avp(destination_did)=$avp(custom_header_did);
$avp(is_authenticated)="1";
} else {
$avp(is_authenticated)="0";
}
}
} else {
$avp(is_authenticated)="0";
}
} else {
$avp(is_authenticated)="0";
}
}
route[46]{
xlog("L_NOTICE", "[ROUTE 46] Passing though ROUTE 46 message from $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 46] Run Load balancer in case of 408 Request Timeout\n");
if (t_is_expired()) {
# No point in continuing if we're dealing with an expired transaction;
# just return to failure_route[3] and remove the call from dialog profiles.
return;
}
if ((method=="INVITE") && t_check_status("408") && (src_ip!=myself) && !t_any_replied() && ($T_rpl($si)==$null)) {
xlog("L_ERROR", "failure route: $mi $rm $fu -> $ru ($du) status 408 Request Timeout\n");
# Determine the extension to use for balancing (target or source)
$var(empty_node)="";
$avp(calltype)="";
route(48);
if (!load_balance("$avp(lb_extension)","$var(empty_node)","$avp(calltype)")) {
xlog("L_ERROR", "[LB] failure route: Could not select a node (all full or other error) \n");
} else {
xlog("L_DEBUG", "[LB] failure route: On failure selected new destination: $du . Tried nodes are: $avp(lb_tried_avp) .\n");
t_on_failure("1");
if( $avp(targetpbxnodeid)!= $null ){
xlog("L_NOTICE", "ROUTE 46 new pbx chosen, if we have avp(targetpbxnodeid) $avp(targetpbxnodeid) we also set hash table pbx_nodeid .\n");
$sht(pbx_nodeid=>$hdr(Call-ID))=$avp(targetpbxnodeid);
}
$var(temp) = "sip:" + $rU + "@" + $(du{uri.host}) + ":" + $(du{uri.port});
avp_pushto("$ruri","$var(temp)");
if(($avp(custom_header_did) != $null ) && ($avp(custom_header_did) != "0") ) {
avp_pushto("$ru/username","$avp(custom_header_did)");
}
xlog("L_DEBUG", "[LB] Ruri se to: $ru .\n");
# we disable dns failover for LB selected route
t_set_disable_failover(1);
# we disable blacklisting for LB selected route
t_set_disable_blst(1);
t_relay_to_tcp();
exit;
}
}
}
#Handle redirect initial INVITE.
route[47]{
# xlog("L_NOTICE", "[ROUTE 47] Passing though ROUTE 47 message from $si : $sp .\n");
# xlog("L_NOTICE", "[ROUTE 47] Handle redirect initial INVITE for request User $rU .\n");
if($(hdr(X-voipnow-user)) == $null) {
# Add X-voipnow-user header containing caller extension to help asterisk identify it
route(4);
}
$var(target_node) = $(hdr(X-voipnow-nodeid));
if($(hdr(X-voipnow-redir)) =="dah"){
$avp(s:target_host)="";
avp_hr_query_set("slave", "<node:pbx:nload | $var(target_node) | 0 | DB_STRING | 5 | zset>",
"SELECT logicip FROM node_role WHERE role='pbx' and nodeid='$var(target_node)' ",
"$avp(s:target_host)", "0");
} else {
# Determine the extension to use for balancing (target or source) and force calltype in case we have a short number
$avp(calltype)="";
route(48);
if (!load_balance("$avp(lb_extension)","$var(target_node)","$avp(calltype)")) {
sl_send_reply("500", "Service full");
xlog("L_NOTICE", "[LB] Could not select a node (all full or other error) \n");
exit;
} else {
xlog("L_DEBUG", "[LB] : Selected new destination: $du . Tried nodes are: $avp(lb_tried_avp) .\n");
$avp(s:target_host)= $(du{uri.host}) + ":" + $(du{uri.port});
if( $avp(targetpbxnodeid)!= $null ){
xlog("L_NOTICE", "ROUTE 47 new pbx chosen, we have avp(targetpbxnodeid) $avp(targetpbxnodeid) we also set hash table pbx_nodeid .\n");
$sht(pbx_nodeid=>$hdr(Call-ID))=$avp(targetpbxnodeid);
}
}
}
#After we determined the target we remove X-voipnow-nodeid header.
remove_hf("X-voipnow-nodeid");
# keep in hash IPs of source and port to send the replies
$sht(pbx_redirect_src=>$hdr(Call-ID)) = $si+ ":" + $sp;
$sht(pbx_redirect_dst=>$hdr(Call-ID)) = $avp(s:target_host);
$avp(destination_uri) = "sip:" + $rU + "@" + $avp(s:target_host);
avp_pushto("$ruri","$avp(destination_uri)");
xlog("L_NOTICE", "ROUTE NOTICE: destination is : $avp(destination_uri) .\n");
remove_hf("To");
if ($tt != $null) {
insert_hf("To: <sip:$rU@$avp(s:target_host);tag=$tt>\r\n","Call-ID");
} else {
insert_hf("To: <sip:$rU@$avp(s:target_host)>\r\n","Call-ID");
}
# Alter From to be set with kamailio IP and port.
$var(from) = $(hdr(From));
$var(from) = $(var(from){s.select,0,@});
$var(from) = $(var(from){s.select,1,:});
remove_hf("From");
if ($ft != $null) {
insert_hf("From: <sip:$var(from)@$cnt(KAMAILIO_IP):$cnt(KAMAILIO_UDP_PORT)>;tag=$ft\r\n","Call-ID");
}else {
insert_hf("From: <sip:$var(from)@$cnt(KAMAILIO_IP):$cnt(KAMAILIO_UDP_PORT)>\r\n","Call-ID");
}
# Alter Call-ID to start with vnr for redirected call.
$var(callid) = $(hdr(Call-ID));
remove_hf("Call-ID");
if ($(var(callid){s.substr,0,3}) == "vnr"){
$avp(new_callid)=$(var(callid){s.substr,3,0});
} else {
$avp(new_callid)="vnr" + $var(callid);
}
insert_hf("Call-ID: $avp(new_callid)\r\n", "CSeq");
add_rr_param(";vnr=1");
# When replies to this request will arrive, they will go through onreply_route(6)
t_on_reply("6");
# Add Record-Route header
if (!is_method("REGISTER|MESSAGE")) record_advertised_route();
xlog("L_DEBUG", "[LB] Ruri se to: $ru .\n");
# we disable dns failover for LB selected route
t_set_disable_failover(1);
# we disable blacklisting for LB selected route
t_set_disable_blst(1);
# Send the request to its destination
if (!t_relay_to_tcp()) {
sl_reply_error();
};
exit;
}
route[48]{
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 48 message from $si : $sp .\n");
# xlog("L_NOTICE", "ROUTE Determine extension to be used for load ballancing.\n");
$avp(lb_extension)="";
$avp(calltype)="";
if (not_empty("$var(request_username)")) {
$var(test_first_digit)= $(var(request_username){s.substr,0,1});
if ($var(test_first_digit) == "*") {
# This is a call from an extension to an app
# Load balancing will be done based on source
$avp(calltype)="app";
# Check if this ia a call to ValetUnpark or ValetList
if (($(var(request_username){s.len}) > 3) && ($(var(request_username){s.substr,0,3}) == "*22")) {
$avp(calltype)="valetparking";
}
} else {
$avp(s:target_is_extension) = "0";
$var(target_extension) = $var(request_username);
if ($(avp(extension_to_check){s.substr,0,3}) == "43*") {
$avp(calltype)="usercall";
avp_db_query("slave", "SELECT extended_number from extension where client_id = $(avp(extension_to_check){s.substr,3,0}) LIMIT 1", "$avp(lb_extension)");
xlog("L_NOTICE","Selecting extension for LB: $avp(lb_extension)\n");
} else {
# Check if it is not a call to a short conference.
if ($(var(request_username){s.len}) == $cnt(EXTLEN)+1 ){
if ($var(test_first_digit) == "8") {
$avp(calltype)="conference";
$var(target_extension) = $(avp(caller_username){s.substr,0,$var(len)}) + $(var(request_username){s.substr,1,$var(len)});
}
} else if ($(var(request_username){s.len}) == $cnt(EXTLEN)){
# we check if it is not a call to a short number call.
$var(len) = $(avp(caller_username){s.len}) - $cnt(EXTLEN);
$var(target_extension) = $(avp(caller_username){s.substr,0,$var(len)}) + $var(request_username);
}
$var(len) = $(var(target_extension){s.len}) - $cnt(EXTLEN) - 1;
if ($var(len) > 0 && ($(var(target_extension){s.substr,$var(len),1}) == $cnt(EXTSEP))) {
$var(usridstr) = $(var(target_extension){s.substr,0,$var(len)});
$var(usrid) = $(var(usridstr){s.int});
$var(len) = $var(len) + 1;
$var(short_number) = $(var(target_extension){s.substr,$var(len),0});
# Check first that the extension exists in hash
avp_hr_query_set("slave", "<res:ind:$var(usrid):ext | $var(short_number) | 0 | DB_INT | 5 | hash>",
"SELECT -1", "$avp(extid)", "0");
}
if ($avp(extid) != -1) {
avp_hr_query_set("slave", "<res:inf:ext:$var(target_extension) | typ | 0 | DB_STRING | 0 | hash> ",
"SELECT type FROM extension WHERE extended_number='$var(target_extension)'",
"$avp(s:target_is_extension)", "0");
}
if (($avp(s:target_is_extension) != 0) && ($avp(s:target_is_extension) != "0")) {
$avp(lb_extension)=$var(target_extension);
if($avp(calltype) == ""){
$avp(calltype)=$avp(s:target_is_extension);
}
}
}
}
}
# If by now we do not have an extension in avp(lb_extension), we try to balance based on source.
if($avp(lb_extension)==""){
$avp(lb_extension)=$avp(extension_to_check);
xlog("L_DEBUG", "[LB] extension to determine pbx is SOURCE $avp(lb_extension) with calltype $avp(calltype) .\n");
}
}
route[49] {
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 49 message from $si : $sp .\n");
# xlog("L_NOTICE", "ROUTE that removes undesired X-voipnow-headers do not come form asterisk .\n");
if (is_present_hf("X-voipnow-redir") ){
remove_hf("X-voipnow-redir");
}
if (is_present_hf("X-voipnow-nodeid") ){
remove_hf("X-voipnow-nodeid");
}
if (is_present_hf("X-voipnow-channel") ){
remove_hf("X-voipnow-channel");
}
}
route[50] {
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 50 message from $si : $sp .\n");
# xlog("L_NOTICE", "ROUTE Final route for subsequent redirects.\n");
# When replies to this request will arrive, they will go through onreply_route(6)
t_on_reply("6");
$var(callid) = $(hdr(Call-ID));
if (($(var(callid){s.substr,0,3}) == "vnr")){
# remember redirect cause.
$avp(redirect_cause)=$(var(callid){s.substr,0,3});
add_rr_param(";vnr=1");
$var(restore_callid)=$(var(callid){s.substr,3,0});
remove_hf("Call-ID");
insert_hf("Call-ID: $var(restore_callid)\r\n","CSeq");
# Restore the original To: header and Request-URI
if ($sht(pbx_redirect_src=>$var(restore_callid)) != $null) {
xlog("L_NOTICE", "ROUTE NOTICE: ROUTE 50 ALTER ruri to: $var(restore_ruri) .\n");
$var(restore_ruri) = "sip:" + $rU + "@" + $sht(pbx_redirect_src=>$var(restore_callid));
avp_pushto("$ruri","$var(restore_ruri)");
if( $tt != $null){
$var(to_header_restore) = "<sip:" + $rU + "@" + $sht(pbx_redirect_src=>$var(restore_callid)) + ">;tag=" + $tt + "\r\n";
} else {
$var(to_header_restore) = "<sip:" + $rU + "@" + $sht(pbx_redirect_src=>$var(restore_callid)) + ">\r\n";
}
xlog("L_NOTICE", "ROUTE NOTICE:NOTICE: ROUTE 50 ALTER var(to_header_restore) to: $var(to_header_restore) .\n");
remove_hf("To");
insert_hf("To: $var(to_header_restore)","From");
}
} else {
# get redirect cause from Route header for subsequent
if (($(hdr(Route))!=$null) && ($(hdr(Route))=~".*;vnr=1")){
$avp(redirect_cause)="vnr";
remove_hf("Call-ID");
insert_hf("Call-ID: vnr$var(callid)\r\n","CSeq");
xlog("L_NOTICE", "ROUTE NOTICE: ROUTE 50 ALTER Call to vnr$var(callid) .\n");
}
if (($sht(pbx_redirect_dst=>$hdr(Call-ID)) != $null)){
xlog("L_NOTICE", "ROUTE NOTICE: ROUTE 50 ALTER ruri to: $var(restore_ruri) .\n");
$var(restore_ruri) = "sip:" + $rU + "@" + $sht(pbx_redirect_dst=>$hdr(Call-ID));
avp_pushto("$ruri","$var(restore_ruri)");
if( $tt != $null){
$var(to_header_restore) = "<sip:" + $rU + "@" + $sht(pbx_redirect_dst=>$hdr(Call-ID)) + ">;tag=" + $tt + "\r\n";
} else {
$var(to_header_restore) = "<sip:" + $rU + "@" + $sht(pbx_redirect_dst=>$hdr(Call-ID)) + ">\r\n";
}
xlog("L_NOTICE", "ROUTE NOTICE: ROUTE 50 ALTER var(to_header_restore) to: $var(to_header_restore) .\n");
remove_hf("To");
insert_hf("To: $var(to_header_restore)","From");
}
}
# send the request to its destination
if (!t_relay_to_tcp()) {
sl_reply_error();
};
}
onreply_route[6]{
# xlog("L_NOTICE", "ROUTE NOTICE:NOTICE: Passing though REPLY ROUTE 6 message from $si : $sp .\n");
# xlog("L_NOTICE", "Reply-route NOTICE: for dahdi redirect handling.\n");
$var(callid) = $(hdr(Call-ID));
if (($(var(callid){s.substr,0,3}) == "vnr")){
# remember redirect cause.
$avp(redirect_cause)=$(var(callid){s.substr,0,3});
$var(restore_callid)=$(var(callid){s.substr,3,0});
remove_hf("Call-ID");
insert_hf("Call-ID: $var(restore_callid)\r\n","CSeq");
# Restore the original To: header
if ($sht(pbx_redirect_src=>$var(restore_callid)) != $null) {
if( $tt != $null){
$var(to_header_restore) = "<sip:" + $rU + "@" + $sht(pbx_redirect_src=>$var(restore_callid)) + ">;tag=" + $tt + "\r\n";
} else {
$var(to_header_restore) = "<sip:" + $rU + "@" + $sht(pbx_redirect_src=>$var(restore_callid)) + ">\r\n";
}
xlog("L_NOTICE", "Reply-route NOTICE: REPLY FROM TARGET var(to_header_restore) is $var(to_header_restore) .\n");
remove_hf("To");
insert_hf("To: $var(to_header_restore)","From");
}
} else {
remove_hf("Call-ID");
insert_hf("Call-ID: vnr$var(callid)\r\n","CSeq");
if (($sht(pbx_redirect_dst=>$hdr(Call-ID)) != $null) && ( $tt != $null )){
if( $tt != $null){
$var(to_header_restore) = "<sip:" + $rU + "@" + $sht(pbx_redirect_dst=>$hdr(Call-ID)) + ">;tag=" + $tt + "\r\n";
} else {
$var(to_header_restore) = "<sip:" + $rU + "@" + $sht(pbx_redirect_dst=>$hdr(Call-ID)) + ">\r\n";
}
xlog("L_NOTICE", "Reply-route NOTICE: REPLY FROM SOURCE var(to_header_restore) is $var(to_header_restore) .\n");
remove_hf("To");
insert_hf("To: $var(to_header_restore)","From");
}
}
}
route[51] {
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 51 message from $si : $sp .\n");
# xlog("L_NOTICE", "ROUTE check virtualized status extension.\n");
# check extension if caller extension is base => if it is set avp extension_to_check avp_caller to the virtual extension.
# Remember initial caller extension in hash table
# $avp(extension_to_check) = $var(request_username);
$avp(getbase)="";
avp_hr_query_set("slave", "<res:ind:vext | $avp(extension_to_check) | 0 | DB_STRING| 5 | hash >",
"SELECT extended_number from extension where extended_number='$avp(extension_to_check)'",
"$avp(getbase)", "0");
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 51 extension $avp(extension_to_check) virtualized on base $avp(getbase) .\n");
if($avp(getbase)!=""){
$sht(virt_callee=>$hdr(Call-ID)) = $(rU{s.escape.common});
$avp(s:target_base_contact)="";
$avp(extension_to_check) =$avp(getbase);
$sht(base_callee_rU=>$hdr(Call-ID)) = $avp(getbase);
xlog("L_NOTICE", "Setting base_calle_rU on callid: $hdr(Call-ID) to $avp(getbase)\n");
} else {
# If the extension is not virtualized elsewhere and it is base for another extension, we reject the call as busy.
$avp(s:getvirt)="";
avp_hr_query_set("slave", "<res:ind:bext | $avp(extension_to_check) | 0 | DB_STRING| 5 | hash>",
"SELECT extended_number from extension where extended_number='$avp(extension_to_check)'",
"$avp(s:getvirt)", "0");
if($avp(s:getvirt)!=""){
xlog("L_NOTICE", "ROUTE 51 message extension $avp(extension_to_check) is not virtualized elsewhere and it is base for another extension .\n");
route(3); #Send negative reply
exit;
}
}
}
route[52] {
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 52 message from $si : $sp .\n");
# xlog("L_NOTICE", "ROUTE check if a caller extension is base.\n");
# check if hash is set => if it is get contact of extension base from sip package.
$avp(s:getvirt)="";
avp_hr_query_set("slave", "<res:ind:bext | $avp(extension_to_check) | 0 | DB_STRING| 5 | hash >",
"SELECT extended_number from extension where extended_number='$avp(extension_to_check)'",
"$avp(s:getvirt)", "0");
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 52 extension $avp(extension_to_check) is base for $avp(s:getvirt) .\n");
if($avp(s:getvirt)!=""){
$sht(base_caller=>$hdr(Call-ID)) = $(hdr(Contact){s.escape.common});
$avp(extension_to_check)=$avp(s:getvirt);
$sht(virt_caller=>$hdr(Call-ID)) = $avp(s:getvirt);
}
}
route[53] {
xlog("L_NOTICE", "ROUTE check if extension exists and set avps user_client_id and ce_exttype.\n");
$avp(user_client_id) = -1;
$avp(s:ce_exttype) = "-1";
$avp(extid) = -1;
$var(len) = $(avp(extension_to_check){s.len}) - $cnt(EXTLEN) - 1;
# Force call limitation on leg 1 for internal entities(user, visitor)
if ($(avp(extension_to_check){s.substr,0,3}) =~ "^(43\*|44\*)$") {
$avp(user_client_id) = "not-used"; # this will be ignored because of the exttype
$avp(s:ce_exttype) = "term";
return;
}
if ($var(len) > 0 && ($(avp(extension_to_check){s.substr,$var(len),1}) == $cnt(EXTSEP))) {
$var(usridstr) = $(avp(extension_to_check){s.substr,0,$var(len)});
$var(usrid) = $(var(usridstr){s.int});
$var(len) = $var(len) + 1;
$var(short_number) = $(avp(extension_to_check){s.substr,$var(len),0});
# xlog("L_NOTICE", "ROUTE usridstr = $var(usridstr) usrid = $var(usrid) short_number = $var(short_number) .\n");
# Check first that the extension exists in hash
avp_hr_query_set("slave", "<res:ind:$var(usrid):ext | $var(short_number) | 0 | DB_INT | 5 | hash>",
"SELECT -1",
"$avp(extid)", "0");
}
if ($avp(extid) != -1) {
# xlog("L_NOTICE","NOTICE: Number $avp(extension_to_check) is an extension with usrid $var(usrid) .\n");
avp_hr_query_set("slave", "<res:inf:ext:$avp(extension_to_check) | usrid | 0 | DB_STRING | 0 | hash> <res:inf:ext:$avp(extension_to_check) | typ | 1 | DB_STRING | 0 | hash>",
"SELECT concat(cl.id,\"\"), ext.type FROM extension ext, client cl WHERE ext.extended_number='$avp(extension_to_check)' and cl.id=ext.client_id",
"$avp(user_client_id);$avp(s:ce_exttype)", "0");
# } else {
# xlog("L_NOTICE","NOTICE: Extension $avp(extension_to_check) does not exist.\n");
}
}
route[54] {
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 54 message from $si : $sp .\n");
# xlog("L_NOTICE", "ROUTE Set advertised_sdp address based on source pbx.\n");
if( $(hdr(X-voipnow-pbx))!= $null ){
# xlog("L_NOTICE", "ROUTE 54 pbx node based on X-voipnow-nodeid header.\n");
$sht(pbx_nodeid=>$hdr(Call-ID))=$(hdr(X-voipnow-pbx));
}
if( $avp(targetpbxnodeid)!= $null ){
# xlog("L_NOTICE", "ROUTE 54 pbx node based on avp(targetpbxnodeid) $avp(targetpbxnodeid) .\n");
$sht(pbx_nodeid=>$hdr(Call-ID))=$avp(targetpbxnodeid);
}
$var(pbx_node)=$sht(pbx_nodeid=>$hdr(Call-ID));
if(not_empty("$var(pbx_node)")){
avp_hr_query_set("slave", "<pbx:directnet:$var(pbx_node) | $T_req($si)) | 0 | DB_STRING| 5 | set >", "SELECT 0", "$avp(advertised_sdp)", "0");
xlog("L_NOTICE", "ROUTE Set advertised_sdp address based on source pbx $avp(advertised_sdp) .\n");
}
# If we did not mathch any direct route we use public IP
if($avp(advertised_sdp) == $null){
avp_hr_query_set("slave", "<pbx:customerip | $var(pbx_node) | 0 | DB_STRING| 5 | hash >", "SELECT customerip FROM role_exposure where nodeid='$var(pbx_node)'", "$avp(advertised_sdp)", "0");
}
}
# Advertise the public ip/port based on direct routing information
# \input $var(proto) outgoing protocol
# \output $var(advertised_ip)
# \output $var(advertised_port)
route[55] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 55 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set advertised_ip address based on Direct Routing and destination.\n");
$var(sipnode)=$cnt(KAMAILIO_NODEID);
avp_hr_query_set("slave", "<sip:directnet:$var(sipnode) | $var(destIP) | 0 | DB_STRING| 5 | set >", "SELECT 0", "$avp(advertised_ip)", "0");
if($avp(advertised_ip) == $null){
# if nothing else is matched we use PUBLIC IP
$avp(advertised_ip)=$cnt(KAMAILIO_IP);
}
$var(advertised_ip)=$avp(advertised_ip);
if($var(rP) == "tcp") {
$var(advertised_port)=$cnt(KAMAILIO_TCP_PORT);
} else if($var(rP) == "tls") {
$var(advertised_port)=$cnt(KAMAILIO_TLS_PORT);
} else if($var(rP) == "ws") {
$var(advertised_port)=$cnt(KAMAILIO_WS_PORT);
} else if($var(rP) == "wss") {
$var(advertised_port)=$cnt(KAMAILIO_WSS_PORT);
} else { # udp
$var(advertised_port)=$cnt(KAMAILIO_UDP_PORT);
}
}
# Advertise the public ip/port (Public IP Cloud Infrastructure Type)
# \input $var(proto) outgoing protocol
# \output $var(advertised_ip)
# \output $var(advertised_port)
route[56] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 56 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set advertised address based on KAMAILIO_IP, KAMAILIO_*_PORT.\n");
$var(advertised_ip)=$cnt(KAMAILIO_IP);
if($var(rP) == "tcp") {
$var(advertised_port)=$cnt(KAMAILIO_TCP_PORT);
} else if($var(rP) == "tls") {
$var(advertised_port)=$cnt(KAMAILIO_TLS_PORT);
} else if($var(rP) == "ws") {
$var(advertised_port)=$cnt(KAMAILIO_WS_PORT);
} else if($var(rP) == "wss") {
$var(advertised_port)=$cnt(KAMAILIO_WSS_PORT);
} else { # udp
$var(advertised_port)=$cnt(KAMAILIO_UDP_PORT);
}
set_advertised_address("$var(advertised_ip)");
set_advertised_port("$var(advertised_port)");
}
# Advertise the local ip/port
# \input $var(rP) outgoing protocol
# \output $var(advertised_ip)
# \output $var(advertised_port)
route[57] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 57 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set advertised address based on LOCAL_IP, LOCAL_*_PORT.\n");
$var(advertised_ip)=$cnt(LOCAL_IP);
if($var(rP) == "tcp") {
$var(advertised_port)=$cnt(LOCAL_TCP_PORT);
} else if($var(rP) == "tls") {
$var(advertised_port)=$cnt(LOCAL_TLS_PORT);
} else if($var(rP) == "ws") {
$var(advertised_port)=$cnt(LOCAL_WS_PORT);
} else if($var(rP) == "wss") {
$var(advertised_port)=$cnt(LOCAL_WSS_PORT);
} else { # udp
$var(advertised_port)=$cnt(LOCAL_UDP_PORT);
}
}
# Advertise the logic ip/port
# \input $var(rP) outgoing protocol
# \output $var(advertised_ip)
# \output $var(advertised_port)
route[58] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 58 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set advertised address based on LOGIC_IP, LOGIC_*_PORT.\n");
$var(advertised_ip)=$cnt(LOGIC_IP);
if ($var(rP) == "tcp") {
$var(advertised_port)=$cnt(LOGIC_TCP_PORT);
} else if ($var(rP) == "tls") {
$var(advertised_port)=$cnt(LOGIC_TLS_PORT);
} else if ($var(rP) == "ws") {
$var(advertised_port)=$cnt(LOGIC_WS_PORT);
} else if ($var(rP) == "wss") {
$var(advertised_port)=$cnt(LOGIC_WSS_PORT);
} else { # udp
$var(advertised_port)=$cnt(LOGIC_UDP_PORT);
}
set_advertised_address("$var(advertised_ip)");
set_advertised_port("$var(advertised_port)");
}
route[60] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 60 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set var(contact) address based on LOCAL_IP and protocol.\n");
if($proto=="tcp") {
$var(contact)=$cnt(LOCAL_IP) + ":" + $cnt(LOCAL_TCP_PORT);
} else if($proto=="tls") {
$var(contact)=$cnt(LOCAL_IP) + ":" + $cnt(LOCAL_TLS_PORT);
} else if($proto=="ws") {
$var(contact)=$cnt(LOCAL_IP) + ":" + $cnt(LOCAL_WS_PORT);
} else if($proto=="wss") {
$var(contact)=$cnt(LOCAL_IP) + ":" + $cnt(LOCAL_WSS_PORT);
} else { # udp
$var(contact)=$cnt(LOCAL_IP) + ":" + $cnt(LOCAL_UDP_PORT);
}
}
route[61] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 61 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set advertised_sdp address based on source pbx.\n");
if( $(hdr(X-voipnow-pbx))!= $null ){
# xlog("L_NOTICE", "ROUTE 61 pbx node based on X-voipnow-nodeid header.\n");
$sht(pbx_nodeid=>$hdr(Call-ID))=$(hdr(X-voipnow-pbx));
}
if( $avp(targetpbxnodeid)!= $null ){
# xlog("L_NOTICE", "ROUTE 61 pbx node based on avp(targetpbxnodeid) $avp(targetpbxnodeid) .\n");
$sht(pbx_nodeid=>$hdr(Call-ID))=$avp(targetpbxnodeid);
}
$var(pbx_node)=$sht(pbx_nodeid=>$hdr(Call-ID));
if(not_empty("$var(pbx_node)")){
avp_hr_query_set("slave", "<pbx:directnet:$var(pbx_node) | $var(destIP) | 0 | DB_STRING| 5 | set >", "SELECT 0", "$avp(advertised_sdp)", "0");
xlog("L_NOTICE", "ROUTE Set advertised_sdp address based on source pbx $avp(advertised_sdp) .\n");
}
# If we did not mathch any direct route we use public IP
if($avp(advertised_sdp) == $null){
avp_hr_query_set("slave", "<pbx:customerip | $var(pbx_node) | 0 | DB_STRING| 5 | hash >", "SELECT customerip FROM role_exposure where nodeid='$var(pbx_node)'", "$avp(advertised_sdp)", "0");
xlog("L_NOTICE", "ROUTE Set advertised_sdp address based on source pbx to public IP of PBX: $avp(advertised_sdp) .\n");
}
}
route[62]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 62 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set contact address based on ser_location.socket for extension sht(remote_extension=>$hdr(Call-ID)) .\n");
# try to use the ser_location.socket information for the source of the request
# if this is not present (this probably means the source is a provider) use Kamailio public or private address, depending on the IP
$avp(socket) = $null;
$avp(remote_extension)=$sht(remote_extension=>$hdr(Call-ID));
if (reg_fetch_contacts("ser_location", "$avp(remote_extension)", "caller")) {
$var(i_counter) = 0;
$var(bcontact)= "0";
xlog("L_NOTICE", "[ROUTE 62] SIPLOC HR Contact count: $(ulc(caller=>count))\n");
while($var(i_counter) < $(ulc(caller=>count))) {
$var(bcontact) = $(ulc(caller=>received)[$var(i_counter)]);
$avp(socket) = $(ulc(caller=>socket)[$var(i_counter)]);
if ($(var(bcontact){s.len}) < 6) {
$var(bcontact) = $(ulc(caller=>addr)[$var(i_counter)]);
}
if (($(var(bcontact){s.len}) >= 6) && ($(var(bcontact){uri.host}) == $T_req($si)) && ($(var(bcontact){uri.port}) == $T_req($sp))) {
if ($avp(socket) != $null) {
$avp(socket_port)=$(avp(socket){s.select,2,:});
$avp(socket_ip)=$(avp(socket){s.select,1,:});
$var(contact) = $avp(socket_ip) + ":" + $avp(socket_port);
break;
}
}
$var(i_counter) = $var(i_counter) + 1;
}
reg_free_contacts("caller");
}
}
route[63]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 63 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set contact address based Direct Route and protocol and fix Direct Route SDP address.\n");
$var(sipnode)=$cnt(KAMAILIO_NODEID);
avp_hr_query_set("slave", "<sip:directnet:$var(sipnode) | $var(destIP) | 0 | DB_STRING| 5 | set >", "SELECT 0", "$avp(s:public_ip)", "0");
# if nothing else is matched we use PUBLIC IP
if($avp(s:public_ip) == $null){
$avp(s:public_ip)=$cnt(KAMAILIO_IP);
}
if($proto=="tcp") {
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_TCP_PORT);
} else if($proto=="tls") {
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_TLS_PORT);
} else if($proto=="ws") {
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_WS_PORT);
} else if($proto=="wss") {
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_WSS_PORT);
} else { # udp
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_UDP_PORT);
}
# we need to set SDP address to the Direct Route of the PBX that sends the RTP
if (has_body("application/sdp")){
route(61);
}
}
route[64] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 64 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set advertised_sdp address based on X-voipnow-pbx header.\n");
$avp(advertised_sdp)=$si;
pv_unset("$var(pbx_node)");
if( ($(hdr(X-voipnow-pbx))!= $null)){
# xlog("L_NOTICE", "ROUTE 64 pbx node based on X-voipnow-nodeid header.\n");
$sht(pbx_nodeid=>$hdr(Call-ID))=$(hdr(X-voipnow-pbx));
}
if( $avp(targetpbxnodeid)!= $null ){
# xlog("L_NOTICE", "ROUTE 64 pbx node based on avp(s:targetpbxnodeid) $avp(targetpbxnodeid) .\n");
$sht(pbx_nodeid=>$hdr(Call-ID))=$avp(targetpbxnodeid);
}
$var(pbx_node)=$sht(pbx_nodeid=>$hdr(Call-ID));
if(not_empty("$var(pbx_node)")){
avp_hr_query_set("slave", "<pbx:customerip | $var(pbx_node) | 0 | DB_STRING| 5 | hash >", "SELECT customerip FROM role_exposure where nodeid='$var(pbx_node)'", "$avp(advertised_sdp)", "0");
}
}
route[65] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 65 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set contact address based Direct Route and protocol and fix Direct Route SDP address.\n");
$var(sipnode)=$cnt(KAMAILIO_NODEID);
avp_hr_query_set("slave", "<sip:directnet:$var(sipnode) | $T_req($si) | 0 | DB_STRING| 5 | set >", "SELECT 0", "$avp(s:public_ip)", "0");
# if nothing else is matched we use PUBLIC IP
if($avp(s:public_ip) == $null){
$avp(s:public_ip)=$cnt(KAMAILIO_IP);
}
if($proto=="tcp") {
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_TCP_PORT);
} else if($proto=="tls") {
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_TLS_PORT);
} else if($proto=="ws") {
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_WS_PORT);
} else if($proto=="wss") {
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_WSS_PORT);
} else { # udp
$var(contact)=$avp(s:public_ip) + ":" + $cnt(KAMAILIO_UDP_PORT);
}
# we need to set SDP address to the Direct Route of the PBX that sends the RTP
if (has_body("application/sdp")){
route(54);
}
}
route[66]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 66 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Handle ACKs from external extensions.\n");
# Try to identify this dialog with one that already exists in memory
route(42);
# If the ACK is within a dialog, we can send it to asterisk and exit. Else, we reject it.
if ($DLG_status!=$null) {
# Set advertised address to LOGIC_IP fo ACK messages
route(72);
# Add X-voipnow-user header containing the caller extension to help asterisk identify it
route(4);
# Final route for requests from an outside destination to asterisk
# Edit necessary headers, set reply/failure routes and send the request
route(2);
return(-1);
} else{
xlog("L_NOTICE", "NOTICE: The ACK is not matching a transaction or a dialog \n");
return(-1);
}
}
route[67]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 67 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE modify headers for subsequent requests from asterisk to virtual caller extensions \n");
if (($rU == $sht(virt_caller=>$hdr(Call-ID)) ) && ($sht(base_caller=>$hdr(Call-ID)) != $null)){
$avp(s:base_caller)=$sht(base_caller=>$hdr(Call-ID)) ;
$avp(s:restore_caller)=$(avp(s:base_caller){s.select,1,:});
if ($avp(s:restore_caller) != '') {
$avp(s:base_caller) = $avp(s:restore_caller);
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 67 s.select,1,: avp(s:base_caller) $avp(s:base_caller).\n");
$avp(s:restore_caller)=$(avp(s:base_caller){s.select,0,;});
if ($avp(s:restore_caller) != '') {
$avp(s:base_caller) = $avp(s:restore_caller);
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 67 s.select,0,; avp(s:base_caller) $avp(s:base_caller).\n");
$avp(s:restore_caller)=$(avp(s:base_caller){s.select,0,@});
if ($avp(s:restore_caller) != '') {
$avp(s:base_caller) = $avp(s:restore_caller);
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 67 s.select,0,@ avp(s:base_caller) $avp(s:base_caller).\n");
avp_pushto("$ru/username","$avp(s:base_caller)");
$avp(s:to)=$hdr(To);
avp_subst("$avp(s:to)","/sip:(.*)@/sip:$avp(s:base_caller)@/g");
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 67 new to is: $avp(s:to) .\n");
remove_hf("To");
insert_hf("To: $avp(s:to)\r\n","From");
}
}
route[68]{
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 68 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE modify headers for subsequent requests from virtual callee extensions to asterisk \n");
if ($sht(base_callee=>$hdr(Call-ID)) != $null){
$avp(s:base_callee)=$sht(base_callee=>$hdr(Call-ID)) ;
$avp(s:restore_callee)=$(avp(s:base_callee){s.select,1,:});
if ($avp(s:restore_callee) != '') {
$avp(s:base_callee) = $avp(s:restore_callee);
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 68 s.select,1,: avp(s:restore_callee) $avp(s:restore_callee).\n");
$avp(s:restore_callee)=$(avp(s:base_callee){s.select,0,;});
if ($avp(s:restore_callee) != '') {
$avp(s:base_callee) = $avp(s:restore_callee);
}
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 68 s.select,0,; avp(s:base_callee) $avp(s:base_callee).\n");
$avp(s:restore_callee)=$(avp(s:base_callee){s.select,0,@});
if ($avp(s:restore_callee) != '') {
$avp(s:base_callee) = $avp(s:restore_callee);
}
if($fU == $avp(s:base_callee) && $sht(virt_callee=>$hdr(Call-ID))!=$null){
$avp(from)=$hdr(From);
avp_subst("$avp(from)","/sip:(.*)@/sip:$sht(virt_callee=>$hdr(Call-ID))@/g");
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 68 new from is: $avp(from) .\n");
remove_hf("From");
insert_hf("From: $avp(from)\r\n","To");
$avp(contact)=$hdr(Contact);
if($avp(contact)!=$null){
remove_hf("Contact");
avp_subst("$avp(contact)","/sip:(.*)@/sip:$sht(virt_callee=>$hdr(Call-ID))@/g");
# xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 68 new contact is: $avp(contact) .\n");
insert_hf("Contact: $avp(contact)\r\n","To");
}
}
}
}
#!ifdef ENABLE_PIKE
route[69] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing through ROUTE 69 message from $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 69] Route used for L1 pike check.\n");
$var(ret) = 0;
while(1) {
if (allow_source_address()) {
xlog("L_NOTICE", "[ROUTE 69] Request from trusted IP. Skipping L1 pike check\n");
break;
}
$var(ret) = pike_check_req("l1_tree");
if ($var(ret) == -2) { # new source of flooding
xlog("L_WARN", "[ROUTE 69] L1 Pike block $rm from $pike_ip\n");
pike_log("" + $Ts + ": L1 Pike block from $pike_ip");
} else if ($var(ret) == -1) {
xlog("L_WARN", "[ROUTE 69] Request from previously blocked ip $pike_ip at L1 pike check\n");
}
break;
}
if ($var(ret) < 0) {
route(3); # Send negative reply
exit;
}
}
route[70] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing through ROUTE 70 message from $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 70] Route used for L2 and L3 pike checks.\n");
$var(ret) = 0;
while(1) {
if (allow_source_address()) {
xlog("L_NOTICE", "[ROUTE 70] Request from trusted IP. Skipping L2 and L3 pike checks\n");
break;
}
$var(ret) = pike_check_req("l3_tree");
if ($var(ret) == -2) {
xlog("L_WARN", "[ROUTE 70] L3 Pike block $rm from $pike_ip\n");
pike_log("" + $Ts + ": L3 Pike block from $pike_ip");
break;
} else if ($var(ret) == -1) {
xlog("L_WARN", "[ROUTE 70] Request from previously blocked ip $pike_ip at L3 pike check\n");
break;
}
$var(ret) = pike_check_req("l2_tree");
if ($var(ret) == -2) {
xlog("L_WARN", "[ROUTE 70] L2 Pike block $rm from $pike_ip\n");
pike_log("" + $Ts + ": L2 Pike block from $pike_ip");
break;
} else if ($var(ret) == -1) {
xlog("L_WARN", "[ROUTE 70] Request from previously blocked ip $pike_ip at L2 pike check\n");
break;
}
break;
}
if ($var(ret) < 0) {
route(3); # Send negative reply
exit;
}
}
route[71] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing through ROUTE 71 message from $si : $sp .\n");
xlog("L_NOTICE", "[ROUTE 71] Route used for L4 pike check.\n");
$var(ret) = 0;
while(1) {
if (allow_source_address()) {
xlog("L_NOTICE", "[ROUTE 71] Request from trusted IP. Skipping L4 pike check\n");
break;
}
$var(ret) = pike_check_req("l4_tree");
if ($var(ret) == -2) { # new source of flooding
xlog("L_WARN", "[ROUTE 71] L4 Pike block $rm from $pike_ip\n");
pike_log("" + $Ts + ": L4 Pike block from $pike_ip");
} else if ($var(ret) == -1) {
xlog("L_WARN", "[ROUTE 71] Request from previously blocked ip $pike_ip at L4 pike check\n");
}
break;
}
if ($var(ret) < 0) {
route(3); # Send negative reply
exit;
}
}
#!endif
route[72] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE 72 message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Set advertised address based on LOGIC_IP, LOGIC_*_PORT.\n");
$var(advertised_ip)=$cnt(LOGIC_IP);
#increment round_lb on each request
$var(round_lb) = $var(round_lb) + 1;
$var(test_mod) = $var(round_lb) mod ($cnt(UDP_LB_ADDRESSES) + 1);
if ($var(test_mod) == 0) {
$var(advertised_port)=$cnt(LOGIC_UDP_PORT);
} else {
$var(advertised_port)=$cnt(UDP_LB_START_PORT) + $var(test_mod) - 1;
}
xlog("L_NOTICE", "ROUTE 72 advartised_port = $var(advertised_port).\n");
set_advertised_address("$var(advertised_ip)");
set_advertised_port("$var(advertised_port)");
}
event_route[xhttp:request] {
xlog("L_NOTICE", "ROUTE NOTICE: Passing though ROUTE xhttp:request message from $si : $sp .\n");
xlog("L_NOTICE", "ROUTE Route for requests from outside over websocket. Initial checks and handle websocket handshake.\n");
set_reply_close();
set_reply_no_connect();
if ($hdr(Upgrade)=~"websocket" && $hdr(Connection)=~"Upgrade" && $rm=~"GET") {
# Validate Host - make sure the client is using the correct
# alias for WebSockets
if ($hdr(Host) == $null || !is_myself("sip:" + $hdr(Host))) {
xlog("L_ERROR", "Bad host $hdr(Host)\n");
xhttp_reply("403", "Forbidden", "", "");
exit;
}
# ws_handle_handshake() exits (no further configuration file
# processing of the request) when complete.
if (ws_handle_handshake())
{
# successful connection
xlog("L_NOTICE", "Websocket handshake completed successfully\n");
exit;
}
}
xhttp_reply("404", "Not found", "", "");
}
event_route[websocket:closed] {
xlog("L_NOTICE", "WebSocket connection from $si:$sp has closed\n");
}
event_route[usrloc:contact-expired] {
xlog("L_NOTICE", "Contact $ulc(exp=>aor) expired\n");
#!ifdef ENABLE_NOTIFICATION
if (not_empty("$ulc(exp=>received)")) {
$var(device_ip) = $(ulc(exp=>received){uri.host}) + ":" + $(ulc(exp=>received){uri.port});
} else {
$var(device_ip) = $(ulc(exp=>addr){uri.host}) + ":" + $(ulc(exp=>addr){uri.port});
}
if (not_empty("$ulc(exp=>device)")) {
$var(device_id) = $ulc(exp=>device);
} else {
$var(device_id) = "";
}
register_notification("$ulc(exp=>aor)", "$ulc(exp=>addr)", "0", "$var(device_ip)", "$var(device_id)");
#!endif
#!ifdef ENABLE_PRESENCE
# Remove forced subscriptions only for records that had 'Force MWI' enabled during the last registration
if ($ulc(exp=>cflags) & (1 << FMWI_BFLAG)) {
pres_remove_active_watcher("sip:$ulc(exp=>aor)@$cnt(KAMAILIO_IP)", "message-summary",
"$ulc(exp=>callid)", "", "$ulc(exp=>callid)1");
}
#!endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment