The following configuration file is a minimal working example of a Residential script that can handle clients connections over both UDP and Websocket transports. This example assumes that the SDP offer is present in the INVITE from the UAC and the SDP answer is in the 200 OK from the UAS.
# # OpenSIPS residential configuration script # by OpenSIPS Solutions <team@opensips-solutions.com> # # Please refer to the Core CookBook at: # http://www.opensips.org/Resources/DocsCookbooks # for a explanation of possible statements, functions and parameters. # ####### Global Parameters ######### debug=3 log_stderror=no log_facility=LOG_LOCAL0 fork=yes children=4 auto_aliases=no listen=udp:127.0.0.0:5060 # TODO: update with your local IP and port listen=ws:127.0.0.0:8080 # TODO: update with your local IP and port ####### Modules Section ######## # set module path mpath="/usr/local/lib/opensips/modules/" #### SIGNALING module loadmodule "signaling.so" #### StateLess module loadmodule "sl.so" #### Transaction Module loadmodule "tm.so" modparam("tm", "fr_timeout", 5) modparam("tm", "fr_inv_timeout", 30) modparam("tm", "restart_fr_on_each_reply", 0) modparam("tm", "onreply_avp_mode", 1) #### Record Route Module loadmodule "rr.so" modparam("rr", "append_fromtag", 0) #### MAX ForWarD module loadmodule "maxfwd.so" #### SIP MSG OPerationS module loadmodule "sipmsgops.so" #### FIFO Management Interface loadmodule "mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo") modparam("mi_fifo", "fifo_mode", 0666) #### URI module loadmodule "uri.so" modparam("uri", "use_uri_table", 0) #### USeR LOCation module loadmodule "usrloc.so" modparam("usrloc", "nat_bflag", "NAT") modparam("usrloc", "db_mode", 0) #### REGISTRAR module loadmodule "registrar.so" #### RTPengine protocol loadmodule "rtpengine.so" modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.0:60000") #### Nathelper protocol loadmodule "nathelper.so" modparam("registrar|nathelper", "received_avp", "$avp(rcv)") #### UDP protocol loadmodule "proto_udp.so" #### WebSocket protocol loadmodule "proto_ws.so" ####### Routing Logic ######## # main request routing logic route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (has_totag()) { # sequential requests within a dialog should # take the path determined by record-routing if (loose_route()) { if (is_method("INVITE")) { # even if in most of the cases is useless, do RR for # re-INVITEs alos, as some buggy clients do change route set # during the dialog. record_route(); } # route it out to whatever destination was set by loose_route() # in $du (destination URI). route(relay); } else { if ( is_method("ACK") ) { if ( t_check_trans() ) { # non 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 here"); } exit; } # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); if (!is_method("REGISTER")) { if (from_uri!=myself) { # if caller is not local, then called number must be local if (!uri==myself) { send_reply("403","Rely forbidden"); exit; } } } # preloaded route checking if (loose_route()) { xlog("L_ERR", "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]"); if (!is_method("ACK")) sl_send_reply("403","Preload Route denied"); exit; } # record routing if (!is_method("REGISTER|MESSAGE")) record_route(); if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(relay); } # requests for my domain if (is_method("PUBLISH|SUBSCRIBE")) { sl_send_reply("503", "Service Unavailable"); exit; } # check if the clients are using WebSockets if (proto == WS) setflag(SRC_WS); # consider the client is behind NAT - always fix the contact fix_nated_contact(); if (is_method("REGISTER")) { # indicate that the client supports DTLS # so we know when he is called if (isflagset(SRC_WS)) setbflag(DST_WS); fix_nated_register(); if (!save("location")) sl_reply_error(); exit; } if ($rU==NULL) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } # do lookup with method filtering if (!lookup("location","m")) { t_newtran(); t_reply("404", "Not Found"); exit; } route(relay); } route[relay] { # for INVITEs enable some additional helper routes if (is_method("INVITE")) { t_on_branch("handle_nat"); t_on_reply("handle_nat"); } else if (is_method("BYE|CANCEL")) { rtpengine_delete(); } if (!t_relay()) { send_reply("500","Internal Error"); }; exit; } branch_route[handle_nat] { if (!is_method("INVITE") || !has_body("application/sdp")) return; if (isflagset(SRC_WS) && isbflagset(DST_WS)) $var(rtpengine_flags) = "ICE=force-relay DTLS=passive"; else if (isflagset(SRC_WS) && !isbflagset(DST_WS)) $var(rtpengine_flags) = "RTP/AVP replace-session-connection replace-origin ICE=remove"; else if (!isflagset(SRC_WS) && isbflagset(DST_WS)) $var(rtpengine_flags) = "UDP/TLS/RTP/SAVPF ICE=force"; else if (!isflagset(SRC_WS) && !isbflagset(DST_WS)) $var(rtpengine_flags) = "RTP/AVP replace-session-connection replace-origin ICE=remove"; rtpengine_offer("$var(rtpengine_flags)"); } onreply_route[handle_nat] { fix_nated_contact(); if (!has_body("application/sdp")) return; if (isflagset(SRC_WS) && isbflagset(DST_WS)) $var(rtpengine_flags) = "ICE=force-relay DTLS=passive"; else if (isflagset(SRC_WS) && !isbflagset(DST_WS)) $var(rtpengine_flags) = "UDP/TLS/RTP/SAVPF ICE=force"; else if (!isflagset(SRC_WS) && isbflagset(DST_WS)) $var(rtpengine_flags) = "RTP/AVP replace-session-connection replace-origin ICE=remove"; else if (!isflagset(SRC_WS) && !isbflagset(DST_WS)) $var(rtpengine_flags) = "RTP/AVP replace-session-connection replace-origin ICE=remove"; rtpengine_answer("$var(rtpengine_flags)"); }