mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1763 lines
53 KiB
1763 lines
53 KiB
#
|
|
# $Id$
|
|
#
|
|
#
|
|
# Applicability of this Configuration File
|
|
# ----------------------------------------
|
|
#
|
|
# This is default SER script as used for example at the iptel.org
|
|
# SIP service; it can deal with NATs, terminate calls to a PSTN
|
|
# gateway, and it implements a couple of basic signaling features
|
|
# (few types of call forwarding). In this scenario you may have
|
|
# multiple SIP proxies sharing one database for accessing provisioned
|
|
# data, which are maintained for example using serweb. The proxy
|
|
# servers also share write-access to user location database (and
|
|
# keeps a full cache of all usrloc entries synchronized using
|
|
# multicast).
|
|
#
|
|
# If you look for a simpler version with a lot less dependencies
|
|
# please refer to the sip-router-basic.cfg file in your SER distribution.
|
|
#
|
|
# If you look for documentation, try http://sip-router.org/wiki/.
|
|
# The right mailing lists for questions about this file is
|
|
# <sr-users@lists.kamailio.org>.
|
|
#
|
|
# Requirements:
|
|
# ---------------
|
|
# running DB, running RTP proxy, one public IP address
|
|
# for SIP service, one private IP address for administrative purposes;
|
|
# optional: IP address of a PSTN gateway
|
|
#
|
|
# HOWTOs:
|
|
# ---------
|
|
# To get this config running you need to execute the following commands
|
|
# with the new serctl (the capital word are just place holders):
|
|
#
|
|
# $ ser_ctl domain add DOMAINNAME
|
|
# $ ser_ctl user add USERNAME@DOMAINNAME -p PASSWORD
|
|
#
|
|
# (ser_ctl can be obtained from
|
|
# http://ftp.iptel.org/pub/serctl/daily-snapshots/ )
|
|
#
|
|
# If you want to have P-Asserted-ID header for your user
|
|
#
|
|
# $ ser_attr add uid=UID asserted_id="PID"
|
|
#
|
|
# If you want to have (PSTN) gateway support:
|
|
#
|
|
# $ ser_db add attr_types name=gw_ip rich_type=string raw_type=2 \
|
|
# description="The gateway IP for the default ser.cfg" default_flags=33
|
|
# $ ser_attr add global gw_ip=GATEWAY-IP
|
|
#
|
|
# Alternatively, you can simple uncomment the relevant line in this file
|
|
# right at the beginning of the main route.
|
|
#
|
|
# You can also use serweb to set all the values above
|
|
# (http://ftp.iptel.org/pub/serweb/daily-snapshots/ or
|
|
# http://developer.berlios.de/projects/serweb).
|
|
#
|
|
# Users with permission to call PSTN using this script must have
|
|
# the $gw_acl attribute set properly, and shall have $asserted_id
|
|
# set to indicate their caller-id for calls to PSTN. For inbound
|
|
# calls from PSTN, additional aliases may be also set.
|
|
#
|
|
# Warning:
|
|
# -----------
|
|
# If this file is installed on Debian from package 'ser-oob' then some
|
|
# options in this configuration file may be set by post-installation
|
|
# script, according to values entered by user at installation time in
|
|
# debconf configuration. These values are then applied automatically to
|
|
# this file each time the 'ser-oob' package is upgraded or reconfigured by
|
|
# calling 'dpkg-reconfigure sip-router-oob'.
|
|
#
|
|
# The parts of this configuration file that may be altered by debconf are
|
|
# enclosed between '#DEBCONF-something-START' and '#DEBCONF-something-END'
|
|
# comment marks. Please do not remove them.
|
|
#
|
|
#
|
|
# TODO (Future possible improvements):
|
|
# ---------------------------------------
|
|
# * protocol tuning
|
|
# - AVP-based diversion for call-forwarding (as opposed to specialized
|
|
# module)
|
|
# - add Date header in 200s to REGISTERs (to be packaged with NTP!)
|
|
# * more security:
|
|
# - pike/rate-limit
|
|
# - identity
|
|
# - TLS
|
|
# - permissions
|
|
# - Re-name all internal headers so that they start with a common prefix,
|
|
# such as P-SER and then wipe all such headers from requests received
|
|
# from untrusted sources, such as the user agents or foreign proxy
|
|
# servers
|
|
# * refined DB use (e.g., flatstore for acc)
|
|
# * miscellaneous:
|
|
# - dialog module for monitoring purposes
|
|
# - more extensive logging using xlog (controlled by gflags/gAVPs)
|
|
# * leveraging 2.1 features:
|
|
# - removal of private IP address (it takes a multicast-specific
|
|
# command which will allow OS to determine source IP address)
|
|
# - timer route:
|
|
# * don't use exec (it takes domain.reload as script command)
|
|
# * compare last-stored timestamp with current timestamp (it takes
|
|
# assignment of gAVPs)
|
|
# * check multicast REGISTERs for their TTL (this is a simple and
|
|
# effective security check to prevent remote multicast messages
|
|
# to damage our traffic)
|
|
# - numerous fine-tuning parameters which are only available in 2.1
|
|
# (mlock_pages, dns_try_naptr, etc.)
|
|
# - better support for preloaded routes with domain name
|
|
#
|
|
# Security considerations:
|
|
# ------------------------
|
|
# The script has been tested against security leaks, but it comes
|
|
# under terms of GPL "as is" without any warranties; better check
|
|
# yourself that:
|
|
# - IP based authentication of PSTN gateway and multicast REGISTERs
|
|
# is compliant to your network setup and security policy.
|
|
# - Multiple gateway IPs can't be provisioned as security checks
|
|
# are applied only to one.
|
|
#
|
|
# Licensing
|
|
# ----------
|
|
# Copyright (C) 2005-2008 iptelorg GmbH
|
|
# This file is part of SER, a free SIP server. It is available under the
|
|
# terms of the GNU General Public License.
|
|
# Numerous folks have contributed to this file, including but not limited
|
|
# to Andrei, Jan, Jiri, Michal, Miklos, Nils.
|
|
#
|
|
#
|
|
# .... that's it, enough of yadiyada, here the real config begins!
|
|
|
|
# ----------- Global Defines / Extra Features -------------------------------
|
|
# (can be enabled either by uncommenting the corresponding #!define
|
|
# statement or by starting with -A WITH_<FEATURE_NAME>, e.g.
|
|
# ser -A WITH_TLS -f /etc/ser/ser-oob.cfg )
|
|
|
|
# enable TLS
|
|
##!define WITH_TLS
|
|
|
|
#enable xmlrpc support
|
|
##!define WITH_XMLRPC
|
|
|
|
# xmlrpc allowed only if it comes on TLS from a client with a valid cert
|
|
##!define XMLRPC_TLS_ONLY
|
|
|
|
# xmlrpc allowed subnets (if defined XMLRPC requests with source ip matching
|
|
# this network addresses will be allowed, if no XMLRPC_ALLOWED_SUBNETx is
|
|
# defined only requests coming from localhost will be allowed).
|
|
# E.g.: ser -A XMLRPC_ALLOW_NET1=192.168.1.0/24 -f ser-oob.cfg
|
|
##!define XMLRPC_ALLOW_NET1 192.168.0.0/16
|
|
##!define XMLRPC_ALLOW_NET2 10.0.0.0/255.0.0.0
|
|
##!define XMLRPC_ALLOW_NET3 172.16.0.0/12
|
|
|
|
|
|
# started from compile directory (not installed)
|
|
##!define LOCAL_TEST_RUN
|
|
|
|
# ----------- Global Configuration Parameters -------------------------------
|
|
|
|
#debug=3 # debug level (cmd line: -ddd)
|
|
#memdbg=10 # memory debug log level
|
|
#memlog=10 # memory statistics log level
|
|
#log_facility=LOG_LOCAL0 # the facility used for logging (see syslog(3))
|
|
|
|
#DEBCONF-SERVERID-START
|
|
server_id=0
|
|
#DEBCONF-SERVERID-END
|
|
|
|
# Uncomment these lines to enter debugging mode or start SER with
|
|
# sip-router -ED
|
|
#
|
|
#fork=no
|
|
#log_stderror=yes
|
|
|
|
check_via=no # (cmd. line: -v)
|
|
dns=no # (cmd. line: -r)
|
|
rev_dns=no # (cmd. line: -R)
|
|
#port=5060
|
|
#children=4
|
|
#user=sip-router
|
|
#group=sip-router
|
|
#disable_core=yes # disables core dumping
|
|
#open_files_limit=20480 # sets the open file descriptors limit
|
|
#mhomed=yes # useful for multihomed hosts, small performance
|
|
# penalty
|
|
disable_tcp=no # be conservative about enabling TCP -- it can
|
|
# degrade performance a lot
|
|
#tcp_accept_aliases=yes # accepts the tcp alias via option
|
|
phone2tel=no # ignore user=phone in request-URIs -- otherwise
|
|
# these URIs would be interpreted as equivalent
|
|
# to TEL URIs, and their lookup would fail in URI
|
|
# database
|
|
reply_to_via=no
|
|
sip_warning=yes
|
|
# public IP address
|
|
#DEBCONF-LISTEN-START
|
|
listen=127.0.0.1
|
|
#DEBCONF-LISTEN-END
|
|
# sip.mcast.net for REGISTER replication
|
|
#DEBCONF-LISTEN_REPL-START
|
|
listen=udp:224.0.1.75
|
|
#DEBCONF-LISTEN_REPL-END
|
|
# administrative interface -- needed for example for multicast source
|
|
# or XML-RPC
|
|
#DEBCONF-LISTEN_ADMIN-START
|
|
listen=udp:127.0.0.1
|
|
#DEBCONF-LISTEN_ADMIN-END
|
|
|
|
#listen=tls:127.0.0.1:5061
|
|
|
|
mlock_pages=yes
|
|
shm_force_alloc=yes
|
|
real_time=7
|
|
|
|
# ------------------- DNS Parameters ----------------------------------------
|
|
# (see doc/dns.txt for more details)
|
|
#
|
|
# minimum timeouts
|
|
dns_retr_time=1
|
|
dns_retr_no=1
|
|
dns_servers_no=1
|
|
dns_use_search_list=no
|
|
dns_try_ipv6=no
|
|
# dns cache & failover
|
|
use_dns_cache=on
|
|
use_dns_failover=on
|
|
# dns_cache_flags=0
|
|
dns_cache_negative_ttl=300
|
|
dns_cache_min_ttl=60
|
|
dns_cache_max_ttl=86400 # 1 day
|
|
dns_cache_mem=2048 # 2 MB
|
|
dns_cache_gc_interval=60 # garbage collection every minute
|
|
# ser 2.1 specific options
|
|
dns_try_naptr=yes
|
|
dns_srv_lb=yes # srv based load balancing
|
|
dns_udp_pref=3 # prefer udp (when resolving naptr record)
|
|
dns_tcp_pref=2 # if no udp available accept tcp (for naptr)
|
|
dns_sctp_pref=2 # same preference as tcp
|
|
#!ifdef WITH_TLS
|
|
dns_tls_pref=1 # low preference (heavy resource use)
|
|
#!else
|
|
dns_tls_pref=-1 # ignore / don't accept tls (for naptr)
|
|
#!endif
|
|
# dns_cache_delete_nonexpired=no
|
|
|
|
# ------------------- Blocklist Parameters ----------------------------------
|
|
# (see doc/dst_blocklist.txt for more details)
|
|
#
|
|
use_dst_blocklist=on
|
|
dst_blocklist_mem=1024 # 1 MB
|
|
dst_blocklist_expire=300 # blocklist default time
|
|
dst_blocklist_gc_interval=150 # 2.5 min
|
|
# for sip-router 2.1 to the above add tm blst_503* parameters and/or use the
|
|
# blst module (see NEWS)
|
|
|
|
# ------------------- TCP Parameters ----------------------------------------
|
|
# (see NEWS for more details)
|
|
tcp_connection_lifetime=3600
|
|
#tcp_max_connections=10240 # default is 2048
|
|
tcp_connect_timeout=1
|
|
tcp_async=yes
|
|
|
|
# ------------------- TLS Parameters ----------------------------------------
|
|
|
|
#!ifdef WITH_TLS
|
|
# Enable TLS hooks so that the TLS module can be used
|
|
tls_enable=yes
|
|
#!endif
|
|
|
|
# -------------------- Custom Parameters ------------------------------------
|
|
# These parameters can be modified at runtime via RPC interface,
|
|
# read the documentation of cfg_rpc module.
|
|
|
|
# Session Timer parameters, RFC 4028
|
|
#
|
|
# Default session interval used by the proxy if the UAC does not support
|
|
# session timer. Set it to "0" to disable session timer proxy support.
|
|
#
|
|
session_timer.default = "1800" desc "default session interval (in s)"
|
|
#
|
|
# Minimum session interval accepted by the proxy, it must not be less
|
|
# than 90 seconds.
|
|
#
|
|
session_timer.min_se = "90" desc "minimum session interval (in s)"
|
|
|
|
# RTP Proxy options
|
|
#
|
|
# Whether to enable or disable the rtp proxy. Possible values are:
|
|
# "0" -- always disable
|
|
# "1" -- always enable regardless of whether UAC or UAS is behind NAT
|
|
# "detect" -- detect whether the UAC or the UAS is behind NAT,
|
|
# and enable the rtp proxy when necessary
|
|
#
|
|
#DEBCONF-RTP_ENABLE-START
|
|
rtp_proxy.enabled = "detect" desc "indicates whether the RTP Proxy is enabled or not (0/1/detect)"
|
|
#DEBCONF-RTP_ENABLE-END
|
|
|
|
# ------------------ Module Loading -----------------------------------------
|
|
#!ifdef LOCAL_TEST_RUN
|
|
loadpath "modules:modules_s"
|
|
#!else
|
|
loadpath "/usr/lib/sip-router/modules:/usr/lib/sip-router/modules_s"
|
|
#!endif
|
|
|
|
# load a SQL database for authentication, domains, user AVPs etc.
|
|
loadmodule "db_mysql"
|
|
#loadmodule "postgres"
|
|
|
|
loadmodule "tm"
|
|
loadmodule "sl"
|
|
loadmodule "rr"
|
|
loadmodule "maxfwd"
|
|
loadmodule "usrloc"
|
|
loadmodule "registrar"
|
|
loadmodule "xlog"
|
|
loadmodule "textops"
|
|
loadmodule "ctl"
|
|
loadmodule "auth"
|
|
loadmodule "auth_db"
|
|
loadmodule "gflags"
|
|
loadmodule "domain"
|
|
loadmodule "uri_db"
|
|
loadmodule "avp"
|
|
loadmodule "avp_db"
|
|
loadmodule "acc_db"
|
|
#!ifdef WITH_XMLRPC
|
|
loadmodule "xmlrpc"
|
|
#!endif
|
|
loadmodule "options"
|
|
loadmodule "sanity"
|
|
loadmodule "nathelper"
|
|
loadmodule "uri"
|
|
loadmodule "speeddial"
|
|
loadmodule "timer"
|
|
loadmodule "db_ops"
|
|
loadmodule "exec"
|
|
loadmodule "cfg_rpc"
|
|
loadmodule "eval"
|
|
loadmodule "enum"
|
|
#!ifdef WITH_TLS
|
|
loadmodule "tls"
|
|
#!endif
|
|
|
|
# ----------------- Declaration of Script Flags -----------------------------
|
|
flags
|
|
FLAG_ACC : 1, # the request will be recorded by ACC
|
|
FLAG_FAILUREROUTE : 2, # we are operating from the failure route
|
|
FLAG_NAT : 3, # the UAC is behind a NAT
|
|
FLAG_REPL_ENABLED : 4, # REGISTER replication is enabled if set
|
|
FLAG_TOTAG : 5, # request has a To tag
|
|
FLAG_PSTN_ALLOWED : 6, # the user is allowed to use the PSTN
|
|
FLAG_DONT_RM_CRED : 7, # do not remove the credentials
|
|
FLAG_AUTH_OK : 8, # authentication succeeded
|
|
FLAG_SERWEB_RSVD1 : 9, # bit reserved for use with serweb
|
|
FLAG_SERWEB_RSVD2 : 10, # bit reserved for use with serweb
|
|
FLAG_SESSIONTIMER : 11, # indicates that the UAC supports Session Timer
|
|
FLAG_RR_DONE : 12, # the request got already one RR header
|
|
FLAG_RTP_PROXY : 13, # the RTP proxy is turned on
|
|
FLAG_NAT_REG : 14, # the UAC behind NAT, stored in location record
|
|
FLAG_INIT_DLG : 15, # init INVITE dialog
|
|
FLAG_REVERSE_DIR : 16, # set if request goes callee -> caller direction, requires rr.append_fromtag=1
|
|
FLAG_ACC_MISSED : 17, # the missed call will be recorded by ACC
|
|
FLAG_USRLOC_FWD : 18, # usrloc based forward
|
|
FLAG_NEXT_ROUTE : 19; # there is a route remaining
|
|
|
|
avpflags
|
|
dialog_cookie; # attribute will be stored in Route headers
|
|
|
|
# ----------------- Module-specific Parameters ------------------------------
|
|
|
|
# path to the database
|
|
#
|
|
#DEBCONF-DBURL-START
|
|
modparam("speeddial|auth_db|usrloc|domain|uri_db|gflags|avp_db|db_ops",
|
|
"db_url", "mysql://ser:heslo@localhost/ser")
|
|
#DEBCONF-DBURL-END
|
|
|
|
# specify the path to your database for accounting
|
|
#DEBCONF-DBURLACC-START
|
|
modparam("acc_db", "db_url", "mysql://ser:heslo@localhost/ser")
|
|
#DEBCONF-DBURLACC-END
|
|
|
|
|
|
# -- usrloc --
|
|
|
|
# Database access mode: 0 -- memory cached, 1 -- write through,
|
|
# 2 -- delayed write. 1 is generally safer than 2. 2 can help
|
|
# to survive peaks in load. However, it creates delayed peaks that can
|
|
# impair request-processing latency later (usrloc would have to be
|
|
# re-redesigned more lock-free to avoid it).
|
|
#DEBCONF-DBMODE-START
|
|
modparam("usrloc", "db_mode", 1)
|
|
#DEBCONF-DBMODE-END
|
|
|
|
# Don't delete expired records from database on a per-contact basis -- that
|
|
# results in bulky DB operations and can lead to synchronization issues
|
|
# in server farm when for a time a server doesn't obtain re-reregistrations
|
|
modparam("usrloc", "db_skip_delete", 1)
|
|
|
|
|
|
# -- registrar --
|
|
|
|
# Maximum expires time. Forces users to re-register every 10 min.
|
|
modparam("registrar", "max_expires", 600)
|
|
|
|
# Minimum expires time. Even if they try, clients cannot register
|
|
# for a shorter time than this.
|
|
modparam("registrar", "min_expires", 240)
|
|
|
|
# Identify natted contacts using a flag.
|
|
modparam("registrar", "load_nat_flag", "FLAG_NAT_REG")
|
|
modparam("registrar", "save_nat_flag", "FLAG_NAT_REG")
|
|
|
|
# Maximum number of contacts.
|
|
modparam("registrar", "max_contacts", 10)
|
|
|
|
|
|
# -- auth --
|
|
|
|
#modparam("auth_db", "calculate_ha1", yes)
|
|
#modparam("auth_db", "password_column", "password")
|
|
|
|
# Minimize replay-attack window.
|
|
modparam("auth", "nonce_expire", 10)
|
|
|
|
# Enable/disable extra authentication checks using the following modparams.
|
|
# The values are: 1 -- Request-URI, 2 -- Call-ID, 4 -- From tag,
|
|
# 8 -- source IP. The options are disabled by default.
|
|
|
|
# For REGISTER requests we hash the Request-URI, Call-ID, and source IP of the
|
|
# request into the nonce string. This ensures that the generated credentials
|
|
# cannot be used with another registrar, user agent with another source IP
|
|
# address or Call-ID. Note that user agents that change Call-ID with every
|
|
# REGISTER message will not be able to register if you enable this.
|
|
#modparam("auth", "auth_checks_register", 11)
|
|
|
|
# For dialog-establishing requests (such as the original INVITE, OPTIONS, etc)
|
|
# we hash the Request-URI and source IP. Hashing Call-ID and From tags takes
|
|
# some extra precaution, because these checks could render some UA unusable.
|
|
#modparam("auth", "auth_checks_no_dlg", 9)
|
|
|
|
# For mid-dialog requests, such as re-INVITE, we can hash source IP and
|
|
# Request-URI just like in the previous case. In addition to that we can hash
|
|
# Call-ID and From tag because these are fixed within a dialog and are
|
|
# guaranteed not to change. This settings effectively restrict the usage of
|
|
# generated credentials to a single user agent within a single dialog.
|
|
#modparam("auth", "auth_checks_in_dlg", 15)
|
|
|
|
# Deal with clients who can't do qop properly
|
|
modparam("auth", "qop", "")
|
|
#DEBCONF-AUTHSECRET-START
|
|
modparam("auth", "secret", "aqwedrftredswqwddcft")
|
|
#DEBCONF-AUTHSECRET-END
|
|
|
|
|
|
# -- rr --
|
|
|
|
# Add value to lr param to make some broken UAs happy.
|
|
modparam("rr", "enable_full_lr", 1)
|
|
|
|
# Limit the length of the AVP cookie to necessary attributes only
|
|
modparam("rr", "cookie_filter", "(account|rproxy|stimer|dialog_id)")
|
|
|
|
# You probably do not want that someone can simply read and change
|
|
# the AVP cookie in your Routes, thus should really change this
|
|
# secret value below
|
|
modparam("rr", "cookie_secret", "sgsatewgdbsnmpoiewh")
|
|
|
|
# The ftag Route parameter may be used to easily determine if a BYE
|
|
# is coming from caller or callee, but we prefer shorter messages
|
|
# Enable when FLAG_REVERSE_DIR is to be used
|
|
modparam("rr", "append_fromtag", 0)
|
|
|
|
|
|
# -- gflags --
|
|
|
|
# Load global attributes.
|
|
modparam("gflags", "load_global_attrs", 1)
|
|
|
|
|
|
# -- domain --
|
|
|
|
# Load domain attributes.
|
|
modparam("domain", "load_domain_attrs", 1)
|
|
|
|
|
|
# -- ctl --
|
|
|
|
# By default, ctl listens on unixs:/tmp/sip-router_ctl if no other address is
|
|
# specified in modparams; this is also the default for sercmd.
|
|
modparam("ctl", "binrpc", "unixs:/tmp/ser_ctl")
|
|
# Listen on the "standard" fifo for backward compatibility.
|
|
modparam("ctl", "fifo", "fifo:/tmp/ser_fifo")
|
|
# Listen on tcp on localhost.
|
|
modparam("ctl", "binrpc", "tcp:127.0.0.1:2046")
|
|
|
|
|
|
# -- acc_db --
|
|
|
|
# Failed transactions (those with negative responses) should be logged, too.
|
|
modparam("acc_db", "failed_transactions", 1)
|
|
|
|
# If you don't want to have accounting entries written into the database,
|
|
# comment the next line out.
|
|
modparam("acc_db", "log_flag", "FLAG_ACC")
|
|
# seems "log_flag" and "log_flag_missed" cannot share the same flag!
|
|
modparam("acc_db", "log_missed_flag", "FLAG_ACC_MISSED")
|
|
|
|
# if you would like to customize your CDRs, do it here....
|
|
#modparam("acc_db", "attrs",
|
|
# "$f.sop_billing_category,$f.isPrepaidCustomer,$f.sop_cf_orig_uid")
|
|
|
|
|
|
# -- tm --
|
|
|
|
# Do not restart the resend timer with each reply. (See INBOUND route
|
|
# below.)
|
|
modparam("tm", "restart_fr_on_each_reply", 0)
|
|
|
|
|
|
# -- xmlrpc --
|
|
|
|
#!ifdef WITH_XMLRPC
|
|
# Use a sub-route. This is a lot safer than relying on the request method
|
|
# to distinguish HTTP from SIP
|
|
modparam("xmlrpc", "route", "XMLRPC");
|
|
#!endif
|
|
|
|
|
|
# -- nathelper --
|
|
|
|
# RTP Proxy address
|
|
#DEBCONF-RTTPPROXY-START
|
|
modparam("nathelper", "rtpproxy_sock", "udp:127.0.0.1:22222")
|
|
#DEBCONF-RTTPPROXY-END
|
|
|
|
# TCP keepalives as simple as CRLF
|
|
modparam("nathelper", "natping_crlf", 0)
|
|
|
|
# How often to send a NAT ping. Set this to 0 to turn NAT ping off.
|
|
#DEBCONF-NATPING_INTERVAL-START
|
|
modparam("nathelper", "natping_interval", 15)
|
|
#DEBCONF-NATPING_INTERVAL-END
|
|
|
|
# Only ping contacts that have the NAT flag set.
|
|
modparam("nathelper", "ping_nated_only", 1)
|
|
|
|
# Send an OPTIONS SIP request as NAT ping. If this is not set, a simple
|
|
# 4-byte ping is used.
|
|
modparam("nathelper", "natping_method", "OPTIONS")
|
|
|
|
# Temporary statefull natping test (only in future versions)
|
|
#modparam("nathelper", "natping_stateful", 1)
|
|
|
|
|
|
# -- exec --
|
|
modparam("exec", "time_to_kill", 200);
|
|
modparam("exec", "setvars", 0);
|
|
|
|
# -- timer --
|
|
|
|
# Register route ON_1MIN_TIMER to be called every minute.
|
|
modparam("timer", "declare_timer",
|
|
"ON_1MIN_TIMER=ON_1MIN_TIMER,60000,slow,enable");
|
|
|
|
#!ifdef WITH_TLS
|
|
# -- tls --
|
|
#!ifdef LOCAL_TEST_RUN
|
|
modparam("tls", "config", "./modules/tls/tls.cfg");
|
|
#!else
|
|
modparam("tls", "config", "tls.cfg");
|
|
#!endif
|
|
#!endif
|
|
|
|
# -- db_ops --
|
|
|
|
modparam("db_ops", "declare_handle", "reload")
|
|
modparam("db_ops", "declare_handle", "gattr_reload")
|
|
|
|
|
|
# ------------------------- Request Routing Logic --------------------------
|
|
|
|
# Main request route.
|
|
#
|
|
# Each request starts here.
|
|
#
|
|
route
|
|
{
|
|
# if you have a PSTN gateway just un-comment the follwoing line and
|
|
# specify the IP address of it to route calls to it.
|
|
#$gw_ip = "1.2.3.4"
|
|
|
|
# Alternatively (even better), set it as global persistent parameter
|
|
# using serweb or ser_attrs). If using a PSTN GW, per-subscriber
|
|
# options must ($gw_acl) or may (asserted_id) be set to enable calls
|
|
# to PSTN. If email-like URIs are used, having a URI alias for
|
|
# processing incoming PSTN-to-ip requests may be useful, too.
|
|
# Important: the script is assuming one global pstn-gw for all
|
|
# domains! Failure to allow gw_ip to be a domain-specific attribute
|
|
# would result in security gaps (onsend_route checks only for one
|
|
# gateway).
|
|
|
|
# First, do some initial sanity checks.
|
|
route(INIT);
|
|
|
|
# Bypass the rest of the script for CANCELs if possible.
|
|
route(CATCH_CANCEL);
|
|
|
|
# Check if the request is routed via Route header.
|
|
route(PROCESS_ROUTES);
|
|
|
|
# Look up domain IDs
|
|
route(DOMAIN);
|
|
|
|
# Answer OPTIONS requests to our system.
|
|
route(OPTIONS_REPLY);
|
|
|
|
# Enforce domain policy.
|
|
route(DOMAIN_POLICY);
|
|
|
|
# Handle REGISTER requests.
|
|
route(REGISTRAR);
|
|
|
|
# From here on we want to know who is calling.
|
|
route(AUTHENTICATION);
|
|
|
|
# We are finished with all the precaution work -- let's
|
|
# try to locate the callee. The first route that matches
|
|
# "wins" and relays the request. If none matches, SER will
|
|
# send a 404.
|
|
|
|
# Check if we should be outbound proxy for a local user.
|
|
route(OUTBOUND);
|
|
|
|
# Redirect in case user dialed a speed dial entry.
|
|
route(SPEEDDIAL);
|
|
|
|
# Place various site-specific routes here.
|
|
route(SITE_SPECIFIC);
|
|
|
|
# Check if the request is for a local user.
|
|
route(INBOUND);
|
|
|
|
# There is SIP user for the called address. Before trying PSTN,
|
|
# you may have to convert the address, for instance by using
|
|
# ENUM.
|
|
#route(ENUM);
|
|
|
|
# Last resort: if none of the previous route has found
|
|
# the recipient, try PSTN.
|
|
route(PSTN);
|
|
|
|
# nothing matched
|
|
sl_reply("404", "No route matched");
|
|
}
|
|
|
|
# Forward a request to the destination set.
|
|
#
|
|
route[FORWARD]
|
|
{
|
|
# If this is called from the failure route we need to add a new
|
|
# branch.
|
|
if (isflagset(FLAG_FAILUREROUTE)) {
|
|
if (!append_branch()) {
|
|
t_reply("500", "Too many branches");
|
|
drop;
|
|
}
|
|
}
|
|
|
|
# If this is an initial INVITE (without a To-tag) we might try
|
|
# another target (call forwarding or voicemail) after receiving
|
|
# an error.
|
|
if (isflagset(FLAG_INIT_DLG)) {
|
|
t_on_failure("FAILURE_ROUTE");
|
|
}
|
|
|
|
# Always use the reply route to check for NATed UAS.
|
|
t_on_reply("REPLY_ROUTE");
|
|
|
|
# Remove credentials to keep requests shorter
|
|
if (isflagset(FLAG_AUTH_OK) && !isflagset(FLAG_DONT_RM_CRED) ) {
|
|
consume_credentials();
|
|
}
|
|
|
|
# Activate the RTP proxy as the second last step because it modifies the
|
|
# body but also sets a dialog AVP cookie.
|
|
route(RTPPROXY);
|
|
|
|
# Insert a Record-Route header into all requests.
|
|
# This has to be done as one of the last steps to include all the
|
|
# RR cookies which might have been created during the script run.
|
|
route(RECORD_ROUTE);
|
|
|
|
# Send it out now.
|
|
if (!t_relay()) {
|
|
if (isflagset(FLAG_FAILUREROUTE)) {
|
|
# XXX This should be replaced with
|
|
# t_reply_error() similar to sl_reply_error()
|
|
# in order to return the proper failure code.
|
|
# Only, there is no such function yet.
|
|
t_reply("500", "Request cannot be forwarded");
|
|
}
|
|
else {
|
|
sl_reply_error();
|
|
}
|
|
}
|
|
drop;
|
|
}
|
|
|
|
|
|
# Perform initial checks on an incoming request.
|
|
#
|
|
# Rejects the request if it fails any of the checks.
|
|
#
|
|
route[INIT]
|
|
{
|
|
# Messages with a Max-Forwards header of zero.
|
|
if (!mf_process_maxfwd_header("10")) {
|
|
sl_reply("483", "Too Many Hops");
|
|
drop;
|
|
}
|
|
|
|
# Set flag for use in the onsend route (because it does not
|
|
# allow to use "select" statements)
|
|
if (@to.tag != "") {
|
|
setflag(FLAG_TOTAG);
|
|
}
|
|
|
|
# Check if the UAC is NATed and fix the message accordingly
|
|
route(UAC_NAT_DETECTION);
|
|
|
|
# Activate accounting for all initial INVITEs. In-dialog requests
|
|
# are accounted by a RR cookie (see below).
|
|
# It should work also when the call has been already forked at a previous router
|
|
if (method == "INVITE" && !isflagset(FLAG_TOTAG)) {
|
|
$dialog_id = @sys.unique; # make unique dialogid
|
|
setflag(FLAG_ACC);
|
|
setflag(FLAG_ACC_MISSED);
|
|
setflag(FLAG_INIT_DLG);
|
|
} else if (isflagset(FLAG_TOTAG) && @hf_value.route[0].params.ftag != @from.tag) {
|
|
setflag(FLAG_REVERSE_DIR); # callee -> caller
|
|
}
|
|
|
|
# if needed then we MUST put after force_rport() which is located in NAT_DETECTION!!!
|
|
# also must be called after FLAG_ACC is set !!!
|
|
# Check t_reply() vs. sl_reply() usage in script
|
|
#if (!t_newtran()) {
|
|
# sl_reply("500", "Internal tm error");
|
|
# drop;
|
|
#}
|
|
|
|
# Set flag and use it instead of the attribute.
|
|
if ($replicate==1) {
|
|
setflag(FLAG_REPL_ENABLED);
|
|
}
|
|
}
|
|
|
|
|
|
# Reply OPTIONS requests sent to the proxy itself.
|
|
#
|
|
route[OPTIONS_REPLY]
|
|
{
|
|
# OPTIONS requests without a username in the Request-URI but one
|
|
# of our domains or IPs are addressed to the proxy itself and
|
|
# can be answered statelessly.
|
|
if (method == "OPTIONS" && strempty(@ruri.user) &&
|
|
(uri == myself || $t.did != ""))
|
|
{
|
|
options_reply();
|
|
drop;
|
|
}
|
|
}
|
|
|
|
|
|
# Check if the sender of the request is behind a NAT device. If so,
|
|
# fix the request so that other devices can talk to the sender nonetheless.
|
|
#
|
|
route[UAC_NAT_DETECTION]
|
|
{
|
|
# Lots of UAs do not include the rport parameter in there Via
|
|
# header, so we put it there regardless.
|
|
force_rport();
|
|
|
|
# If a reliable transport was used store the connection internally
|
|
# so that SERs core can re-use the connection later.
|
|
if (proto==TCP || proto == TLS)
|
|
{
|
|
force_tcp_alias();
|
|
}
|
|
|
|
# Check if the request contains hints for a NATed UAC. Also, try to
|
|
# rewrite contacts using maddr. Using maddr is a really dubious
|
|
# technique and we better replace such with transport address.
|
|
# Downside: it fails for clients fronted by another server, in
|
|
# which case a valid contact we dislike because of maddr will be
|
|
# substituted inapproprietely (e.g., WM from other domains will
|
|
# fail). If you are worried about that, remove tests for maddr and
|
|
# recompile SER using HONOR_MADDR. Also note that rewriting
|
|
# contacts may possibly lead to client denying subseqent requests
|
|
# to them because they don't recognized fixed contacts as their
|
|
# own. Should you encounter such a case, a possible solution
|
|
# would be to store the original information as a contact parameter
|
|
# and restore it on its way back.
|
|
|
|
# In case of UDP we test for
|
|
# - private IPs in Contact
|
|
# - mismatch of transport IP and IP in Via
|
|
# - mismatch of transport port and port in Via
|
|
# in all other cases we skip the port test, because lots of clients
|
|
# do not correctly advertise their ephemeral port number in their Via
|
|
# header in case of reliable transports (although they are not behind
|
|
# a NAT).
|
|
|
|
# Warning: if you are dealing with SIP implementations which are
|
|
# running on public IP and do as-symmetric signaling for whatever
|
|
# reason the following check will make their signaling symmetric.
|
|
# If you need to support as-symmetric signaling reduce the following
|
|
# nat_uac_test for UDP to "3" or even "1".
|
|
if ((proto == UDP && nat_uac_test("19")) ||
|
|
(nat_uac_test("3")) ||
|
|
(@hf_value["contact"] != "" && @contact.uri.params.maddr != ""))
|
|
{
|
|
setflag(FLAG_NAT);
|
|
if (method == "REGISTER") {
|
|
# Prepare the Contact so that the registrar module
|
|
# saves the source address and port as well.
|
|
fix_nated_register();
|
|
}
|
|
else {
|
|
# Overwrite the Contact to allow proper in-dialog
|
|
# routing.
|
|
# but do not override if there is already a proxy in the path, we'll route by record-route,
|
|
# RURI responsibility takes to previous proxy
|
|
# TODO: shouldn't we rather limit to methods which are dialog aware (INVITE, UPDATE, SUBSCRIBE, ..)
|
|
if (strempty(@hf_value.record_route) || (@hf_value["contact"]!="" && @contact.uri.params.maddr!="")) {
|
|
fix_nated_contact();
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Check if the receiver of the request is behind a NAT device. If so,
|
|
# fix the Contact header to allow proper routing of in-dialog requests.
|
|
route[UAS_NAT_DETECTION]
|
|
{
|
|
# Fix the Contact in the reply if it contains a private IP to
|
|
# allow proper routing of in-dialog messages.
|
|
# Do the same if the contact is maddred.
|
|
|
|
# But skip 3XX responses, because we do not know the right IP for that,
|
|
# even if they contain private IPs.
|
|
if (status=~"(3[0-9][0-9])") {
|
|
break;
|
|
}
|
|
|
|
# prevent contact overwriting when a proxy between ser and UAS.
|
|
# We get it from record-route but it's rather difficult or
|
|
# do it only for UAS which is registered in usrloc database and has no
|
|
# proxy on path.
|
|
# Note: destination forced by $fwd_always_target is not NAT detected and contact left untouched!
|
|
if (isflagset(FLAG_INIT_DLG) && !isflagset(FLAG_USRLOC_FWD)) {
|
|
break;
|
|
}
|
|
# for in-dialog requests we get it easily because it provides loose_route()
|
|
if (!isflagset(FLAG_INIT_DLG) && isflagset(FLAG_NEXT_ROUTE)) {
|
|
break;
|
|
}
|
|
|
|
# Prevent that we over-write the Contact with the IP of our proxy when
|
|
# the reply loops through ourself.
|
|
if (src_ip == myself) {
|
|
break;
|
|
}
|
|
|
|
# In this case we check only if the Contact URI contains a private
|
|
# IP, because the Via header contains only informations from the UAC.
|
|
# Additionally we check if the port in the Contact URI differs from
|
|
# the port of the transport to catch UAS or ALG which put the public
|
|
# IP address into the Contact header, but "forget" about the port.
|
|
|
|
# Warning: if you are dealing with SIP implementations which are
|
|
# running on public IP and do as-symmetric signaling for whatever
|
|
# reason the following check will make their signaling symmetric.
|
|
# If you need to support as-symmetric signaling reduce the following
|
|
# nat_uac_test for UDP to just "1".
|
|
if ( (proto == UDP && nat_uac_test("33")) ||
|
|
(nat_uac_test("1") ||
|
|
(@hf_value["contact"] != "" && @contact.uri.params.maddr != "")))
|
|
{
|
|
# TODO: check if no proxy between UAS&myself
|
|
fix_nated_contact();
|
|
}
|
|
}
|
|
|
|
|
|
# Activates RTP proxy if necessary.
|
|
#
|
|
route[RTPPROXY]
|
|
{
|
|
if (@cfg_get.rtp_proxy.enabled == "0") {
|
|
# RTP Proxy is disabled
|
|
break;
|
|
} else if (@cfg_get.rtp_proxy.enabled == "detect") {
|
|
if (!isflagset(FLAG_NAT)) {
|
|
# If no NAT is involved we don't have to do here anything.
|
|
break;
|
|
}
|
|
} else if (@cfg_get.rtp_proxy.enabled != "1") {
|
|
# This is not a valid setting
|
|
xlog("L_ERR", "Unknown option for rtp_proxy.enabled: %@cfg_get.rtp_proxy.enabled\n");
|
|
break;
|
|
} # else rtp proxy is permanently enabled
|
|
|
|
# If the message terminates a dialog for which the RTP proxy
|
|
# was turned on, turn it off again.
|
|
if ((method == "BYE" && isflagset(FLAG_RTP_PROXY)) ||
|
|
(method == "CANCEL")) {
|
|
unforce_rtp_proxy();
|
|
append_hf("P-RTP-Proxy: Off\r\n");
|
|
break;
|
|
}
|
|
|
|
# Turn the RTP proxy on for INVITEs and UPDATEs, if they
|
|
# have a body
|
|
if (((method=="INVITE" || method == "UPDATE") && @msg.body != "")
|
|
&& !isflagset(FLAG_RTP_PROXY))
|
|
{
|
|
force_rtp_proxy('r');
|
|
append_hf("P-RTP-Proxy: On\r\n");
|
|
setflag(FLAG_RTP_PROXY);
|
|
$rproxy = 1;
|
|
setavpflag($rproxy, "dialog_cookie");
|
|
}
|
|
}
|
|
|
|
|
|
# Handling of Route headers
|
|
#
|
|
route[PROCESS_ROUTES]
|
|
{
|
|
# subsequent messages within a dialog should take the
|
|
# path determined by the Route headers.
|
|
if (loose_route()) {
|
|
if (!defined $dialog_id) {
|
|
$dialog_id = $t.dialog_id; # there is only 1 dialog_id
|
|
}
|
|
if (@rr.next_route != "") {
|
|
setflag("FLAG_NEXT_ROUTE");
|
|
}
|
|
xlog("L_DEBUG", "\n%mb\n\ndialogid -/from/to=%$dialog_id/%$f.dialog_id/%$t.dialog_id");
|
|
if (method == "INVITE" || method == "UPDATE" || method == "ACK" || method == "BYE") {
|
|
if (!defined $dialog_id) {
|
|
sl_reply("400", "Missing cookie");
|
|
drop;
|
|
}
|
|
}
|
|
|
|
# Mark routing logic in request.
|
|
append_hf("P-hint: rr-enforced\r\n");
|
|
|
|
# If the Route contained the accounting AVP cookie we
|
|
# set the accounting flag for the acc_db module.
|
|
# This is more for demonstration purpose as this could
|
|
# also be solved without RR cookies.
|
|
# Note: this means all in-dialog request will show up in
|
|
# the accounting tables, so prepare your accounting software
|
|
# for this.
|
|
if ($account == "yes") {
|
|
setflag(FLAG_ACC);
|
|
setflag(FLAG_ACC_MISSED);
|
|
}
|
|
|
|
# Restore the RTP proxy flag if present
|
|
if ($rproxy == "1") {
|
|
setflag(FLAG_RTP_PROXY);
|
|
}
|
|
|
|
# Restore Session Timer flag and headers.
|
|
if ( defined $stimer && ($stimer != "0")) {
|
|
route(SESSION_TIMER);
|
|
}
|
|
|
|
# Some broken devices override the dialog route set with the
|
|
# Record-Route headers from each in-dialog request. So, we
|
|
# better add Record-Route headers again. If we call
|
|
# record_route() after loose_route(), the AVP cookies are
|
|
# restored automatically. Additionally, there is a scenario
|
|
# where Record-Route headers are necessary if an initial
|
|
# SUBSCRIBE is forked.
|
|
#
|
|
# Note that here we forward before authentication checks
|
|
# are executed. Generally, we only authenticate
|
|
# out-of-dialog requests. Some in-dialog requests can't be
|
|
# authenticated at all, see the call-forwarding example in
|
|
# route[DOMAIN].
|
|
route(RECORD_ROUTE);
|
|
|
|
route(FORWARD);
|
|
}
|
|
}
|
|
|
|
|
|
# Add a Record-Route header
|
|
#
|
|
route[RECORD_ROUTE]
|
|
{
|
|
if (!isflagset(FLAG_RR_DONE) && method != "REGISTER") {
|
|
# We record-route all messages to make sure that
|
|
# subsequent messages will go through our proxy. This is
|
|
# particularly good if upstream and downstream entities
|
|
# use different transport protocols.
|
|
|
|
# If the ACC flag is set, store this in a Record-Route
|
|
# AVP cookie. This is more for demonstration purposes.
|
|
if (isflagset(FLAG_ACC)) {
|
|
$account = "yes";
|
|
setavpflag($account, "dialog_cookie");
|
|
}
|
|
|
|
setavpflag("$f.dialog_id", "dialog_cookie");
|
|
|
|
# Insert the RR header.
|
|
record_route();
|
|
|
|
# This flag allows to call this route several times
|
|
# without inserting several RR headers.
|
|
setflag(FLAG_RR_DONE);
|
|
}
|
|
}
|
|
|
|
|
|
# Look up the domains of the caller and the callee.
|
|
#
|
|
route[DOMAIN]
|
|
{
|
|
# Check whether the caller is from a local domain.
|
|
lookup_domain("$fd", "@from.uri.host");
|
|
|
|
# Check whether the callee is at a local domain
|
|
lookup_domain("$td", "@ruri.host");
|
|
}
|
|
|
|
|
|
# Check domain usage policies and reject illegal requests.
|
|
#
|
|
route[DOMAIN_POLICY]
|
|
{
|
|
|
|
# If we don't know the domain of the caller nor the domain of the
|
|
# callee, somone tries to use our proxy as a relay. However, we
|
|
# can only apply this check out-of-dialog requests without a To
|
|
# tag. In some cases such as call-forwarding, subsequent requests
|
|
# may not include served domain neither as origination nor
|
|
# destination (a@A calls b@B who forwards to c@C. A BYE by c@C is
|
|
# then From b@B and To a@A. There is no mentioning of c@C despite
|
|
# legitimate behaviour of c@C).
|
|
if (!isflagset(FLAG_TOTAG) && strempty($t.did) && strempty($f.did)) {
|
|
sl_reply("403", "Relaying Forbidden");
|
|
drop;
|
|
}
|
|
}
|
|
|
|
|
|
# The Registrar
|
|
#
|
|
route[REGISTRAR]
|
|
{
|
|
# Process only REGISTERs here.
|
|
if (method != "REGISTER") {
|
|
break;
|
|
}
|
|
|
|
# If this is a replica (sent to the multicast address), trust it to
|
|
# be secure and store it in usrloc
|
|
if (dst_ip==224.0.1.75) {
|
|
if (!isflagset(FLAG_REPL_ENABLED)) {
|
|
# Multicast replication administratively disabled.
|
|
# Ignore.
|
|
drop;
|
|
}
|
|
|
|
# Read marker from master
|
|
if (search("^Repl-Marker: nated")) {
|
|
setflag(FLAG_NAT);
|
|
}
|
|
|
|
# If the replicating server added its own server id to the
|
|
# request, obtain the value and store it in an attribute.
|
|
# This is used by registrar.
|
|
$server_id = @msg.header["SER-Server-ID"];
|
|
|
|
# Assume URI in form of UID@mydomain and store contacts
|
|
# under this UID. Note that this only works if local policy
|
|
# causes UIDs to have form compliant to RFC3261 URI
|
|
# usernames.
|
|
if (@ruri.user!="")
|
|
$tu.uid = @ruri.user;
|
|
if (isflagset(FLAG_NAT)) {
|
|
setflag(FLAG_NAT_REG);
|
|
}
|
|
if (!save_mem_nr("location")) {
|
|
log(1, "Error while saving replicated REGISTER.\n");
|
|
}
|
|
drop;
|
|
}
|
|
else {
|
|
# This is a REGISTER request received from the UA. Remove
|
|
# our internal header fields if they are present. The may
|
|
# have been added maliciously.
|
|
remove_hf("SER-Server-ID");
|
|
remove_hf("Repl-Marker");
|
|
}
|
|
|
|
# Check if the REGISTER if for one of our local domains.
|
|
if (strempty($t.did)) {
|
|
sl_reply("403", "Register Forwarding Forbidden");
|
|
drop;
|
|
}
|
|
|
|
# The REGISTER target is in the To header, so reload the domain.
|
|
if (!lookup_domain("$td", "@to.uri.host")) {
|
|
sl_reply("404", "Unknown Domain");
|
|
drop;
|
|
}
|
|
|
|
# Useful for clients that ignore expires in 200 (OK). This is an
|
|
# attempt to keep them sticking to our value of 600.
|
|
append_to_reply("Expires: 600\r\n");
|
|
append_to_reply("Min-Expires: 240\r\n");
|
|
|
|
# We want only authenticated users to be registered.
|
|
if (!www_authenticate("$fd.digest_realm", "credentials")) {
|
|
if ($? == -2) {
|
|
sl_reply("500", "Internal Server Error");
|
|
}
|
|
else if ($? == -3) {
|
|
sl_reply("400", "Bad Request");
|
|
}
|
|
else {
|
|
if ($digest_challenge != "") {
|
|
append_to_reply("%$digest_challenge");
|
|
}
|
|
sl_reply("401", "Unauthorized");
|
|
}
|
|
drop;
|
|
}
|
|
|
|
# Check if the authenticated user is the same as the target user.
|
|
if (!lookup_user("$tu.uid", "@to.uri")) {
|
|
sl_reply("404", "Unknown user in To");
|
|
drop;
|
|
}
|
|
|
|
# the authentication ID does not match the ID in the To header
|
|
if ($f.uid != $t.uid) {
|
|
sl_reply("403", "Authentication and To-Header mismatch");
|
|
drop;
|
|
}
|
|
|
|
# Check if the authenticated user is the same as the request
|
|
# originator. You may uncomment it if you care, which URI is in
|
|
# the From header.
|
|
#if (!lookup_user("$fr.uid", "@from.uri")) {
|
|
# sl_reply("404", "Unknown user in From");
|
|
# drop;
|
|
#}
|
|
#if ($fu.uid != $fr.uid) {
|
|
# sl_reply("403", "Authentication and From-Header mismatch");
|
|
# drop;
|
|
#}
|
|
|
|
if (isflagset(FLAG_NAT)) {
|
|
setflag(FLAG_NAT_REG);
|
|
}
|
|
# Everything is fine. Store the binding.
|
|
if (!save_contacts("location")) {
|
|
sl_reply("400", "Invalid REGISTER Request");
|
|
drop;
|
|
}
|
|
# do not delete the following 3 lines, they are used by debconf
|
|
#DEBCONF-REPLICATION1-START
|
|
#
|
|
#DEBCONF-REPLICATION1-END
|
|
if (isflagset(FLAG_REPL_ENABLED)) {
|
|
if (isflagset(FLAG_NAT)) {
|
|
append_hf("Repl-Marker: nated\r\n");
|
|
}
|
|
# Append this server's unique ID to the request
|
|
append_hf_value("SER-Server-ID", "%@sys.server_id");
|
|
# We are multicasting a successful REGISTER to all proxies
|
|
# on the multicast network to replicate the contact
|
|
# addresses to all of them. In case they share the same IP
|
|
# address (VIP) it is important to set the sending IP
|
|
# address to an unshared one (in the future a special mcast
|
|
# module may use unbound sockets for sending and leave
|
|
# the source IP address decision up to kernel routing
|
|
# tables).
|
|
#DEBCONF-REPL_SEND_ADDR-START
|
|
force_send_socket(udp:127.0.0.1);
|
|
#DEBCONF-REPL_SEND_ADDR-END
|
|
# Put the UID in the Request-URI so that it doesn't have to
|
|
# be looked up in the database by all multicast receivers.
|
|
attr2uri("$tu.uid", "user");
|
|
forward_udp(224.0.1.75,5060);
|
|
}
|
|
#DEBCONF-REPLICATION2-START
|
|
#
|
|
#DEBCONF-REPLICATION2-END
|
|
|
|
drop;
|
|
}
|
|
|
|
|
|
# Authentication of request originators claiming to belong to one of our
|
|
# domains.
|
|
#
|
|
route[AUTHENTICATION]
|
|
{
|
|
# CANCELs and ACKs cannot be challenged.
|
|
if (method=="CANCEL" || method=="ACK") {
|
|
break;
|
|
}
|
|
|
|
# Requests from non-local to local domains should be permitted.
|
|
# Remove this if you want a walled garden.
|
|
if (strempty($f.did)) {
|
|
break;
|
|
}
|
|
|
|
# Gateways are usually not able to authenticate for their requests.
|
|
# You have to trust them base on some other information such as the
|
|
# source IP address.
|
|
# WARNING: If at all this is only safe in a local network!
|
|
if (src_ip == $gw_ip) {
|
|
break;
|
|
}
|
|
|
|
if (!proxy_authenticate("$fd.digest_realm", "credentials")) {
|
|
if ($? == -2) {
|
|
sl_reply("500", "Internal Server Error");
|
|
}
|
|
else if ($? == -3) {
|
|
sl_reply("400", "Bad Request");
|
|
}
|
|
else {
|
|
if (defined $digest_challenge && $digest_challenge != "") {
|
|
append_to_reply("%$digest_challenge");
|
|
}
|
|
sl_reply("407", "Proxy Authentication Required");
|
|
}
|
|
drop;
|
|
}
|
|
|
|
# Check if the UID derived from authentication matches that from
|
|
# the From header.
|
|
if (!lookup_user("$fr.uid", "@from.uri")) {
|
|
sl_reply("403", "Fake Identity");
|
|
drop;
|
|
}
|
|
if ($fu.uid != $fr.uid) {
|
|
sl_reply("403", "Fake Identity");
|
|
drop;
|
|
}
|
|
setflag(FLAG_AUTH_OK);
|
|
|
|
# Load the user attributes of the caller.
|
|
load_attrs("$fu", "$f.uid");
|
|
}
|
|
|
|
|
|
# Process request targeted to non-local domains.
|
|
#
|
|
route[OUTBOUND]
|
|
{
|
|
# If a local user calls to a foreign domain we play outbound
|
|
# proxy for them.
|
|
# Comment this out if you want a walled garden.
|
|
if ($f.did != "" && strempty($t.did)) {
|
|
append_hf("P-hint: outbound\r\n");
|
|
route(FORWARD);
|
|
}
|
|
}
|
|
|
|
|
|
# Process speeddial addresses.
|
|
#
|
|
route[SPEEDDIAL]
|
|
{
|
|
# If the caller is local and uses two digits only, we redirect the
|
|
# UA to the real target.
|
|
if ($fd.did != "" && uri =~ "sip:[0-9][0-9]@") {
|
|
if (sd_lookup("speed_dial")) {
|
|
sl_reply("302", "Speed Dial Redirect");
|
|
}
|
|
else {
|
|
sl_reply("404", "Speed Dial Not Found");
|
|
}
|
|
drop;
|
|
}
|
|
}
|
|
|
|
|
|
# Process requests targeted to a local user.
|
|
#
|
|
route[INBOUND]
|
|
{
|
|
# lets see if know the callee
|
|
if (!lookup_user("$tu.uid", "@ruri")) {
|
|
break;
|
|
}
|
|
|
|
# Load the attributes of the callee.
|
|
load_attrs("$tu", "$t.uid");
|
|
|
|
# You can check if the called URI is in fact an alias like this.
|
|
#if (! $tu.uri_canonical) {
|
|
# # If the alias URI has different attributes, you can load
|
|
# # them into the URI track like this.
|
|
# load_attrs("$tr", "@ruri");
|
|
#}
|
|
|
|
# Check for call forwarding of the callee.
|
|
# Note: The forwarding target has to be full routable URI
|
|
# in this example.
|
|
if (defined $tu.fwd_always_target && $tu.fwd_always_target != "") {
|
|
attr2uri("$tu.fwd_always_target");
|
|
|
|
# If we are forwarding to ourselves, don't remove
|
|
# credentials. Otherwise the request would be challenged
|
|
# again.
|
|
# Note: This doesn't apply to failure_route which may
|
|
# still be problematic -- credentials are already
|
|
# removed when we forward. Consider using a 3xx.
|
|
lookup_domain("$td", "@ruri.host");
|
|
if (defined $t.did && $t.did != "") {
|
|
setflag(FLAG_DONT_RM_CRED);
|
|
}
|
|
route(FORWARD);
|
|
}
|
|
|
|
# Native SIP destinations are handled using the usrloc database.
|
|
if (lookup_contacts("location")) {
|
|
append_hf("P-hint: usrloc applied\r\n");
|
|
|
|
# destination is behind NAT
|
|
if (isflagset(FLAG_NAT_REG)) {
|
|
setflag(FLAG_NAT); /* client was behind NAT when made registration */
|
|
}
|
|
# We set the tm module timers according to the preferences
|
|
# of the callee (avoid too long ringing of his phones).
|
|
# Note1: Timer values have to be in ms now!
|
|
# Note2: This makes even more sense if you switch to a
|
|
# voicemail from the FAILURE_ROUTE below.
|
|
if ($t.fr_inv_timer) {
|
|
if ($t.fr_timer) {
|
|
t_set_fr("$t.fr_inv_timer", "$t.fr_timer");
|
|
}
|
|
else {
|
|
t_set_fr("$t.fr_inv_timer");
|
|
}
|
|
}
|
|
|
|
# This enables session timer support as long as one side
|
|
# supports it. If you want to have session timer support
|
|
# only for calls from your PSTN gateway but not between pure
|
|
# VoIP calls you can remove the comment marks from the if
|
|
# clause in the next line and closing bracket below.
|
|
# WARNING: If at all you should trust IP addresses only in
|
|
# your local network!
|
|
#if (src_ip == $gw_ip) {
|
|
route(SESSION_TIMER);
|
|
#}
|
|
|
|
route(FORWARD);
|
|
}
|
|
else {
|
|
sl_reply("480", "Temporarily unavailable");
|
|
drop;
|
|
}
|
|
}
|
|
|
|
|
|
# Process calls for PSTN.
|
|
#
|
|
route[PSTN]
|
|
{
|
|
# Check some conditions first:
|
|
# PSTN is available for our own users only.
|
|
if (strempty($f.did)) {
|
|
break;
|
|
}
|
|
|
|
# If the attribute $gw_ip isn't set, there is no PSTN service
|
|
# active.
|
|
if (!defined $gw_ip) {
|
|
break;
|
|
}
|
|
|
|
# And finally, the username of the Request-URI must look like
|
|
# a phone number.
|
|
if (!uri =~ "^sips?:\+?[0-9]{3,18}@") {
|
|
break;
|
|
}
|
|
|
|
# You may have to convert the number in the Request-URI into a
|
|
# format that is accepted by your gateway here.
|
|
|
|
# Check permissions of the caller for initial INVITEs.
|
|
if (isflagset(FLAG_INIT_DLG)) {
|
|
if ($f.gw_acl != "1") {
|
|
sl_reply("403", "PSTN Not Permitted");
|
|
drop;
|
|
}
|
|
}
|
|
|
|
# If the attribute $asserted_id is set, we add its contents as a
|
|
# Remote-Party-ID header.
|
|
# Depending on your gateway, you may have to add a
|
|
# P-Asserted-Identity header here instead.
|
|
if (defined $asserted_id) {
|
|
xlset_attr("$rpidheader",
|
|
"<sip:%$asserted_id@%@ruri.host>;screen=yes");
|
|
replace_attr_hf("Remote-Party-ID", "$rpidheader");
|
|
}
|
|
|
|
# Enable Session Timer support with the gateway.
|
|
route(SESSION_TIMER);
|
|
|
|
# Replace the domain part of the Request-URI with the value from
|
|
# the attribute and send it out.
|
|
attr2uri("$gw_ip", "domain");
|
|
|
|
# Set the PSTN_ALLOWED flag. This will be checked on the
|
|
# onsend_route.
|
|
setflag(FLAG_PSTN_ALLOWED);
|
|
route(FORWARD);
|
|
}
|
|
|
|
|
|
# Try to process CANCEL requests quickly.
|
|
#
|
|
route[CATCH_CANCEL]
|
|
{
|
|
if (method == CANCEL) {
|
|
# t_relay_cancel() will stop processing if a matching
|
|
# INVITE was found.
|
|
xlog("L_DEBUG", "catching cancel dialogid=%$dialog_id\n");
|
|
if (!t_relay_cancel()) {
|
|
# An INVITE was found but some error occurred.
|
|
sl_reply("500", "Internal Server Error");
|
|
drop;
|
|
}
|
|
# Bad luck, no corresponding INVITE was found, we have to
|
|
# continue with the script.
|
|
}
|
|
}
|
|
|
|
|
|
# Site specific policy.
|
|
#
|
|
route[SITE_SPECIFIC]
|
|
{
|
|
# This is only relevant for requests for one of our domains.
|
|
if (strempty($t.did)) {
|
|
break;
|
|
}
|
|
|
|
# Do site specific routing such as peering.
|
|
# For example:
|
|
if (uri=~"^sip:000777") {
|
|
rewritehostport("sems01.iptel.org:5074");
|
|
route(FORWARD);
|
|
}
|
|
}
|
|
|
|
# Process Session-Timer.
|
|
#
|
|
route[SESSION_TIMER]
|
|
{
|
|
# We are only interested in session establishment or session
|
|
# refreshing.
|
|
#
|
|
if (method != "INVITE" && method != "UPDATE") {
|
|
break;
|
|
}
|
|
|
|
# Let's check if the Session-Expires header is already present.
|
|
if (@hf_value.session_expires != "") {
|
|
# Compare the Session-Expires header value with the
|
|
# configured Min-SE.
|
|
eval_push("x:%@hf_value.session_expires.uri");
|
|
eval_oper("(int)", -1);
|
|
eval_push("x:%@cfg_get.session_timer.min_se");
|
|
eval_oper("(int)", -1);
|
|
eval_oper(">=", -2);
|
|
|
|
# Let's check for the Supported header.
|
|
if (hf_value_exists("Supported", "timer")) {
|
|
# The UAC supports Session-Timer, so we
|
|
# only need to take a look at the values
|
|
if (@eval.pop[-1] == "0") {
|
|
# Session interval is lower than the
|
|
# configured Min-SE
|
|
append_to_reply("Min-SE: %@cfg_get.session_timer.min_se\r\n");
|
|
sl_reply("422", "Session Interval Too Small");
|
|
drop;
|
|
}
|
|
|
|
# We store the session expires value for the reply
|
|
# route and mark the attribute for inserting as
|
|
# Record-Route cookie.
|
|
$stimer = @hf_value.session_expires.uri;
|
|
setavpflag($stimer, "dialog_cookie");
|
|
|
|
# Set the session timer flag that indicates the
|
|
# UAC supports the extension.
|
|
setflag(FLAG_SESSIONTIMER);
|
|
}
|
|
else {
|
|
# Session epxires was already inserted by some other
|
|
# proxy
|
|
if (@eval.pop[-1] == "0") {
|
|
# Session interval is lower than the
|
|
# configured Min-SE. There is no point in
|
|
# sending 422 response, because the UAC
|
|
# does not support the extension, the values
|
|
# can be corrected instead.
|
|
assign_hf_value("Session-Expires",
|
|
"%@cfg_get.session_timer.min_se");
|
|
remove_hf_value("Min-SE");
|
|
append_hf_value("Min-SE",
|
|
"%@cfg_get.session_timer.min_se");
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
# No Session Timer is requested yet, neither by UAC nor by
|
|
# proxy
|
|
if (@cfg_get.session_timer.default != "0") {
|
|
# Add a Session Expires header to see if the UAS
|
|
# supports Session Timer. We do not insert a
|
|
# Required header because then the call might fail.
|
|
append_hf_value("Session-Expires",
|
|
"%@cfg_get.session_timer.default");
|
|
if (@cfg_get.session_timer.min_se != "90") {
|
|
append_hf_value("Min-SE",
|
|
"%@cfg_get.session_timer.min_se");
|
|
}
|
|
|
|
# Mark the attribute to be inserted as a
|
|
# Record-Route cookie
|
|
$stimer = @cfg_get.session_timer.default;
|
|
setavpflag($stimer, "dialog_cookie");
|
|
}
|
|
}
|
|
}
|
|
|
|
# Route which checks and performs ENUM queries
|
|
# #
|
|
route[ENUM]
|
|
{
|
|
# perform ENUM query only if the RURI contains an E.164
|
|
# number as uer part
|
|
if (uri =~ "sip:\+[0-9]?@") {
|
|
# if the ENUM query was successful send it right
|
|
# away of to the new target, otherwise do nothing
|
|
if (enum_query()) {
|
|
route(FORWARD);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Failure route for initial INVITEs.
|
|
#
|
|
failure_route[FAILURE_ROUTE]
|
|
{
|
|
if (isflagset(FLAG_INIT_DLG)) {
|
|
# Mark that we are operating from a failure route.
|
|
setflag(FLAG_FAILUREROUTE);
|
|
|
|
if (t_check_status("486|600")) {
|
|
# If we received a busy and a busy target is set, forward
|
|
# it there.
|
|
# Note: Again, the forwarding target has to be a routeable
|
|
# URI. We redirect using 3xx to avoid possible issues with
|
|
# credentials (if we consumed them, they may be missing in
|
|
# a loop, if we don't consume them, messages are bigger and
|
|
# more vulnerable)
|
|
if ($tu.fwd_busy_target != "") {
|
|
attr2uri("$tu.fwd_busy_target");
|
|
#attr_destination("$tu.fwd_busy_target");
|
|
#route(FORWARD);
|
|
t_reply("302", "Redirect On Busy");
|
|
}
|
|
# Alternatively, you could forward the request to
|
|
# SEMS/voicemail here
|
|
}
|
|
else if (t_check_status("408|480")) {
|
|
# If we received no answer and the noanswer target is set,
|
|
# forward it there.
|
|
# Note: See above.
|
|
if ($tu.fwd_noanswer_target != "") {
|
|
attr2uri("$tu.fwd_noanswer_target");
|
|
#attr_destination("$tu.fwd_noanswer_target");
|
|
#route(FORWARD);
|
|
t_reply("302", "Redirect On No Answer");
|
|
}
|
|
}
|
|
} # if (isflagset...
|
|
}
|
|
|
|
|
|
# Onreply route that fixes NAT in responses.
|
|
#
|
|
onreply_route[REPLY_ROUTE]
|
|
{
|
|
# Check and fix the Contact in the reply to
|
|
# allow proper routing of in-dialog messages.
|
|
route(UAS_NAT_DETECTION);
|
|
|
|
# If RTP proxy was activated and this is a 18x or 2xx reply with a
|
|
# body, inform RTP proxy.
|
|
if (isflagset(FLAG_RTP_PROXY)
|
|
&& status=~"(18[03])|(2[0-9][0-9])"
|
|
&& @msg.body != "")
|
|
{
|
|
force_rtp_proxy('r');
|
|
}
|
|
|
|
# Let's check for session timer support.
|
|
if (isflagset(FLAG_SESSIONTIMER) && status =~ "2[0-9][0-9]") {
|
|
# The UAC wanted to have a session timer.
|
|
if (strempty(@hf_value.session_expires)) {
|
|
# But the UAS does not support it, so we will try
|
|
# to convince the UAC to do it.
|
|
append_hf_value("Session-Expires",
|
|
"%$stimer;refresher=uac");
|
|
if (!hf_value_exists("Require", "timer")) {
|
|
include_hf_value("Require", "timer");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# Do some final checks before a request is sent out.
|
|
onsend_route
|
|
{
|
|
# Bypass check: Eliminate requests to the PSTN gateway if they have
|
|
# not passed ACL checks and are not marked with FLAG_PSTN_ALLOWED
|
|
# but are dialog-initiating requests (no to-tag, no CANCEL, no ACK).
|
|
# This helps to stop policy bypasses (gateway IP uploaded as a
|
|
# forked contact, or a call-forwarding destination, or a DNS name,
|
|
# or a preloaded route, or something else possibly)
|
|
if (defined $g.gw_ip && to_ip==$g.gw_ip && !isflagset(FLAG_PSTN_ALLOWED)
|
|
&& !isflagset(FLAG_TOTAG)
|
|
&& method != "ACK" && method != "CANCEL")
|
|
{
|
|
log(1, "ALERT: non authorized packet for PSTN, dropping...\n%mb\n");
|
|
|
|
# You can't use advanced features from onsend_route.
|
|
# xlog("L_ALERT", "non authorized packet for PSTN, dropping...\n%mb\n");
|
|
drop;
|
|
}
|
|
|
|
# RFC 1918 relay protection: Useful if SER is attached to an
|
|
# administrative network using private IP address space and you
|
|
# wish to prevent UACs from relaying their packets there.
|
|
#
|
|
# You will have to comment this out, if you are regularly serving
|
|
# an RFC 1918 address space.
|
|
if (to_ip==10.0.0.0/8 || to_ip==172.16.0.0/12
|
|
|| to_ip==192.168.0.0/16)
|
|
{
|
|
log(1, "ALERT: Packet targeted to an RFC1918 address dropped\n");
|
|
drop;
|
|
}
|
|
}
|
|
|
|
|
|
# Run every minute by the timer module.
|
|
#
|
|
route[ON_1MIN_TIMER] {
|
|
# Cleanup expired location records
|
|
# MySQL version:
|
|
db_query("delete from location where expires<utc_timestamp()");
|
|
# PostgreSQL version:
|
|
#db_query("delete from location where expires<now()");
|
|
|
|
|
|
# Reload domains if domain table has been changed recently.
|
|
# Note: because global attributes are read-only and we can't
|
|
# easily remember the "last" status, we check for changed
|
|
# timestamp in 2 minute time-interval.
|
|
# MySQL version:
|
|
db_query("select value from global_attrs where name='domain_data_version' and type=0 and cast(value as unsigned int) between unix_timestamp(now())-120 and unix_timestamp(now())", "reload");
|
|
# PostgreSQL version:
|
|
#db_query("select value from global_attrs where name='domain_data_version' and type=0 and cast(value as integer) between date_part('epoch', now() - interval '120 seconds') and date_part('epoch', now())", "reload");
|
|
if (@db.fetch.reload.count=="1") {
|
|
# Domain reload only available as fifo command.
|
|
exec_msg("sercmd domain.reload");
|
|
}
|
|
db_close("reload");
|
|
|
|
# Reload global attributes (they are cached in memory) if the contents of
|
|
# the global_attrs table has been changed recently.
|
|
db_query("select value from global_attrs where name='gattr_timestamp' and type=0 and cast(value as unsigned int) between unix_timestamp(now())-120 and unix_timestamp(now())", "gattr_reload");
|
|
if (@db.fetch.gattr_reload.count=="1") {
|
|
exec_msg("sercmd global.reload");
|
|
}
|
|
db_close("gattr_reload");
|
|
}
|
|
|
|
|
|
#!ifdef WITH_XMLRPC
|
|
# handle xmlrpcs
|
|
route[XMLRPC]{
|
|
# accept xmlrpc requests only from localhost
|
|
if (src_ip!=127.0.0.1
|
|
#!ifdef XMLRPC_ALLOW_NET1
|
|
&& src_ip != XMLRPC_ALLOW_NET1
|
|
#!endif
|
|
#!ifdef XMLRPC_ALLOW_NET2
|
|
&& src_ip != XMLRPC_ALLOW_NET2
|
|
#!endif
|
|
#!ifdef XMLRPC_ALLOW_NET3
|
|
&& src_ip != XMLRPC_ALLOW_NET3
|
|
#!endif
|
|
) {
|
|
xmlrpc_reply("400", "xmlrpc not allowed from this address");
|
|
return;
|
|
}
|
|
if (method!="POST" && method!="GET") {
|
|
xmlrpc_reply("400", "unsupported HTTP method");
|
|
return;
|
|
}
|
|
if (msg:len >= 8192) {
|
|
xmlrpc_reply("513", "request too big");
|
|
return;
|
|
}
|
|
#!ifdef XMLRPC_TLS_ONLY
|
|
# allow xmlrpc only on TLS and only if the client certificate is valid
|
|
if (proto!=TLS){
|
|
xmlrpc_reply("400", "xmlrpc allowed only over TLS");
|
|
return;
|
|
}
|
|
if (@tls.peer.verified!=""){
|
|
xmlrpc_reply("400", "Unauthorized");
|
|
return;
|
|
}
|
|
#!endif
|
|
|
|
# close connection only for xmlrpclib user agents (there is a bug in
|
|
# xmlrpclib: it waits for EOF before interpreting the response).
|
|
if (search("^User-Agent:.*xmlrpclib"))
|
|
set_reply_close();
|
|
set_reply_no_connect(); # optional
|
|
dispatch_rpc();
|
|
}
|