|
|
tcp_accept_no_cl=yes
|
|
|
|
|
|
listen=TCP_WS
|
|
|
#!ifdef TLS_ROLE
|
|
|
listen=TLS_WSS
|
|
|
#!endif
|
|
|
|
|
|
######## NAT Traversal module - signaling functions ########
|
|
|
#!ifndef NATHELPER_LOADED
|
|
|
#!trydef NATHELPER_LOADED
|
|
|
loadmodule "nathelper.so"
|
|
|
#!endif
|
|
|
|
|
|
######## Generic Hash Table container in shared memory ########
|
|
|
modparam("htable", "htable", "websockets=>size=16;initval=0")
|
|
|
|
|
|
######## Basic HTTP request handling ########
|
|
|
loadmodule "xhttp.so"
|
|
|
|
|
|
|
|
|
#!trydef WS_KEEPALIVE_MECHANISM 1
|
|
|
#!trydef WS_KEEPALIVE_TIMEOUT 30
|
|
|
#!trydef WS_KEEPALIVE_PROCESSES 3
|
|
|
#!trydef WS_KEEPALIVE_INTERVAL 1
|
|
|
#!trydef WS_KEEPALIVE_DATA "Kazoo encourages you to keep alive"
|
|
|
#!trydef WS_MAX_CONNECTIONS_PER_IP 50
|
|
|
#!trydef WS_MAX_CONNECTIONS_PER_PROXY 0
|
|
|
#!trydef WS_ALLOWED_PROXIES "0.0.0.0/0"
|
|
|
#!trydef WS_CONNECTIONS_FROM_PROXY_ONLY 0
|
|
|
|
|
|
######## Websocket module ########
|
|
|
loadmodule "websocket.so"
|
|
|
modparam("websocket", "keepalive_mechanism", WS_KEEPALIVE_MECHANISM)
|
|
|
modparam("websocket", "keepalive_timeout", WS_KEEPALIVE_TIMEOUT)
|
|
|
modparam("websocket", "keepalive_processes", WS_KEEPALIVE_PROCESSES)
|
|
|
modparam("websocket", "keepalive_interval", WS_KEEPALIVE_INTERVAL)
|
|
|
modparam("websocket", "ping_application_data", WS_KEEPALIVE_DATA)
|
|
|
modparam("websocket", "sub_protocols", 1)
|
|
|
|
|
|
####### Websocket Logic ########
|
|
|
|
|
|
kazoo.ws_allowed_proxies = WS_ALLOWED_PROXIES desc "comma separated list of allowed proxies in cidr notation"
|
|
|
kazoo.ws_max_connection_per_ip = WS_MAX_CONNECTIONS_PER_IP desc "max connections per ip"
|
|
|
kazoo.ws_max_connection_per_proxy = WS_MAX_CONNECTIONS_PER_PROXY desc "max connections per proxy"
|
|
|
kazoo.ws_connections_via_proxy_only = WS_CONNECTIONS_FROM_PROXY_ONLY desc "only allow connections via proxy"
|
|
|
|
|
|
event_route[xhttp:request]
|
|
|
{
|
|
|
set_reply_close();
|
|
|
set_reply_no_connect();
|
|
|
|
|
|
if (!($rm =~ "GET")) {
|
|
|
xlog("L_INFO", "websocket|log|rejecting HTTP request $rm from $si:$sp\n");
|
|
|
xhttp_reply("405", "Method Not Allowed", "", "");
|
|
|
exit;
|
|
|
}
|
|
|
|
|
|
if (!($hdr(Connection) =~ "Upgrade")) {
|
|
|
xlog("L_INFO", "websocket|log|rejecting HTTP connection $hdr(Connection) request from $si:$sp\n");
|
|
|
xhttp_reply("400", "Bad Request", "", "");
|
|
|
exit;
|
|
|
}
|
|
|
|
|
|
if (!($hdr(Upgrade) =~ "websocket")) {
|
|
|
xlog("L_INFO", "websocket|log|rejecting HTTP upgrade $hdr(Upgrade) request from $si:$sp\n");
|
|
|
xhttp_reply("400", "Bad Request", "", "");
|
|
|
exit;
|
|
|
}
|
|
|
|
|
|
if (!($hdr(Sec-WebSocket-Protocol) =~ "sip")) {
|
|
|
xlog("L_INFO", "websocket|log|rejecting request for websocket protocol $hdr(Sec-WebSocket-Protocol) from $si:$sp\n");
|
|
|
xhttp_reply("400", "Bad Request", "", "");
|
|
|
exit;
|
|
|
}
|
|
|
|
|
|
#!ifdef MY_WEBSOCKET_DOMAIN
|
|
|
if (!($hdr(Origin) =~ "MY_WEBSOCKET_DOMAIN")) {
|
|
|
xlog("L_INFO", "websocket|log|rejecting HTTP request with unauthorized origin $hdr(Origin) from $si:$sp, allowed origin is MY_WEBSOCKET_DOMAIN\n");
|
|
|
xhttp_reply("400", "Bad Request", "", "");
|
|
|
exit;
|
|
|
}
|
|
|
#!endif
|
|
|
|
|
|
if ($hdr(X-Forwarded-For) == $null) {
|
|
|
if($sel(cfg_get.kazoo.ws_connections_via_proxy_only) == 1) {
|
|
|
xlog("L_INFO", "websocket|log|request from $si without X-Forwarded-For Header and only allowed connections are via proxy\n");
|
|
|
xhttp_reply("403", "Forbidden", "", "");
|
|
|
exit;
|
|
|
} else {
|
|
|
$var(ws_orig_ip) = $si;
|
|
|
}
|
|
|
} else {
|
|
|
xlog("L_INFO", "websocket|log|request X-Forwarded-For $hdr(X-Forwarded-For) from $si\n");
|
|
|
$var(ws_orig_ip) = $hdr(X-Forwarded-For);
|
|
|
}
|
|
|
|
|
|
if($si != $var(ws_orig_ip)) {
|
|
|
if(!is_in_subnet($si, $sel(cfg_get.kazoo.ws_allowed_proxies))) {
|
|
|
xlog("L_WARNING", "websocket|log|request X-Forwarded-For $hdr(X-Forwarded-For) from invalid ip $si - allowed $sel(cfg_get.kazoo.ws_allowed_proxies)\n");
|
|
|
xhttp_reply("403", "Forbidden", "", "");
|
|
|
exit;
|
|
|
}
|
|
|
|
|
|
if($sel(cfg_get.kazoo.ws_max_connection_per_proxy) > 0 && $sht(websockets=>$si::count) > $sel(cfg_get.kazoo.ws_max_connection_per_proxy)) {
|
|
|
xlog("L_WARN", "websocket|log|$si is at the maximum $sel(cfg_get.kazoo.ws_max_connection_per_proxy) allowable sockets per PROXY IP, rejecting request for another websocket\n");
|
|
|
xhttp_reply("403", "Forbidden", "", "");
|
|
|
exit;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if($sel(cfg_get.kazoo.ws_max_connection_per_ip) > 0 && $sht(websockets=>$var(ws_orig_ip)::count) > $sel(cfg_get.kazoo.ws_max_connection_per_ip)) {
|
|
|
xlog("L_WARN", "websocket|log|$var(ws_orig_ip) is at the maximum $sel(cfg_get.kazoo.ws_max_connection_per_ip) allowable sockets per IP, rejecting request for another websocket\n");
|
|
|
xhttp_reply("403", "Forbidden", "", "");
|
|
|
exit;
|
|
|
}
|
|
|
|
|
|
if (ws_handle_handshake()) {
|
|
|
$var(count) = $shtinc(websockets=>$var(ws_orig_ip)::count);
|
|
|
$sht(websockets=>$ws_conid::ws_orig_ip) = $var(ws_orig_ip);
|
|
|
if($si != $var(ws_orig_ip)) {
|
|
|
$var(proxy_count) = $shtinc(websockets=>$si::count);
|
|
|
xlog("L_INFO", "websocket|log|opened proxied websocket $ws_conid from $si for $var(ws_orig_ip):$sp\n");
|
|
|
} else {
|
|
|
xlog("L_INFO", "websocket|log|opened websocket $ws_conid from $var(ws_orig_ip):$sp\n");
|
|
|
}
|
|
|
exit;
|
|
|
}
|
|
|
|
|
|
xlog("L_INFO", "websocket|log|unhandled HTTP request $rm from $si:$sp\n");
|
|
|
xhttp_reply("404", "Not Found", "", "");
|
|
|
}
|
|
|
|
|
|
event_route[websocket:closed]
|
|
|
{
|
|
|
$var(ws_orig_ip) = $sht(websockets=>$ws_conid::ws_orig_ip);
|
|
|
$sht(websockets=>$ws_conid::ws_orig_ip) = $null;
|
|
|
|
|
|
$var(count) = $shtdec(websockets=>$si::count);
|
|
|
if($var(ws_orig_ip) != $null && $si != $var(ws_orig_ip)) {
|
|
|
$var(countip) = $shtdec(websockets=>$var(ws_orig_ip)::count);
|
|
|
xlog("L_INFO", "websocket|log|$si closed proxied websocket $ws_conid for $var(ws_orig_ip):$sp\n");
|
|
|
if ($var(countip) < 1) $sht(websockets=>$var(ws_orig_ip)::count) = $null;
|
|
|
} else {
|
|
|
xlog("L_INFO", "websocket|log|closed websocket $ws_conid from $var(ws_orig_ip):$sp\n");
|
|
|
}
|
|
|
if ($var(count) < 1) $sht(websockets=>$si::count) = $null;
|
|
|
}
|