diff --git a/restcomm/restcomm.commons/src/main/java/org/mobicents/servlet/restcomm/util/SdpUtils.java b/restcomm/restcomm.commons/src/main/java/org/mobicents/servlet/restcomm/util/SdpUtils.java index 121072427e..4d75f1fbb9 100644 --- a/restcomm/restcomm.commons/src/main/java/org/mobicents/servlet/restcomm/util/SdpUtils.java +++ b/restcomm/restcomm.commons/src/main/java/org/mobicents/servlet/restcomm/util/SdpUtils.java @@ -123,4 +123,16 @@ private static void fix(final Connection connection, final String externalIp) th } } + public static boolean isWebRTCSDP(final String contentType, final byte[] data) throws SdpParseException { + boolean isWebRTC = false; + if (contentType.equalsIgnoreCase("application/sdp")) { + String sdp = getSdp(contentType, data); + if (sdp != null && sdp.contains("RTP/SAVP") || sdp.contains("rtp/savp") + || sdp.contains("RTP/SAVPF") || sdp.contains("rtp/savpf")) { + isWebRTC = true; + } + } + return isWebRTC; + } + } diff --git a/restcomm/restcomm.telephony/src/main/java/org/mobicents/servlet/restcomm/telephony/CallManager.java b/restcomm/restcomm.telephony/src/main/java/org/mobicents/servlet/restcomm/telephony/CallManager.java index d79289e239..96b1d5d825 100644 --- a/restcomm/restcomm.telephony/src/main/java/org/mobicents/servlet/restcomm/telephony/CallManager.java +++ b/restcomm/restcomm.telephony/src/main/java/org/mobicents/servlet/restcomm/telephony/CallManager.java @@ -41,6 +41,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; +import javax.sdp.SdpParseException; import javax.servlet.ServletContext; import javax.servlet.sip.Address; import javax.servlet.sip.AuthInfo; @@ -79,6 +80,7 @@ import org.mobicents.servlet.restcomm.patterns.StopObserving; import org.mobicents.servlet.restcomm.telephony.util.B2BUAHelper; import org.mobicents.servlet.restcomm.telephony.util.CallControlHelper; +import org.mobicents.servlet.restcomm.util.SdpUtils; import org.mobicents.servlet.restcomm.util.UriUtils; import scala.concurrent.Await; @@ -470,9 +472,19 @@ private boolean isWebRTC(final SipServletRequest request) { if (userAgent != null && !userAgent.isEmpty() && userAgent.equalsIgnoreCase("wss-sipunit")) { return true; } - if (!request.getInitialTransport().equalsIgnoreCase(transport)) + if (!request.getInitialTransport().equalsIgnoreCase(transport)) { transport = request.getInitialTransport(); - return "ws".equalsIgnoreCase(transport) || "wss".equalsIgnoreCase(transport); + if ("ws".equalsIgnoreCase(transport) || "wss".equalsIgnoreCase(transport)) + return true; + } + try { + if (SdpUtils.isWebRTCSDP(request.getContentType(), request.getRawContent())) { + return true; + } + } catch (SdpParseException e) {} + catch (IOException e) {} + + return false; } private void proxyThroughMediaServer(final SipServletRequest request, final Client client, final String destNumber) { diff --git a/restcomm/restcomm.testsuite/src/test/java/org/mobicents/servlet/restcomm/telephony/ClientsDialTest.java b/restcomm/restcomm.testsuite/src/test/java/org/mobicents/servlet/restcomm/telephony/ClientsDialTest.java index b984081286..590658d6a2 100644 --- a/restcomm/restcomm.testsuite/src/test/java/org/mobicents/servlet/restcomm/telephony/ClientsDialTest.java +++ b/restcomm/restcomm.testsuite/src/test/java/org/mobicents/servlet/restcomm/telephony/ClientsDialTest.java @@ -67,6 +67,105 @@ public class ClientsDialTest { 86, 80, 32, 48, 13, 10, 97, 61, 114, 116, 112, 109, 97, 112, 58, 48, 32, 80, 67, 77, 85, 47, 56, 48, 48, 48, 13, 10 }; private static final String body = new String(bytes); + private static final String webRtcBody = "v=0\n" + + "o=- 655836341935372758 2 IN IP4 24.217.142.20\n" + + "s=Restcomm B2BUA\n" + + "t=0 0\n" + + "a=group:BUNDLE audio video\n" + + "a=msid-semantic:WMS ARDAMS\n" + + "m=audio 9 UDP/TLS/RTP/SAVPF 111 103 9 102 0 8 106 105 13 126\n" + + "c=IN IP4 24.217.142.20\n" + + "a=rtcp:9 IN IP4 0.0.0.0\n" + + "a=candidate:704553097 1 udp 2122260223 192.168.1.3 60475 typ host generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:2158047068 1 udp 1686052607 24.217.142.20 60475 typ srflx raddr 192.168.1.3 rport 60475 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:152961445 1 udp 41885695 50.97.253.79 55954 typ relay raddr 24.217.142.20 rport 60475 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:152961445 1 udp 41886207 50.97.253.79 49214 typ relay raddr 24.217.142.20 rport 60475 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:1201536341 1 udp 25108735 50.97.253.79 63824 typ relay raddr 24.217.142.20 rport 54517 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:1201536341 1 udp 25108223 50.97.253.79 50246 typ relay raddr 24.217.142.20 rport 33927 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=ice-ufrag:PFs2\n" + + "a=ice-pwd:MZHsJBkJ+vdWaNvb4Z7iCejt\n" + + "a=fingerprint:sha-256 56:B1:98:06:06:EA:B1:BB:31:51:28:35:C7:8C:45:11:CD:3A:0D:28:C1:10:5B:D5:99:EA:21:46:8E:ED:08:89\n" + + "a=setup:actpass\n" + + "a=mid:audio\n" + + "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\n" + + "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\n" + + "a=sendrecv\n" + + "a=rtcp-mux\n" + + "a=rtpmap:111 opus/48000/2\n" + + "a=rtcp-fb:111 transport-cc\n" + + "a=fmtp:111 minptime=10;useinbandfec=1\n" + + "a=rtpmap:103 ISAC/16000\n" + + "a=rtpmap:9 G722/8000\n" + + "a=rtpmap:102 ILBC/8000\n" + + "a=rtpmap:0 PCMU/8000\n" + + "a=rtpmap:8 PCMA/8000\n" + + "a=rtpmap:106 CN/32000\n" + + "a=rtpmap:105 CN/16000\n" + + "a=rtpmap:13 CN/8000\n" + + "a=rtpmap:126 telephone-event/8000\n" + + "a=ssrc:743432057 cname:USPxCkO2V7yoEmg/\n" + + "a=ssrc:743432057 msid:ARDAMS ARDAMSa0\n" + + "a=ssrc:743432057 mslabel:ARDAMS\n" + + "a=ssrc:743432057 label:ARDAMSa0\n" + + "m=video 9 UDP/TLS/RTP/SAVPF 100 101 116 117 121 96 97 98 99\n" + + "c=IN IP4 24.217.142.20\n" + + "a=rtcp:9 IN IP4 0.0.0.0\n" + + "a=candidate:704553097 1 udp 2122260223 192.168.1.3 40839 typ host generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:2158047068 1 udp 1686052607 24.217.142.20 40839 typ srflx raddr 192.168.1.3 rport 40839 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:152961445 1 udp 41886207 50.97.253.79 53691 typ relay raddr 24.217.142.20 rport 40839 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:152961445 1 udp 41885695 50.97.253.79 63503 typ relay raddr 24.217.142.20 rport 40839 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:1201536341 1 udp 25108735 50.97.253.79 56359 typ relay raddr 24.217.142.20 rport 33643 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=candidate:1201536341 1 udp 25108223 50.97.253.79 54107 typ relay raddr 24.217.142.20 rport 44827 generation 0 ufrag PFs2 network-id 3 network-cost 10\n" + + "a=ice-ufrag:PFs2\n" + + "a=ice-pwd:MZHsJBkJ+vdWaNvb4Z7iCejt\n" + + "a=fingerprint:sha-256 56:B1:98:06:06:EA:B1:BB:31:51:28:35:C7:8C:45:11:CD:3A:0D:28:C1:10:5B:D5:99:EA:21:46:8E:ED:08:89\n" + + "a=setup:actpass\n" + + "a=mid:video\n" + + "a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n" + + "a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\n" + + "a=extmap:4 urn:3gpp:video-orientation\n" + + "a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\n" + + "a=sendrecv\n" + + "a=rtcp-mux\n" + + "a=rtcp-rsize\n" + + "a=rtpmap:100 VP8/90000\n" + + "a=rtcp-fb:100 ccm fir\n" + + "a=rtcp-fb:100 nack\n" + + "a=rtcp-fb:100 nack pli\n" + + "a=rtcp-fb:100 goog-remb\n" + + "a=rtcp-fb:100 transport-cc\n" + + "a=rtpmap:101 VP9/90000\n" + + "a=rtcp-fb:101 ccm fir\n" + + "a=rtcp-fb:101 nack\n" + + "a=rtcp-fb:101 nack pli\n" + + "a=rtcp-fb:101 goog-remb\n" + + "a=rtcp-fb:101 transport-cc\n" + + "a=rtpmap:116 red/90000\n" + + "a=rtpmap:117 ulpfec/90000\n" + + "a=rtpmap:121 H264/90000\n" + + "a=rtcp-fb:121 ccm fir\n" + + "a=rtcp-fb:121 nack\n" + + "a=rtcp-fb:121 nack pli\n" + + "a=rtcp-fb:121 goog-remb\n" + + "a=rtcp-fb:121 transport-cc\n" + + "a=rtpmap:96 rtx/90000\n" + + "a=fmtp:96 apt=100\n" + + "a=rtpmap:97 rtx/90000\n" + + "a=fmtp:97 apt=101\n" + + "a=rtpmap:98 rtx/90000\n" + + "a=fmtp:98 apt=116\n" + + "a=rtpmap:99 rtx/90000\n" + + "a=fmtp:99 apt=121\n" + + "a=ssrc-group:FID 2457543170 3154322644\n" + + "a=ssrc:2457543170 cname:USPxCkO2V7yoEmg/\n" + + "a=ssrc:2457543170 msid:ARDAMS ARDAMSv0\n" + + "a=ssrc:2457543170 mslabel:ARDAMS\n" + + "a=ssrc:2457543170 label:ARDAMSv0\n" + + "a=ssrc:3154322644 cname:USPxCkO2V7yoEmg/\n" + + "a=ssrc:3154322644 msid:ARDAMS ARDAMSv0\n" + + "a=ssrc:3154322644 mslabel:ARDAMS\n" + + "a=ssrc:3154322644 label:ARDAMSv0"; + @ArquillianResource private Deployer deployer; @ArquillianResource @@ -558,6 +657,66 @@ public void testClientDialOutPstnSimulateWebRTCClient() throws ParseException, I // assertTrue(georgeCall.respondToDisconnect()); } + @Test //Non regression test for issue https://github.com/RestComm/Restcomm-Connect/issues/1379 - Webrtc calls from non WS clients aren't routed to PSTN #1379 + public void testClientDialOutPstnWebRTCClientwithSDP() throws ParseException, InterruptedException { + + assertNotNull(mariaRestcommClientSid); + assertNotNull(dimitriRestcommClientSid); + + SipURI uri = mariaSipStack.getAddressFactory().createSipURI(null, "127.0.0.1:5080"); + assertTrue(mariaPhone.register(uri, "maria", "1234", mariaContact, 14400, 3600)); + Thread.sleep(3000); + + Credential c = new Credential("127.0.0.1", "maria", "1234"); + mariaPhone.addUpdateCredential(c); + + final SipCall georgeCall = georgePhone.createSipCall(); + georgeCall.listenForIncomingCall(); + + + Thread.sleep(1000); + + // Maria initiates a call to Dimitri + final SipCall mariaCall = mariaPhone.createSipCall(); + mariaCall.initiateOutgoingCall(mariaContact, "sip:"+pstnNumber+"@127.0.0.1:5080", null, webRtcBody, "application", "sdp", null, null); + assertLastOperationSuccess(mariaCall); + assertTrue(mariaCall.waitForAuthorisation(3000)); + + assertTrue(mariaCall.waitOutgoingCallResponse(5 * 1000)); + int responseMaria = mariaCall.getLastReceivedResponse().getStatusCode(); + assertTrue(responseMaria == Response.TRYING || responseMaria == Response.RINGING); + + Dialog mariaDialog = null; + + if (responseMaria == Response.TRYING) { + assertTrue(mariaCall.waitOutgoingCallResponse(5 * 1000)); + assertEquals(Response.RINGING, mariaCall.getLastReceivedResponse().getStatusCode()); + mariaDialog = mariaCall.getDialog(); + assertNotNull(mariaDialog); + } + + assertTrue(mariaCall.waitOutgoingCallResponse(5 * 1000)); + assertEquals(Response.OK, mariaCall.getLastReceivedResponse().getStatusCode()); + assertTrue(mariaCall.sendInviteOkAck()); + assertTrue(georgeCall.waitForIncomingCall(5 * 1000)); + assertTrue(georgeCall.sendIncomingCallResponse(Response.RINGING, "RINGING-George", 3600)); + + SipRequest lastReceivedRequest = georgeCall.getLastReceivedRequest(); + String receivedBody = new String(lastReceivedRequest.getRawContent()); + assertTrue(georgeCall.sendIncomingCallResponse(Response.OK, "OK-George", 3600, receivedBody, "application", "sdp", null, + null)); + + // For a reason the ACK will never reach Restcomm. This is only when working with the sipUnit + // assertTrue(georgeCall.waitForAck(5 * 1000)); + + Thread.sleep(3000); + georgeCall.listenForDisconnect(); + assertTrue(mariaCall.disconnect()); + + // assertTrue(georgeCall.waitForDisconnect(5 * 1000)); + // assertTrue(georgeCall.respondToDisconnect()); + } + @Test public void testClientDialToInvalidNumber() throws ParseException, InterruptedException, InvalidArgumentException, SipException { String invalidNumber = "+123456789";