예제 #1
0
        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;
            }
        }
예제 #2
0
        /// <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;
                }
            }
        }
예제 #3
0
        /*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();
            }
        }
예제 #5
0
        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();
            }
        }
예제 #8
0
        /// <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;
            }
        }
예제 #11
0
        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);
            }
        }
예제 #13
0
        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.");
        }