mirror of https://github.com/sipwise/kamailio.git
496 lines
12 KiB
496 lines
12 KiB
#!KAMAILIO
|
|
#
|
|
# Simple/sample kamailio.cfg for running a proxy/registrar with TLS and
|
|
# WebSockets support.
|
|
|
|
#!substdef "!DBURL!sqlite:///etc/kamailio/db.sqlite!g"
|
|
#!substdef "!MY_IP_ADDR!a.b.c.d!g"
|
|
#!substdef "!MY_DOMAIN!example.com!g"
|
|
#!substdef "!MY_WS_PORT!80!g"
|
|
#!substdef "!MY_WSS_PORT!443!g"
|
|
#!substdef "!MY_MSRP_PORT!9000!g"
|
|
#!substdef "!MY_WS_ADDR!tcp:MY_IP_ADDR:MY_WS_PORT!g"
|
|
#!substdef "!MY_WSS_ADDR!tls:MY_IP_ADDR:MY_WSS_PORT!g"
|
|
#!substdef "!MY_MSRP_ADDR!tls:MY_IP_ADDR:MY_MSRP_PORT!g"
|
|
#!substdef "!MSRP_MIN_EXPIRES!1800!g"
|
|
#!substdef "!MSRP_MAX_EXPIRES!3600!g"
|
|
|
|
##!define LOCAL_TEST_RUN
|
|
#!define WITH_TLS
|
|
#!define WITH_WEBSOCKETS
|
|
#!define WITH_MSRP
|
|
|
|
|
|
####### Global Parameters #########
|
|
|
|
fork=yes
|
|
children=4
|
|
|
|
#!ifdef WITH_TLS
|
|
enable_tls=1
|
|
#!endif
|
|
|
|
listen=MY_IP_ADDR
|
|
#!ifdef WITH_WEBSOCKETS
|
|
listen=MY_WS_ADDR
|
|
#!ifdef WITH_TLS
|
|
listen=MY_WSS_ADDR
|
|
#!endif
|
|
#!endif
|
|
#!ifdef WITH_MSRP
|
|
listen=MY_MSRP_ADDR
|
|
#!endif
|
|
|
|
tcp_connection_lifetime=3604
|
|
tcp_accept_no_cl=yes
|
|
tcp_rd_buf_size=16384
|
|
|
|
#!ifdef LOCAL_TEST_RUN
|
|
debug=2
|
|
mpath="modules"
|
|
#!else
|
|
debug=0
|
|
mpath="/usr/lib64/kamailio/modules/"
|
|
#!endif
|
|
|
|
loadmodule "db_sqlite.so"
|
|
loadmodule "tm.so"
|
|
loadmodule "sl.so"
|
|
loadmodule "rr.so"
|
|
loadmodule "pv.so"
|
|
loadmodule "maxfwd.so"
|
|
loadmodule "usrloc.so"
|
|
loadmodule "registrar.so"
|
|
loadmodule "textops.so"
|
|
loadmodule "siputils.so"
|
|
loadmodule "xlog.so"
|
|
loadmodule "sanity.so"
|
|
loadmodule "ctl.so"
|
|
loadmodule "auth.so"
|
|
loadmodule "auth_db.so"
|
|
loadmodule "kex.so"
|
|
loadmodule "mi_rpc.so"
|
|
loadmodule "corex.so"
|
|
#!ifdef WITH_TLS
|
|
loadmodule "tls.so"
|
|
#!endif
|
|
#!ifdef WITH_MSRP
|
|
loadmodule "msrp.so"
|
|
loadmodule "htable.so"
|
|
loadmodule "cfgutils.so"
|
|
#!endif
|
|
#!ifdef WITH_WEBSOCKETS
|
|
loadmodule "xhttp.so"
|
|
loadmodule "websocket.so"
|
|
loadmodule "nathelper.so"
|
|
#!endif
|
|
|
|
# ----------------- setting module-specific parameters ---------------
|
|
|
|
# ----- tm params -----
|
|
# auto-discard branches from previous serial forking leg
|
|
modparam("tm", "failure_reply_mode", 3)
|
|
# default retransmission timeout: 30sec
|
|
modparam("tm", "fr_timer", 30000)
|
|
# default invite retransmission timeout after 1xx: 120sec
|
|
modparam("tm", "fr_inv_timer", 120000)
|
|
|
|
# ----- rr params -----
|
|
# add value to ;lr param to cope with most of the UAs
|
|
modparam("rr", "enable_full_lr", 1)
|
|
# do not append from tag to the RR (no need for this script)
|
|
modparam("rr", "append_fromtag", 0)
|
|
|
|
# ----- registrar params -----
|
|
modparam("registrar", "method_filtering", 1)
|
|
modparam("registrar", "max_expires", 3600)
|
|
modparam("registrar", "gruu_enabled", 0)
|
|
|
|
# ----- usrloc params -----
|
|
modparam("usrloc", "db_url", "DBURL")
|
|
modparam("usrloc", "db_mode", 0)
|
|
|
|
# ----- auth params -----
|
|
modparam("auth", "nonce_count", 1)
|
|
modparam("auth", "qop", "auth")
|
|
|
|
# ----- auth_db params -----
|
|
modparam("auth_db", "db_url", "DBURL")
|
|
modparam("auth_db", "calculate_ha1", yes)
|
|
modparam("auth_db", "password_column", "password")
|
|
modparam("auth_db", "load_credentials", "id")
|
|
|
|
# ----- corex params -----
|
|
modparam("corex", "alias_subdomains", "MY_DOMAIN")
|
|
|
|
#!ifdef WITH_TLS
|
|
# ----- tls params -----
|
|
modparam("tls", "tls_method", "TLSv1.2+")
|
|
modparam("tls", "certificate", "/etc/pki/CA/ser1_cert.pem")
|
|
modparam("tls", "private_key", "/etc/pki/CA/privkey.pem")
|
|
modparam("tls", "ca_list", "/etc/pki/CA/calist.pem")
|
|
#!endif
|
|
|
|
#!ifdef WITH_WEBSOCKETS
|
|
# ----- nathelper params -----
|
|
modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
|
|
# Note: leaving NAT pings turned off here as nathelper is _only_ being used for
|
|
# WebSocket connections. NAT pings are not needed as WebSockets have
|
|
# their own keep-alives.
|
|
#!endif
|
|
|
|
#!ifdef WITH_MSRP
|
|
# ----- htable params -----
|
|
modparam("htable", "htable", "msrp=>size=8;autoexpire=MSRP_MAX_EXPIRES;")
|
|
#!endif
|
|
|
|
|
|
####### Routing Logic ########
|
|
|
|
# Main SIP request routing logic
|
|
# - processing of any incoming SIP request starts with this route
|
|
# - note: this is the same as route { ... }
|
|
request_route {
|
|
if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT)
|
|
&& !(proto == WS || proto == WSS)) || $Rp == MY_MSRP_PORT) {
|
|
xlog("L_WARN", "SIP request received on $Rp\n");
|
|
sl_send_reply("403", "Forbidden");
|
|
exit;
|
|
}
|
|
|
|
# per request initial checks
|
|
route(REQINIT);
|
|
|
|
#!ifdef WITH_WEBSOCKETS
|
|
if (nat_uac_test(64)) {
|
|
# Do NAT traversal stuff for requests from a WebSocket
|
|
# connection - even if it is not behind a NAT!
|
|
# This won't be needed in the future if Kamailio and the
|
|
# WebSocket client support Outbound and Path.
|
|
force_rport();
|
|
if (is_method("REGISTER")) {
|
|
fix_nated_register();
|
|
} else {
|
|
if (!add_contact_alias()) {
|
|
xlog("L_ERR", "Error aliasing contact <$ct>\n");
|
|
sl_send_reply("400", "Bad Request");
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
#!endif
|
|
|
|
# handle requests within SIP dialogs
|
|
route(WITHINDLG);
|
|
|
|
### only initial requests (no To tag)
|
|
|
|
# CANCEL processing
|
|
if (is_method("CANCEL")) {
|
|
if (t_check_trans()) {
|
|
t_relay();
|
|
}
|
|
exit;
|
|
}
|
|
|
|
t_check_trans();
|
|
|
|
# authentication
|
|
route(AUTH);
|
|
|
|
# record routing for dialog forming requests (in case they are routed)
|
|
# - remove preloaded route headers
|
|
remove_hf("Route");
|
|
if (is_method("INVITE")) {
|
|
record_route();
|
|
}
|
|
|
|
# handle registrations
|
|
route(REGISTRAR);
|
|
|
|
if ($rU==$null) {
|
|
# request with no Username in RURI
|
|
sl_send_reply("484", "Address Incomplete");
|
|
exit;
|
|
}
|
|
|
|
# user location service
|
|
route(LOCATION);
|
|
|
|
route(RELAY);
|
|
}
|
|
|
|
route[RELAY] {
|
|
if (!t_relay()) {
|
|
sl_reply_error();
|
|
}
|
|
exit;
|
|
}
|
|
|
|
# Per SIP request initial checks
|
|
route[REQINIT] {
|
|
if (!mf_process_maxfwd_header("10")) {
|
|
sl_send_reply("483", "Too Many Hops");
|
|
exit;
|
|
}
|
|
|
|
if (!sanity_check("1511", "7")) {
|
|
xlog("Malformed SIP message from $si:$sp\n");
|
|
exit;
|
|
}
|
|
|
|
if (uri == myself && is_method("OPTIONS") && !(uri=~"sip:.*[@]+.*")) {
|
|
options_reply();
|
|
exit;
|
|
}
|
|
}
|
|
|
|
# Handle requests within SIP dialogs
|
|
route[WITHINDLG] {
|
|
if (has_totag()) {
|
|
# sequential request within a dialog should
|
|
# take the path determined by record-routing
|
|
if (loose_route()) {
|
|
#!ifdef WITH_WEBSOCKETS
|
|
if ($du == "") {
|
|
if (!handle_ruri_alias()) {
|
|
xlog("L_ERR", "Bad alias <$ru>\n");
|
|
sl_send_reply("400", "Bad Request");
|
|
exit;
|
|
}
|
|
}
|
|
#!endif
|
|
route(RELAY);
|
|
} else {
|
|
if ( is_method("ACK") ) {
|
|
if ( t_check_trans() ) {
|
|
# no loose-route, but stateful ACK;
|
|
# must be an ACK after a 487
|
|
# or e.g. 404 from upstream server
|
|
t_relay();
|
|
exit;
|
|
} else {
|
|
# ACK without matching transaction...
|
|
# ignore and discard
|
|
exit;
|
|
}
|
|
}
|
|
sl_send_reply("404", "Not Found");
|
|
}
|
|
exit;
|
|
}
|
|
}
|
|
|
|
# Handle SIP registrations
|
|
route[REGISTRAR] {
|
|
if (is_method("REGISTER")) {
|
|
if (!save("location")) {
|
|
sl_reply_error();
|
|
}
|
|
exit;
|
|
}
|
|
}
|
|
|
|
# USER location service
|
|
route[LOCATION] {
|
|
if (!is_subscriber("$ru", "subscriber", "1")) {
|
|
t_newtran();
|
|
send_reply("404", "Not Found");
|
|
exit;
|
|
}
|
|
|
|
if (!lookup("location")) {
|
|
$var(rc) = $rc;
|
|
t_newtran();
|
|
switch ($var(rc)) {
|
|
case -1:
|
|
send_reply("480", "Temporarily Unavailable");
|
|
exit;
|
|
case -2:
|
|
send_reply("405", "Method Not Allowed");
|
|
exit;
|
|
case -3:
|
|
send_reply("500", "Server Internal Error");
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Authentication route
|
|
route[AUTH] {
|
|
if (is_method("REGISTER") || from_uri==myself) {
|
|
# authenticate requests
|
|
if (!auth_check("$fd", "subscriber", "1")) {
|
|
auth_challenge("$fd", "0");
|
|
exit;
|
|
}
|
|
# user authenticated - remove auth header
|
|
if(!is_method("REGISTER")) {
|
|
consume_credentials();
|
|
}
|
|
}
|
|
# if caller is not local subscriber, then check if it calls
|
|
# a local destination, otherwise deny, not an open relay here
|
|
if (from_uri!=myself && uri!=myself) {
|
|
sl_send_reply("403", "Forbidden");
|
|
exit;
|
|
}
|
|
}
|
|
|
|
#!ifdef WITH_WEBSOCKETS
|
|
onreply_route {
|
|
if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT)
|
|
&& !(proto == WS || proto == WSS)) || $Rp == MY_MSRP_PORT) {
|
|
xlog("L_WARN", "SIP response received on $Rp\n");
|
|
drop;
|
|
}
|
|
|
|
if (nat_uac_test(64)) {
|
|
# Do NAT traversal stuff for replies to a WebSocket connection
|
|
# - even if it is not behind a NAT!
|
|
# This won't be needed in the future if Kamailio and the
|
|
# WebSocket client support Outbound and Path.
|
|
add_contact_alias();
|
|
}
|
|
}
|
|
|
|
event_route[xhttp:request] {
|
|
set_reply_close();
|
|
set_reply_no_connect();
|
|
|
|
if ($Rp != MY_WS_PORT
|
|
#!ifdef WITH_TLS
|
|
&& $Rp != MY_WSS_PORT
|
|
#!endif
|
|
) {
|
|
xlog("L_WARN", "HTTP request received on $Rp\n");
|
|
xhttp_reply("403", "Forbidden", "", "");
|
|
exit;
|
|
}
|
|
|
|
xlog("L_DBG", "HTTP Request Received\n");
|
|
|
|
if ($hdr(Upgrade)=~"websocket"
|
|
&& $hdr(Connection)=~"Upgrade"
|
|
&& $rm=~"GET") {
|
|
|
|
# Validate Host - make sure the client is using the correct
|
|
# alias for WebSockets
|
|
if ($hdr(Host) == $null || !is_myself("sip:" + $hdr(Host))) {
|
|
xlog("L_WARN", "Bad host $hdr(Host)\n");
|
|
xhttp_reply("403", "Forbidden", "", "");
|
|
exit;
|
|
}
|
|
|
|
# Optional... validate Origin - make sure the client is from an
|
|
# authorised website. For example,
|
|
#
|
|
# if ($hdr(Origin) != "http://communicator.MY_DOMAIN"
|
|
# && $hdr(Origin) != "https://communicator.MY_DOMAIN") {
|
|
# xlog("L_WARN", "Unauthorised client $hdr(Origin)\n");
|
|
# xhttp_reply("403", "Forbidden", "", "");
|
|
# exit;
|
|
# }
|
|
|
|
# Optional... perform HTTP authentication
|
|
|
|
# ws_handle_handshake() exits (no further configuration file
|
|
# processing of the request) when complete.
|
|
if (ws_handle_handshake())
|
|
{
|
|
# Optional... cache some information about the
|
|
# successful connection
|
|
exit;
|
|
}
|
|
}
|
|
|
|
xhttp_reply("404", "Not Found", "", "");
|
|
}
|
|
|
|
event_route[websocket:closed] {
|
|
xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n");
|
|
}
|
|
#!endif
|
|
|
|
#!ifdef WITH_MSRP
|
|
event_route[msrp:frame-in] {
|
|
msrp_reply_flags("1");
|
|
|
|
if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT)
|
|
&& !(proto == WS || proto == WSS)) && $Rp != MY_MSRP_PORT) {
|
|
xlog("L_WARN", "MSRP request received on $Rp\n");
|
|
msrp_reply("403", "Action-not-allowed");
|
|
exit;
|
|
}
|
|
|
|
if (msrp_is_reply()) {
|
|
msrp_relay();
|
|
} else if($msrp(method)=="AUTH") {
|
|
if($msrp(nexthops)>0) {
|
|
msrp_relay();
|
|
exit;
|
|
}
|
|
|
|
if (!www_authenticate("MY_DOMAIN", "subscriber",
|
|
"$msrp(method)")) {
|
|
if (auth_get_www_authenticate("MY_DOMAIN", "1",
|
|
"$var(wauth)")) {
|
|
msrp_reply("401", "Unauthorized",
|
|
"$var(wauth)");
|
|
} else {
|
|
msrp_reply("500", "Server Error");
|
|
}
|
|
exit;
|
|
}
|
|
|
|
if ($hdr(Expires) != $null) {
|
|
$var(expires) = (int) $hdr(Expires);
|
|
if ($var(expires) < MSRP_MIN_EXPIRES) {
|
|
msrp_reply("423", "Interval Out-of-Bounds",
|
|
"Min-Expires: MSRP_MIN_EXPIRES\r\n");
|
|
exit;
|
|
} else if ($var(expires) > MSRP_MAX_EXPIRES) {
|
|
msrp_reply("423", "Interval Out-of-Bounds",
|
|
"Max-Expires: MSRP_MAX_EXPIRES\r\n");
|
|
exit;
|
|
}
|
|
} else {
|
|
$var(expires) = MSRP_MAX_EXPIRES;
|
|
}
|
|
|
|
$var(cnt) = $var(cnt) + 1;
|
|
pv_printf("$var(sessid)", "s.$(pp).$(var(cnt)).$(RANDOM)");
|
|
$sht(msrp=>$var(sessid)::srcaddr) = $msrp(srcaddr);
|
|
$sht(msrp=>$var(sessid)::srcsock) = $msrp(srcsock);
|
|
$shtex(msrp=>$var(sessid)) = $var(expires) + 5;
|
|
# - Use-Path: the MSRP address for server + session id
|
|
$var(hdrs) = "Use-Path: msrps://MY_IP_ADDR:MY_MSRP_PORT/"
|
|
+ $var(sessid) + ";tcp\r\n"
|
|
+ "Expires: " + $var(expires) + "\r\n";
|
|
msrp_reply("200", "OK", "$var(hdrs)");
|
|
} else if ($msrp(method)=="SEND" || $msrp(method)=="REPORT") {
|
|
if ($msrp(nexthops)>1) {
|
|
if ($msrp(method)!="REPORT") {
|
|
msrp_reply("200", "OK");
|
|
}
|
|
msrp_relay();
|
|
exit;
|
|
}
|
|
$var(sessid) = $msrp(sessid);
|
|
if ($sht(msrp=>$var(sessid)::srcaddr) == $null) {
|
|
# one more hop, but we don't have address in htable
|
|
msrp_reply("481", "Session-does-not-exist");
|
|
exit;
|
|
} else if ($msrp(method)!="REPORT") {
|
|
msrp_reply("200", "OK");
|
|
}
|
|
msrp_relay_flags("1");
|
|
msrp_set_dst("$sht(msrp=>$var(sessid)::srcaddr)",
|
|
"$sht(msrp=>$var(sessid)::srcsock)");
|
|
msrp_relay();
|
|
} else {
|
|
msrp_reply("501", "Request-method-not-understood");
|
|
}
|
|
}
|
|
#!endif
|