Standard ser.cfg

Keywords: 0.9.x | Book | Development | domain | Site | SSL | stateless
#
# $Id: seri.cfg,v 1.42 2007/05/30 12:28:17 tirpi Exp $
#

# First start SERi sample config script with:
#   database, accounting, authentication, multi-domain support
#   PSTNi 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.

# To get this config running you need to execute the following commands
# with the new serctli (the capital word are just place holders)
# - ser_ctl domain add DOMAINNAME
# - ser_ctl user add USERNAME@DOMAINNAME -p PASSWORD
# 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 configuration parameters ------------------------

debug=3         # 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
forki=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  # usefull for multihomed hosts, small performance penalty
#disable_tcp=yes
#tcp_accept_aliases=yes # accepts the tcp alias via option (see NEWS)

#

# ------------------ module loading ----------------------------------

# load a SQL database for authentication, domains, user AVPs etc.
loadmodule "/usr/local/lib/ser/modules/mysql.so"

loadmodule "/usr/local/lib/ser/modules/sl.so"
loadmodule "/usr/local/lib/ser/modules/tm.so"
loadmodule "/usr/local/lib/ser/modules/rr.so"
loadmodule "/usr/local/lib/ser/modules/maxfwd.so"
loadmodule "/usr/local/lib/ser/modules/usrloc.so"
loadmodule "/usr/local/lib/ser/modules/registrar.so"
loadmodule "/usr/local/lib/ser/modules/xlog.so"
loadmodule "/usr/local/lib/ser/modules/textops.so"
loadmodule "/usr/local/lib/ser/modules/ctl.so"
loadmodule "/usr/local/lib/ser/modules/fifo.so"
loadmodule "/usr/local/lib/ser/modules/auth.so"
loadmodule "/usr/local/lib/ser/modules/auth_db.so"
loadmodule "/usr/local/lib/ser/modules/gflags.so"
loadmodule "/usr/local/lib/ser/modules/domain.so"
loadmodule "/usr/local/lib/ser/modules/uri_db.so"
loadmodule "/usr/local/lib/ser/modules/avp.so"
loadmodule "/usr/local/lib/ser/modules/avp_db.so"
loadmodule "/usr/local/lib/ser/modules/acc_db.so"
loadmodule "/usr/local/lib/ser/modules/xmlrpc.so"

# ----------------- setting script FLAGS -----------------------------
flags
  FLAG_ACC          : 1,  # include message in accouting
  FLAG_FAILUREROUTE : 2;  # we are operating from a failure route

avpflags
  dialogi_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:localhost:2046")

# -- acc_db params --
# failed transactionsi (=negative responses) should be logged to
modparam("acc_db", "failed_transactions", 1)

# comment the next line if you dont want to have accouting 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")

# -- xmlrpc params --
# using a sub-route from the module is a lot safer then 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 RTPi relay or not

    # if this is called from the failure route we need to open a new branchi
    if (isflagset(FLAG_FAILUREROUTE)) {
        append_branch();
    }

    # if this is an initial INVITE (without a To-tagi) we might try another
    # (forwarding or voicemail) target after receiving an error
    if (method=="INVITE" && @to.tag=="") {
        t_on_failure("FAILURE_ROUTE");
    }

    # send it out now; use statefuli 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 >=  max_len ) {
        sl_reply("513", "Message too big");
        drop;
    }

    # you could add some NATi 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" && @to.tag=="") {
        setflag(FLAG_ACC);
    }
}

route[RPC]
{
    # allow XMLRPC from localhost
    if ((method=="POST" || method=="GET") &&
        src_ip==127.0.0.1) {

        if (msg:len >= 8192) {
            sl_reply("513", "Request to big");
            drop;
        }

        # lets see if a module wants to answer this
        dispatch_rpc();
        drop;
    }
}

route[RR]
{
    # subsequent messages withing 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
        # accouting 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 upstreami and downstreami entities
        # use different transport protocol

        # if the inital 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 dont know the domain of the caller and also not
    # the domain of the callee -> somone uses our proxy as
    # a relay
    if (!$t.did && !$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 (!$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;
        #}

        # everyhting 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 (! $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 (! $tu.uri_canonical) {
            # if the alias URIi 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 prefences
            # 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");
                }
            }

            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 SEMSi/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
    }
}

Home |  Recent changes |  Search |  Glossary |  Sitemap |  Login