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.
619 lines
17 KiB
619 lines
17 KiB
#
|
|
# $Id$
|
|
#
|
|
# Example configuration file (simpler than ser-oob.cfg, but more
|
|
# complex then ser-basic.cfg).
|
|
#
|
|
# First start SER sample config script with:
|
|
# database, accounting, authentication, multi-domain support
|
|
# PSTN GW section, named flags, named routes, global-,
|
|
# domain- and user-preferences with AVPs
|
|
# Several of these features are only here for demonstration purpose
|
|
# what can be achieved with the SER config script language.
|
|
#
|
|
# If you look for a simpler version with a lot less dependencies
|
|
# please refer to the ser-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>.
|
|
|
|
# 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 PID header for your user
|
|
# - ser_attr add uid=UID asserted_id="PID"
|
|
# If you want to have 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
|
|
|
|
|
|
# ----------- 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
|
|
|
|
# started from compile directory (not installed)
|
|
##!define LOCAL_TEST_RUN
|
|
|
|
# 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
|
|
|
|
|
|
# ----------- global configuration parameters ------------------------
|
|
|
|
debug=2 # debug level (cmd line: -dddddddddd)
|
|
#memdbg=10 # memory debug log level
|
|
#memlog=10 # memory statistics log level
|
|
#log_facility=LOG_LOCAL0 # sets the facility used for logging (see syslog(3))
|
|
|
|
/* Uncomment these lines to enter debugging mode
|
|
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=ser
|
|
#group=ser
|
|
#disable_core=yes #disables core dumping
|
|
#open_fd_limit=1024 # sets the open file descriptors limit
|
|
#mhomed=yes # useful for multihomed hosts, small performance penalty
|
|
#disable_tcp=yes
|
|
#tcp_accept_aliases=yes # accepts the tcp alias via option (see NEWS)
|
|
sip_warning=yes
|
|
#!ifdef WITH_TLS
|
|
enable_tls=yes
|
|
#!endif
|
|
|
|
#
|
|
|
|
# ------------------ module loading ----------------------------------
|
|
|
|
#!ifdef LOCAL_TEST_RUN
|
|
loadpath "modules:modules_s"
|
|
#!else
|
|
loadpath "/usr/lib/ser/modules:/usr/lib/ser/modules_s"
|
|
#!endif
|
|
|
|
# load a SQL database for authentication, domains, user AVPs etc.
|
|
loadmodule "db_mysql"
|
|
|
|
loadmodule "tm"
|
|
loadmodule "sl"
|
|
loadmodule "rr"
|
|
loadmodule "maxfwd"
|
|
loadmodule "usrloc"
|
|
loadmodule "registrar"
|
|
loadmodule "xlog"
|
|
loadmodule "textops"
|
|
loadmodule "ctl"
|
|
loadmodule "cfg_rpc"
|
|
loadmodule "auth"
|
|
loadmodule "auth_db"
|
|
loadmodule "gflags"
|
|
loadmodule "domain"
|
|
loadmodule "uri_db"
|
|
loadmodule "avp"
|
|
loadmodule "avp_db"
|
|
loadmodule "acc_db"
|
|
loadmodule "xmlrpc"
|
|
#!ifdef WITH_TLS
|
|
loadmodule "tls"
|
|
#!endif
|
|
|
|
# ----------------- setting script FLAGS -----------------------------
|
|
flags
|
|
FLAG_ACC : 1, # include message in accounting
|
|
FLAG_FAILUREROUTE : 2; # we are operating from a failure route
|
|
|
|
avpflags
|
|
dialog_cookie; # handled by rr module
|
|
|
|
# ----------------- setting module-specific parameters ---------------
|
|
|
|
# specify the path to you database here
|
|
modparam("acc_db|auth_db|avp_db|domain|gflags|usrloc|uri_db", "db_url", "mysql://ser:heslo@127.0.0.1/ser")
|
|
|
|
# -- usrloc params --
|
|
|
|
# as we use the database anyway we will use it for usrloc as well
|
|
modparam("usrloc", "db_mode", 1)
|
|
|
|
# -- auth params --
|
|
modparam("auth_db", "calculate_ha1", yes)
|
|
modparam("auth_db", "plain_password_column", "password")
|
|
|
|
# -- rr params --
|
|
# 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 only necessary ones
|
|
modparam("rr", "cookie_filter", "(account)")
|
|
#
|
|
# 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", "MyRRAVPcookiesecret")
|
|
|
|
# -- gflags params --
|
|
# load the global AVPs
|
|
modparam("gflags", "load_global_attrs", 1)
|
|
|
|
# -- domain params --
|
|
# load the domain AVPs
|
|
modparam("domain", "load_domain_attrs", 1)
|
|
|
|
# -- ctl params --
|
|
# by default ctl listens on unixs:/tmp/ser_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, localhost
|
|
modparam("ctl", "binrpc", "tcp:127.0.0.1:2046")
|
|
|
|
# -- acc_db params --
|
|
# failed transactions (=negative responses) should be logged to
|
|
modparam("acc_db", "failed_transactions", 1)
|
|
|
|
# comment the next line if you don't want to have accounting to DB
|
|
modparam("acc_db", "log_flag", "FLAG_ACC")
|
|
|
|
# -- tm params --
|
|
# uncomment the following line if you want to avoid that each new reply
|
|
# restarts the resend timer (see INBOUND route below)
|
|
#modparam("tm", "restart_fr_on_each_reply", "0")
|
|
|
|
#!ifdef WITH_TLS
|
|
# -- tls params --
|
|
modparam("tls", "verify_certificate", 0)
|
|
#!ifdef LOCAL_TEST_RUN
|
|
modparam("tls", "certificate", "./modules/tls/sip-router-selfsigned.pem")
|
|
modparam("tls", "private_key", "./modules/tls/sip-router-selfsigned.key")
|
|
#separate TLS config file
|
|
#modparam("tls", "config", "./modules/tls/tls.cfg")
|
|
#!else
|
|
modparam("tls", "certificate", "ser-selfsigned.pem")
|
|
modparam("tls", "private_key", "ser-selfsigned.key")
|
|
#separate TLS config file
|
|
#modparam("tls", "config", "tls.cfg")
|
|
#!endif
|
|
|
|
|
|
# -- xmlrpc params --
|
|
# using a sub-route from the module is a lot safer than relying on the
|
|
# request method to distinguish HTTP from SIP
|
|
modparam("xmlrpc", "route", "RPC");
|
|
|
|
# ------------------------- request routing logic -------------------
|
|
|
|
# main routing logic
|
|
|
|
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"
|
|
|
|
# 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 or
|
|
# needs a Record-Route header
|
|
route(RR);
|
|
|
|
# check if the request belongs to our proxy
|
|
route(DOMAIN);
|
|
|
|
# handle REGISTER requests
|
|
route(REGISTRAR);
|
|
|
|
# from here on we want to know you is calling
|
|
route(AUTHENTICATION);
|
|
|
|
# check if we should be outbound proxy for a local user
|
|
route(OUTBOUND);
|
|
|
|
# check if the request is for a local user
|
|
route(INBOUND);
|
|
|
|
# here you could for example try to do an ENUM lookup before
|
|
# the call gets routed to the PSTN
|
|
#route(ENUM);
|
|
|
|
# lets see if someone wants to call a PSTN number
|
|
route(PSTN);
|
|
|
|
# nothing matched, reject it finally
|
|
sl_reply("404", "No route matched");
|
|
}
|
|
|
|
route[FORWARD]
|
|
{
|
|
# here you could decide wether this call needs a RTP relay or not
|
|
|
|
# if this is called from the failure route we need to open a new branch
|
|
if (isflagset(FLAG_FAILUREROUTE)) {
|
|
append_branch();
|
|
}
|
|
|
|
# if this is an initial INVITE (without a To-tag) we might try another
|
|
# (forwarding or voicemail) target after receiving an error
|
|
if (method=="INVITE" && strempty(@to.tag)) {
|
|
t_on_failure("FAILURE_ROUTE");
|
|
}
|
|
|
|
# send it out now; use stateful forwarding as it works reliably
|
|
# even for UDP2TCP
|
|
if (!t_relay()) {
|
|
sl_reply_error();
|
|
}
|
|
drop;
|
|
}
|
|
|
|
route[INIT]
|
|
{
|
|
# initial sanity checks -- messages with
|
|
# max_forwards==0, or excessively long requests
|
|
if (!mf_process_maxfwd_header("10")) {
|
|
sl_reply("483", "Too Many Hops");
|
|
drop;
|
|
}
|
|
|
|
if (msg:len >= 4096 ) {
|
|
sl_reply("513", "Message too big");
|
|
drop;
|
|
}
|
|
|
|
# you could add some NAT detection here for example
|
|
|
|
# or you cuold call here some of the check from the sanity module
|
|
|
|
# lets account all initial INVITEs
|
|
# further in-dialog requests are accounted by a RR cookie (see below)
|
|
if (method=="INVITE" && strempty(@to.tag)) {
|
|
setflag(FLAG_ACC);
|
|
}
|
|
}
|
|
|
|
route[RPC]
|
|
{
|
|
# allow XMLRPC from localhost
|
|
if ((method=="POST" || method=="GET") &&
|
|
(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
|
|
)) {
|
|
|
|
if (msg:len >= 8192) {
|
|
sl_reply("513", "Request to big");
|
|
drop;
|
|
}
|
|
|
|
# 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
|
|
# lets see if a module wants to answer this
|
|
dispatch_rpc();
|
|
drop;
|
|
}
|
|
}
|
|
|
|
route[RR]
|
|
{
|
|
# subsequent messages within a dialog should take the
|
|
# path determined by record-routing
|
|
if (loose_route()) {
|
|
# 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);
|
|
}
|
|
|
|
# for broken devices which overwrite their Route's with each
|
|
# (not present) RR from within dialog requests it is better
|
|
# to repeat the RRing
|
|
# and if we call rr after loose_route the AVP cookies are restored
|
|
# automatically :)
|
|
record_route();
|
|
|
|
route(FORWARD);
|
|
} else if (!method=="REGISTER") {
|
|
# we record-route all messages -- to make sure that
|
|
# subsequent messages will go through our proxy; that's
|
|
# particularly good if upstream and downstream entities
|
|
# use different transport protocol
|
|
|
|
# if the initial INVITE got the ACC flag store this in
|
|
# an RR AVP cookie. This is more for demonstration purpose
|
|
if (isflagset(FLAG_ACC)) {
|
|
$account = "yes";
|
|
setavpflag($account, "dialog_cookie");
|
|
}
|
|
|
|
record_route();
|
|
}
|
|
}
|
|
|
|
route[DOMAIN]
|
|
{
|
|
# check if the caller is from a local domain
|
|
lookup_domain("$fd", "@from.uri.host");
|
|
|
|
# check if the callee is at a local domain
|
|
lookup_domain("$td", "@ruri.host");
|
|
|
|
# we don't know the domain of the caller and also not
|
|
# the domain of the callee -> somone uses our proxy as
|
|
# a relay
|
|
if (strempty($t.did) && strempty($f.did)) {
|
|
sl_reply("403", "Relaying Forbidden");
|
|
drop;
|
|
}
|
|
}
|
|
|
|
route[REGISTRAR]
|
|
{
|
|
# if the request is a REGISTER lets take care of it
|
|
if (method=="REGISTER") {
|
|
# check if the REGISTER if for one of our local domains
|
|
if (strempty($t.did)) {
|
|
sl_reply("403", "Register forwarding forbidden");
|
|
drop;
|
|
}
|
|
|
|
# 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;
|
|
}
|
|
|
|
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, what uri is in From header
|
|
#if (!lookup_user("$fu.uid", "@from.uri")) {
|
|
# sl_reply("404", "Unknown user in From");
|
|
# drop;
|
|
#}
|
|
#if ($fu.uid != $tu.uid) {
|
|
# sl_reply("403", "Authentication and From-Header mismatch");
|
|
# drop;
|
|
#}
|
|
|
|
# everything is fine so lets store the binding
|
|
if (!save_contacts("location")) {
|
|
sl_reply("400", "Invalid REGISTER Request");
|
|
drop;
|
|
}
|
|
drop;
|
|
}
|
|
}
|
|
|
|
route[AUTHENTICATION]
|
|
{
|
|
if (method=="CANCEL" || method=="ACK") {
|
|
# you are not allowed to challenge these methods
|
|
break;
|
|
}
|
|
|
|
# requests from non-local to local domains should be permitted
|
|
# remove this if you want a walled garden
|
|
if (strempty($f.did)) {
|
|
break;
|
|
}
|
|
|
|
# as gateways are usually not able to authenticate for their
|
|
# requests you will have trust them base on some other information
|
|
# like the source IP address. WARNING: if at all this is only safe
|
|
# in a local network!!!
|
|
#if (src_ip==a.b.c.d) {
|
|
# 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 ($digest_challenge != "") {
|
|
append_to_reply("%$digest_challenge");
|
|
}
|
|
sl_reply("407", "Proxy Authentication Required");
|
|
}
|
|
drop;
|
|
}
|
|
|
|
# check if the UID from the authentication meets the From header
|
|
$authuid = $uid;
|
|
if (!lookup_user("$fu.uid", "@from.uri")) {
|
|
del_attr("$uid");
|
|
}
|
|
if ($fu.uid != $fr.authuid) {
|
|
sl_reply("403", "Fake Identity");
|
|
drop;
|
|
}
|
|
# load the user AVPs (preferences) of the caller, e.g. for RPID header
|
|
load_attrs("$fu", "$f.uid");
|
|
}
|
|
|
|
route[OUTBOUND]
|
|
{
|
|
# if a local user calls to a foreign domain we play outbound proxy for him
|
|
# comment this out if you want a walled garden
|
|
if ($f.did != "" && $t.did == "") {
|
|
append_hf("P-hint: outbound\r\n");
|
|
route(FORWARD);
|
|
}
|
|
}
|
|
|
|
route[INBOUND]
|
|
{
|
|
# lets see if know the callee
|
|
if (lookup_user("$tu.uid", "@ruri")) {
|
|
|
|
# load the preferences of the callee to have his timeout values loaded
|
|
load_attrs("$tu", "$t.uid");
|
|
|
|
# if you want to know if the callee username was an alias
|
|
# check it like this
|
|
#if (strempty($tu.uri_canonical)) {
|
|
# if the alias URI has different AVPs/preferences
|
|
# 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 ($tu.fwd_always_target != "") {
|
|
attr2uri("$tu.fwd_always_target");
|
|
route(FORWARD);
|
|
}
|
|
|
|
# native SIP destinations are handled using our USRLOC DB
|
|
if (lookup_contacts("location")) {
|
|
append_hf("P-hint: usrloc applied\r\n");
|
|
|
|
# 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 != 0) {
|
|
if ($t.fr_timer != 0) {
|
|
t_set_fr("$t.fr_inv_timer", "$t.fr_timer");
|
|
} else {
|
|
t_set_fr("$t.fr_inv_timer");
|
|
}
|
|
}
|
|
|
|
route(FORWARD);
|
|
} else {
|
|
sl_reply("480", "User temporarily not available");
|
|
drop;
|
|
}
|
|
}
|
|
}
|
|
|
|
route[PSTN]
|
|
{
|
|
# Only if the AVP 'gw_ip' is set and the request URI contains
|
|
# only a number we consider sending this to the PSTN GW.
|
|
# Only users from a local domain are permitted to make calls.
|
|
# Additionally you might want to check the acl AVP to verify
|
|
# that the user is allowed to make such expensives calls.
|
|
if ($f.did != "" && $gw_ip != "" &&
|
|
uri=~"sips?:\+?[0-9]{3,18}@.*") {
|
|
# probably you need to convert the number in the request
|
|
# URI according to the requirements of your gateway here
|
|
|
|
# if an AVP 'asserted_id' is set we insert an RPID header
|
|
if ($asserted_id != "") {
|
|
xlset_attr("$rpidheader", "<sip:%$asserted_id@%@ruri.host>;screen=yes");
|
|
replace_attr_hf("Remote-Party-ID", "$rpidheader");
|
|
}
|
|
|
|
# just replace the domain part of the RURI with the
|
|
# value from the AVP and send it out
|
|
attr2uri("$gw_ip", "domain");
|
|
route(FORWARD);
|
|
}
|
|
}
|
|
|
|
route[CATCH_CANCEL] {
|
|
# check whether there is a corresponding INVITE to the CANCEL,
|
|
# and bypass the rest of the script if possible
|
|
|
|
if (method == CANCEL) {
|
|
if (!t_relay_cancel()) { # implicit drop if the INVITE was found
|
|
|
|
# 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
|
|
}
|
|
}
|
|
|
|
failure_route[FAILURE_ROUTE]
|
|
{
|
|
# mark for the other routes that we are operating from here on 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
|
|
if ($tu.fwd_busy_target != "") {
|
|
attr2uri("$tu.fwd_busy_target");
|
|
route(FORWARD);
|
|
}
|
|
# 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: again the target has to be a routeable URI
|
|
if ($tu.fwd_noanswer_target != "") {
|
|
attr2uri("$tu.fwd_noanswer_target");
|
|
route(FORWARD);
|
|
}
|
|
# alternatively you could forward the request to SEMS/voicemail here
|
|
}
|
|
}
|