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> /// The current call has ended. Reset the state of the user agent. /// </summary> private void CallEnded() { m_uac = null; m_uas = null; if (MediaSession != null && !MediaSession.IsClosed) { MediaSession.Close("normal"); MediaSession = null; } OnCallHungup?.Invoke(); }
/// <summary> /// The current call has ended. Reset the state of the user agent. /// </summary> private void CallEnded() { m_uac = null; m_uas = null; if (MediaSession != null) { MediaSession.SessionMediaChanged -= MediaSessionOnSessionMediaChanged; MediaSession.Close(); MediaSession = null; } OnCallHungup?.Invoke(); }
/// <summary> /// This method can be used to start the processing of a new incoming call request. /// The user agent will is acting as a server for this operation and it can be considered /// the opposite of the Call method. This is only the first step in answering an incoming /// call. It can still be rejected or answered after this point. /// </summary> /// <param name="inviteRequest">The invite request representing the incoming call.</param> /// <returns>An ID string that needs to be supplied when the call is answered or rejected /// (used to manage multiple pending incoming calls).</returns> public SIPServerUserAgent AcceptCall(SIPRequest inviteRequest) { UASInviteTransaction uasTransaction = new UASInviteTransaction(m_transport, inviteRequest, m_outboundProxy); SIPServerUserAgent uas = new SIPServerUserAgent(m_transport, m_outboundProxy, null, null, SIPCallDirection.In, null, null, null, uasTransaction); uas.CallCancelled += (pendingUas) => { CallEnded(); ServerCallCancelled?.Invoke(pendingUas); }; uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); return(uas); }
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> /// <param name="customHeaders">Custom SIP-Headers to use in Answer.</param> public async Task Answer(SIPServerUserAgent uas, IMediaSession mediaSession, string[] customHeaders) { // This call is now taking over any existing call. if (IsCallActive) { Hangup(); } else if (uas.IsCancelled) { logger.LogDebug("The incoming call has been cancelled."); mediaSession?.Close("call cancelled"); } else { m_cts = new CancellationTokenSource(); var sipRequest = uas.ClientTransaction.TransactionRequest; MediaSession = mediaSession; MediaSession.OnRtpEvent += OnRemoteRtpEvent; //MediaSession.OnRtpClosed += (reason) => Hangup(); MediaSession.OnRtpClosed += (reason) => { if (!MediaSession.IsClosed) { logger.LogWarning($"RTP channel was closed with reason {reason}."); } }; SDP remoteSdp = SDP.ParseSDPDescription(sipRequest.Body); MediaSession.setRemoteDescription(new RTCSessionDescription { sdp = remoteSdp, type = RTCSdpType.offer });; var sdpAnswer = await MediaSession.createAnswer(null).ConfigureAwait(false); MediaSession.setLocalDescription(new RTCSessionDescription { sdp = sdpAnswer, type = RTCSdpType.answer }); await MediaSession.Start().ConfigureAwait(false); m_uas = uas; m_uas.Answer(m_sdpContentType, sdpAnswer.ToString(), null, SIPDialogueTransferModesEnum.Default, customHeaders); Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; } }
/// <summary> /// This method can be used to start the processing of a new incoming call request. /// The user agent will is acting as a server for this operation and it can be considered /// the opposite of the Call method. /// </summary> /// <param name="uasInviteTx">The invite transaction representing the incoming call.</param> /// <returns>True if the call is accepted, false otherwise.</returns> public bool AcceptCall(UASInviteTransaction uasInviteTx) { SIPServerUserAgent uas = new SIPServerUserAgent(m_transport, m_outboundProxy, null, null, SIPCallDirection.In, null, null, null, uasInviteTx); uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); // TODO: Decide how to deal with multiple simultaneous calls. if (m_uas != null) { uas.Reject(SIPResponseStatusCodesEnum.BusyHere, null, null); return(false); } else { uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); m_uas = uas; return(true); } }
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> public async Task Answer(SIPServerUserAgent uas, IMediaSession mediaSession) { // This call is now taking over any existing call. if (IsCallActive) { Hangup(); } var sipRequest = uas.ClientTransaction.TransactionRequest; MediaSession = mediaSession; MediaSession.SessionMediaChanged += MediaSessionOnSessionMediaChanged; MediaSession.OnRtpClosed += (reason) => Hangup(); var sdpAnswer = await MediaSession.AnswerOffer(sipRequest.Body).ConfigureAwait(false); m_uas = uas; m_uas.Answer(m_sdpContentType, sdpAnswer, null, SIPDialogueTransferModesEnum.Default); Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; }
/// <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) } } } }; }
/// <summary> /// Processes the callback action that is initiated by the callmanager service. The callback method is typically initiated from /// a link on an authenticated web page and requires the user to be authenticated. /// </summary> /// <param name="username">The authenticated username of the user making the callback request.</param> /// <param name="dialString1">The first leg dial string of the callback.</param> /// <param name="dialString2">The second leg dial string of the callback.</param> /// <returns>A string that is returned to the user making the callmanager request that inidcates what action was taken.</returns> public string ProcessCallback(string username, string dialString1, string dialString2) { Customer customer = null; try { customer = m_customerPersistor.Get(c => c.CustomerUsername == username); if (customer == null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Callback rejected for " + username + " as no matching user.", null)); return "Sorry no matching user was found, the callback was not initiated."; } else if (customer.Suspended) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Callback rejected for " + username + " as user account is suspended.", null)); return "Sorry your account is suspended."; } else if (customer.ServiceLevel == CustomerServiceLevels.PremiumPayReqd.ToString() || customer.ServiceLevel == CustomerServiceLevels.ProfessionalPayReqd.ToString() || customer.ServiceLevel == CustomerServiceLevels.SwitchboardPayReqd.ToString()) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Callback rejected for " + username + ", user account requires payment.", null)); return "Sorry your account requires payment."; } else { UASInviteTransaction dummyTransaction = GetDummyWebCallbackTransaction("callback"); ISIPServerUserAgent uas = new SIPServerUserAgent(m_sipTransport, m_outboundProxy, username, SIPDomainManager.DEFAULT_LOCAL_DOMAIN, SIPCallDirection.Out, GetSIPAccount_External, null, Log_External, dummyTransaction); string callbackScript = "sys.Log(\"Callback dialString1=" + dialString1 + ", dialString2=" + dialString2 + ".\")\n" + "sys.Callback(\"" + dialString1 + "\",\"" + dialString2 + "\", 0)\n"; SIPDialPlan callbackDialPlan = new SIPDialPlan(username, null, null, callbackScript, SIPDialPlanScriptTypesEnum.Ruby); callbackDialPlan.Id = Guid.Empty; // Prevents the increment and decrement on the execution counts. DialPlanScriptContext scriptContext = new DialPlanScriptContext( Log_External, m_sipTransport, CreateDialogueBridge, m_outboundProxy, uas, callbackDialPlan, GetSIPProviders_External(p => p.Owner == username, null, 0, Int32.MaxValue), null, null, customer, null, GetCanonicalDomain_External); m_dialPlanEngine.Execute(scriptContext, uas, SIPCallDirection.Out, CreateDialogueBridge, this); return null; } } catch (Exception excp) { logger.Error("Exception SIPCallManager ProcessCallback. " + excp.Message); return "Sorry there was an unexpected error, the callback was not initiated."; } }
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. AppendTraceMessage("Call hungup by server: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n"); AppendSIPTraceMessage("Request Received " + localSIPEndPoint + "<-" + remoteEndPoint + "\n" + sipRequest.ToString()); m_uac.SIPDialogue.Hangup(m_sipTransport, null); ResetToCallStartState(); } else if (m_uas != null && m_uas.SIPDialogue != null && sipRequest.Header.CallId == m_uas.SIPDialogue.CallId) { // Call has been hungup by remote end. AppendTraceMessage("Call hungup by client: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n"); AppendSIPTraceMessage("Request Received " + localSIPEndPoint + "<-" + remoteEndPoint + "\n" + sipRequest.ToString()); m_uas.SIPDialogue.Hangup(m_sipTransport, null); ResetToCallStartState(); } else { AppendTraceMessage("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) { ResetToCallStartState(); ClearTraces(); AppendTraceMessage("Incoming call request: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n"); SetVisibility(m_uacGrid, Visibility.Collapsed); SetVisibility(m_uasGrid, Visibility.Visible); UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); m_uas = new SIPServerUserAgent(m_sipTransport, null, null, null, SIPCallDirection.In, null, null, LogTraceMessage, uasTransaction); m_uas.CallCancelled += UASCallCancelled; } else if (sipRequest.Method == SIPMethodsEnum.CANCEL) { UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE)); if (inviteTransaction != null) { AppendTraceMessage("Matching CANCEL request received " + sipRequest.URI.ToString() + ".\n"); SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction); cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest); } else { AppendTraceMessage("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 { AppendTraceMessage("SIP " + sipRequest.Method + " request received but no processing has been set up for it, rejecting.\n"); SIPResponse notAllowedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); m_sipTransport.SendResponse(notAllowedResponse); } }
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); 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 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> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> /// <param name="customHeaders">Custom SIP-Headers to use in Answer.</param> /// <returns>True if the call was successfully answered or false if there was a problem /// such as incompatible codecs.</returns> public async Task <bool> Answer(SIPServerUserAgent uas, IMediaSession mediaSession, string[] customHeaders) { // This call is now taking over any existing call. if (IsCallActive) { Hangup(); } if (uas.IsCancelled) { logger.LogDebug("The incoming call has been cancelled."); mediaSession?.Close("call cancelled"); return(false); } else { m_cts = new CancellationTokenSource(); var sipRequest = uas.ClientTransaction.TransactionRequest; MediaSession = mediaSession; MediaSession.OnRtpEvent += OnRemoteRtpEvent; MediaSession.OnRtpClosed += (reason) => { if (!MediaSession.IsClosed) { logger.LogWarning($"RTP channel was closed with reason {reason}."); } }; string sdp = null; if (!String.IsNullOrEmpty(sipRequest.Body)) { // The SDP offer was included in the INVITE request. SDP remoteSdp = SDP.ParseSDPDescription(sipRequest.Body); var setRemoteResult = MediaSession.SetRemoteDescription(remoteSdp); if (setRemoteResult != SetDescriptionResultEnum.OK) { logger.LogWarning($"Error setting remote description from INVITE {setRemoteResult}."); uas.Reject(SIPResponseStatusCodesEnum.NotAcceptable, setRemoteResult.ToString()); MediaSession.Close("sdp offer not acceptable"); Hangup(); return(false); } else { var sdpAnswer = MediaSession.CreateAnswer(null); sdp = sdpAnswer.ToString(); } } else { // No SDP offer was included in the INVITE request need to wait for the ACK. var sdpAnnounceAddress = NetServices.GetLocalAddressForRemote(sipRequest.RemoteSIPEndPoint.GetIPEndPoint().Address); var sdpOffer = MediaSession.CreateOffer(sdpAnnounceAddress); sdp = sdpOffer.ToString(); } m_uas = uas; // In cases where the initial INVITE did not contain an SDP offer the action sequence is: // - INVITE with no SDP offer received, // - Reply with OK and an SDP offer, // - Wait for ACK with SDP answer. TaskCompletionSource <SIPDialogue> dialogueCreatedTcs = new TaskCompletionSource <SIPDialogue>(TaskCreationOptions.RunContinuationsAsynchronously); m_uas.OnDialogueCreated += (dialogue) => dialogueCreatedTcs.TrySetResult(dialogue); m_uas.Answer(m_sdpContentType, sdp, null, SIPDialogueTransferModesEnum.Default, customHeaders); await Task.WhenAny(dialogueCreatedTcs.Task, Task.Delay(WAIT_DIALOG_TIMEOUT)).ConfigureAwait(false); if (Dialogue != null) { if (MediaSession.RemoteDescription == null) { // If the initial INVITE did not contain an offer then the remote description will not yet be set. var remoteSDP = SDP.ParseSDPDescription(Dialogue.RemoteSDP); var setRemoteResult = MediaSession.SetRemoteDescription(remoteSDP); if (setRemoteResult != SetDescriptionResultEnum.OK) { // Failed to set the remote SDP from the ACK request. Only option is to hangup. logger.LogWarning($"Error setting remote description from ACK {setRemoteResult}."); MediaSession.Close(setRemoteResult.ToString()); Hangup(); return(false); } else { // SDP from the ACK request was accepted. Start the RTP session. Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; await MediaSession.Start().ConfigureAwait(false); return(true); } } else { Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; await MediaSession.Start().ConfigureAwait(false); return(true); } } else { logger.LogWarning("The attempt to answer a call failed as the dialog was not created. The likely cause is the ACK not being received in time."); MediaSession.Close("dialog creation failed"); Hangup(); return(false); } } }
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> public Task <bool> Answer(SIPServerUserAgent uas, IMediaSession mediaSession) { return(Answer(uas, mediaSession, null)); }
/// <summary> /// Handler for processing incoming 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."); SIPNonInviteTransaction byeTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); byeTransaction.SendFinalResponse(byeResponse); 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; m_uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); m_uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); 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); } }
/// <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(); }
/// <summary> /// Handler for when an in dialog request is received on an established call. /// Typical types of request will be re-INVITES for things like putting a call on or /// off hold and REFER requests for transfers. Some in dialog request types, such /// as re-INVITES have specific events so they can be bubbled up to the /// application to deal with. /// </summary> /// <param name="request">The in dialog request received.</param> public async Task InDialogRequestReceivedAsync(SIPRequest sipRequest) { // Make sure the request matches our dialog and is not a stray. // A dialog request should match on to tag, from tag and call ID. We'll be more // accepting just in case the sender got the tags wrong. if (Dialogue == null || sipRequest.Header.CallId != Dialogue.CallId) { var noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null); var sendResult = await SendResponse(noCallLegResponse); if (sendResult != SocketError.Success) { logger.LogWarning($"SIPUserAgent send response failed in InCallRequestReceivedAsync with {sendResult}."); } } else { if (sipRequest.Method == SIPMethodsEnum.BYE) { logger.LogDebug($"Matching dialogue found for {sipRequest.StatusLine}."); SIPNonInviteTransaction byeTransaction = m_transport.CreateNonInviteTransaction(sipRequest, m_outboundProxy); SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); byeTransaction.SendFinalResponse(byeResponse); CallHungup?.Invoke(); m_uac = null; m_uas = null; } else if (sipRequest.Method == SIPMethodsEnum.INVITE) { logger.LogDebug($"Re-INVITE request received {sipRequest.StatusLine}."); UASInviteTransaction reInviteTransaction = m_transport.CreateUASTransaction(sipRequest, m_outboundProxy); if (OnReinviteRequest == null) { // The application isn't prepared to accept re-INVITE requests. We'll reject as gently as we can to try and not lose the call. SIPResponse notAcceptableResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotAcceptable, null); reInviteTransaction.SendFinalResponse(notAcceptableResponse); } else { SIPResponse tryingResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Trying, null); reInviteTransaction.SendProvisionalResponse(tryingResponse); OnReinviteRequest(reInviteTransaction); } } else if (sipRequest.Method == SIPMethodsEnum.OPTIONS) { //Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "OPTIONS request for established dialogue " + dialogue.DialogueName + ".", dialogue.Owner)); SIPNonInviteTransaction optionsTransaction = m_transport.CreateNonInviteTransaction(sipRequest, m_outboundProxy); SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); okResponse.Body = Dialogue.RemoteSDP; okResponse.Header.ContentLength = okResponse.Body.Length; okResponse.Header.ContentType = m_sdpContentType; optionsTransaction.SendFinalResponse(okResponse); } else if (sipRequest.Method == SIPMethodsEnum.MESSAGE) { //Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "MESSAGE for call " + sipRequest.URI.ToString() + ": " + sipRequest.Body + ".", dialogue.Owner)); SIPNonInviteTransaction messageTransaction = m_transport.CreateNonInviteTransaction(sipRequest, m_outboundProxy); SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); messageTransaction.SendFinalResponse(okResponse); } else if (sipRequest.Method == SIPMethodsEnum.REFER) { //Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "REFER received on dialogue " + dialogue.DialogueName + ", transfer mode is " + dialogue.TransferMode + ".", dialogue.Owner)); SIPNonInviteTransaction referTransaction = m_transport.CreateNonInviteTransaction(sipRequest, m_outboundProxy); if (sipRequest.Header.ReferTo.IsNullOrBlank()) { // A REFER request must have a Refer-To header. //Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Bad REFER request, no Refer-To header.", dialogue.Owner)); SIPResponse invalidResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.BadRequest, "Missing mandatory Refer-To header"); referTransaction.SendFinalResponse(invalidResponse); } else { //TODO: Add handling logic for indialog REFER requests. } } } }
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> public async Task Answer(SIPServerUserAgent uas, IMediaSession mediaSession) { await Answer(uas, mediaSession, null).ConfigureAwait(false); }
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> public async Task Answer(SIPServerUserAgent uas, IMediaSession mediaSession) { await Answer(uas, mediaSession, null); }
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); } } }
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); } }
/// <summary> /// Answers the call request contained in the user agent server parameter. Note the /// <see cref="AcceptCall(SIPRequest)"/> method should be used to create the user agent server. /// Any existing call will be hungup. /// </summary> /// <param name="uas">The user agent server holding the pending call to answer.</param> /// <param name="mediaSession">The media session used for this call</param> /// <param name="customHeaders">Custom SIP-Headers to use in Answer.</param> public async Task Answer(SIPServerUserAgent uas, IMediaSession mediaSession, string[] customHeaders) { // This call is now taking over any existing call. if (IsCallActive) { Hangup(); } else if (uas.IsCancelled) { logger.LogDebug("The incoming call has been cancelled."); mediaSession?.Close("call cancelled"); } else { m_cts = new CancellationTokenSource(); var sipRequest = uas.ClientTransaction.TransactionRequest; MediaSession = mediaSession; MediaSession.OnRtpEvent += OnRemoteRtpEvent; //MediaSession.OnRtpClosed += (reason) => Hangup(); MediaSession.OnRtpClosed += (reason) => { if (!MediaSession.IsClosed) { logger.LogWarning($"RTP channel was closed with reason {reason}."); } }; string sdp = null; if (!String.IsNullOrEmpty(sipRequest.Body)) { // The SDP offer was included in the INVITE request. SDP remoteSdp = SDP.ParseSDPDescription(sipRequest.Body); MediaSession.setRemoteDescription(new RTCSessionDescription { sdp = remoteSdp, type = RTCSdpType.offer }); var sdpAnswer = await MediaSession.createAnswer(null).ConfigureAwait(false); if (sdpAnswer == null) { logger.LogWarning($"Could not generate an SDP answer."); m_uas.Reject(SIPResponseStatusCodesEnum.NotAcceptable, null); return; } else { MediaSession.setLocalDescription(new RTCSessionDescription { sdp = sdpAnswer, type = RTCSdpType.answer }); sdp = sdpAnswer.ToString(); } } else { // No SDP offer was included in the INVITE request need to wait for the ACK. RTCOfferOptions offerOptions = new RTCOfferOptions { RemoteSignallingAddress = sipRequest.RemoteSIPEndPoint.GetIPEndPoint().Address }; var sdpOffer = await MediaSession.createOffer(offerOptions).ConfigureAwait(false); if (sdpOffer == null) { // This shouldn't occur unless we're unable to create an audio/video track. logger.LogWarning($"Could not generate an SDP answer."); m_uas.Reject(SIPResponseStatusCodesEnum.NotAcceptable, null); return; } else { MediaSession.setLocalDescription(new RTCSessionDescription { sdp = sdpOffer, type = RTCSdpType.offer }); sdp = sdpOffer.ToString(); } } m_uas = uas; // In cases where the initial INVITE did not contain an SDP offer the action sequence is: // - INVITE with no SDP offer received, // - Reply with OK and an SDP offer, // - Wait for ACK with SDP answer. TaskCompletionSource <SIPDialogue> dialogueCreatedTcs = new TaskCompletionSource <SIPDialogue>(TaskCreationOptions.RunContinuationsAsynchronously); m_uas.OnDialogueCreated += (dialogue) => dialogueCreatedTcs.TrySetResult(dialogue); m_uas.Answer(m_sdpContentType, sdp, null, SIPDialogueTransferModesEnum.Default, customHeaders); await Task.WhenAny(dialogueCreatedTcs.Task, Task.Delay(WAIT_DIALOG_TIMEOUT)).ConfigureAwait(false); if (Dialogue != null) { if (MediaSession.remoteDescription == null || MediaSession.remoteDescription.sdp == null) { // If the initial INVITE did not contain an offer then the remote description will not yet be set. var remoteSDP = SDP.ParseSDPDescription(Dialogue.RemoteSDP); MediaSession.setRemoteDescription(new RTCSessionDescription { sdp = remoteSDP, type = RTCSdpType.answer }); } Dialogue.DialogueState = SIPDialogueStateEnum.Confirmed; await MediaSession.Start().ConfigureAwait(false); } else { logger.LogWarning("The attempt to answer a call failed as the dialog was not created. The likely cause is the ACK not being received in time."); MediaSession.Close("dialog creation failed"); Hangup(); } } }
/// <summary> /// Processes actions initiated by the callmanager web service EXCEPT for the callback method. /// </summary> /// <param name="username">The UNAUTHENTICATED username that was specified in the callmanager request URL.</param> /// <param name="number">The number parameter that was specified in the callmanager request URL.</param> /// <param name="dialplanName">The dialplan to use to process web calls, typically this will be ahrd coded to a known dialplan name.</param> /// <param name="replacesCallID">The replacesCallID parameter that was specified in the callmanager request URL.</param> /// <returns>A string that is returned to the user making the callmanager request that inidcates what action was taken.</returns> public string ProcessWebCall(string username, string number, string dialplanName, string replacesCallID) { //bool wasExecutionCountIncremented = false; Customer customer = null; SIPDialPlan dialPlan = null; try { customer = m_customerPersistor.Get(c => c.CustomerUsername == username); SIPDialogue replacesDialogue = (!replacesCallID.IsNullOrBlank() && customer != null) ? m_sipDialogueManager.GetDialogueRelaxed(customer.CustomerUsername, replacesCallID) : null; if (customer == null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web " + dialplanName + " rejected for " + username + " and " + number + ", as no matching user.", null)); return "Sorry no matching user was found, the " + dialplanName + " was not initiated."; } else if (customer.Suspended) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web " + dialplanName + " rejected for " + username + " and " + number + ", user account is suspended.", null)); return "Sorry the user's account is suspended."; } else if (customer.ServiceLevel == CustomerServiceLevels.PremiumPayReqd.ToString() || customer.ServiceLevel == CustomerServiceLevels.ProfessionalPayReqd.ToString() || customer.ServiceLevel == CustomerServiceLevels.SwitchboardPayReqd.ToString()) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web " + dialplanName + " rejected for " + username + " and " + number + ", user account requires payment.", null)); return "Sorry the user's account requires payment."; } else if (!replacesCallID.IsNullOrBlank() && replacesDialogue == null) { return "Sorry the blind transfer could not be initiated, the Call-ID to transfer could not be found."; } else { dialPlan = GetDialPlan_External(d => d.Owner == username && d.DialPlanName == dialplanName); if (dialPlan == null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web " + dialplanName + " rejected as no " + dialplanName + " dialplan exists.", username)); return "Sorry the specified user has not enabled callbacks, the callback was not initiated."; } else { if (!IsDialPlanExecutionAllowed(dialPlan, customer)) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Execution of web call for dialplan " + dialplanName + " was not processed as maximum execution count has been reached.", username)); return "Sorry the callback was not initiated, dial plan execution exceeded maximum allowed"; } else { //IncrementDialPlanExecutionCount(dialPlan, customer, originalExecutionCount + 1); //IncrementCustomerExecutionCount(customer); Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Web call for " + dialplanName + " initialising to " + number + ".", username)); ISIPServerUserAgent uas = null; if (replacesCallID.IsNullOrBlank()) { UASInviteTransaction dummyTransaction = GetDummyWebCallbackTransaction(number); uas = new SIPServerUserAgent(m_sipTransport, m_outboundProxy, username, SIPDomainManager.DEFAULT_LOCAL_DOMAIN, SIPCallDirection.Out, GetSIPAccount_External, null, Log_External, dummyTransaction); } else { SIPDialogue oppositeDialogue = m_sipDialogueManager.GetOppositeDialogue(replacesDialogue); uas = new SIPTransferServerUserAgent(Log_External, m_sipDialogueManager.DialogueTransfer, m_sipTransport, m_outboundProxy, replacesDialogue, oppositeDialogue, number, customer.CustomerUsername, customer.AdminId); m_inProgressTransfers.Add(oppositeDialogue, uas as SIPTransferServerUserAgent); } dialPlan.AuthorisedApps = customer.AuthorisedApps + ";" + dialPlan.AuthorisedApps; DialPlanScriptContext scriptContext = new DialPlanScriptContext( Log_External, m_sipTransport, CreateDialogueBridge, m_outboundProxy, uas, dialPlan, GetSIPProviders_External(p => p.Owner == username, null, 0, Int32.MaxValue), m_traceDirectory, null, customer, null, GetCanonicalDomain_External); //scriptContext.DialPlanComplete += () => { DecrementCustomerExecutionCount(customer); }; m_dialPlanEngine.Execute(scriptContext, uas, SIPCallDirection.Out, CreateDialogueBridge, this); if (replacesCallID.IsNullOrBlank()) { return "Web call was successfully initiated."; } else { return "Blind transfer was successfully initiated."; } } } } } catch (Exception excp) { logger.Error("Exception SIPCallManager ProcessWebCall. " + excp.Message); //if (wasExecutionCountIncremented) //{ //DecrementDialPlanExecutionCount(dialPlan, customer.Id, originalExecutionCount); //DecrementCustomerExecutionCount(customer); //} return "Sorry there was an unexpected error, the callback was not initiated."; } }