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);
            }
        }
예제 #2
0
        /// <summary>
        /// Initiates a SUBSCRIBE request to a notification server.
        /// </summary>
        /// <param name="subscribeToURI">The SIP user that dialog notifications are being subscribed to.</param>
        public void Subscribe(SIPURI subscribeURI, int expiry, SIPEventPackage sipEventPackage, string subscribeCallID, SIPURI contactURI)
        {
            try
            {
                if (m_attempts >= MAX_SUBSCRIBE_ATTEMPTS)
                {
                    Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.NotifierClient, SIPMonitorEventTypesEnum.SubscribeFailed, "Susbcription to " + subscribeURI.ToString() + " reached the maximum number of allowed attempts without a failure condition.", null));
                    m_subscribed = false;
                    SubscriptionFailed(subscribeURI, SIPResponseStatusCodesEnum.InternalServerError, "Subscription reached the maximum number of allowed attempts.");
                    m_waitForSubscribeResponse.Set();
                }
                else
                {
                    m_attempts++;
                    m_localCSeq++;

                    SIPRequest subscribeRequest = m_sipTransport.GetRequest(
                        SIPMethodsEnum.SUBSCRIBE,
                        m_resourceURI,
                        new SIPToHeader(null, subscribeURI, m_subscriptionToTag),
                        null);


                    if (contactURI != null)
                    {
                        subscribeRequest.Header.Contact = new List <SIPContactHeader>()
                        {
                            new SIPContactHeader(null, contactURI)
                        };
                    }

                    subscribeRequest.Header.From    = new SIPFromHeader(null, new SIPURI(m_authUsername, m_authDomain, null, SIPSchemesEnum.sip, SIPProtocolsEnum.udp), m_subscriptionFromTag);
                    subscribeRequest.Header.CSeq    = m_localCSeq;
                    subscribeRequest.Header.Expires = expiry;
                    subscribeRequest.Header.Event   = sipEventPackage.ToString();
                    subscribeRequest.Header.CallId  = subscribeCallID;

                    if (!m_filter.IsNullOrBlank())
                    {
                        subscribeRequest.Body = m_filter;
                        subscribeRequest.Header.ContentLength = m_filter.Length;
                        subscribeRequest.Header.ContentType   = m_filterTextType;
                    }

                    SIPEndPoint dstEndPoint = m_outboundProxy;
                    if (dstEndPoint == null)
                    {
                        SIPDNSLookupResult lookupResult = m_sipTransport.GetURIEndPoint(m_resourceURI, false);
                        if (lookupResult.LookupError != null)
                        {
                            Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.NotifierClient, SIPMonitorEventTypesEnum.SubscribeFailed, "Could not resolve " + m_resourceURI.Host + ", " + lookupResult.LookupError, null));
                        }
                        else
                        {
                            dstEndPoint = lookupResult.GetSIPEndPoint();
                        }
                    }

                    SIPNonInviteTransaction subscribeTransaction = m_sipTransport.CreateNonInviteTransaction(subscribeRequest, dstEndPoint, null, m_outboundProxy);
                    subscribeTransaction.NonInviteTransactionFinalResponseReceived += SubscribeTransactionFinalResponseReceived;
                    subscribeTransaction.NonInviteTransactionTimedOut += SubsribeTransactionTimedOut;

                    m_sipTransport.SendSIPReliable(subscribeTransaction);

                    LastSubscribeAttempt = DateTime.Now;
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception SIPNotifierClient Subscribe. " + excp.Message);
                SubscriptionFailed(m_resourceURI, SIPResponseStatusCodesEnum.InternalServerError, "Exception Subscribing. " + excp.Message);
                m_waitForSubscribeResponse.Set();
            }
        }
