tcp_accept_no_cl=yes listen=TCP_WS listen=UDP_WS_SIP #!ifdef TLS_ROLE listen=TLS_WSS listen=UDP_WSS_SIP #!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;autoexpire=7200;initval=0") ######## Basic HTTP request handling ######## loadmodule "xhttp.so" ######## Websocket module ######## loadmodule "websocket.so" modparam("websocket", "keepalive_mechanism", 0) modparam("websocket", "keepalive_timeout", 30) modparam("websocket", "keepalive_processes", 1) modparam("websocket", "keepalive_interval", 1) modparam("websocket", "ping_application_data", "Kazoo encourages you to keep alive") modparam("websocket", "sub_protocols", 1) ####### Websocket Logic ######## route[HANDLE_WEBSOCKETS] { # 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. if (nat_uac_test(64)) { xlog("L_INFO", "$ci|log|this is a websocket request\n"); force_rport(); if (is_method("REGISTER")) { fix_nated_register(); } else { if (!add_contact_alias()) { xlog("L_INFO", "$ci|stop|error aliasing contact <$ct>\n"); sl_send_reply("400", "Bad Request"); exit; } } } } route[NAT_WEBSOCKETS_CORRECT] { # 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. if (nat_uac_test(64)) { xlog("L_INFO", "$ci|log|this is a websocket request\n"); add_contact_alias(); } } 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 unknown origin $hdr(Origin) from $si:$sp\n"); xhttp_reply("400", "Bad Request", "", ""); exit; } #!endif if($sht(websockets=>$si::count) > 50) { xlog("L_WARN", "websocket|log|$si:$sp is at the maximum allowable sockets per IP, rejecting request for another websocket\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } if (ws_handle_handshake()) { $var(shtinc) = $shtinc(websockets=>$si::count); xlog("L_INFO", "websocket|log|opened websocket $var(count) of 50 for $si:$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(shtdec) = $shtdec(websockets=>$si::count); if ($sht(websockets=>$si::count) < 1) { xlog("L_INFO", "websocket|log|$si:$sp closed last websocket to that IP\n"); sht_rm_name_re("websockets=>$(si{re.subst,/\\./\\\\./g})::.*"); } else { xlog("L_INFO", "websocket|log|closed websocket from $si:$sp, $var(count) remaining from that IP\n"); } }