private static void SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) { if (sipRequest.Method == SIPMethodsEnum.INVITE) { Console.WriteLine("INVITE received from " + localSIPEndPoint.ToString()); IPEndPoint sipPhoneEndPoint = SDP.GetSDPRTPEndPoint(sipRequest.Body); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); SIPServerUserAgent uas = new SIPServerUserAgent(m_sipTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction); SIPResponse tryingResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Trying, null); uasTransaction.SendInformationalResponse(tryingResponse); if (m_xmppClient == null) { m_xmppClient = new XMPPClient(XMPP_SERVER, XMPP_SERVER_PORT, XMPP_REALM, m_xmppUsername, m_xmppPassword); m_xmppClient.Disconnected += XMPPDisconnected; m_xmppClient.IsBound += () => { XMPPPlaceCall(uas); }; ThreadPool.QueueUserWorkItem(delegate { m_xmppClient.Connect(); }); } else { XMPPPlaceCall(uas); } } else if (sipRequest.Method == SIPMethodsEnum.CANCEL) { UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE)); if (inviteTransaction != null) { Console.WriteLine("Matching CANCEL request received " + sipRequest.URI.ToString() + "."); SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction); cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else { Console.WriteLine("No matching transaction was found for CANCEL to " + sipRequest.URI.ToString() + "."); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } } else if (sipRequest.Method == SIPMethodsEnum.BYE) { Console.WriteLine("BYE request received."); if (m_activeCalls.ContainsKey(sipRequest.Header.CallId)) { SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); m_sipTransport.SendResponse(okResponse); m_activeCalls[sipRequest.Header.CallId].TerminateXMPPCall(); m_activeCalls.Remove(sipRequest.Header.CallId); } else { SIPResponse doesntExistResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(doesntExistResponse); } } }
public async Task CancelCallUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); SIPTransport aliceTransport = new SIPTransport(); aliceTransport.AddSIPChannel(new SIPUDPChannel(IPAddress.Loopback, 0)); var alice = new SIPUserAgent(aliceTransport, null, true); SIPServerUserAgent uas = null; // Auto accept but NOT answering. alice.OnIncomingCall += (ua, req) => uas = ua.AcceptCall(req); SIPTransport bobTransport = new SIPTransport(); bobTransport.AddSIPChannel(new SIPUDPChannel(IPAddress.Loopback, 0)); var bob = new SIPUserAgent(bobTransport, null, true); var callTask = bob.Call(alice.ContactURI.ToString(), null, null, CreateMediaSession()); await Task.Delay(500); Assert.True(bob.IsRinging); Assert.NotNull(uas); Assert.False(uas.IsCancelled); bob.Cancel(); await Task.Delay(500); Assert.False(alice.IsCallActive); Assert.False(bob.IsCallActive); Assert.True(uas.IsCancelled); }
/// <summary> /// Answers an incoming SIP call. /// </summary> public async Task <bool> Answer() { if (m_pendingIncomingCall == null) { StatusMessage(this, $"There was no pending call available to answer."); return(false); } else { var sipRequest = m_pendingIncomingCall.ClientTransaction.TransactionRequest; // Assume that if the INVITE request does not contain an SDP offer that it will be an // audio only call. bool hasAudio = true; bool hasVideo = false; if (sipRequest.Body != null) { SDP offerSDP = SDP.ParseSDPDescription(sipRequest.Body); hasAudio = offerSDP.Media.Any(x => x.Media == SDPMediaTypesEnum.audio && x.MediaStreamStatus != MediaStreamStatusEnum.Inactive); hasVideo = offerSDP.Media.Any(x => x.Media == SDPMediaTypesEnum.video && x.MediaStreamStatus != MediaStreamStatusEnum.Inactive); } MediaSession = CreateMediaSession(); m_userAgent.RemotePutOnHold += OnRemotePutOnHold; m_userAgent.RemoteTookOffHold += OnRemoteTookOffHold; bool result = await m_userAgent.Answer(m_pendingIncomingCall, MediaSession); m_pendingIncomingCall = null; return(result); } }
private static void XMPPPlaceCall(SIPServerUserAgent uas) { if (!uas.IsCancelled) { XMPPPhoneSession phoneSession = m_xmppClient.GetPhoneSession(); SIPToXMPPCall call = new SIPToXMPPCall(uas, phoneSession, m_sipTransport, m_ipAddress); m_activeCalls.Add(uas.CallRequest.Header.CallId, call); call.Call(uas.CallRequest.URI.User); } }
public async Task BlindTransferCancelUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); SIPTransport transport = new SIPTransport(); transport.AddSIPChannel(new MockSIPChannel(new System.Net.IPEndPoint(IPAddress.Any, 0))); SIPUserAgent userAgent = new SIPUserAgent(transport, null); string inviteReqStr = "INVITE sip:192.168.11.50:5060 SIP/2.0" + m_CRLF + "Via: SIP/2.0/UDP 192.168.11.50:60163;rport;branch=z9hG4bKPj869f70960bdd4204b1352eaf242a3691" + m_CRLF + "To: <sip:[email protected]>;tag=ZUJSXRRGXQ" + m_CRLF + "From: <sip:[email protected]>;tag=4a60ce364b774258873ff199e5e39938" + m_CRLF + "Call-ID: 17324d6df8744d978008c8997bfd208d" + m_CRLF + "CSeq: 3532 INVITE" + m_CRLF + "Contact: <sip:[email protected]:60163;ob>" + m_CRLF + "Max-Forwards: 70" + m_CRLF + "User-Agent: MicroSIP/3.19.22" + m_CRLF + "Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS" + m_CRLF + "Supported: replaces, 100rel, timer, norefersub" + m_CRLF + "Content-Length: 343" + m_CRLF + "Content-Type: application/sdp" + m_CRLF + "Session-Expires: 1800" + m_CRLF + "Min-SE: 90" + m_CRLF + "" + m_CRLF + "v=0" + m_CRLF + "o=- 3785527268 3785527269 IN IP4 192.168.11.50" + m_CRLF + "s=pjmedia" + m_CRLF + "t=0 0" + m_CRLF + "m=audio 4032 RTP/AVP 0 101" + m_CRLF + "c=IN IP4 192.168.11.50" + m_CRLF + "a=rtpmap:0 PCMU/8000" + m_CRLF + "a=rtpmap:101 telephone-event/8000" + m_CRLF + "a=fmtp:101 0-16" + m_CRLF + "a=sendrecv"; SIPEndPoint dummySipEndPoint = new SIPEndPoint(new IPEndPoint(IPAddress.Any, 0)); SIPMessageBuffer sipMessageBuffer = SIPMessageBuffer.ParseSIPMessage(inviteReqStr, dummySipEndPoint, dummySipEndPoint); SIPRequest inviteReq = SIPRequest.ParseSIPRequest(sipMessageBuffer); UASInviteTransaction uasTx = new UASInviteTransaction(transport, inviteReq, null); SIPServerUserAgent mockUas = new SIPServerUserAgent(transport, null, null, null, SIPCallDirection.In, null, null, null, uasTx); await userAgent.Answer(mockUas, CreateMediaSession()); CancellationTokenSource cts = new CancellationTokenSource(); var blindTransferTask = userAgent.BlindTransfer(SIPURI.ParseSIPURIRelaxed("127.0.0.1"), TimeSpan.FromSeconds(2), cts.Token); cts.Cancel(); Assert.False(await blindTransferTask); //await Assert.ThrowsAnyAsync<TaskCanceledException>(async () => { bool result = ; }); }
/// <summary> /// Answers an incoming SIP call. /// </summary> public async Task <bool> Answer() { if (m_pendingIncomingCall == null) { StatusMessage(this, $"There was no pending call available to answer."); return(false); } else { var sipRequest = m_pendingIncomingCall.ClientTransaction.TransactionRequest; SDP offerSDP = SDP.ParseSDPDescription(sipRequest.Body); bool hasAudio = offerSDP.Media.Any(x => x.Media == SDPMediaTypesEnum.audio && x.MediaStreamStatus != MediaStreamStatusEnum.Inactive); bool hasVideo = offerSDP.Media.Any(x => x.Media == SDPMediaTypesEnum.video && x.MediaStreamStatus != MediaStreamStatusEnum.Inactive); AudioOptions audioOpts = new AudioOptions { AudioSource = AudioSourcesEnum.None }; if (hasAudio) { audioOpts = new AudioOptions { AudioSource = AudioSourcesEnum.CaptureDevice, OutputDeviceIndex = m_audioOutDeviceIndex, AudioCodecs = new List <SDPMediaFormatsEnum> { SDPMediaFormatsEnum.PCMU, SDPMediaFormatsEnum.PCMA } }; } VideoOptions videoOpts = new VideoOptions { VideoSource = VideoSourcesEnum.None }; if (hasVideo) { videoOpts = new VideoOptions { VideoSource = VideoSourcesEnum.TestPattern, SourceFile = RtpAVSession.VIDEO_TESTPATTERN, SourceFramesPerSecond = VIDEO_LIVE_FRAMES_PER_SECOND }; } MediaSession = new RtpAVSession(audioOpts, videoOpts); m_userAgent.RemotePutOnHold += OnRemotePutOnHold; m_userAgent.RemoteTookOffHold += OnRemoteTookOffHold; bool result = await m_userAgent.Answer(m_pendingIncomingCall, MediaSession); m_pendingIncomingCall = null; return(result); } }
public SIPToXMPPCall(SIPServerUserAgent uas, XMPPPhoneSession xmppCall, SIPTransport sipTransport, IPAddress ipAddress) { m_uas = uas; m_xmppCall = xmppCall; m_sipTransport = sipTransport; m_ipAddress = ipAddress; m_uas.CallCancelled += SIPCallCancelled; m_xmppCall.Accepted += Answered; m_xmppCall.Rejected += CallFailed; m_xmppCall.Hungup += Hangup; }
/// <summary> /// Cleans up after a SIP call has completely finished. /// </summary> private void CallFinished() { if (_mediaManager != null) { _mediaManager.EndCall(); _mediaManager = null; } m_uac = null; m_uas = null; CallEnded(); }
private DialPlanLineContext GetDummyDialPlanContext(string testDialPlan, string dst) { SIPDialPlan dialPlan = new SIPDialPlan(null, null, null, testDialPlan, SIPDialPlanScriptTypesEnum.Asterisk); SIPTransactionEngine transactionEngine = new SIPTransactionEngine(); SIPTransport sipTransport = new SIPTransport(MockSIPDNSManager.Resolve, transactionEngine); SIPURI dummyURI = SIPURI.ParseSIPURI(dst); SIPRequest inviteRequest = GetDummyINVITERequest(dummyURI); SIPEndPoint dummyEndPoint = SIPEndPoint.ParseSIPEndPoint("udp:0.0.0.0:5060"); UASInviteTransaction uasTransaction = sipTransport.CreateUASTransaction(inviteRequest, dummyEndPoint, dummyEndPoint, null); SIPServerUserAgent uas = new SIPServerUserAgent(sipTransport, null, "test", "sipsorcery.com", SIPCallDirection.In, null, null, null, uasTransaction); DialPlanLineContext dialPlanContext = new DialPlanLineContext(null, null, null, null, uas, dialPlan, null, null, null, null); return(dialPlanContext); }
/// <summary> /// Cleans up after a SIP call has completely finished. /// </summary> private void CallFinished() { if (_mediaManager != null) { _mediaManager.EndCall(); _mediaManager = null; } //_cancelCallTokenSource.Cancel(); m_uac = null; m_uas = null; CallEnded(); }
/// <summary> /// Answers an incoming SIP call. /// </summary> public async Task Answer() { if (m_pendingIncomingCall == null) { StatusMessage(this, $"There was no pending call available to answer."); } else { var sipRequest = m_pendingIncomingCall.ClientTransaction.TransactionRequest; SDP offerSDP = SDP.ParseSDPDescription(sipRequest.Body); bool hasAudio = offerSDP.Media.Any(x => x.Media == SDPMediaTypesEnum.audio); bool hasVideo = offerSDP.Media.Any(x => x.Media == SDPMediaTypesEnum.video); AudioOptions audioOpts = new AudioOptions { AudioSource = AudioSourcesEnum.None }; if (hasAudio) { audioOpts = new AudioOptions { AudioSource = AudioSourcesEnum.Microphone }; } VideoOptions videoOpts = new VideoOptions { VideoSource = VideoSourcesEnum.None }; if (hasVideo) { videoOpts = new VideoOptions { VideoSource = VideoSourcesEnum.TestPattern, SourceFile = RtpAVSession.VIDEO_TESTPATTERN, SourceFramesPerSecond = VIDEO_LIVE_FRAMES_PER_SECOND }; } MediaSession = new RtpAVSession(sipRequest.RemoteSIPEndPoint.Address.AddressFamily, audioOpts, videoOpts); m_userAgent.RemotePutOnHold += OnRemotePutOnHold; m_userAgent.RemoteTookOffHold += OnRemoteTookOffHold; await m_userAgent.Answer(m_pendingIncomingCall, MediaSession); m_pendingIncomingCall = null; } }
/// <summary> /// Hangs up the current call. /// </summary> /// <param name="uas">The user agent server to hangup the call on.</param> private static async Task Hangup(SIPServerUserAgent uas) { try { if (uas?.IsHungup == false) { uas?.Hangup(false); // Give the BYE or CANCEL request time to be transmitted. SIPSorcery.Sys.Log.Logger.LogInformation("Waiting 1s for call to hangup..."); await Task.Delay(1000); } } catch (Exception excp) { SIPSorcery.Sys.Log.Logger.LogError($"Exception Hangup. {excp.Message}"); } }
/// <summary> /// Answers an incoming SIP call. /// </summary> public async Task Answer() { if (m_pendingIncomingCall == null) { StatusMessage(this, $"There was no pending call available to answer."); } else { var sipRequest = m_pendingIncomingCall.ClientTransaction.TransactionRequest; m_rtpMediaSessionManager.Create(sipRequest.RemoteSIPEndPoint.Address.AddressFamily); m_rtpMediaSessionManager.RTPMediaSession.RemotePutOnHold += OnRemotePutOnHold; m_rtpMediaSessionManager.RTPMediaSession.RemoteTookOffHold += OnRemoteTookOffHold; await m_userAgent.Answer(m_pendingIncomingCall, m_rtpMediaSessionManager.RTPMediaSession); m_pendingIncomingCall = null; } }
/// <param name="rtpListenAddress">The local IP address to establish the RTP listener socket on.</param> /// <param name="sdpAdvertiseAddress">The public IP address to put into the SDP sent back to the caller.</param> /// <param name="request">The INVITE request that instigated the RTP diagnostics job.</param> public RTPDiagnosticsJob(IPAddress rtpListenAddress, IPAddress sdpAdvertiseAddress, SIPServerUserAgent uas, SIPRequest request) { m_request = request; m_remoteSDP = SDP.ParseSDPDescription(request.Body); RemoteRTPEndPoint = new IPEndPoint(IPAddress.Parse(m_remoteSDP.Connection.ConnectionAddress), m_remoteSDP.Media[0].Port); UAS = uas; //m_rawSourceStream = new RawSourceWaveStream(m_outStream, WaveFormat.CreateMuLawFormat(8000, 1)); //m_waveFileWriter = new WaveFileWriter("out.wav", new WaveFormat(8000, 16, 1)); m_waveFileWriter = new WaveFileWriter("out.wav", new WaveFormat(8000, 16, 1)); //m_outPCMStream = WaveFormatConversionStream.CreatePcmStream(m_rawSourceStream); //m_rawRTPPayloadWriter = new StreamWriter("out.rtp"); //m_rawRTPPayloadReader = new StreamReader("in.rtp"); //IPEndPoint rtpListenEndPoint = null; IPEndPoint rtpListenEndPoint = null; NetServices.CreateRandomUDPListener(rtpListenAddress, RTP_PORTRANGE_START, RTP_PORTRANGE_END, m_inUsePorts, out rtpListenEndPoint); RTPListenEndPoint = rtpListenEndPoint; m_inUsePorts.Add(rtpListenEndPoint.Port); //RTPListenEndPoint = new IPEndPoint(rtpListenAddress, RTP_PORTRANGE_START); m_rtpChannel = new RTPChannel(RTPListenEndPoint); m_rtpChannel.SampleReceived += SampleReceived; ThreadPool.QueueUserWorkItem(delegate { GetAudioSamples(); }); LocalSDP = new SDP() { SessionId = Crypto.GetRandomString(6), Address = sdpAdvertiseAddress.ToString(), SessionName = "sipsorcery", Timing = "0 0", Connection = new SDPConnectionInformation(sdpAdvertiseAddress.ToString()), Media = new List <SDPMediaAnnouncement>() { new SDPMediaAnnouncement() { Media = SDPMediaTypesEnum.audio, Port = RTPListenEndPoint.Port, MediaFormats = new List <SDPMediaFormat>() { new SDPMediaFormat((int)SDPMediaFormatsEnum.PCMU) } } } }; }
static void Main(string[] args) { Console.WriteLine("SIPSorcery user agent server example."); Console.WriteLine("Press h to hangup a call or ctrl-c to exit."); EnableConsoleLogger(); IPAddress listenAddress = IPAddress.Any; IPAddress listenIPv6Address = IPAddress.IPv6Any; if (args != null && args.Length > 0) { if (!IPAddress.TryParse(args[0], out var customListenAddress)) { Log.LogWarning($"Command line argument could not be parsed as an IP address \"{args[0]}\""); listenAddress = IPAddress.Any; } else { if (customListenAddress.AddressFamily == AddressFamily.InterNetwork) { listenAddress = customListenAddress; } if (customListenAddress.AddressFamily == AddressFamily.InterNetworkV6) { listenIPv6Address = customListenAddress; } } } // Set up a default SIP transport. var sipTransport = new SIPTransport(); var localhostCertificate = new X509Certificate2("localhost.pfx"); // IPv4 channels. sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(listenAddress, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(listenAddress, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTLSChannel(localhostCertificate, new IPEndPoint(listenAddress, SIPS_LISTEN_PORT))); //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.Any, SIP_WEBSOCKET_LISTEN_PORT)); //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.Any, SIP_SECURE_WEBSOCKET_LISTEN_PORT, localhostCertificate)); // IPv6 channels. sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(listenIPv6Address, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(listenIPv6Address, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTLSChannel(localhostCertificate, new IPEndPoint(listenIPv6Address, SIPS_LISTEN_PORT))); //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.IPv6Any, SIP_WEBSOCKET_LISTEN_PORT)); //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.IPv6Any, SIP_SECURE_WEBSOCKET_LISTEN_PORT, localhostCertificate)); EnableTraceLogs(sipTransport); string executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); // To keep things a bit simpler this example only supports a single call at a time and the SIP server user agent // acts as a singleton SIPServerUserAgent uas = null; CancellationTokenSource rtpCts = null; // Cancellation token to stop the RTP stream. RtpAVSession rtpSession = null; // Because this is a server user agent the SIP transport must start listening for client user agents. sipTransport.SIPTransportRequestReceived += async(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => { try { if (sipRequest.Method == SIPMethodsEnum.INVITE) { SIPSorcery.Sys.Log.Logger.LogInformation($"Incoming call request: {localSIPEndPoint}<-{remoteEndPoint} {sipRequest.URI}."); // Check there's a codec we support in the INVITE offer. var offerSdp = SDP.ParseSDPDescription(sipRequest.Body); IPEndPoint dstRtpEndPoint = SDP.GetSDPRTPEndPoint(sipRequest.Body); if (offerSdp.Media.Any(x => x.Media == SDPMediaTypesEnum.audio && x.HasMediaFormat((int)SDPMediaFormatsEnum.PCMU))) { Log.LogDebug($"Client offer contained PCMU audio codec."); rtpSession = new RtpAVSession( new AudioOptions { AudioSource = AudioSourcesEnum.Music, SourceFile = executableDir + "/" + AUDIO_FILE_PCMU }, null); rtpSession.setRemoteDescription(new RTCSessionDescription { type = RTCSdpType.offer, sdp = offerSdp }); } if (rtpSession == null) { // Didn't get a match on the codecs we support. SIPResponse noMatchingCodecResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotAcceptableHere, null); await sipTransport.SendResponseAsync(noMatchingCodecResponse); } else { // If there's already a call in progress hang it up. Of course this is not ideal for a real softphone or server but it // means this example can be kept simpler. if (uas?.IsHungup == false) { uas?.Hangup(false); } rtpCts?.Cancel(); rtpCts = new CancellationTokenSource(); UASInviteTransaction uasTransaction = new UASInviteTransaction(sipTransport, sipRequest, null); uas = new SIPServerUserAgent(sipTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction); uas.CallCancelled += (uasAgent) => { rtpCts?.Cancel(); rtpSession.CloseSession(null); }; rtpSession.OnRtpClosed += (reason) => uas?.Hangup(false); uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); var answerSdp = await rtpSession.createAnswer(null); uas.Answer(SDP.SDP_MIME_CONTENTTYPE, answerSdp.ToString(), null, SIPDialogueTransferModesEnum.NotAllowed); await rtpSession.Start(); } } else if (sipRequest.Method == SIPMethodsEnum.BYE) { SIPSorcery.Sys.Log.Logger.LogInformation("Call hungup."); SIPResponse byeResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); await sipTransport.SendResponseAsync(byeResponse); uas?.Hangup(true); rtpSession?.CloseSession(null); rtpCts?.Cancel(); } else if (sipRequest.Method == SIPMethodsEnum.SUBSCRIBE) { SIPResponse notAllowededResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); await sipTransport.SendResponseAsync(notAllowededResponse); } else if (sipRequest.Method == SIPMethodsEnum.OPTIONS || sipRequest.Method == SIPMethodsEnum.REGISTER) { SIPResponse optionsResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); await sipTransport.SendResponseAsync(optionsResponse); } } catch (Exception reqExcp) { SIPSorcery.Sys.Log.Logger.LogWarning($"Exception handling {sipRequest.Method}. {reqExcp.Message}"); } }; ManualResetEvent exitMre = new ManualResetEvent(false); Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; SIPSorcery.Sys.Log.Logger.LogInformation("Exiting..."); Hangup(uas).Wait(); rtpSession?.CloseSession(null); rtpCts?.Cancel(); if (sipTransport != null) { SIPSorcery.Sys.Log.Logger.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } exitMre.Set(); }; // Task to handle user key presses. Task.Run(() => { try { while (!exitMre.WaitOne(0)) { var keyProps = Console.ReadKey(); if (keyProps.KeyChar == 'h' || keyProps.KeyChar == 'q') { Console.WriteLine(); Console.WriteLine("Hangup requested by user..."); Hangup(uas).Wait(); rtpSession?.CloseSession(null); rtpCts?.Cancel(); } if (keyProps.KeyChar == 'q') { SIPSorcery.Sys.Log.Logger.LogInformation("Quitting..."); if (sipTransport != null) { SIPSorcery.Sys.Log.Logger.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } exitMre.Set(); } } } catch (Exception excp) { SIPSorcery.Sys.Log.Logger.LogError($"Exception Key Press listener. {excp.Message}."); } }); exitMre.WaitOne(); }
public async Task HandleInvalidSdpPortOnPlaceCallUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); // This transport will act as the call receiver. It allows the test to provide a // tailored response to an incoming call. SIPTransport calleeTransport = new SIPTransport(); // This transport will be used by the SIPUserAgent being tested to place the call. SIPTransport callerTransport = new SIPTransport(); RTPSession rtpSession = new RTPSession(false, false, false); try { calleeTransport.AddSIPChannel(new SIPUDPChannel(IPAddress.Loopback, 0)); calleeTransport.SIPTransportRequestReceived += async(lep, rep, req) => { if (req.Method != SIPMethodsEnum.INVITE) { SIPResponse notAllowedResponse = SIPResponse.GetResponse(req, SIPResponseStatusCodesEnum.MethodNotAllowed, null); await calleeTransport.SendResponseAsync(notAllowedResponse); } else { UASInviteTransaction uasTransaction = new UASInviteTransaction(calleeTransport, req, null); var uas = new SIPServerUserAgent(calleeTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction); uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); var answerSdp = @" v=0 o=- 1838015445 0 IN IP4 127.0.0.1 s=- c=IN IP4 127.0.0.1 t=0 0 m=audio 79762 RTP/AVP 0 a=rtpmap:0 PCMU/8000 a=sendrecv"; uas.Answer(SDP.SDP_MIME_CONTENTTYPE, answerSdp, null, SIPDialogueTransferModesEnum.NotAllowed); } }; SIPUserAgent userAgent = new SIPUserAgent(callerTransport, null); MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); rtpSession.addTrack(audioTrack); SIPURI dstUri = new SIPURI(SIPSchemesEnum.sip, calleeTransport.GetSIPChannels().First().ListeningSIPEndPoint); var result = await userAgent.Call(dstUri.ToString(), null, null, rtpSession); Assert.False(result); } finally { rtpSession?.Close("normal"); callerTransport?.Shutdown(); calleeTransport?.Shutdown(); } }
public async Task HangupUserAgentUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); SIPTransport transport = new SIPTransport(false, MockSIPDNSManager.Resolve); MockSIPChannel mockChannel = new MockSIPChannel(new System.Net.IPEndPoint(IPAddress.Any, 0)); transport.AddSIPChannel(mockChannel); SIPUserAgent userAgent = new SIPUserAgent(transport, null); string inviteReqStr = "INVITE sip:192.168.11.50:5060 SIP/2.0" + m_CRLF + "Via: SIP/2.0/UDP 192.168.11.50:60163;rport;branch=z9hG4bKPj869f70960bdd4204b1352eaf242a3691" + m_CRLF + "To: <sip:[email protected]>;tag=ZUJSXRRGXQ" + m_CRLF + "From: <sip:[email protected]>;tag=4a60ce364b774258873ff199e5e39938" + m_CRLF + "Call-ID: 17324d6df8744d978008c8997bfd208d" + m_CRLF + "CSeq: 3532 INVITE" + m_CRLF + "Contact: <sip:[email protected]:60163;ob>" + m_CRLF + "Max-Forwards: 70" + m_CRLF + "User-Agent: MicroSIP/3.19.22" + m_CRLF + "Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS" + m_CRLF + "Supported: replaces, 100rel, timer, norefersub" + m_CRLF + "Content-Length: 343" + m_CRLF + "Content-Type: application/sdp" + m_CRLF + "Session-Expires: 1800" + m_CRLF + "Min-SE: 90" + m_CRLF + "" + m_CRLF + "v=0" + m_CRLF + "o=- 3785527268 3785527269 IN IP4 192.168.11.50" + m_CRLF + "s=pjmedia" + m_CRLF + "t=0 0" + m_CRLF + "m=audio 4032 RTP/AVP 0 101" + m_CRLF + "c=IN IP4 192.168.11.50" + m_CRLF + "a=rtpmap:0 PCMU/8000" + m_CRLF + "a=rtpmap:101 telephone-event/8000" + m_CRLF + "a=fmtp:101 0-16" + m_CRLF + "a=sendrecv"; SIPEndPoint dummySipEndPoint = new SIPEndPoint(new IPEndPoint(IPAddress.Loopback, 0)); SIPMessageBuffer sipMessageBuffer = SIPMessageBuffer.ParseSIPMessage(inviteReqStr, dummySipEndPoint, dummySipEndPoint); SIPRequest inviteReq = SIPRequest.ParseSIPRequest(sipMessageBuffer); UASInviteTransaction uasTx = new UASInviteTransaction(transport, inviteReq, null); SIPServerUserAgent mockUas = new SIPServerUserAgent(transport, null, null, null, SIPCallDirection.In, null, null, null, uasTx); await userAgent.Answer(mockUas, CreateMediaSession()); // Incremented Cseq and modified Via header from original request. Means the request is the same dialog but different tx. string inviteReqStr2 = "BYE sip:192.168.11.50:5060 SIP/2.0" + m_CRLF + "Via: SIP/2.0/UDP 192.168.11.50:60163;rport;branch=z9hG4bKPj869f70960bdd4204b1352eaf242a3700" + m_CRLF + "To: <sip:[email protected]>;tag=ZUJSXRRGXQ" + m_CRLF + "From: <sip:[email protected]>;tag=4a60ce364b774258873ff199e5e39938" + m_CRLF + "Call-ID: 17324d6df8744d978008c8997bfd208d" + m_CRLF + "CSeq: 3533 BYE" + m_CRLF + "Contact: <sip:[email protected]:60163;ob>" + m_CRLF + "Max-Forwards: 70" + m_CRLF + "User-Agent: MicroSIP/3.19.22" + m_CRLF + "Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS" + m_CRLF + "Supported: replaces, 100rel, timer, norefersub" + m_CRLF + ""; mockChannel.FireMessageReceived(dummySipEndPoint, dummySipEndPoint, Encoding.UTF8.GetBytes(inviteReqStr2)); }
static void Main() { Console.WriteLine("SIPSorcery client user agent server example."); Console.WriteLine("Press ctrl-c to exit."); // Logging configuration. Can be ommitted if internal SIPSorcery debug and warning messages are not required. var loggerFactory = new Microsoft.Extensions.Logging.LoggerFactory(); var loggerConfig = new LoggerConfiguration() .Enrich.FromLogContext() .MinimumLevel.Is(Serilog.Events.LogEventLevel.Debug) .WriteTo.Console() .CreateLogger(); loggerFactory.AddSerilog(loggerConfig); SIPSorcery.Sys.Log.LoggerFactory = loggerFactory; // Set up a default SIP transport. var sipTransport = new SIPTransport(); sipTransport.ContactHost = Dns.GetHostName(); sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, SIP_LISTEN_PORT))); //sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.IPv6Any, SIP_LISTEN_PORT))); //sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(IPAddress.Any, SIP_LISTEN_PORT))); //sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(IPAddress.IPv6Any, SIP_LISTEN_PORT))); //if (File.Exists("localhost.pfx")) //{ // var certificate = new X509Certificate2(@"localhost.pfx", ""); // sipTransport.AddSIPChannel(new SIPTLSChannel(certificate, new IPEndPoint(IPAddress.Any, SIPS_LISTEN_PORT))); // sipTransport.AddSIPChannel(new SIPTLSChannel(certificate, new IPEndPoint(IPAddress.IPv6Any, SIPS_LISTEN_PORT))); //} //EnableTraceLogs(sipTransport); // To keep things a bit simpler this example only supports a single call at a time and the SIP server user agent // acts as a singleton SIPServerUserAgent uas = null; CancellationTokenSource rtpCts = null; // Cancellation token to stop the RTP stream. // Because this is a server user agent the SIP transport must start listening for client user agents. sipTransport.SIPTransportRequestReceived += async(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => { if (sipRequest.Method == SIPMethodsEnum.INVITE) { SIPSorcery.Sys.Log.Logger.LogInformation("Incoming call request: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + "."); //SIPSorcery.Sys.Log.Logger.LogDebug(sipRequest.ToString()); // If there's already a call in progress hang it up. Of course this is not ideal for a real softphone or server but it // means this example can be kept simpler. if (uas?.IsHungup == false) { uas?.Hangup(false); } rtpCts?.Cancel(); UASInviteTransaction uasTransaction = sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); uas = new SIPServerUserAgent(sipTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction); rtpCts = new CancellationTokenSource(); // In practice there could be a number of reasons to reject the call. Unsupported extensions, mo matching codecs etc. etc. if (sipRequest.Header.HasUnknownRequireExtension) { // The caller requires an extension that we don't support. SIPSorcery.Sys.Log.Logger.LogWarning($"Rejecting incoming call due to one or more required exensions not being supported, required extensions: {sipRequest.Header.Require}."); uas.Reject(SIPResponseStatusCodesEnum.NotImplemented, "Unsupported Require Extension", null); } else { uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); // Simulating answer delay to test provisional response retransmits. await Task.Delay(2000); // Initialise an RTP session to receive the RTP packets from the remote SIP server. Socket rtpSocket = null; Socket controlSocket = null; NetServices.CreateRtpSocket(localSIPEndPoint.Address, 49000, 49100, false, out rtpSocket, out controlSocket); IPEndPoint rtpEndPoint = rtpSocket.LocalEndPoint as IPEndPoint; IPEndPoint dstRtpEndPoint = SDP.GetSDPRTPEndPoint(sipRequest.Body); var rtpSession = new RTPSession((int)RTPPayloadTypesEnum.PCMU, null, null); var rtpTask = Task.Run(() => SendRecvRtp(rtpSocket, rtpSession, dstRtpEndPoint, AUDIO_FILE, rtpCts)) .ContinueWith(_ => { if (uas?.IsHungup == false) { uas?.Hangup(false); } }); uas.Answer(SDP.SDP_MIME_CONTENTTYPE, GetSDP(rtpEndPoint).ToString(), null, SIPDialogueTransferModesEnum.NotAllowed); } } else if (sipRequest.Method == SIPMethodsEnum.BYE) { SIPSorcery.Sys.Log.Logger.LogInformation("Call hungup."); SIPNonInviteTransaction byeTransaction = sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); byeTransaction.SendFinalResponse(byeResponse); uas?.Hangup(true); rtpCts?.Cancel(); } else if (sipRequest.Method == SIPMethodsEnum.OPTIONS) { try { SIPSorcery.Sys.Log.Logger.LogInformation($"{localSIPEndPoint.ToString()}<-{remoteEndPoint.ToString()}: {sipRequest.StatusLine}"); //SIPSorcery.Sys.Log.Logger.LogDebug(sipRequest.ToString()); SIPResponse optionsResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); sipTransport.SendResponse(optionsResponse); } catch (Exception optionsExcp) { SIPSorcery.Sys.Log.Logger.LogWarning($"Failed to send SIP OPTIONS response. {optionsExcp.Message}"); } } }; Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; SIPSorcery.Sys.Log.Logger.LogInformation("Exiting..."); rtpCts?.Cancel(); if (uas?.IsHungup == false) { uas?.Hangup(false); // Give the BYE or CANCEL request time to be transmitted. SIPSorcery.Sys.Log.Logger.LogInformation("Waiting 1s for call to hangup..."); Task.Delay(1000).Wait(); } if (sipTransport != null) { SIPSorcery.Sys.Log.Logger.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } }; }
/// <summary> /// Cleans up after a SIP call has completely finished. /// </summary> private void CallFinished(SIPDialogue dialogue) { m_pendingIncomingCall = null; CallEnded(this); }
public void GotRequest(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) { try { // Used in the proxy monitor messages only, plays no part in request routing. string fromUser = (sipRequest.Header.From != null) ? sipRequest.Header.From.FromURI.User : null; string fromURIStr = (sipRequest.Header.From != null) ? sipRequest.Header.From.FromURI.ToString() : "null"; //string toUser = (sipRequest.Header.To != null) ? sipRequest.Header.To.ToURI.User : null; //string summaryStr = "req " + sipRequest.Method + " from=" + fromUser + ", to=" + toUser + ", " + remoteEndPoint.ToString(); //logger.Debug("AppServerCore GotRequest " + sipRequest.Method + " from " + remoteEndPoint.ToString() + " callid=" + sipRequest.Header.CallId + "."); SIPDialogue dialogue = null; // Check dialogue requests for an existing dialogue. if ((sipRequest.Method == SIPMethodsEnum.BYE || sipRequest.Method == SIPMethodsEnum.INFO || sipRequest.Method == SIPMethodsEnum.INVITE || sipRequest.Method == SIPMethodsEnum.MESSAGE || sipRequest.Method == SIPMethodsEnum.NOTIFY || sipRequest.Method == SIPMethodsEnum.OPTIONS || sipRequest.Method == SIPMethodsEnum.REFER) && sipRequest.Header.From != null && sipRequest.Header.From.FromTag != null && sipRequest.Header.To != null && sipRequest.Header.To.ToTag != null) { dialogue = m_sipDialogueManager.GetDialogue(sipRequest); } if (dialogue != null && sipRequest.Method != SIPMethodsEnum.ACK) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Matching dialogue found for " + sipRequest.Method + " to " + sipRequest.URI.ToString() + " from " + remoteEndPoint + ".", dialogue.Owner)); if (sipRequest.Method != SIPMethodsEnum.REFER) { m_sipDialogueManager.ProcessInDialogueRequest(localSIPEndPoint, remoteEndPoint, sipRequest, dialogue); } else { m_sipDialogueManager.ProcessInDialogueReferRequest(localSIPEndPoint, remoteEndPoint, sipRequest, dialogue, m_callManager.BlindTransfer); } } else if (sipRequest.Method == SIPMethodsEnum.CANCEL) { #region CANCEL request handling. UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE)); if (inviteTransaction != null) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Cancelling call for " + sipRequest.URI.ToString() + ".", fromUser)); SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction); cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No matching transaction was found for CANCEL to " + sipRequest.URI.ToString() + ".", fromUser)); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } #endregion } else if (sipRequest.Method == SIPMethodsEnum.BYE) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No dialogue matched for BYE to " + sipRequest.URI.ToString() + ".", fromUser)); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } else if (sipRequest.Method == SIPMethodsEnum.REFER) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No dialogue matched for REFER to " + sipRequest.URI.ToString() + ".", fromUser)); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } else if (sipRequest.Method == SIPMethodsEnum.ACK) { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "No transaction matched for ACK for " + sipRequest.URI.ToString() + ".", fromUser)); } else if (sipRequest.Method == SIPMethodsEnum.INVITE) { #region INVITE request processing. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "AppServerCore INVITE received, uri=" + sipRequest.URI.ToString() + ", cseq=" + sipRequest.Header.CSeq + ".", null)); if (sipRequest.URI.User == m_dispatcherUsername) { // Incoming call from monitoring process checking the application server is still running. UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy, true); //uasTransaction.CDR = null; SIPServerUserAgent incomingCall = new SIPServerUserAgent(m_sipTransport, m_outboundProxy, sipRequest.URI.User, sipRequest.URI.Host, SIPCallDirection.In, null, null, null, uasTransaction); //incomingCall.NoCDR(); uasTransaction.NewCallReceived += (local, remote, transaction, request) => { m_callManager.QueueNewCall(incomingCall); }; uasTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else if (GetCanonicalDomain_External(sipRequest.Header.From.FromUserField.URI.Host, false) != null) { // Call identified as outgoing call for application server serviced domain. string fromDomain = GetCanonicalDomain_External(sipRequest.Header.From.FromUserField.URI.Host, false); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy); SIPServerUserAgent outgoingCall = new SIPServerUserAgent(m_sipTransport, m_outboundProxy, fromUser, fromDomain, SIPCallDirection.Out, GetSIPAccount_External, SIPRequestAuthenticator.AuthenticateSIPRequest, FireProxyLogEvent, uasTransaction); uasTransaction.NewCallReceived += (local, remote, transaction, request) => { m_callManager.QueueNewCall(outgoingCall); }; uasTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else if (GetCanonicalDomain_External(sipRequest.URI.Host, true) != null) { // Call identified as incoming call for application server serviced domain. if (sipRequest.URI.User.IsNullOrBlank()) { // Cannot process incoming call if no user is specified. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "INVITE received with an empty URI user " + sipRequest.URI.ToString() + ", returning address incomplete.", null)); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy); SIPResponse addressIncompleteResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.AddressIncomplete, "No user specified"); uasTransaction.SendFinalResponse(addressIncompleteResponse); } else { // Send the incoming call to the call manager for processing. string uriUser = sipRequest.URI.User; string uriDomain = GetCanonicalDomain_External(sipRequest.URI.Host, true); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy); SIPServerUserAgent incomingCall = new SIPServerUserAgent(m_sipTransport, m_outboundProxy, uriUser, uriDomain, SIPCallDirection.In, GetSIPAccount_External, null, FireProxyLogEvent, uasTransaction); uasTransaction.NewCallReceived += (local, remote, transaction, request) => { m_callManager.QueueNewCall(incomingCall); }; uasTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } } else { // Return not found for non-serviced domain. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Domain not serviced " + sipRequest.URI.ToString() + ", returning not found.", null)); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy); SIPResponse notServicedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotFound, "Domain not serviced"); uasTransaction.SendFinalResponse(notServicedResponse); } #endregion } else if (sipRequest.Method == SIPMethodsEnum.MESSAGE) { #region Processing non-INVITE requests that are accepted by the dialplan processing engine. if (GetCanonicalDomain_External(sipRequest.Header.From.FromUserField.URI.Host, false) != null) { // Call identified as outgoing request for application server serviced domain. string fromDomain = GetCanonicalDomain_External(sipRequest.Header.From.FromUserField.URI.Host, false); SIPNonInviteTransaction nonInviteTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy); SIPNonInviteServerUserAgent outgoingRequest = new SIPNonInviteServerUserAgent(m_sipTransport, m_outboundProxy, fromUser, fromDomain, SIPCallDirection.Out, GetSIPAccount_External, SIPRequestAuthenticator.AuthenticateSIPRequest, FireProxyLogEvent, nonInviteTransaction); nonInviteTransaction.NonInviteRequestReceived += (local, remote, transaction, request) => { m_callManager.QueueNewCall(outgoingRequest); }; nonInviteTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else if (GetCanonicalDomain_External(sipRequest.URI.Host, true) != null) { // Call identified as incoming call for application server serviced domain. if (sipRequest.URI.User.IsNullOrBlank()) { // Cannot process incoming call if no user is specified. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, sipRequest.Method + " request received with an empty URI user " + sipRequest.URI.ToString() + ", returning address incomplete.", null)); SIPResponse addressIncompleteResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.AddressIncomplete, "No user specified"); m_sipTransport.SendResponse(addressIncompleteResponse); } else { // Send the incoming call to the call manager for processing. string uriUser = sipRequest.URI.User; string uriDomain = GetCanonicalDomain_External(sipRequest.URI.Host, true); SIPNonInviteTransaction nonInviteTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, m_outboundProxy); SIPNonInviteServerUserAgent incomingRequest = new SIPNonInviteServerUserAgent(m_sipTransport, m_outboundProxy, uriUser, uriDomain, SIPCallDirection.In, GetSIPAccount_External, null, FireProxyLogEvent, nonInviteTransaction); nonInviteTransaction.NonInviteRequestReceived += (local, remote, transaction, request) => { m_callManager.QueueNewCall(incomingRequest); }; nonInviteTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } } else { // Return not found for non-serviced domain. FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Domain not serviced " + sipRequest.URI.ToString() + ", returning not found.", null)); SIPResponse notServicedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotFound, "Domain not serviced"); m_sipTransport.SendResponse(notServicedResponse); } #endregion } else { FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.UnrecognisedMessage, "MethodNotAllowed response for " + sipRequest.Method + " from " + fromUser + " socket " + remoteEndPoint.ToString() + ".", null)); SIPResponse notAllowedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); m_sipTransport.SendResponse(notAllowedResponse); } } catch (Exception excp) { string reqExcpError = "Exception SIPAppServerCore GotRequest (" + remoteEndPoint + "). " + excp.Message; logger.Error(reqExcpError); SIPMonitorEvent reqExcpEvent = new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.Error, reqExcpError, sipRequest, null, localSIPEndPoint, remoteEndPoint, SIPCallDirection.In); FireProxyLogEvent(reqExcpEvent); throw excp; } }
private void Agent_CallCancelled(ISIPServerUserAgent uas) { agent = null; }
/// <summary> /// Handler for processing incomign SIP requests. /// </summary> /// <param name="localSIPEndPoint">The end point the request was received on.</param> /// <param name="remoteEndPoint">The end point the request came from.</param> /// <param name="sipRequest">The SIP request received.</param> private void SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) { if (sipRequest.Method == SIPMethodsEnum.BYE) { if (m_uac != null && m_uac.SIPDialogue != null && sipRequest.Header.CallId == m_uac.SIPDialogue.CallId) { // Call has been hungup by remote end. StatusMessage("Call hungup by remote end."); SIPNonInviteTransaction byeTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); byeTransaction.SendFinalResponse(byeResponse); CallFinished(); } else if (m_uas != null && m_uas.SIPDialogue != null && sipRequest.Header.CallId == m_uas.SIPDialogue.CallId) { // Call has been hungup by remote end. StatusMessage("Call hungup."); m_uas.SIPDialogue.Hangup(m_sipTransport, null); CallFinished(); } else { logger.Debug("Unmatched BYE request received for " + sipRequest.URI.ToString() + "."); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } } else if (sipRequest.Method == SIPMethodsEnum.INVITE) { StatusMessage("Incoming call request: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + "."); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); m_uas = new SIPServerUserAgent(m_sipTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction); m_uas.CallCancelled += UASCallCancelled; IncomingCall(); } else if (sipRequest.Method == SIPMethodsEnum.CANCEL) { UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE)); if (inviteTransaction != null) { StatusMessage("Call was cancelled by remote end."); SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction); cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else { logger.Debug("No matching transaction was found for CANCEL to " + sipRequest.URI.ToString() + "."); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } CallFinished(); } else { logger.Debug("SIP " + sipRequest.Method + " request received but no processing has been set up for it, rejecting."); SIPResponse notAllowedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); m_sipTransport.SendResponse(notAllowedResponse); } }
private static readonly int RTP_REPORTING_PERIOD_SECONDS = 5; // Period at which to write RTP stats. static void Main() { Console.WriteLine("SIPSorcery client user agent server example."); Console.WriteLine("Press ctrl-c to exit."); // Logging configuration. Can be ommitted if internal SIPSorcery debug and warning messages are not required. var loggerFactory = new Microsoft.Extensions.Logging.LoggerFactory(); var loggerConfig = new LoggerConfiguration() .Enrich.FromLogContext() .MinimumLevel.Is(Serilog.Events.LogEventLevel.Debug) .WriteTo.Console() .CreateLogger(); loggerFactory.AddSerilog(loggerConfig); SIPSorcery.Sys.Log.LoggerFactory = loggerFactory; // Set up a default SIP transport. IPAddress defaultAddr = LocalIPConfig.GetDefaultIPv4Address(); var sipTransport = new SIPTransport(SIPDNSManager.ResolveSIPService, new SIPTransactionEngine()); int port = FreePort.FindNextAvailableUDPPort(SIPConstants.DEFAULT_SIP_PORT); var sipChannel = new SIPUDPChannel(new IPEndPoint(defaultAddr, port)); sipTransport.AddSIPChannel(sipChannel); // To keep things a bit simpler this example only supports a single call at a time and the SIP server user agent // acts as a singleton SIPServerUserAgent uas = null; CancellationTokenSource uasCts = null; // Because this is a server user agent the SIP transport must start listening for client user agents. sipTransport.SIPTransportRequestReceived += (SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => { if (sipRequest.Method == SIPMethodsEnum.INVITE) { SIPSorcery.Sys.Log.Logger.LogInformation("Incoming call request: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + "."); // If there's already a call in progress hang it up. Of course this is not ideal for a real softphone or server but it // means this example can be kept a little it simpler. uas?.Hangup(); UASInviteTransaction uasTransaction = sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); uas = new SIPServerUserAgent(sipTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction); uasCts = new CancellationTokenSource(); uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); // Initialise an RTP session to receive the RTP packets from the remote SIP server. Socket rtpSocket = null; Socket controlSocket = null; NetServices.CreateRtpSocket(defaultAddr, 49000, 49100, false, out rtpSocket, out controlSocket); IPEndPoint rtpEndPoint = rtpSocket.LocalEndPoint as IPEndPoint; IPEndPoint dstRtpEndPoint = SDP.GetSDPRTPEndPoint(sipRequest.Body); var rtpSession = new RTPSession((int)RTPPayloadTypesEnum.PCMU, null, null); var rtpTask = Task.Run(() => SendRecvRtp(rtpSocket, rtpSession, dstRtpEndPoint, AUDIO_FILE, uasCts)) .ContinueWith(_ => { if (uas?.IsHungup == false) { uas?.Hangup(); } }); uas.Answer(SDP.SDP_MIME_CONTENTTYPE, GetSDP(rtpEndPoint).ToString(), null, SIPDialogueTransferModesEnum.NotAllowed); } else if (sipRequest.Method == SIPMethodsEnum.BYE) { SIPSorcery.Sys.Log.Logger.LogInformation("Call hungup."); SIPNonInviteTransaction byeTransaction = sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); byeTransaction.SendFinalResponse(byeResponse); uas?.Hangup(); uasCts?.Cancel(); } }; Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; SIPSorcery.Sys.Log.Logger.LogInformation("Exiting..."); if (uas?.IsHungup == false) { uas?.Hangup(); } uasCts?.Cancel(); if (sipTransport != null) { SIPSorcery.Sys.Log.Logger.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } }; }
private static void SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) { if (sipRequest.Method == SIPMethodsEnum.BYE) { var rtpJob = (from job in m_rtpJobs.Values where job.UAS.CallRequest.Header.CallId == sipRequest.Header.CallId select job).FirstOrDefault(); if (rtpJob != null) { rtpJob.Stop(); // Call has been hungup by remote end. Console.WriteLine("Call hungup by client: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n"); Publish(rtpJob.QueueName, "BYE request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); //Console.WriteLine("Request Received " + localSIPEndPoint + "<-" + remoteEndPoint + "\n" + sipRequest.ToString()); //m_uas.SIPDialogue.Hangup(m_sipTransport, null); SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); m_sipTransport.SendResponse(okResponse); } else { Console.WriteLine("Unmatched BYE request received for " + sipRequest.URI.ToString() + ".\n"); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } } else if (sipRequest.Method == SIPMethodsEnum.INVITE) { Console.WriteLine("Incoming call request: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n"); Publish(sipRequest.URI.User, "INVITE request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); Console.WriteLine(sipRequest.Body); SIPPacketMangler.MangleSIPRequest(SIPMonitorServerTypesEnum.Unknown, sipRequest, null, LogTraceMessage); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); var uas = new SIPServerUserAgent(m_sipTransport, null, null, null, SIPCallDirection.In, null, null, LogTraceMessage, uasTransaction); uas.CallCancelled += UASCallCancelled; RTPDiagnosticsJob rtpJob = new RTPDiagnosticsJob(m_rtpListenIPAddress, m_publicIPAddress, uas, sipRequest); string sdpAddress = SDP.GetSDPRTPEndPoint(sipRequest.Body).Address.ToString(); // Only mangle if there is something to change. For example the server could be on the same private subnet in which case it can't help. IPEndPoint expectedRTPEndPoint = new IPEndPoint(rtpJob.RemoteRTPEndPoint.Address, rtpJob.RemoteRTPEndPoint.Port); if (IPSocket.IsPrivateAddress(rtpJob.RemoteRTPEndPoint.Address.ToString())) { expectedRTPEndPoint.Address = remoteEndPoint.Address; } Publish(sipRequest.URI.User, "Advertised RTP remote socket " + rtpJob.RemoteRTPEndPoint + ", expecting from " + expectedRTPEndPoint + "."); m_rtpJobs.Add(rtpJob.RTPListenEndPoint.Port, rtpJob); //ThreadPool.QueueUserWorkItem(delegate { StartRTPListener(rtpJob); }); Console.WriteLine(rtpJob.LocalSDP.ToString()); uas.Answer("application/sdp", rtpJob.LocalSDP.ToString(), CallProperties.CreateNewTag(), null, SIPDialogueTransferModesEnum.NotAllowed); var hangupTimer = new Timer(delegate { if (!rtpJob.StopJob) { if (uas != null && uas.SIPDialogue != null) { if (rtpJob.RTPPacketReceived && !rtpJob.ErrorOnRTPSend) { Publish(sipRequest.URI.User, "Test completed. There were no RTP send or receive errors."); } else if (!rtpJob.RTPPacketReceived) { Publish(sipRequest.URI.User, "Test completed. An error was identified, no RTP packets were received."); } else { Publish(sipRequest.URI.User, "Test completed. An error was identified, there was a problem when attempting to send an RTP packet."); } rtpJob.Stop(); uas.SIPDialogue.Hangup(m_sipTransport, null); } } }, null, HANGUP_TIMEOUT, Timeout.Infinite); } else if (sipRequest.Method == SIPMethodsEnum.CANCEL) { UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE)); if (inviteTransaction != null) { Console.WriteLine("Matching CANCEL request received " + sipRequest.URI.ToString() + ".\n"); Publish(sipRequest.URI.User, "CANCEL request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction); cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else { Console.WriteLine("No matching transaction was found for CANCEL to " + sipRequest.URI.ToString() + ".\n"); SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); m_sipTransport.SendResponse(noCallLegResponse); } } else { Console.WriteLine("SIP " + sipRequest.Method + " request received but no processing has been set up for it, rejecting.\n"); Publish(sipRequest.URI.User, sipRequest.Method + " request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + "."); SIPResponse notAllowedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); m_sipTransport.SendResponse(notAllowedResponse); } }
/// <summary> /// Accepts an incoming call. This is the first step in answering a call. /// From this point the call can still be rejected, redirected or answered. /// </summary> /// <param name="sipRequest">The SIP request containing the incoming call request.</param> public void Accept(SIPRequest sipRequest) { m_pendingIncomingCall = m_userAgent.AcceptCall(sipRequest); }
static void Main(string[] args) { Console.WriteLine("SIPSorcery user agent server example."); Console.WriteLine("Press h to hangup a call or ctrl-c to exit."); EnableConsoleLogger(); IPAddress listenAddress = IPAddress.Any; IPAddress listenIPv6Address = IPAddress.IPv6Any; if (args != null && args.Length > 0) { if (!IPAddress.TryParse(args[0], out var customListenAddress)) { Log.LogWarning($"Command line argument could not be parsed as an IP address \"{args[0]}\""); listenAddress = IPAddress.Any; } else { if (customListenAddress.AddressFamily == AddressFamily.InterNetwork) { listenAddress = customListenAddress; } if (customListenAddress.AddressFamily == AddressFamily.InterNetworkV6) { listenIPv6Address = customListenAddress; } } } // Set up a default SIP transport. var sipTransport = new SIPTransport(); var localhostCertificate = new X509Certificate2("localhost.pfx"); // IPv4 channels. sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(listenAddress, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(listenAddress, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTLSChannel(localhostCertificate, new IPEndPoint(listenAddress, SIPS_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.Any, SIP_WEBSOCKET_LISTEN_PORT)); sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.Any, SIP_SECURE_WEBSOCKET_LISTEN_PORT, localhostCertificate)); // IPv6 channels. sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(listenIPv6Address, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(listenIPv6Address, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTLSChannel(localhostCertificate, new IPEndPoint(listenIPv6Address, SIPS_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.IPv6Any, SIP_WEBSOCKET_LISTEN_PORT)); sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.IPv6Any, SIP_SECURE_WEBSOCKET_LISTEN_PORT, localhostCertificate)); EnableTraceLogs(sipTransport); // To keep things a bit simpler this example only supports a single call at a time and the SIP server user agent // acts as a singleton SIPServerUserAgent uas = null; CancellationTokenSource rtpCts = null; // Cancellation token to stop the RTP stream. Socket rtpSocket = null; Socket controlSocket = null; // Because this is a server user agent the SIP transport must start listening for client user agents. sipTransport.SIPTransportRequestReceived += (SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => { try { if (sipRequest.Method == SIPMethodsEnum.INVITE) { SIPSorcery.Sys.Log.Logger.LogInformation($"Incoming call request: {localSIPEndPoint}<-{remoteEndPoint} {sipRequest.URI}."); // Check there's a codec we support in the INVITE offer. var offerSdp = SDP.ParseSDPDescription(sipRequest.Body); IPEndPoint dstRtpEndPoint = SDP.GetSDPRTPEndPoint(sipRequest.Body); RTPSession rtpSession = null; string audioFile = null; if (offerSdp.Media.Any(x => x.Media == SDPMediaTypesEnum.audio && x.HasMediaFormat((int)RTPPayloadTypesEnum.G722))) { Log.LogDebug($"Using G722 RTP media type and audio file {AUDIO_FILE_G722}."); rtpSession = new RTPSession((int)RTPPayloadTypesEnum.G722, null, null); audioFile = AUDIO_FILE_G722; } else if (offerSdp.Media.Any(x => x.Media == SDPMediaTypesEnum.audio && x.HasMediaFormat((int)RTPPayloadTypesEnum.PCMU))) { Log.LogDebug($"Using PCMU RTP media type and audio file {AUDIO_FILE_PCMU}."); rtpSession = new RTPSession((int)RTPPayloadTypesEnum.PCMU, null, null); audioFile = AUDIO_FILE_PCMU; } if (rtpSession == null) { // Didn't get a match on the codecs we support. SIPResponse noMatchingCodecResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotAcceptableHere, null); sipTransport.SendResponse(noMatchingCodecResponse); } else { // If there's already a call in progress hang it up. Of course this is not ideal for a real softphone or server but it // means this example can be kept simpler. if (uas?.IsHungup == false) { uas?.Hangup(false); } rtpCts?.Cancel(); UASInviteTransaction uasTransaction = sipTransport.CreateUASTransaction(sipRequest, null); uas = new SIPServerUserAgent(sipTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction); rtpCts = new CancellationTokenSource(); uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); // Initialise an RTP session to receive the RTP packets from the remote SIP server. NetServices.CreateRtpSocket(dstRtpEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, RTP_PORT_START, RTP_PORT_END, false, out rtpSocket, out controlSocket); // The RTP socket is listening on IPAddress.Any but the IP address placed into the SDP needs to be one the caller can reach. IPAddress rtpAddress = NetServices.GetLocalAddressForRemote(dstRtpEndPoint.Address); IPEndPoint rtpEndPoint = new IPEndPoint(rtpAddress, (rtpSocket.LocalEndPoint as IPEndPoint).Port); var rtpTask = Task.Run(() => SendRecvRtp(rtpSocket, rtpSession, dstRtpEndPoint, audioFile, rtpCts)) .ContinueWith(_ => { if (uas?.IsHungup == false) { uas?.Hangup(false); } }); uas.Answer(SDP.SDP_MIME_CONTENTTYPE, GetSDP(rtpEndPoint).ToString(), null, SIPDialogueTransferModesEnum.NotAllowed); } } else if (sipRequest.Method == SIPMethodsEnum.BYE) { SIPSorcery.Sys.Log.Logger.LogInformation("Call hungup."); SIPNonInviteTransaction byeTransaction = sipTransport.CreateNonInviteTransaction(sipRequest, null); SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); byeTransaction.SendFinalResponse(byeResponse); uas?.Hangup(true); rtpCts?.Cancel(); rtpSocket?.Close(); controlSocket?.Close(); } else if (sipRequest.Method == SIPMethodsEnum.SUBSCRIBE) { SIPResponse notAllowededResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); sipTransport.SendResponse(notAllowededResponse); } else if (sipRequest.Method == SIPMethodsEnum.OPTIONS || sipRequest.Method == SIPMethodsEnum.REGISTER) { SIPResponse optionsResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); sipTransport.SendResponse(optionsResponse); } } catch (Exception reqExcp) { SIPSorcery.Sys.Log.Logger.LogWarning($"Exception handling {sipRequest.Method}. {reqExcp.Message}"); } }; ManualResetEvent exitMre = new ManualResetEvent(false); Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; SIPSorcery.Sys.Log.Logger.LogInformation("Exiting..."); Hangup(uas).Wait(); rtpCts?.Cancel(); rtpSocket?.Close(); controlSocket?.Close(); if (sipTransport != null) { SIPSorcery.Sys.Log.Logger.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } exitMre.Set(); }; Task.Run(() => { try { while (!exitMre.WaitOne(0)) { var keyProps = Console.ReadKey(); if (keyProps.KeyChar == 'h' || keyProps.KeyChar == 'q') { Console.WriteLine(); Console.WriteLine("Hangup requested by user..."); Hangup(uas).Wait(); rtpCts?.Cancel(); rtpSocket?.Close(); controlSocket?.Close(); } if (keyProps.KeyChar == 'q') { SIPSorcery.Sys.Log.Logger.LogInformation("Quitting..."); if (sipTransport != null) { SIPSorcery.Sys.Log.Logger.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } exitMre.Set(); } } } catch (Exception excp) { SIPSorcery.Sys.Log.Logger.LogError($"Exception Key Press listener. {excp.Message}."); } }); exitMre.WaitOne(); }
/// <summary> /// Cleans up after a SIP call has completely finished. /// </summary> private void CallFinished() { m_pendingIncomingCall = null; CallEnded(this); }
private void Transport_SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) { var endpoint = new SIPEndPoint(SIPProtocolsEnum.udp, publicIPAddress, localSIPEndPoint.Port); if (sipRequest.Method == SIPMethodsEnum.INVITE) { if (transaction != null) { return; } logger.DebugFormat("{0} Incoming call from {1}", prefix, sipRequest.Header.From.FromURI.User); transaction = transport.CreateUASTransaction(sipRequest, remoteEndPoint, endpoint, null); agent = new SIPServerUserAgent( transport, null, sipRequest.Header.From.FromURI.User, null, SIPCallDirection.In, null, null, null, transaction); agent.CallCancelled += Agent_CallCancelled; agent.TransactionComplete += Agent_TransactionComplete; agent.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); agent.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); var answer = SDP.ParseSDPDescription(agent.CallRequest.Body); var address = IPAddress.Parse(answer.Connection.ConnectionAddress); var port = answer.Media.FirstOrDefault(m => m.Media == SDPMediaTypesEnum.audio).Port; var random = Crypto.GetRandomInt(5).ToString(); var sdp = new SDP { Version = 2, Username = "******", SessionId = random, Address = localIPEndPoint.Address.ToString(), SessionName = "redfox_" + random, Timing = "0 0", Connection = new SDPConnectionInformation(publicIPAddress.ToString()) }; rtpChannel = new RTPChannel { DontTimeout = true, RemoteEndPoint = new IPEndPoint(address, port) }; rtpChannel.SetFrameType(FrameTypesEnum.Audio); // TODO Fix hardcoded ports rtpChannel.ReservePorts(15000, 15090); rtpChannel.OnFrameReady += Channel_OnFrameReady; rtpChannel.Start(); // Send some setup parameters to punch a hole in the firewall/router rtpChannel.SendRTPRaw(new byte[] { 80, 95, 198, 88, 55, 96, 225, 141, 215, 205, 185, 242, 00 }); rtpChannel.OnControlDataReceived += (b) => { logger.Debug($"{prefix} Control Data Received; {b.Length} bytes"); }; rtpChannel.OnControlSocketDisconnected += () => { logger.Debug($"{prefix} Control Socket Disconnected"); }; var announcement = new SDPMediaAnnouncement { Media = SDPMediaTypesEnum.audio, MediaFormats = new List <SDPMediaFormat>() { new SDPMediaFormat((int)SDPMediaFormatsEnum.PCMU, "PCMU", 8000) }, Port = rtpChannel.RTPPort }; sdp.Media.Add(announcement); SetState(State.Listening, sipRequest.Header.From.FromURI.User); agent.Progress(SIPResponseStatusCodesEnum.Accepted, null, null, null, null); agent.Answer(SDP.SDP_MIME_CONTENTTYPE, sdp.ToString(), null, SIPDialogueTransferModesEnum.NotAllowed); SetState(State.Busy, ""); return; } if (sipRequest.Method == SIPMethodsEnum.BYE) { if (State != State.Busy) { return; } logger.DebugFormat("{0} Hangup from {1}", prefix, sipRequest.Header.From.FromURI.User); var noninvite = transport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, endpoint, null); var response = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); noninvite.SendFinalResponse(response); SetState(State.Finished, Endpoint); rtpChannel.OnFrameReady -= Channel_OnFrameReady; rtpChannel.Close(); agent.TransactionComplete -= Agent_TransactionComplete; agent.CallCancelled -= Agent_CallCancelled; agent = null; transaction = null; SetState(State.Ready, Endpoint); return; } if (sipRequest.Method == SIPMethodsEnum.ACK) { } if (sipRequest.Method == SIPMethodsEnum.CANCEL) { } }