예제 #3
0
        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 rtpCts = new CancellationTokenSource(); // Cancellation token to stop the RTP stream.
            bool isCallHungup  = false;
            bool hasCallFailed = false;

            AddConsoleLogger();

            SIPURI callUri = SIPURI.ParseSIPURI(DEFAULT_DESTINATION_SIP_URI);

            Log.LogInformation($"Call destination {callUri}.");

            // Set up a default SIP transport.
            var sipTransport = new SIPTransport();

            sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, 0)));

            // Un/comment this line to see/hide each SIP message sent and received.
            //EnableTraceLogs(sipTransport);

            // Determine the local IP address to use for our RTP end point based on the address it will be sending to.
            var lookupResult = sipTransport.GetURIEndPoint(callUri, false);

            if (lookupResult != null)
            {
                var       uasSIPEndPoint = lookupResult.GetSIPEndPoint();
                IPAddress rtpAddress     = NetServices.GetLocalAddressForRemote(uasSIPEndPoint.GetIPEndPoint().Address);

                Log.LogInformation($"Call URI {callUri} resolved to {uasSIPEndPoint}.");

                // Initialise an RTP session to receive the RTP packets from the remote SIP server.
                Socket rtpSocket     = null;
                Socket controlSocket = null;
                NetServices.CreateRtpSocket(rtpAddress, 49000, 49100, false, out rtpSocket, out controlSocket);
                var rtpRecvSession = new RTPSession((int)RTPPayloadTypesEnum.PCMU, null, null);
                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) =>
                {
                    Log.LogInformation($"{uac.CallDescriptor.To} Trying: {resp.StatusCode} {resp.ReasonPhrase}.");
                    Log.LogDebug(resp.ToString());
                };
                uac.CallRinging += (uac, resp) => Log.LogInformation($"{uac.CallDescriptor.To} Ringing: {resp.StatusCode} {resp.ReasonPhrase}.");
                uac.CallFailed  += (uac, err) =>
                {
                    Log.LogWarning($"{uac.CallDescriptor.To} Failed: {err}");
                    hasCallFailed = true;
                    rtpCts.Cancel();
                };
                uac.CallAnswered += (uac, resp) =>
                {
                    if (resp.Status == SIPResponseStatusCodesEnum.Ok)
                    {
                        Log.LogInformation($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}.");

                        _remoteRtpEndPoint = SDP.GetSDPRTPEndPoint(resp.Body);

                        Log.LogDebug($"Remote RTP socket {_remoteRtpEndPoint}.");
                    }
                    else
                    {
                        Log.LogWarning($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}.");

                        if (resp.StatusCode >= 400)
                        {
                            // Call failed.
                            hasCallFailed = true;
                            rtpCts.Cancel();
                        }
                    }
                };

                // 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, null);
                        SIPResponse             byeResponse    = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                        byeTransaction.SendFinalResponse(byeResponse);

                        if (uac.IsUACAnswered)
                        {
                            Log.LogInformation("Call was hungup by remote server.");
                            isCallHungup = true;
                            rtpCts.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(() => RecvRtp(rtpSocket, rtpRecvSession, rtpCts));
                Task.Run(() => SendRtp(rtpSocket, rtpSendSession, rtpCts));

                // Start the thread that places the call.
                SIPCallDescriptor callDescriptor = new SIPCallDescriptor(
                    SIPConstants.SIP_DEFAULT_USERNAME,
                    null,
                    callUri.ToString(),
                    SIPConstants.SIP_DEFAULT_FROMURI,
                    null, null, null, null,
                    SIPCallDirection.Out,
                    SDP.SDP_MIME_CONTENTTYPE,
                    GetSDP(rtpSocket.LocalEndPoint as IPEndPoint).ToString(),
                    null);

                uac.Call(callDescriptor);

                // Ctrl-c will gracefully exit the call at any point.
                Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
                {
                    e.Cancel = true;
                    rtpCts.Cancel();
                };

                // At this point the call is established. We'll wait for a few seconds and then transfer.
                // Note the empty ContinueWith is to catch and ignore the TaskCancelationExeption generated if
                // rtpCts is set by the call failing.
                Task.Delay(DELAY_UNTIL_TRANSFER_MILLISECONDS, rtpCts.Token).ContinueWith(tsk => { }).Wait();

                if (!hasCallFailed)
                {
                    SIPRequest referRequest         = GetReferRequest(uac, SIPURI.ParseSIPURI(TRANSFER_DESTINATION_SIP_URI));
                    SIPNonInviteTransaction referTx = sipTransport.CreateNonInviteTransaction(referRequest, null);

                    referTx.NonInviteTransactionFinalResponseReceived += (SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) =>
                    {
                        if (sipResponse.Header.CSeqMethod == SIPMethodsEnum.REFER && sipResponse.Status == SIPResponseStatusCodesEnum.Accepted)
                        {
                            Log.LogInformation("Call transfer was accepted by remote server.");
                            isCallHungup = true;
                            rtpCts.Cancel();
                        }
                    };

                    referTx.SendReliableRequest();
                }

                // At this point the call transfer 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.

                // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed.
                rtpCts.Token.WaitHandle.WaitOne();

                Log.LogInformation("Exiting...");

                rtpSocket?.Close();
                controlSocket?.Close();

                if (!isCallHungup && uac != null)
                {
                    if (uac.IsUACAnswered)
                    {
                        Log.LogInformation($"Hanging up call to {uac.CallDescriptor.To}.");
                        uac.Hangup();
                    }
                    else if (!hasCallFailed)
                    {
                        Log.LogInformation($"Cancelling call to {uac.CallDescriptor.To}.");
                        uac.Cancel();
                    }

                    // Give the BYE or CANCEL request time to be transmitted.
                    Log.LogInformation("Waiting 1s for call to clean up...");
                    Task.Delay(1000).Wait();
                }
            }

            SIPSorcery.Net.DNSManager.Stop();

            if (sipTransport != null)
            {
                Log.LogInformation("Shutting down SIP transport...");
                sipTransport.Shutdown();
            }
        }