Hi Inaki,
- A callback (i.e. a script "route[X]" in kamailio.cfg) that is called
when a new WebSocket connection is requested by a client, by providing access to the HTTP GET request (so the admin, based on local policy can inspect the HTTP request and allow/dissalow the connection after checking the Cookie header, the Origin header, the Host header or whatever).
All new WebSocket connections are handled by an event_route[xhttp:request]. This allows any inspection of the headers you want, and you can perform HTTP digest authentication using this event_route. Once you are happy that the connection should be allowed you call the ws_handle_handshake() function to do some further checks, generate the handshake response, and upgrade the connection in Kamailio core. So, for example:
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect();
if ($Rp != 80 && $Rp != 443) { 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") { xlog("L_DBG", "WebSocket\n"); xlog("L_DBG", " Host: $hdr(Host)\n"); xlog("L_DBG", " Origin: $hdr(Origin)\n");
if ($hdr(Host) == $null || !is_myself($hdr(Host))) { xlog("L_WARN", "Bad host $hdr(Host)\n"); xhttp_reply("403", "Forbidden", "", ""); exit; }
# Optional... validate Origin # Optional... perform HTTP authentication
# ws_handle_handshake() exits (no further configuration file # processing of the request) when complete. ws_handle_handshake(); }
xhttp_reply("404", "Not found", "", ""); }
- A mechanism to associate custom attributes for each WebSocket
connection (i.e. an authorized user identifier, which has been authorized by means of the previous HTTP GET request inspection in step 1).
You can do this by creating a hash table using the htable module (http://kamailio.org/docs/modules/stable/modules_k/htable.html). This table can be filled in as part of the event_route[xhttp:request] above. If required ws_handle_handshake() could easily be updated to return success/failure instead of exiting on completion to help with this (but that change may not be needed).
The hash table should be indexed on the source address and port of the connection.
- A mechanism to retrieve, in the kamailio.cfg script, the attributes of
the WebSocket connection from which a SIP request arrives to Kamailio (i.e. useful to enforce that the From URI matches the connection user identifier retrieved in any custom way in step 1).
This information can be put in the hash table and retrieved on demand. You can always get the source address and port (which the hash table would be indexed on) of a request in the Kamailio configuration.
- A mechanism to close an existing WebSocket connection from the
kamailio.cfg script (i.e: during the WebSocket handshake we decided, somehow, that the user identifier is "alice", but at some point we receive an INVITE from that WS connection with From user "bob", so it could be an attacker and we want to close the connection).
I believe that calling the existing "set_reply_close()" function and then sending a SIP reply to the INVITE will kill the connection. Adding an API to explicitly kill the WebSocket connection immediately wouldn't be hard - but shouldn't be needed.
- A callback that is called when a previously accepted WebSocket
connection is closed (by passing as argument the terminator of the connection: the client or kamailio.cfg).
There is no support for this at the moment, but it would be relatively easy to add an event_route[] to the WebSocket module (probably just a few lines in ws_conn.c:wsconn_close_now() or ws_conn.c:wsconn_rm()). If the event_route[] makes sure appropriate pseudo-vars (containing information like source address and port of the terminated connection and so on) are provided then this can be used to clean-up the hash-table (otherwise entries will just have to time-out).
Said that, WWW people WON'T like the idea of providing a SIP username and SIP password in the JavaScript code retrieved by the user when visiting the web page, and for sure most of WWW people will prefer to validate/authenticate/authorize the WebSocket connections by inspecting the HTTP GET request (Origin, Cookie, some custom API_KEY in the request-URI of the GET method or whatever).
Yes I thought that. That is one of the reasons I decided to use the xhttp module to handle the WebSocket handshake instead of coding it all in the module.
Regards,
Peter