
debug=3         # debug level (cmd line: -dddddddddd)
fork=yes
log_stderror=no # (cmd line: -E)
sip_warning=no

/* 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=20

# ------------------ module loading ----------------------------------
loadmodule "/usr/local/lib/ser/modules/sl.so"
loadmodule "/usr/local/lib/ser/modules/tm.so"
loadmodule "/usr/local/lib/ser/modules/maxfwd.so"
loadmodule "/usr/local/lib/ser/modules/rr.so"
loadmodule "/usr/local/lib/ser/modules/textops.so"
loadmodule "/usr/local/lib/ser/modules/ctl.so"
loadmodule "/usr/local/lib/ser/modules/usrloc.so"
loadmodule "/usr/local/lib/ser/modules/registrar.so"
loadmodule "/usr/local/lib/ser/modules/avpops.so"
loadmodule "/usr/local/lib/ser/modules/fifo.so"
loadmodule "/usr/local/lib/ser/modules/options.so"
loadmodule "/usr/local/lib/ser/modules/xprint.so"
# Load OSP module
loadmodule "/usr/local/lib/ser/modules/osp.so"

# ----------------- setting module-specific parameters ---------------

#
# PEERING PARAMETERS:
# ===================
# This section contains OSP parameters that users may need to configure for multi-lateral
# peering. (sp1_uri must be configured.) Additional detail on OSP Module parameters and
# functions is provided in the "OSP Module for Secure, Multi-Lateral Peering" document
# located at:http://developer.berlios.de/docman/?group_id=3799
#
# Configure Peering Servers:
# ==========================
# SER can be configured to query two peering servers for routing information and peering 
# authorization tokens using the sp1_uri and sp2_uri parameters. A configuration for sp1_uri
# is required, configuring sp2_uri is optional. The peering server address should be 
# configured as a standard URL beginning with either http:// or https:// followed by the 
# domain name of the OSP server or the IP address enclosed in brackets. The domain name 
# or IP address should be followed by the peering server TCP port number and uniform 
# resource identifier. Below are example configurations.
#
modparam("osp", "sp1_uri", "http://osptestserver.transnexus.com:1080/osp")
# modparam("osp", "sp2_uri", "https://[1.2.3.4]:1443/osp")

#
# SER IP Address
# ==============
# device_ip is a recommended parameter that explicitly defines the IP address of SER in 
# a peering request message (as SourceAlternate type=transport).  The IP address must 
# be in brackets as shown in the example below.
#
# modparam("osp", "device_ip", "[1.1.1.1]")

#
# Peering Token Validation
# ========================
# When SER receives a SIP INVITE with a peering token, the OSP Module will validate the token to
# determine whether or not the call has been authorized by a peering server.  Peering tokens may,
# or may not, be digitally signed.  This parameter defines if SER will validate signed or unsigned
# tokens or both. The values for "token format" are defined below.  The default value is 2.
#
# 0 - Validate only signed tokens.  Calls with valid signed tokens are allowed.
# 1 - Validate only unsigned tokens.  Calls with valid unsigned tokens are allowed.
# 2 - Validate both signed and unsigned tokens are allowed.  Calls with valid tokens are allowed.
#
# modparam("osp", "token_format", 2)

#
# Crypto files from Peering Server Enrollment
# ===========================================
# These parameters identify crypto files used for validating peering authorization tokens
# and establishing a secure channel between SER and a peering server using SSL.  The files are 
# generated using the 'Enroll' utility from the OSP toolkit.  By default, the proxy will look
# for pkey.pem, localcert.pem, and cacart_0.pem in the default configuration directory. 
# The default config directory is set at compile time using CFG_DIR and defaults to 
# /usr/local/etc/ser/.  The files may be copied to the expected file location or the 
# parameters below may be changed.
#
# If the default CFG_DIR value was used at compile time, the files will be loaded from:
# modparam("osp", "private_key", "/usr/local/etc/ser/pkey.pem")
# modparam("osp", "local_certificate", "/usr/local/etc/ser/localcert.pem")
# modparam("osp", "ca_certificates", "/usr/local/etc/ser/cacert_0.pem")

#
# Use Remote-Party-ID for calling number
# ===========================================
# This parameter is used to tell OSP module if the calling number should be obtained from RPID header. 
# The default value is 1.
#
# 0 - OSP module will use the calling number in From header.
# 1 - OSP module will use the calling number in RPID header if a RPID header exists.
#
# modparam("osp", "use_rpid_for_calling_number", 1)

#
# URI Format for Redirection Messages
# ===========================================
# This parameter is used to tell OSP module which URI format should be used for redirection messages.
# The default value is 0.
#
# 0 - "xxxxxxxxxx@xxx.xxx.xxx.xxx"
# 1 - "<xxxxxxxxxx@xxx.xxx.xxx.xxx>". This is for Cisco 2600 IP-IP gateway.
#
# modparam("osp", "redirection_uri_format", 0)

# listen on the "standard" fifo for backward compatibility
modparam("ctl", "fifo", "fifo:/tmp/ser_fifo")

# -- usrloc params --
modparam("usrloc", "db_mode", 0)

# -- rr params --
avpflags dialog_cookie;

# add value to ;lr param to make some broken UAs happy
modparam("rr", "enable_full_lr", 1)

# enable append_fromtag, request's from-tag is appended to record-route; 
# that's useful for understanding whether subsequent requests (such as BYE) come from
# caller (route's from-tag==BYE's from-tag) or callee (route's from-tag==BYE's to-tag) 
modparam("rr", "append_fromtag", 1)

# -- tm params --
# Timer which hits if no final reply for a request or ACK for a
# negative INVITE reply arrives (in seconds).  For example - UA server is off-line.
# In other words, if the proxy does not receive a response to an Invite before this
# timer expires, the proxy will retry the call and send an Invite to the next VoIP
# destination in the routing list.
modparam("tm", "fr_timer", 5000)

# Timer which hits if no final reply for an INVITE arrives after
# a provisional message was received (in seconds).
# For example - user is not picking up the phone
modparam("tm", "fr_inv_timer", 30000)


# -------------------------  request routing logic -------------------

# main routing logic
route{
    xplog("L_INFO", "----ROUTE: Route IN - M=%rm RURI=%ru F=%fu T=%tu IP=%si ID=$ci\n");

    # initial sanity checks
    if (!mf_process_maxfwd_header("10")) {
        xplog("L_WARN", "----ROUTE: Too many hops, $rm from '%fu' to '%tu'\n");
        sl_send_reply("483", "Too Many Hops");
        break;
    };

    if (msg:len >= max_len) {
        xplog("L_WARN", "----ROUTE: Message too big, $rm from '%fu' to '%tu'\n");
        sl_send_reply("513", "Message Too Big");
        break;
    };

    # 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 (method!="INVITE") {
        record_route();
    }

    # loose-route processing
    if (loose_route()) {
        if (method=="INVITE") {
            log(2, "----ROUTE: Relay re-INVITE\n");
            # send it out now; use stateful forwarding as it works reliably even for UDP2TCP
            if (!t_relay()) {
                sl_reply_error();
            }
            break;
        } else if (method=="ACK") {
            log(2, "----ROUTE: Relay ACK\n");
            # send it out now; use stateful forwarding as it works reliably even for UDP2TCP
            if (!t_relay()) {
                sl_reply_error();
            }
            break;
        }
    } else {
        if (method=="BYE") {
            xplog("L_WARN", "----ROUTE: Processing BYE without route header - F=$fu T=$tu IP=$si ID=$ci\n");
            if (t_lookup_request()) {
                log(2, "----ROUTE: Duplicated BYE\n");
            } else {
                # NOTE - don't t_relay before reporting usage
                if (!reportospusage()) {
                    xplog("L_WARN", "----ROUTE: failed to report usage, from '%fu' to '%tu'\n");
                }
                sl_send_reply("400", "Bad Request - no route header in BYE message");
            }
            break;
        }
    }

    if (method=="INVITE") {
        log(2, "----ROUTE: Processing INVITE\n");

        # Stop retransmission
        sl_send_reply("100", "Trying");

        if (t_lookup_request()) {
            log(2, "----ROUTE: Duplicated INVITE\n");
            break;
        }

        # Authentication
        log(3, "OSP authorization validation logic\n");

        # This function looks for OSP peering token in the message.  It will fail
        # if the token is not present
        if (checkospheader()) {
            log(3, "With OSP token, validate it\n"); 

            # The function validates OSP tokens.  It will fail
            # if the token is not valid or has expired
            if (validateospheader()) {
                # Authorization is valid.  The proxy can now use its own database of
                # registered users for routing information.
                # The proxy could also issue another OSP peering authorization and
                # routing request by calling route(1) function.
                log(3, "OSP authorization valid\n");

                # Remove the OSP peering token from the received message
                # Otherwise it will be forwarded on to the next hop
                remove_hf("P-OSP-Auth-Token");
            } else {
                log(3, "OSP authorization invalid\n");
                sl_send_reply("401", "Unauthorized");
                break;
            };
        } else {
            log(3, "Without OSP token, apply different authentication strategy\n"); 
            log(3, "Go ahead, everyone is welcomed\n");

            # # Implement authentication strategy here or simply add the
            # # statements below to block all invites without OSP peering tokens
            # sl_send_reply("401", "Unauthorized");
            # break;
        }

        log(2, "----ROUTE: Authentication passed\n");
        
        # Routing

#        if (lookup("location")) {
#            log(2, "----ROUTE: Registered user, forward the message\n");
#            append_hf("P-hint: usrloc\r\n");
#            record_route();
#            t_relay();
#        } else {
#            log(2, "----ROUTE: Unregistered user, use OSP to get routing\n");
#            route(2);
#        }
        log(2, "----ROUTE: Use OSP to get routing\n");
        route(2);
    } else if (method=="ACK") {
        log(2, "----ROUTE: Processing ACK\n");

        if (t_lookup_request()) {
            log(2, "----ROUTE: Relay E2E ACK\n");
            t_relay();
        } else {
            log(2, "----ROUTE: Not to relay ACK");
        }
    } else if (method=="BYE") {
        log(2, "----ROUTE: Processing BYE\n");

        if (t_lookup_request()) {
            log(2, "----ROUTE: Duplicated BYE\n");
            break;
        }

        # NOTE - don't t_relay before reporting usage
        if (!reportospusage()) {
            xplog("L_WARN", "----ROUTE: failed to report usage, from '%fu' to '%tu'\n");
        }

        t_relay();
    } else if (method=="CANCEL") {
        log(2, "----ROUTE: Processing CANCEL\n");

        if (t_lookup_request()) {
            t_relay();
        } else {
            xplog("L_WARN", "----ROUTE: CANCEL without matching transaction, from '$fu' to '$tu'\n");
        }
    } else if ((method=="OPTIONS") && (uri==myself)) {
        log(2, "----ROUTE: Processing OPTIONS\n");
        options_reply();
    } else if (method=="PRACK") {
        log(2, "----ROUTE: Processing PRACK\n");
        t_relay();
    } else if (method=="INFO") {
        log(2, "----ROUTE: Processing INFO\n");
        t_relay();
    } else if (method=="UPDATE") {
        log(2, "----ROUTE: Processing UPDATE\n");
        t_relay();
#    } else if (method=="REGISTER") {
#        log(2, "----ROUTE: Processing REGISTER\n");
#
#        # Stop retransmission
#        sl_send_reply("100", "Trying");
#        
#        if (uri==myself) {
#            log(2, "----ROUTE: Registered\n");
#            save("location");
#        } else {
#            log(2, "----ROUTE: Register from outside domain rejected\n");
#            sl_send_reply("488", "Unknown Domain");
#        }
    } else {
        xplog("L_WARN", "----ROUTE: Unsupported message, $rm from '%fu' to '%tu'\n");
        sl_send_reply("500", "Unsupported Message");
    }

    log(3, "----ROUTE: Route OUT\n");
}


# OSP Authorization and Routing
route[2] {
    log(3, "OSP authorization and routing logic\n");

    # Is request to a phone number?
    # A phone number consists of digits (0 through 9)
    # and can begin with +
#    if (uri=~"sip:[+,0-9][0-9]*@") {
        # Requesting OSP peering routing and authorization
        # The request may fail if:
        #  o OSP peering servers are not available
        #  o Authentication failed
        #  o There is no route to destination or the route is blocked
        log(3, "Requesting OSP authorization and routing\n");

        requestosprouting();
        if ($retcode == 1) {
            log(3, "Response received\n");

            setavpflag("fr._osp_orig_cookie_", dialog_cookie);
            setavpflag("fr._osp_term_cookie_", dialog_cookie);
            record_route();
            
            # Now we have 3 options.
            #  o route(3) - sends a redirect to all available routes
            #  o route(4) - fork off to all available routes
            #  o route(5) in conjunction with failure_route(1) - sequentially tries all routes

            # route(3);
            # route(4);
            route(5);
        } else if ($retcode == -403) {
            xplog("L_WARN", "----ROUTE: Call to '%tu' from source device '%si' is blocked on OSP Server.\n");
            sl_send_reply("403", "Forbidden - Call is blocked");
        } else if ($retcode == -404) {
            xplog("L_WARN", "----ROUTE: No route on OSP server for call to '%tu' from source device '%si'.\n");
            sl_send_reply("404", "Route Not Found");
        } else if ($retcode == -500) {
            log(3, "----ROUTE: Internal Server Error\n");
            sl_send_reply("500", "Internal Server Error");
        } else {
            log(3, "----ROUTE: OSP Authorization failed\n");
            sl_send_reply("503", "Service Not Available");
        }
#    } else {
#        log(3, "Wrong phone number\n");
#        sl_send_reply("401", "Not Phone Number");
#    }
}


route[3] {
    log(3, "Prepare all routes and redirect\n");

    if (prepareallosproutes()) {
        sl_send_reply("300", "Redirect");
    } else {
        log(3, "Failed to prepare all routes\n");
        sl_send_reply("500", "Internal Server Error");
    }
}


route[4] {
    log(3, "Prepare all routes and fork-off\n");

    if (prepareallosproutes()) {
        t_relay();
    } else {
        log(3, "Failed to prepare all routes\n");
        sl_send_reply("500", "Internal Server Error");
    }
}


route[5] {
    log(3, "Try the 1st route\n");

    if (prepareospfirstroute()) {
        t_on_branch("1");

        t_on_failure("1");

        t_relay();
    } else {
        log(3, "Could  not use the 1st route\n");
        sl_send_reply("500", "Internal Server Error");
    }
}


failure_route[1] {
    if (t_check_status("487")) {
        log(3, "Call canceled (status 487)\n");
        break;
    }

    if (t_check_status("486")) {
        log(3, "User busy (status 486)\n");
        break;
    }

    # tm's t_local_replied has not been implemented in SER yet.
    # if (t_check_status("408")) {
    #     if (!t_local_replied("last")) {
    #         log(3, "User unavailable (status 408)\n");
    #         break;
    #     }
    # }

    log(3, "Try the next route\n");

    if (prepareospnextroute()) {
        t_on_branch("1");

        t_on_failure("1");

        t_relay();
    } else {
        xplog("L_WARN", "----ROUTE: All destinations attempted for call ID '%ci'. Call cannot be completed.\n");
        t_reply("503", "Service Not Available - Call cannot be completed");
    }
}


branch_route[1] {
    log(3, "Prepare route specific OSP information\n");
    appendospheaders();
}
