public void Hangup() { if (m_sipDialogue == null) { return; } try { SIPEndPoint localEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultSIPEndPoint(m_outboundProxy) : m_sipTransport.GetDefaultSIPEndPoint(GetRemoteTargetEndpoint()); SIPRequest byeRequest = GetByeRequest(localEndPoint); SIPNonInviteTransaction byeTransaction = m_sipTransport.CreateNonInviteTransaction(byeRequest, null, localEndPoint, m_outboundProxy); byeTransaction.NonInviteTransactionFinalResponseReceived += ByeServerFinalResponseReceived; byeTransaction.SendReliableRequest(); } catch (Exception excp) { logger.Error("Exception SIPServerUserAgent Hangup. " + excp.Message); throw; } }
/// <summary> /// Used to hangup the call or indicate that the client hungup. /// </summary> /// <param name="clientHungup">True if the BYE request was received from the client. False if the hangup /// needs to originate from this agent.</param> public void Hangup(bool clientHungup) { m_isHungup = true; if (m_sipDialogue == null) { return; } // Only need to send a BYE request if the client didn't already do so. if (clientHungup == false) { try { SIPEndPoint localEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultSIPEndPoint(m_outboundProxy) : m_sipTransport.GetDefaultSIPEndPoint(GetRemoteTargetEndpoint()); SIPRequest byeRequest = GetByeRequest(localEndPoint); SIPNonInviteTransaction byeTransaction = m_sipTransport.CreateNonInviteTransaction(byeRequest, null, localEndPoint, m_outboundProxy); byeTransaction.NonInviteTransactionFinalResponseReceived += ByeServerFinalResponseReceived; byeTransaction.SendReliableRequest(); } catch (Exception excp) { logger.LogError("Exception SIPServerUserAgent Hangup. " + excp.Message); throw; } } }
/*public static string GetDialogueId(string callId, string localTag, string remoteTag) * { * return Crypto.GetSHAHashAsString(callId + localTag + remoteTag); * } * * public static string GetDialogueId(SIPHeader sipHeader) * { * return Crypto.GetSHAHashAsString(sipHeader.CallId + sipHeader.To.ToTag + sipHeader.From.FromTag); * }*/ public void Hangup(SIPTransport sipTransport, SIPEndPoint outboundProxy) { try { SIPEndPoint byeOutboundProxy = null; if (outboundProxy != null && IPAddress.IsLoopback(outboundProxy.Address)) { byeOutboundProxy = outboundProxy; } else if (!ProxySendFrom.IsNullOrBlank()) { byeOutboundProxy = new SIPEndPoint(new IPEndPoint(SIPEndPoint.ParseSIPEndPoint(ProxySendFrom).Address, m_defaultSIPPort)); } else if (outboundProxy != null) { byeOutboundProxy = outboundProxy; } SIPEndPoint localEndPoint = (byeOutboundProxy != null) ? sipTransport.GetDefaultSIPEndPoint(byeOutboundProxy.Protocol) : sipTransport.GetDefaultSIPEndPoint(GetRemoteTargetProtocol()); SIPRequest byeRequest = GetByeRequest(localEndPoint); SIPNonInviteTransaction byeTransaction = sipTransport.CreateNonInviteTransaction(byeRequest, null, localEndPoint, byeOutboundProxy); byeTransaction.SendReliableRequest(); } catch (Exception excp) { logger.Error("Exception SIPDialogue Hangup. " + excp.Message); throw; } }
/// <summary> /// Initialises a SIP transport to act as a server in single request/response exchange. /// </summary> /// <param name="testServerChannel">The server SIP channel to test.</param> /// <param name="cts">Cancellation token to tell the server when to shutdown.</param> private void RunServer(SIPChannel testServerChannel, CancellationTokenSource cts) { logger.LogDebug($"Starting server task for {testServerChannel.SIPChannelEndPoint.ToString()}."); var serverSIPTransport = new SIPTransport(); try { serverSIPTransport.AddSIPChannel(testServerChannel); logger.LogDebug(serverSIPTransport.GetDefaultSIPEndPoint().ToString()); serverSIPTransport.SIPTransportRequestReceived += (SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => { logger.LogDebug($"Request received {localSIPEndPoint.ToString()}<-{remoteEndPoint.ToString()}: {sipRequest.StatusLine}"); if (sipRequest.Method == SIPMethodsEnum.OPTIONS) { SIPResponse optionsResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); serverSIPTransport.SendResponse(optionsResponse); } }; cts.Token.WaitHandle.WaitOne(); //WaitHandle.WaitAny(new[] { cts.Token.WaitHandle }); } finally { logger.LogDebug($"Server task for completed for {testServerChannel.SIPChannelEndPoint.ToString()}."); serverSIPTransport.Shutdown(); } }
public void SendRequest(SIPMethodsEnum method) { try { var req = GetRequest(method); var tran = m_sipTransport.CreateNonInviteTransaction(req, null, m_sipTransport.GetDefaultSIPEndPoint(), m_outboundProxy); using var waitForResponse = new ManualResetEvent(false); tran.NonInviteTransactionTimedOut += RequestTimedOut; tran.NonInviteTransactionFinalResponseReceived += ServerResponseReceived; tran.SendReliableRequest(); } catch (Exception excp) { logger.Error("Exception SIPNonInviteClientUserAgent SendRequest to " + m_callDescriptor.Uri + ". " + excp.Message); throw; } }
public SIPRegistrationUserAgent( SIPTransport sipTransport, string username, string password, string server) { m_sipTransport = sipTransport; m_localEndPoint = sipTransport.GetDefaultSIPEndPoint(); m_sipAccountAOR = new SIPURI(username, server, null, SIPSchemesEnum.sip, SIPProtocolsEnum.udp); m_authUsername = username; m_password = password; m_registrarHost = server; m_contactURI = new SIPURI(SIPSchemesEnum.sip, m_localEndPoint); m_expiry = DEFAULT_REGISTER_EXPIRY; m_callID = Guid.NewGuid().ToString(); Log_External = (ev) => logger.LogDebug(ev?.Message); }
/// <summary> /// Initialises a SIP tranpsort to act as the client in a single request/response exchange. /// </summary> /// <param name="testClientChannel">The client SIP channel to test.</param> /// <param name="serverUri">The URI of the server end point to test the client against.</param> /// <param name="tcs">The task completion source that this method will set if it receives the expected response.</param> private async Task RunClient(SIPChannel testClientChannel, SIPURI serverUri, TaskCompletionSource <bool> tcs) { logger.LogDebug($"Starting client task for {testClientChannel.SIPChannelEndPoint.ToString()}."); var clientSIPTransport = new SIPTransport(); try { clientSIPTransport.AddSIPChannel(testClientChannel); logger.LogDebug(clientSIPTransport.GetDefaultSIPEndPoint().ToString()); clientSIPTransport.SIPTransportResponseReceived += (SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPResponse sipResponse) => { logger.LogDebug($"Response received {localSIPEndPoint.ToString()}<-{remoteEndPoint.ToString()}: {sipResponse.ShortDescription}"); logger.LogDebug(sipResponse.ToString()); if (sipResponse.Status == SIPResponseStatusCodesEnum.Ok) { // Got the expected response, set the signal. tcs.SetResult(true); } }; var optionsRequest = clientSIPTransport.GetRequest(SIPMethodsEnum.OPTIONS, serverUri); logger.LogDebug(optionsRequest.ToString()); clientSIPTransport.SendRequest(optionsRequest); await tcs.Task; } finally { logger.LogDebug($"Client task completed for {testClientChannel.SIPChannelEndPoint.ToString()}."); clientSIPTransport.Shutdown(); } }
/// <summary> /// Establishes a new call with the client end tied to the proxy. Since the proxy will not be sending any audio the idea is that once /// the call is up it should be re-INVITED off somewhere else pronto to avoid the callee sitting their listening to dead air. /// </summary> /// <param name="dest1">The dial string of the first call to place.</param> /// <param name="dest2">The dial string of the second call to place.</param> /// <param name="delaySeconds">Delay in seconds before placing the first call. Gives the user a chance to hangup their phone if they are calling themselves back.</param> /// <param name="ringTimeoutLeg1">The ring timeout for the first call leg, If 0 the max timeout will be used.</param> /// <param name="ringTimeoutLeg1">The ring timeout for the second call leg, If 0 the max timeout will be used.</param> /// <param name="customHeadersCallLeg1">A | delimited string that contains a list of custom SIP headers to add to the INVITE request sent for the first call leg.</param> /// /// <param name="customHeadersCallLeg2">A | delimited string that contains a list of custom SIP headers to add to the INVITE request sent for the second call leg.</param> /// <returns>The result of the call.</returns> public void Callback(string dest1, string dest2, int delaySeconds, int ringTimeoutLeg1, int ringTimeoutLeg2, string customHeadersCallLeg1, string customHeadersCallLeg2) { var ts = new CancellationTokenSource(); CancellationToken ct = ts.Token; try { if (delaySeconds > 0) { delaySeconds = (delaySeconds > MAXCALLBACK_DELAY_SECONDS) ? MAXCALLBACK_DELAY_SECONDS : delaySeconds; Log("Callback app delaying by " + delaySeconds + "s."); Thread.Sleep(delaySeconds * 1000); } Log("Callback app commencing first leg to " + dest1 + "."); SIPEndPoint defaultUDPEP = m_sipTransport.GetDefaultSIPEndPoint(SIPProtocolsEnum.udp); SIPRequest firstLegDummyInviteRequest = GetCallbackInviteRequest(defaultUDPEP.GetIPEndPoint(), null); ForkCall firstLegCall = new ForkCall(m_sipTransport, Log_External, m_callManager.QueueNewCall, null, m_username, m_adminMemberId, m_outboundProxy, m_callManager, null); m_firstLegDialogue = Dial(firstLegCall, dest1, ringTimeoutLeg1, 0, firstLegDummyInviteRequest, SIPCallDescriptor.ParseCustomHeaders(customHeadersCallLeg1)); if (m_firstLegDialogue == null) { Log("The first call leg to " + dest1 + " was unsuccessful."); return; } // Persist the dialogue to the database so any hangup can be detected. m_sipDialoguePersistor.Add(new SIPDialogueAsset(m_firstLegDialogue)); SDP firstLegSDP = SDP.ParseSDPDescription(m_firstLegDialogue.RemoteSDP); string call1SDPIPAddress = firstLegSDP.Connection.ConnectionAddress; int call1SDPPort = firstLegSDP.Media[0].Port; Log("The first call leg to " + dest1 + " was successful, audio socket=" + call1SDPIPAddress + ":" + call1SDPPort + "."); Log("Callback app commencing second leg to " + dest2 + "."); SIPRequest secondLegDummyInviteRequest = GetCallbackInviteRequest(defaultUDPEP.GetIPEndPoint(), m_firstLegDialogue.RemoteSDP); ForkCall secondLegCall = new ForkCall(m_sipTransport, Log_External, m_callManager.QueueNewCall, null, m_username, m_adminMemberId, m_outboundProxy, m_callManager, null); Task.Factory.StartNew(() => { while (true) { Thread.Sleep(CHECK_FIRST_LEG_FOR_HANGUP_PERIOD); Console.WriteLine("Checking if first call leg is still up..."); if (ct.IsCancellationRequested) { Console.WriteLine("Checking first call leg task was cancelled."); break; } else { // Check that the first call leg hasn't been hung up. var dialog = m_sipDialoguePersistor.Get(m_firstLegDialogue.Id); if (dialog == null) { Console.WriteLine("First call leg has been hungup."); // The first call leg has been hungup while waiting for the second call. Log("The first call leg was hungup while the second call leg was waiting for an answer."); secondLegCall.CancelNotRequiredCallLegs(CallCancelCause.ClientCancelled); break; } } } Console.WriteLine("Checking first call leg task finished..."); }, ct); SIPDialogue secondLegDialogue = Dial(secondLegCall, dest2, ringTimeoutLeg2, 0, secondLegDummyInviteRequest, SIPCallDescriptor.ParseCustomHeaders(customHeadersCallLeg2)); ts.Cancel(); if (secondLegDialogue == null) { Log("The second call leg to " + dest2 + " was unsuccessful."); m_firstLegDialogue.Hangup(m_sipTransport, m_outboundProxy); return; } // Check that the first call leg hasn't been hung up. var firstLegDialog = m_sipDialoguePersistor.Get(m_firstLegDialogue.Id); if (firstLegDialog == null) { // The first call leg has been hungup while waiting for the second call. Log("The first call leg was hungup while waiting for the second call leg."); secondLegDialogue.Hangup(m_sipTransport, m_outboundProxy); return; } SDP secondLegSDP = SDP.ParseSDPDescription(secondLegDialogue.RemoteSDP); string call2SDPIPAddress = secondLegSDP.Connection.ConnectionAddress; int call2SDPPort = secondLegSDP.Media[0].Port; Log("The second call leg to " + dest2 + " was successful, audio socket=" + call2SDPIPAddress + ":" + call2SDPPort + "."); // Persist the second leg dialogue and update the bridge ID on the first call leg. Guid bridgeId = Guid.NewGuid(); secondLegDialogue.BridgeId = bridgeId; m_sipDialoguePersistor.Add(new SIPDialogueAsset(secondLegDialogue)); m_sipDialoguePersistor.UpdateProperty(firstLegDialog.Id, "BridgeID", bridgeId.ToString()); //m_callManager.CreateDialogueBridge(m_firstLegDialogue, secondLegDialogue, m_username); Log("Re-inviting Callback dialogues to each other."); m_callManager.ReInvite(m_firstLegDialogue, secondLegDialogue); //m_callManager.ReInvite(secondLegDialogue, m_firstLegDialogue.RemoteSDP); SendRTPPacket(call2SDPIPAddress + ":" + call2SDPPort, call1SDPIPAddress + ":" + call1SDPPort); SendRTPPacket(call1SDPIPAddress + ":" + call1SDPPort, call2SDPIPAddress + ":" + call2SDPPort); } catch (Exception excp) { logger.Error("Exception CallbackApp. " + excp); Log("Exception in Callback. " + excp); } finally { if (!ts.IsCancellationRequested) { ts.Cancel(); } } }
private void SendNotifyRequestForSubscription(SIPEventSubscription subscription) { try { subscription.SubscriptionDialogue.CSeq++; //logger.Debug(DateTime.Now.ToString("HH:mm:ss:fff") + " Sending NOTIFY request for " + subscription.SubscriptionDialogue.Owner + " event " + subscription.SubscriptionEventPackage.ToString() // + " and " + subscription.ResourceURI.ToString() + " to " + subscription.SubscriptionDialogue.RemoteTarget.ToString() + ", cseq=" + (subscription.SubscriptionDialogue.CSeq) + "."); int secondsRemaining = Convert.ToInt32(subscription.LastSubscribe.AddSeconds(subscription.Expiry).Subtract(DateTime.Now).TotalSeconds % Int32.MaxValue); SIPRequest notifyRequest = m_sipTransport.GetRequest(SIPMethodsEnum.NOTIFY, subscription.SubscriptionDialogue.RemoteTarget); notifyRequest.Header.From = SIPFromHeader.ParseFromHeader(subscription.SubscriptionDialogue.LocalUserField.ToString()); notifyRequest.Header.To = SIPToHeader.ParseToHeader(subscription.SubscriptionDialogue.RemoteUserField.ToString()); notifyRequest.Header.Event = subscription.SubscriptionEventPackage.ToString(); notifyRequest.Header.CSeq = subscription.SubscriptionDialogue.CSeq; notifyRequest.Header.CallId = subscription.SubscriptionDialogue.CallId; notifyRequest.Body = subscription.GetNotifyBody(); notifyRequest.Header.ContentLength = notifyRequest.Body.Length; notifyRequest.Header.SubscriptionState = "active;expires=" + secondsRemaining.ToString(); notifyRequest.Header.ContentType = subscription.NotifyContentType; notifyRequest.Header.ProxySendFrom = subscription.SubscriptionDialogue.ProxySendFrom; // If the outbound proxy is a loopback address, as it will normally be for local deployments, then it cannot be overriden. SIPEndPoint dstEndPoint = m_outboundProxy; if (m_outboundProxy != null && IPAddress.IsLoopback(m_outboundProxy.Address)) { dstEndPoint = m_outboundProxy; } else if (subscription.SubscriptionDialogue.ProxySendFrom != null) { // The proxy will always be listening on UDP port 5060 for requests from internal servers. dstEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(SIPEndPoint.ParseSIPEndPoint(subscription.SubscriptionDialogue.ProxySendFrom).Address, m_defaultSIPPort)); } SIPNonInviteTransaction notifyTransaction = m_sipTransport.CreateNonInviteTransaction(notifyRequest, dstEndPoint, m_sipTransport.GetDefaultSIPEndPoint(dstEndPoint), m_outboundProxy); notifyTransaction.NonInviteTransactionFinalResponseReceived += (local, remote, transaction, response) => { NotifyTransactionFinalResponseReceived(local, remote, transaction, response, subscription); }; m_sipTransport.SendSIPReliable(notifyTransaction); //logger.Debug(notifyRequest.ToString()); MonitorLogEvent_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.NotifySent, "Notification sent for " + subscription.SubscriptionEventPackage.ToString() + " and " + subscription.ResourceURI.ToString() + " to " + subscription.SubscriptionDialogue.RemoteTarget.ToString() + " (cseq=" + notifyRequest.Header.CSeq + ").", subscription.SubscriptionDialogue.Owner)); subscription.NotificationSent(); } catch (Exception excp) { logger.Error("Exception SendNotifyRequestForSubscription. " + excp.Message); throw; } }
/// <summary> /// Used to send a request from an internal server agent to an external SIP user agent. The difference between this method and /// the SendTransparent method is that this one will set Via headers in accordance with RFC3261. /// </summary> /// <param name="receivedOnEP">The proxy SIP end point the request was received on.</param> /// <param name="dstSocket">The SIP end point the request is being sent to.</param> /// <param name="sipRequest">The SIP request to send.</param> /// <param name="proxyBranch">The branch parameter for the top Via header that has been pre-calculated by the proxy core.</param> /// <param name="sendFromSocket">The proxy SIP end point to send this request from. If the SIP request has its ProxySendFrom header /// value set that will overrule this parameter.</param> public void SendExternal(SIPEndPoint receivedOnEP, SIPEndPoint dstSIPEndPoint, SIPRequest sipRequest, string proxyBranch, IPAddress publicIPAddress) { try { if (!IsDestinationValid(sipRequest, dstSIPEndPoint)) { logger.Debug("SendExternal failed destination check."); return; } // Determine the external SIP endpoint that the proxy will use to send this request. SIPEndPoint localSIPEndPoint = m_sipTransport.GetDefaultSIPEndPoint(dstSIPEndPoint); if (!sipRequest.Header.ProxySendFrom.IsNullOrBlank()) { SIPChannel proxyChannel = m_sipTransport.FindSIPChannel(SIPEndPoint.ParseSIPEndPoint(sipRequest.Header.ProxySendFrom)); if (proxyChannel == null) { logger.Warn("No SIP channel could be found for\n" + sipRequest.ToString()); } localSIPEndPoint = (proxyChannel != null) ? proxyChannel.SIPChannelEndPoint : localSIPEndPoint; } if (receivedOnEP != localSIPEndPoint) { // The proxy is being requested to send the request on a different socket to the one it was received on. // A second Via header is added to ensure the response can navigate back the same path. The calculated branch // parameter needs to go on the top Via header so that whichever internal socket the request is being sent to can // determine re-transmits. SIPViaHeader via = new SIPViaHeader(receivedOnEP, CallProperties.CreateBranchId()); sipRequest.Header.Vias.PushViaHeader(via); SIPViaHeader externalVia = new SIPViaHeader(localSIPEndPoint, proxyBranch); sipRequest.Header.Vias.PushViaHeader(externalVia); } else { // Only a single Via header is required as any response to this request will be sent from the same socket it gets received on. SIPViaHeader via = new SIPViaHeader(localSIPEndPoint, proxyBranch); sipRequest.Header.Vias.PushViaHeader(via); } if (sipRequest.Method != SIPMethodsEnum.REGISTER) { AdjustContactHeader(sipRequest.Header, localSIPEndPoint, publicIPAddress); } sipRequest.LocalSIPEndPoint = localSIPEndPoint; // Proxy sepecific headers that don't need to be seen by external UAs. sipRequest.Header.ProxyReceivedOn = null; sipRequest.Header.ProxyReceivedFrom = null; sipRequest.Header.ProxySendFrom = null; m_sipTransport.SendRequest(dstSIPEndPoint, sipRequest); } catch (Exception excp) { logger.Error("Exception SIPRequest SendExternal. " + excp.Message); logger.Error(sipRequest.ToString()); throw; } }
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 example."); Console.WriteLine("Press ctrl-c to exit."); // Plumbing code to facilitate a graceful exit. CancellationTokenSource cts = new CancellationTokenSource(); bool isCallHungup = false; bool hasCallFailed = false; // 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(); int port = SIPConstants.DEFAULT_SIP_PORT + 1000; sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Loopback, port))); sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.IPv6Loopback, port))); sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(LocalIPConfig.GetDefaultIPv4Address(), port))); sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(LocalIPConfig.GetDefaultIPv6Address(), port))); // Select the IP address to use for RTP based on the destination SIP URI. SIPURI callURI = SIPURI.ParseSIPURIRelaxed(DESTINATION_SIP_URI); var endPointForCall = callURI.ToSIPEndPoint() == null?sipTransport.GetDefaultSIPEndPoint(callURI.Protocol) : sipTransport.GetDefaultSIPEndPoint(callURI.ToSIPEndPoint()); // Initialise an RTP session to receive the RTP packets from the remote SIP server. Socket rtpSocket = null; Socket controlSocket = null; NetServices.CreateRtpSocket(endPointForCall.Address, 49000, 49100, false, out rtpSocket, out controlSocket); var rtpSendSession = new RTPSession((int)RTPPayloadTypesEnum.PCMU, null, null); // Create a client user agent to place a call to a remote SIP server along with event handlers for the different stages of the call. var uac = new SIPClientUserAgent(sipTransport); uac.CallTrying += (uac, resp) => { SIPSorcery.Sys.Log.Logger.LogInformation($"{uac.CallDescriptor.To} Trying: {resp.StatusCode} {resp.ReasonPhrase}."); }; uac.CallRinging += (uac, resp) => SIPSorcery.Sys.Log.Logger.LogInformation($"{uac.CallDescriptor.To} Ringing: {resp.StatusCode} {resp.ReasonPhrase}."); uac.CallFailed += (uac, err) => { SIPSorcery.Sys.Log.Logger.LogWarning($"{uac.CallDescriptor.To} Failed: {err}"); hasCallFailed = true; }; uac.CallAnswered += (uac, resp) => { if (resp.Status == SIPResponseStatusCodesEnum.Ok) { SIPSorcery.Sys.Log.Logger.LogInformation($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); SIPSorcery.Sys.Log.Logger.LogDebug(resp.ToString()); IPEndPoint remoteRtpEndPoint = SDP.GetSDPRTPEndPoint(resp.Body); SIPSorcery.Sys.Log.Logger.LogDebug($"Sending initial RTP packet to remote RTP socket {remoteRtpEndPoint}."); // Send a dummy packet to open the NAT session on the RTP path. rtpSendSession.SendAudioFrame(rtpSocket, remoteRtpEndPoint, 0, new byte[] { 0x00 }); } else { SIPSorcery.Sys.Log.Logger.LogWarning($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); } }; // The only incoming request that needs to be explicitly handled for this example is if the remote end hangs up the call. sipTransport.SIPTransportRequestReceived += (SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => { if (sipRequest.Method == SIPMethodsEnum.BYE) { SIPNonInviteTransaction byeTransaction = sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null); SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); byeTransaction.SendFinalResponse(byeResponse); if (uac.IsUACAnswered) { SIPSorcery.Sys.Log.Logger.LogInformation("Call was hungup by remote server."); isCallHungup = true; cts.Cancel(); } } }; // It's a good idea to start the RTP receiving socket before the call request is sent. // A SIP server will generally start sending RTP as soon as it has processed the incoming call request and // being ready to receive will stop any ICMP error response being generated. Task.Run(() => SendRecvRtp(rtpSocket, rtpSendSession, cts)); // Start the thread that places the call. SIPCallDescriptor callDescriptor = new SIPCallDescriptor( SIPConstants.SIP_DEFAULT_USERNAME, null, DESTINATION_SIP_URI, SIPConstants.SIP_DEFAULT_FROMURI, null, null, null, null, SIPCallDirection.Out, SDP.SDP_MIME_CONTENTTYPE, GetSDP(rtpSocket.LocalEndPoint as IPEndPoint).ToString(), null); uac.Call(callDescriptor); // At this point the call has been initiated and everything will be handled in an event handler or on the RTP // receive task. The code below is to gracefully exit. // Ctrl-c will gracefully exit the call at any point. Console.CancelKeyPress += async delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; cts.Cancel(); SIPSorcery.Sys.Log.Logger.LogInformation("Exiting..."); rtpSocket?.Close(); controlSocket?.Close(); if (!isCallHungup && uac != null) { if (uac.IsUACAnswered) { SIPSorcery.Sys.Log.Logger.LogInformation($"Hanging up call to {uac.CallDescriptor.To}."); uac.Hangup(); } else if (!hasCallFailed) { SIPSorcery.Sys.Log.Logger.LogInformation($"Cancelling call to {uac.CallDescriptor.To}."); uac.Cancel(); } // Give the BYE or CANCEL request time to be transmitted. SIPSorcery.Sys.Log.Logger.LogInformation("Waiting 1s for call to clean up..."); await Task.Delay(1000); } SIPSorcery.Net.DNSManager.Stop(); if (sipTransport != null) { SIPSorcery.Sys.Log.Logger.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } }; }
public void ProcessNotifyRequest(SIPRequest sipRequest) { try { // Hack to work around MWI request from callcentric not having a trailing CRLF and breaking some softphones like the Bria. if (sipRequest.Header.Event == MWI_EVENT_TYPE && sipRequest.Body.NotNullOrBlank() && sipRequest.Body.Substring(sipRequest.Body.Length - 2) != m_crlf) { sipRequest.Body += m_crlf; } string fromURI = (sipRequest.Header.From != null && sipRequest.Header.From.FromURI != null) ? sipRequest.Header.From.FromURI.ToString() : "unknown"; string domain = GetCanonicalDomain_External(sipRequest.URI.Host, true); if (domain != null) { SIPAccountAsset sipAccount = GetSIPAccount_External(s => s.SIPUsername == sipRequest.URI.User && s.SIPDomain == domain); if (sipAccount != null) { List <SIPRegistrarBinding> bindings = GetSIPAccountBindings_External(b => b.SIPAccountId == sipAccount.Id, null, 0, MAX_FORWARD_BINDINGS); if (bindings != null) { foreach (SIPRegistrarBinding binding in bindings) { SIPURI dstURI = binding.MangledContactSIPURI; SIPEndPoint localSIPEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultSIPEndPoint(m_outboundProxy.Protocol) : m_sipTransport.GetDefaultSIPEndPoint(dstURI.Protocol); SIPEndPoint dstSIPEndPoint = null; // If the outbound proxy is a loopback address, as it will normally be for local deployments, then it cannot be overriden. if (m_outboundProxy != null && IPAddress.IsLoopback(m_outboundProxy.Address)) { dstSIPEndPoint = m_outboundProxy; } else if (binding.ProxySIPEndPoint != null) { // If the binding has a specific proxy end point sent then the request needs to be forwarded to the proxy's default end point for it to take care of. dstSIPEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(binding.ProxySIPEndPoint.Address, m_defaultSIPPort)); } else if (m_outboundProxy != null) { dstSIPEndPoint = m_outboundProxy; } else { SIPDNSLookupResult lookupResult = m_sipTransport.GetURIEndPoint(dstURI, false); if (lookupResult.LookupError != null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "A NOTIFY request from " + fromURI + " was not forwarded due to DNS failure for " + dstURI.Host + ", " + lookupResult.LookupError + ".", sipAccount.Owner)); } else { dstSIPEndPoint = lookupResult.GetSIPEndPoint(); } } if (dstSIPEndPoint != null) { // Rather than create a brand new request copy the received one and modify the headers that need to be unique. SIPRequest notifyRequest = sipRequest.Copy(); notifyRequest.URI = dstURI; notifyRequest.Header.Contact = SIPContactHeader.CreateSIPContactList(new SIPURI(dstURI.Scheme, localSIPEndPoint)); notifyRequest.Header.To = new SIPToHeader(null, dstURI, null); notifyRequest.Header.CallId = CallProperties.CreateNewCallId(); SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId()); notifyRequest.Header.Vias = new SIPViaSet(); notifyRequest.Header.Vias.PushViaHeader(viaHeader); // If the binding has a proxy socket defined set the header to ask the upstream proxy to use it. if (binding.ProxySIPEndPoint != null) { notifyRequest.Header.ProxySendFrom = binding.ProxySIPEndPoint.ToString(); // If the binding has a specific proxy end point sent then the request needs to be forwarded to the proxy's default end point for it to take care of. dstSIPEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(binding.ProxySIPEndPoint.Address, m_defaultSIPPort)); } Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "Forwarding NOTIFY request from " + fromURI + " to registered binding at " + dstURI.ToString() + ", proxy " + dstSIPEndPoint.ToString() + ".", sipAccount.Owner)); SIPNonInviteTransaction notifyTransaction = m_sipTransport.CreateNonInviteTransaction(notifyRequest, dstSIPEndPoint, localSIPEndPoint, dstSIPEndPoint); notifyTransaction.SendReliableRequest(); } else { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "A NOTIFY request from " + fromURI + " was not forwarded as no destination end point was resolved.", sipAccount.Owner)); } } // Send OK response to server. SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); m_sipTransport.SendResponse(okResponse); } else { // Send unavailable response to server. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "NOTIFY request from " + fromURI + " for " + sipAccount.SIPUsername + "@" + sipAccount.SIPDomain + " but no bindings available, responding with temporarily unavailable.", sipAccount.Owner)); SIPResponse notAvailableResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.TemporarilyUnavailable, null); m_sipTransport.SendResponse(notAvailableResponse); } } else { // Send Not found response to server. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "NOTIFY request from " + fromURI + " for " + sipRequest.URI.ToString() + " but no matching SIP account, responding with not found.", null)); SIPResponse notFoundResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotFound, null); m_sipTransport.SendResponse(notFoundResponse); } } else { // Send Not Serviced response to server. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "NOTIFY request from " + fromURI + " for a non-serviced domain responding with not found.", null)); SIPResponse notServicedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotFound, "Domain not serviced"); m_sipTransport.SendResponse(notServicedResponse); } } catch (Exception excp) { logger.Error("Exception SIPNotifyManager ProcessNotifyRequest. " + excp.Message); } }
private void StartSTUNServer(IPEndPoint primaryEndPoint, IPEndPoint secondaryEndPoint, SIPTransport sipTransport) { STUNListener secondarySTUNListener = new STUNListener(secondaryEndPoint); // This end point is only for secondary STUN messages. STUNSendMessageDelegate primarySend = (dst, buffer) => { m_sipTransport.SendRaw(m_sipTransport.GetDefaultSIPEndPoint(SIPProtocolsEnum.udp), new SIPEndPoint(dst), buffer); }; m_stunServer = new STUNServer(primaryEndPoint, primarySend, secondaryEndPoint, secondarySTUNListener.Send); sipTransport.STUNRequestReceived += m_stunServer.STUNPrimaryReceived; sipTransport.STUNRequestReceived += LogPrimarySTUNRequestReceived; secondarySTUNListener.MessageReceived += m_stunServer.STUNSecondaryReceived; secondarySTUNListener.MessageReceived += LogSecondarySTUNRequestReceived; logger.Debug("STUN server successfully initialised."); }