예제 #1
0
        public void AddInviteRequest(SIPRequest inviteRequest)
        {
            if (inviteRequest.Method != SIPMethodsEnum.INVITE)
            {
                SIPResponse notSupportedResponse = SIPResponse.GetResponse(inviteRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, "Invite requests only");
                _sipTransport.SendResponseAsync(notSupportedResponse).Wait();
            }
            else
            {
                if (_inviteQueue.Count < MAX_INVITE_QUEUE_SIZE)
                {
                    UASInviteTransaction uasTransaction = new UASInviteTransaction(_sipTransport, inviteRequest, null);
                    var trying = SIPResponse.GetResponse(inviteRequest, SIPResponseStatusCodesEnum.Trying, null);
                    uasTransaction.SendProvisionalResponse(trying).Wait();

                    _inviteQueue.Enqueue(uasTransaction);
                }
                else
                {
                    Logger.LogWarning($"Invite queue exceeded max queue size {MAX_INVITE_QUEUE_SIZE} overloaded response sent.");
                    SIPResponse overloadedResponse = SIPResponse.GetResponse(inviteRequest, SIPResponseStatusCodesEnum.TemporarilyUnavailable, "B2BUA overloaded, please try again shortly");
                    _sipTransport.SendResponseAsync(overloadedResponse).Wait();
                }

                _inviteARE.Set();
            }
        }
예제 #2
0
        /// <summary>
        /// Handler for processing incoming SIP requests.
        /// </summary>
        /// <param name="localSIPEndPoint">The end point the request was received on.</param>
        /// <param name="remoteEndPoint">The end point the request came from.</param>
        /// <param name="sipRequest">The SIP request received.</param>
        private Task SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            if (sipRequest.Header.From != null &&
                sipRequest.Header.From.FromTag != null &&
                sipRequest.Header.To != null &&
                sipRequest.Header.To.ToTag != null)
            {
                // This is an in-dialog request that will be handled directly by a user agent instance.
            }
            else if (sipRequest.Method == SIPMethodsEnum.INVITE)
            {
                bool?callAccepted = IncomingCall?.Invoke(sipRequest);

                if (callAccepted == false)
                {
                    // All user agents were already on a call return a busy response.
                    UASInviteTransaction uasTransaction = new UASInviteTransaction(SIPTransport, sipRequest, null);
                    SIPResponse          busyResponse   = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.BusyHere, null);
                    uasTransaction.SendFinalResponse(busyResponse);
                }
            }
            else
            {
                logger.Debug("SIP " + sipRequest.Method + " request received but no processing has been set up for it, rejecting.");
                SIPResponse notAllowedResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null);
                return(SIPTransport.SendResponseAsync(notAllowedResponse));
            }

            return(Task.FromResult(0));
        }
예제 #3
0
        public SIPServerUserAgent(
            SIPTransport sipTransport,
            SIPEndPoint outboundProxy,
            string sipUsername,
            string sipDomain,
            SIPCallDirection callDirection,
            SIPAssetGetDelegate<SIPAccount> getSIPAccount,
            SIPAuthenticateRequestDelegate sipAuthenticateRequest,
            SIPMonitorLogDelegate logDelegate,
            UASInviteTransaction uasTransaction)
        {
            m_sipTransport = sipTransport;
            m_outboundProxy = outboundProxy;
            m_sipUsername = sipUsername;
            m_sipDomain = sipDomain;
            m_sipCallDirection = callDirection;
            GetSIPAccount_External = getSIPAccount;
            SIPAuthenticateRequest_External = sipAuthenticateRequest;
            Log_External = logDelegate ?? Log_External;
            m_uasTransaction = uasTransaction;

            m_uasTransaction.TransactionTraceMessage += TransactionTraceMessage;
            m_uasTransaction.UASInviteTransactionTimedOut += ClientTimedOut;
            m_uasTransaction.UASInviteTransactionCancelled += UASTransactionCancelled;
            m_uasTransaction.TransactionRemoved += new SIPTransactionRemovedDelegate(UASTransaction_TransactionRemoved);
            //m_uasTransaction.TransactionStateChanged += (t) => { logger.Debug("Transaction state change to " + t.TransactionState + ", uri=" + t.TransactionRequestURI.ToString() + "."); };
        }
예제 #4
0
        private static void SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            if (sipRequest.Method == SIPMethodsEnum.INVITE)
            {
                Console.WriteLine("INVITE received from  " + localSIPEndPoint.ToString());
                IPEndPoint sipPhoneEndPoint = SDP.GetSDPRTPEndPoint(sipRequest.Body);

                UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null);
                SIPServerUserAgent   uas            = new SIPServerUserAgent(m_sipTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction);

                SIPResponse tryingResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Trying, null);
                uasTransaction.SendInformationalResponse(tryingResponse);

                if (m_xmppClient == null)
                {
                    m_xmppClient = new XMPPClient(XMPP_SERVER, XMPP_SERVER_PORT, XMPP_REALM, m_xmppUsername, m_xmppPassword);
                    m_xmppClient.Disconnected += XMPPDisconnected;
                    m_xmppClient.IsBound      += () => { XMPPPlaceCall(uas); };
                    ThreadPool.QueueUserWorkItem(delegate { m_xmppClient.Connect(); });
                }
                else
                {
                    XMPPPlaceCall(uas);
                }
            }
            else if (sipRequest.Method == SIPMethodsEnum.CANCEL)
            {
                UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE));

                if (inviteTransaction != null)
                {
                    Console.WriteLine("Matching CANCEL request received " + sipRequest.URI.ToString() + ".");
                    SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction);
                    cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest);
                }
                else
                {
                    Console.WriteLine("No matching transaction was found for CANCEL to " + sipRequest.URI.ToString() + ".");
                    SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null);
                    m_sipTransport.SendResponse(noCallLegResponse);
                }
            }
            else if (sipRequest.Method == SIPMethodsEnum.BYE)
            {
                Console.WriteLine("BYE request received.");

                if (m_activeCalls.ContainsKey(sipRequest.Header.CallId))
                {
                    SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                    m_sipTransport.SendResponse(okResponse);
                    m_activeCalls[sipRequest.Header.CallId].TerminateXMPPCall();
                    m_activeCalls.Remove(sipRequest.Header.CallId);
                }
                else
                {
                    SIPResponse doesntExistResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null);
                    m_sipTransport.SendResponse(doesntExistResponse);
                }
            }
        }
        public SIPServerUserAgent(
            SIPTransport sipTransport,
            SIPEndPoint outboundProxy,
            string sipUsername,
            string sipDomain,
            SIPCallDirection callDirection,
            GetSIPAccountDelegate getSIPAccount,
            SIPAuthenticateRequestDelegate sipAuthenticateRequest,
            SIPMonitorLogDelegate logDelegate,
            UASInviteTransaction uasTransaction)
        {
            m_sipTransport                  = sipTransport;
            m_outboundProxy                 = outboundProxy;
            m_sipUsername                   = sipUsername;
            m_sipDomain                     = sipDomain;
            CallDirection                   = callDirection;
            GetSIPAccount_External          = getSIPAccount;
            SIPAuthenticateRequest_External = sipAuthenticateRequest;
            Log_External                    = logDelegate ?? Log_External;
            m_uasTransaction                = uasTransaction;

            m_uasTransaction.TransactionTraceMessage       += TransactionTraceMessage;
            m_uasTransaction.UASInviteTransactionTimedOut  += ClientTimedOut;
            m_uasTransaction.UASInviteTransactionCancelled += UASTransactionCancelled;
            m_uasTransaction.TransactionRemoved            += new SIPTransactionRemovedDelegate(UASTransaction_TransactionRemoved);
        }
예제 #6
0
        public SIPServerUserAgent(
            SIPTransport sipTransport,
            SIPEndPoint outboundProxy,
            string sipUsername,
            string sipDomain,
            SIPCallDirection callDirection,
            SIPAssetGetDelegate <SIPAccount> getSIPAccount,
            SIPAuthenticateRequestDelegate sipAuthenticateRequest,
            SIPMonitorLogDelegate logDelegate,
            UASInviteTransaction uasTransaction)
        {
            m_sipTransport                  = sipTransport;
            m_outboundProxy                 = outboundProxy;
            m_sipUsername                   = sipUsername;
            m_sipDomain                     = sipDomain;
            m_sipCallDirection              = callDirection;
            GetSIPAccount_External          = getSIPAccount;
            SIPAuthenticateRequest_External = sipAuthenticateRequest;
            Log_External                    = logDelegate ?? Log_External;
            m_uasTransaction                = uasTransaction;

            m_uasTransaction.TransactionTraceMessage       += TransactionTraceMessage;
            m_uasTransaction.UASInviteTransactionTimedOut  += ClientTimedOut;
            m_uasTransaction.UASInviteTransactionCancelled += UASTransactionCancelled;
            m_uasTransaction.TransactionRemoved            += new SIPTransactionRemovedDelegate(UASTransaction_TransactionRemoved);
            //m_uasTransaction.TransactionStateChanged += (t) => { logger.Debug("Transaction state change to " + t.TransactionState + ", uri=" + t.TransactionRequestURI.ToString() + "."); };
        }
        public void Call(SIPCallDescriptor sipCallDescriptor)
        {
            try
            {
                m_uacCallDescriptor = sipCallDescriptor;
                SIPRequest uacInviteRequest = GetInviteRequest(m_uacCallDescriptor.Uri, sipCallDescriptor);
                if (sipCallDescriptor.MangleResponseSDP && sipCallDescriptor.MangleIPAddress != null)
                {
                    uacInviteRequest.Header.ProxyReceivedFrom = sipCallDescriptor.MangleIPAddress.ToString();
                }

                uacInviteRequest.Body = sipCallDescriptor.Content;
                uacInviteRequest.Header.ContentType = sipCallDescriptor.ContentType;
                uacInviteRequest.LocalSIPEndPoint   = m_blackhole;
                uacInviteRequest.RemoteSIPEndPoint  = m_blackhole;

                // Now that we have a destination socket create a new UAC transaction for forwarded leg of the call.
                m_uacTransaction =
                    m_sipTransport.CreateUACTransaction(uacInviteRequest, m_blackhole, m_blackhole, null);
                if (m_uacTransaction.CDR != null)
                {
                    m_uacTransaction.CDR.Owner             = m_uacOwner;
                    m_uacTransaction.CDR.AdminMemberId     = m_uacAdminMemberId;
                    m_uacTransaction.CDR.DialPlanContextID = (m_uacCallDescriptor != null)
                        ? m_uacCallDescriptor.DialPlanContextID
                        : Guid.Empty;
                }

                //uacTransaction.UACInviteTransactionInformationResponseReceived += ServerInformationResponseReceived;
                //uacTransaction.UACInviteTransactionFinalResponseReceived += ServerFinalResponseReceived;
                //uacTransaction.UACInviteTransactionTimedOut += ServerTimedOut;
                //uacTransaction.TransactionTraceMessage += TransactionTraceMessage;

                m_uacTransaction.SendInviteRequest(m_blackhole, m_uacTransaction.TransactionRequest);

                SIPRequest uasInviteRequest = uacInviteRequest.Copy();
                uasInviteRequest.LocalSIPEndPoint  = m_blackhole;
                uasInviteRequest.RemoteSIPEndPoint = m_blackhole;
                uasInviteRequest.Header.Vias.TopViaHeader.Branch = CallProperties.CreateBranchId();
                m_uasTransaction =
                    m_sipTransport.CreateUASTransaction(uasInviteRequest, m_blackhole, m_blackhole, null);

                SetOwner(sipCallDescriptor.ToSIPAccount.Owner, sipCallDescriptor.ToSIPAccount.AdminMemberId);
                //m_uasTransaction.TransactionTraceMessage += TransactionTraceMessage;
                //m_uasTransaction.UASInviteTransactionTimedOut += ClientTimedOut;
                //m_uasTransaction.UASInviteTransactionCancelled += (t) => { };

                QueueNewCall_External(this);

                CallTrying.Invoke(this, null);
            }
            catch (Exception excp)
            {
                logger.Error("Exception SIPB2BUserAgent Call. " + excp.Message);
            }
        }
예제 #8
0
 public SIPB2BUserAgent(
     SIPTransport sipTransport,
     SIPEndPoint outboundProxy,
     UASInviteTransaction uasTransaction,
     ISIPAccount sipAccount) :
     base(sipTransport, outboundProxy, uasTransaction, sipAccount)
 {
     IsB2B = true;
     base.CallCancelled += SIPServerUserAgent_CallCancelled;
 }
        public async Task BlindTransferCancelUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            SIPTransport transport = new SIPTransport();

            transport.AddSIPChannel(new MockSIPChannel(new System.Net.IPEndPoint(IPAddress.Any, 0)));

            SIPUserAgent userAgent = new SIPUserAgent(transport, null);

            string inviteReqStr = "INVITE sip:192.168.11.50:5060 SIP/2.0" + m_CRLF +
                                  "Via: SIP/2.0/UDP 192.168.11.50:60163;rport;branch=z9hG4bKPj869f70960bdd4204b1352eaf242a3691" + m_CRLF +
                                  "To: <sip:[email protected]>;tag=ZUJSXRRGXQ" + m_CRLF +
                                  "From: <sip:[email protected]>;tag=4a60ce364b774258873ff199e5e39938" + m_CRLF +
                                  "Call-ID: 17324d6df8744d978008c8997bfd208d" + m_CRLF +
                                  "CSeq: 3532 INVITE" + m_CRLF +
                                  "Contact: <sip:[email protected]:60163;ob>" + m_CRLF +
                                  "Max-Forwards: 70" + m_CRLF +
                                  "User-Agent: MicroSIP/3.19.22" + m_CRLF +
                                  "Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS" + m_CRLF +
                                  "Supported: replaces, 100rel, timer, norefersub" + m_CRLF +
                                  "Content-Length: 343" + m_CRLF +
                                  "Content-Type: application/sdp" + m_CRLF +
                                  "Session-Expires: 1800" + m_CRLF +
                                  "Min-SE: 90" + m_CRLF +
                                  "" + m_CRLF +
                                  "v=0" + m_CRLF +
                                  "o=- 3785527268 3785527269 IN IP4 192.168.11.50" + m_CRLF +
                                  "s=pjmedia" + m_CRLF +
                                  "t=0 0" + m_CRLF +
                                  "m=audio 4032 RTP/AVP 0 101" + m_CRLF +
                                  "c=IN IP4 192.168.11.50" + m_CRLF +
                                  "a=rtpmap:0 PCMU/8000" + m_CRLF +
                                  "a=rtpmap:101 telephone-event/8000" + m_CRLF +
                                  "a=fmtp:101 0-16" + m_CRLF +
                                  "a=sendrecv";

            SIPEndPoint      dummySipEndPoint = new SIPEndPoint(new IPEndPoint(IPAddress.Any, 0));
            SIPMessageBuffer sipMessageBuffer = SIPMessageBuffer.ParseSIPMessage(inviteReqStr, dummySipEndPoint, dummySipEndPoint);
            SIPRequest       inviteReq        = SIPRequest.ParseSIPRequest(sipMessageBuffer);

            UASInviteTransaction uasTx   = new UASInviteTransaction(transport, inviteReq, null);
            SIPServerUserAgent   mockUas = new SIPServerUserAgent(transport, null, null, null, SIPCallDirection.In, null, null, null, uasTx);
            await userAgent.Answer(mockUas, CreateMediaSession());

            CancellationTokenSource cts = new CancellationTokenSource();
            var blindTransferTask       = userAgent.BlindTransfer(SIPURI.ParseSIPURIRelaxed("127.0.0.1"), TimeSpan.FromSeconds(2), cts.Token);

            cts.Cancel();

            Assert.False(await blindTransferTask);

            //await Assert.ThrowsAnyAsync<TaskCanceledException>(async () => { bool result = ; });
        }
        public async Task CheckRemoteSocketProxyReceivedUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            SIPEndPoint dummyEP = new SIPEndPoint(new IPEndPoint(IPAddress.Any, 5060));

            string inviteReqStr =
                @"INVITE sip:[email protected]:12014 SIP/2.0
Via: SIP/2.0/UDP 127.0.0.1:1234;branch=z9hG4bK5f37455955ca433a902f8fea0ce2dc27
To: <sip:[email protected]:12014>
From: <sip:[email protected]>;tag=2062917371
Call-ID: 8ae45c15425040179a4285d774ccbaf6
CSeq: 1 INVITE
Contact: <sip:127.0.0.1:1234>
Max-Forwards: 70
User-Agent: unittest
Content-Length: 5
Content-Type: application/sdp
Proxy-ReceivedFrom: udp:192.168.0.50:5080

dummy";
            var        sipReqBuffer = SIPMessageBuffer.ParseSIPMessage(inviteReqStr, dummyEP, dummyEP);
            SIPRequest inviteReq    = SIPRequest.ParseSIPRequest(sipReqBuffer);

            string okRespStr =
                @"SIP/2.0 200 OK
Via: SIP/2.0/UDP 127.0.0.1:1234;branch=z9hG4bK5f37455955ca433a902f8fea0ce2dc27;rport=12013
To: <sip:[email protected]:12014>
From: <sip:[email protected]>;tag=2062917371
Call-ID: 8ae45c15425040179a4285d774ccbaf6
CSeq: 1 INVITE
Content-Length: 5
Content-Type: application/sdp

dummy";

            var         sipRespBuffer = SIPMessageBuffer.ParseSIPMessage(okRespStr, dummyEP, dummyEP);
            SIPResponse okResponse    = SIPResponse.ParseSIPResponse(sipRespBuffer);

            SIPTransport transport = new SIPTransport();

            transport.AddSIPChannel(new MockSIPChannel(dummyEP.GetIPEndPoint()));
            UASInviteTransaction uasTx = new UASInviteTransaction(transport, inviteReq, null);
            await uasTx.GotResponse(dummyEP, dummyEP, okResponse);

            var dialogue = new SIPDialogue(uasTx);

            Assert.NotNull(dialogue);
            Assert.Equal(SIPURI.ParseSIPURI("sip:127.0.0.1:1234"), dialogue.RemoteTarget);
            Assert.Equal(SIPEndPoint.ParseSIPEndPoint("udp:192.168.0.50:5080"), dialogue.RemoteSIPEndPoint);

            logger.LogDebug("---------------------------------------------------");
        }
        public void AckRecognitionUnitTest()
        {
            SIPTransport clientTransport = null;
            SIPTransport serverTransport = null;

            try
            {
                SIPTransactionEngine clientEngine   = new SIPTransactionEngine();   // Client side of the INVITE.
                SIPEndPoint          clientEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(IPAddress.Loopback, 12013));
                clientTransport = new SIPTransport(MockSIPDNSManager.Resolve, clientEngine, new SIPUDPChannel(clientEndPoint.GetIPEndPoint()), false);
                SetTransportTraceEvents(clientTransport);

                SIPTransactionEngine serverEngine      = new SIPTransactionEngine(); // Server side of the INVITE.
                UASInviteTransaction serverTransaction = null;
                SIPEndPoint          serverEndPoint    = new SIPEndPoint(new IPEndPoint(IPAddress.Loopback, 12014));
                serverTransport = new SIPTransport(MockSIPDNSManager.Resolve, serverEngine, new SIPUDPChannel(serverEndPoint.GetIPEndPoint()), false);
                SetTransportTraceEvents(serverTransport);
                serverTransport.SIPTransportRequestReceived += (localEndPoint, remoteEndPoint, sipRequest) =>
                {
                    Console.WriteLine("Server Transport Request In: " + sipRequest.Method + ".");
                    serverTransaction = serverTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localEndPoint, null);
                    SetTransactionTraceEvents(serverTransaction);
                    serverTransaction.GotRequest(localEndPoint, remoteEndPoint, sipRequest);
                };

                SIPURI     dummyURI      = SIPURI.ParseSIPURI("sip:dummy@" + serverEndPoint);
                SIPRequest inviteRequest = GetDummyINVITERequest(dummyURI);
                inviteRequest.LocalSIPEndPoint = clientTransport.GetDefaultTransportContact(SIPProtocolsEnum.udp);

                // Send the invite to the server side.
                UACInviteTransaction clientTransaction = new UACInviteTransaction(clientTransport, inviteRequest, serverEndPoint, clientEndPoint, null);
                SetTransactionTraceEvents(clientTransaction);
                clientEngine.AddTransaction(clientTransaction);
                clientTransaction.SendInviteRequest(serverEndPoint, inviteRequest);

                Thread.Sleep(500);

                Assert.IsTrue(clientTransaction.TransactionState == SIPTransactionStatesEnum.Completed, "Client transaction in incorrect state.");
                Assert.IsTrue(serverTransaction.TransactionState == SIPTransactionStatesEnum.Confirmed, "Server transaction in incorrect state.");
            }
            finally
            {
                if (clientTransport != null)
                {
                    clientTransport.Shutdown();
                }

                if (serverTransport != null)
                {
                    serverTransport.Shutdown();
                }
            }
        }
예제 #12
0
        public SIPServerUserAgent(
            SIPTransport sipTransport,
            SIPEndPoint outboundProxy,
            UASInviteTransaction uasTransaction,
            ISIPAccount sipAccount)
        {
            m_sipTransport   = sipTransport;
            m_outboundProxy  = outboundProxy;
            m_uasTransaction = uasTransaction;
            m_sipAccount     = sipAccount;

            m_uasTransaction.UASInviteTransactionFailed    += ClientTransactionFailed;
            m_uasTransaction.UASInviteTransactionCancelled += UASTransactionCancelled;
        }
        private DialPlanLineContext GetDummyDialPlanContext(string testDialPlan, string dst)
        {
            SIPDialPlan          dialPlan          = new SIPDialPlan(null, null, null, testDialPlan, SIPDialPlanScriptTypesEnum.Asterisk);
            SIPTransactionEngine transactionEngine = new SIPTransactionEngine();
            SIPTransport         sipTransport      = new SIPTransport(MockSIPDNSManager.Resolve, transactionEngine);
            SIPURI               dummyURI          = SIPURI.ParseSIPURI(dst);
            SIPRequest           inviteRequest     = GetDummyINVITERequest(dummyURI);
            SIPEndPoint          dummyEndPoint     = SIPEndPoint.ParseSIPEndPoint("udp:0.0.0.0:5060");
            UASInviteTransaction uasTransaction    = sipTransport.CreateUASTransaction(inviteRequest, dummyEndPoint, dummyEndPoint, null);
            SIPServerUserAgent   uas             = new SIPServerUserAgent(sipTransport, null, "test", "sipsorcery.com", SIPCallDirection.In, null, null, null, uasTransaction);
            DialPlanLineContext  dialPlanContext = new DialPlanLineContext(null, null, null, null, uas, dialPlan, null, null, null, null);

            return(dialPlanContext);
        }
예제 #14
0
        public SIPServerUserAgent(
            SIPTransport sipTransport,
            SIPEndPoint outboundProxy,
            UASInviteTransaction uasTransaction,
            ISIPAccount sipAccount)
        {
            m_sipTransport   = sipTransport;
            m_outboundProxy  = outboundProxy;
            m_uasTransaction = uasTransaction;
            m_sipAccount     = sipAccount;

            m_uasTransaction.UASInviteTransactionTimedOut  += ClientTimedOut;
            m_uasTransaction.UASInviteTransactionCancelled += UASTransactionCancelled;
            m_uasTransaction.TransactionRemoved            += new SIPTransactionRemovedDelegate(UASTransaction_TransactionRemoved);
        }
예제 #15
0
        /// <summary>
        /// This method can be used to start the processing of a new incoming call request.
        /// The user agent will is acting as a server for this operation and it can be considered
        /// the opposite of the Call method. This is only the first step in answering an incoming
        /// call. It can still be rejected or answered after this point.
        /// </summary>
        /// <param name="inviteRequest">The invite request representing the incoming call.</param>
        /// <returns>An ID string that needs to be supplied when the call is answered or rejected
        /// (used to manage multiple pending incoming calls).</returns>
        public SIPServerUserAgent AcceptCall(SIPRequest inviteRequest)
        {
            UASInviteTransaction uasTransaction = new UASInviteTransaction(m_transport, inviteRequest, m_outboundProxy);
            SIPServerUserAgent   uas            = new SIPServerUserAgent(m_transport, m_outboundProxy, null, null, SIPCallDirection.In, null, null, null, uasTransaction);

            uas.CallCancelled += (pendingUas) =>
            {
                CallEnded();
                ServerCallCancelled?.Invoke(pendingUas);
            };

            uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null);
            uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null);

            return(uas);
        }
        public void AckRecognitionIIUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            SIPTransport         sipTransport = new SIPTransport();
            SIPTransactionEngine engine       = sipTransport.m_transactionEngine; // Client side of the INVITE.

            string inviteRequestStr =
                "INVITE sip:[email protected] SIP/2.0" + m_CRLF +
                "Via: SIP/2.0/UDP 192.168.1.2:5065;rport;branch=z9hG4bKFBB7EAC06934405182D13950BD51F001" + m_CRLF +
                "From: SER Test X <sip:[email protected]:5065>;tag=196468136" + m_CRLF +
                "To: <sip:[email protected]>" + m_CRLF +
                "Contact: <sip:[email protected]:5065>" + m_CRLF +
                "Call-ID: [email protected]" + m_CRLF +
                "CSeq: 49429 INVITE" + m_CRLF +
                "Max-Forwards: 70" + m_CRLF +
                "Content-Type: application/sdp" + m_CRLF +
                "User-Agent: Dummy" + m_CRLF +
                m_CRLF;

            SIPRequest inviteRequest = SIPRequest.ParseSIPRequest(inviteRequestStr);

            // Server has received the invite.
            SIPEndPoint          dummySIPEndPoint  = new SIPEndPoint(new IPEndPoint(IPAddress.Loopback, 1234));
            UASInviteTransaction serverTransaction = new UASInviteTransaction(sipTransport, inviteRequest, null, true);

            engine.AddTransaction(serverTransaction);

            string ackRequestStr =
                "ACK sip:[email protected] SIP/2.0" + m_CRLF +
                "Via: SIP/2.0/UDP 192.168.1.2:5065;rport;branch=z9hG4bKFBB7EAC06934405182D13950BD51F001" + m_CRLF +
                "From: SER Test X <sip:[email protected]:5065>;tag=196468136" + m_CRLF +
                "To: <sip:[email protected]>" + m_CRLF +
                "Contact: <sip:[email protected]:5065>" + m_CRLF +
                "Call-ID: [email protected]" + m_CRLF +
                "CSeq: 49429 ACK" + m_CRLF +
                "Max-Forwards: 70" + m_CRLF +
                "User-Agent: Dummy" + m_CRLF +
                m_CRLF;

            SIPRequest ackRequest = SIPRequest.ParseSIPRequest(ackRequestStr);

            SIPTransaction matchingTransaction = engine.GetTransaction(ackRequest);

            Assert.True(matchingTransaction.TransactionId == serverTransaction.TransactionId, "ACK transaction did not match INVITE transaction.");
        }
예제 #17
0
        public RTSPApp(
            SIPMonitorLogDelegate statefulProxyLogEvent,
            UASInviteTransaction clientTransaction,
            string username)
        {
            m_statefulProxyLogEvent = statefulProxyLogEvent;

            m_clientTransaction = clientTransaction;
            //m_clientTransaction.TransactionCancelled += new SIPTransactionCancelledDelegate(CallCancelled);

            m_clientUsername = username;

            logger.Debug(m_clientTransaction.TransactionRequest.Body);

            m_rtpPort = Convert.ToInt32(Regex.Match(m_clientTransaction.TransactionRequest.Body, @"m=audio (?<port>\d+)", RegexOptions.Singleline).Result("${port}"));
            logger.Debug("RTP port=" + m_rtpPort);
        }
        public void AckRecognitionIIUnitTest()
        {
            SIPTransactionEngine engine = new SIPTransactionEngine();     // Client side of the INVITE.

            string inviteRequestStr =
                "INVITE sip:[email protected] SIP/2.0" + m_CRLF +
                "Via: SIP/2.0/UDP 192.168.1.2:5065;rport;branch=z9hG4bKFBB7EAC06934405182D13950BD51F001" + m_CRLF +
                "From: SER Test X <sip:[email protected]:5065>;tag=196468136" + m_CRLF +
                "To: <sip:[email protected]>" + m_CRLF +
                "Contact: <sip:[email protected]:5065>" + m_CRLF +
                "Call-ID: [email protected]" + m_CRLF +
                "CSeq: 49429 INVITE" + m_CRLF +
                "Max-Forwards: 70" + m_CRLF +
                "Content-Type: application/sdp" + m_CRLF +
                "User-Agent: Dummy" + m_CRLF +
                m_CRLF;

            SIPRequest inviteRequest = SIPRequest.ParseSIPRequest(inviteRequestStr);

            // Server has received the invite.
            SIPEndPoint          dummySIPEndPoint  = new SIPEndPoint(new IPEndPoint(IPAddress.Loopback, 1234));
            UASInviteTransaction serverTransaction = new UASInviteTransaction(new SIPTransport(MockSIPDNSManager.Resolve, null), inviteRequest, dummySIPEndPoint, dummySIPEndPoint, null, IPAddress.Loopback, true);

            engine.AddTransaction(serverTransaction);

            //SIPResponse errorResponse = SIPTransport.GetResponse(inviteRequest.Header, SIPResponseStatusCodesEnum.Decline, "Unit Test", null, null);

            string ackRequestStr =
                "ACK sip:[email protected] SIP/2.0" + m_CRLF +
                "Via: SIP/2.0/UDP 192.168.1.2:5065;rport;branch=z9hG4bKFBB7EAC06934405182D13950BD51F001" + m_CRLF +
                "From: SER Test X <sip:[email protected]:5065>;tag=196468136" + m_CRLF +
                "To: <sip:[email protected]>" + m_CRLF +
                "Contact: <sip:[email protected]:5065>" + m_CRLF +
                "Call-ID: [email protected]" + m_CRLF +
                "CSeq: 49429 ACK" + m_CRLF +
                "Max-Forwards: 70" + m_CRLF +
                "User-Agent: Dummy" + m_CRLF +
                m_CRLF;

            SIPRequest ackRequest = SIPRequest.ParseSIPRequest(ackRequestStr);

            SIPTransaction matchingTransaction = engine.GetTransaction(ackRequest);

            Assert.IsTrue(matchingTransaction.TransactionId == serverTransaction.TransactionId, "ACK transaction did not match INVITE transaction.");
        }
예제 #19
0
        /// <summary>
        /// Event handler for receiving a re-INVITE request on an established call.
        /// In call requests can be used for multitude of different purposes. In this
        /// example program we're only concerned with re-INVITE requests being used
        /// to place a call on/off hold.
        /// </summary>
        /// <param name="uasTransaction">The user agent server invite transaction that
        /// was created for the request. It needs to be used for sending responses
        /// to ensure reliable delivery.</param>
        private static void ReinviteRequestReceived(UASInviteTransaction uasTransaction)
        {
            SIPRequest reinviteRequest = uasTransaction.TransactionRequest;

            // Re-INVITEs can also be changing the RTP end point. We can update this each time.
            IPEndPoint dstRtpEndPoint = SDP.GetSDPRTPEndPoint(reinviteRequest.Body);

            _remoteRtpEndPoint = dstRtpEndPoint;

            // If the RTP callfow attribute has changed it's most likely due to being placed on/off hold.
            SDP newSDP = SDP.ParseSDPDescription(reinviteRequest.Body);

            if (GetRTPStatusAttribute(newSDP) == RTP_ATTRIBUTE_SENDONLY)
            {
                Log.LogInformation("Remote call party has placed us on hold.");
                _holdStatus = HoldStatus.RemotePutOnHold;

                _ourSDP = GetSDP(_ourRtpSocket.LocalEndPoint as IPEndPoint, RTP_ATTRIBUTE_RECVONLY);
                var okResponse = SIPTransport.GetResponse(reinviteRequest, SIPResponseStatusCodesEnum.Ok, null);
                okResponse.Header.ContentType = SDP.SDP_MIME_CONTENTTYPE;
                okResponse.Body = _ourSDP.ToString();
                uasTransaction.SendFinalResponse(okResponse);
            }
            else if (GetRTPStatusAttribute(newSDP) == RTP_ATTRIBUTE_SENDRECV && _holdStatus != HoldStatus.None)
            {
                Log.LogInformation("Remote call party has taken us off hold.");
                _holdStatus = HoldStatus.None;

                _ourSDP = GetSDP(_ourRtpSocket.LocalEndPoint as IPEndPoint, RTP_ATTRIBUTE_SENDRECV);
                var okResponse = SIPTransport.GetResponse(reinviteRequest, SIPResponseStatusCodesEnum.Ok, null);
                okResponse.Header.ContentType = SDP.SDP_MIME_CONTENTTYPE;
                okResponse.Body = _ourSDP.ToString();
                uasTransaction.SendFinalResponse(okResponse);
            }
            else
            {
                Log.LogWarning("Not sure what the remote call party wants us to do...");

                // We'll just reply Ok and hope eveything is good.
                var okResponse = SIPTransport.GetResponse(reinviteRequest, SIPResponseStatusCodesEnum.Ok, null);
                okResponse.Header.ContentType = SDP.SDP_MIME_CONTENTTYPE;
                okResponse.Body = _ourSDP.ToString();
                uasTransaction.SendFinalResponse(okResponse);
            }
        }
예제 #20
0
        /// <summary>
        /// This method can be used to start the processing of a new incoming call request.
        /// The user agent will is acting as a server for this operation and it can be considered
        /// the opposite of the Call method.
        /// </summary>
        /// <param name="uasInviteTx">The invite transaction representing the incoming call.</param>
        /// <returns>True if the call is accepted, false otherwise.</returns>
        public bool AcceptCall(UASInviteTransaction uasInviteTx)
        {
            SIPServerUserAgent uas = new SIPServerUserAgent(m_transport, m_outboundProxy, null, null, SIPCallDirection.In, null, null, null, uasInviteTx);

            uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null);

            // TODO: Decide how to deal with multiple simultaneous calls.
            if (m_uas != null)
            {
                uas.Reject(SIPResponseStatusCodesEnum.BusyHere, null, null);
                return(false);
            }
            else
            {
                uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null);
                m_uas = uas;
                return(true);
            }
        }
예제 #21
0
        private void Forward(UASInviteTransaction uasTx)
        {
            SIPB2BUserAgent b2bua = new SIPB2BUserAgent(_sipTransport, null, uasTx, null);

            b2bua.CallAnswered += (uac, resp) => ForwardCallAnswered(uac, b2bua);

            var dst = _getDestination(uasTx);

            if (dst == null)
            {
                Logger.LogInformation($"B2BUA lookup did not return a destination. Rejecting UAS call.");

                var notFoundResp = SIPResponse.GetResponse(uasTx.TransactionRequest, SIPResponseStatusCodesEnum.NotFound, null);
                uasTx.SendFinalResponse(notFoundResp);
            }
            else
            {
                Logger.LogInformation($"B2BUA forwarding call to {dst.Uri}.");
                b2bua.Call(dst);
            }
        }
예제 #22
0
        private async Task Forward(UASInviteTransaction uasTx, ISIPAccount callerSIPAccount)
        {
            var invReq = uasTx.TransactionRequest;

            //uasTx.TransactionStateChanged += (tx) => Logger.LogDebug($"B2B uas tx state changed to {tx.TransactionState}.");
            //uasTx.TransactionTraceMessage += (tx, msg) => Logger.LogDebug($"B2B uas tx trace message. {msg}");

            Logger.LogDebug($"B2B commencing forward for caller {invReq.Header.From.FromURI} to {invReq.URI}.");

            SIPB2BUserAgent b2bua = new SIPB2BUserAgent(_sipTransport, null, uasTx, callerSIPAccount);

            bool isAuthenticated = false;

            if (callerSIPAccount != null)
            {
                isAuthenticated = b2bua.AuthenticateCall();
            }

            if (callerSIPAccount == null || isAuthenticated == true)
            {
                b2bua.CallAnswered += (uac, resp) => ForwardCallAnswered(uac, b2bua);

                var dst = await _sipdialPlan.Lookup(uasTx, null);

                if (dst == null)
                {
                    Logger.LogInformation($"B2BUA lookup did not return a destination. Rejecting UAS call.");

                    var notFoundResp = SIPResponse.GetResponse(uasTx.TransactionRequest, SIPResponseStatusCodesEnum.NotFound, null);
                    uasTx.SendFinalResponse(notFoundResp);

                    OnAcceptCallFailure?.Invoke(uasTx.TransactionRequest.RemoteSIPEndPoint, CallFailureEnum.NotFound, invReq);
                }
                else
                {
                    Logger.LogInformation($"B2BUA forwarding call to {dst.Uri}.");
                    b2bua.Call(dst);
                }
            }
        }
예제 #23
0
        /// <summary>
        /// This function type is to allow B2B user agents to lookup the forwarding destination
        /// for an accepted User Agent Server (UAS) call leg. The intent is that functions
        /// can implement a form of a dialplan and pass to the B2BUA core.
        /// </summary>
        /// <param name="uas">A User Agent Server (UAS) transaction that has been accepted
        /// for forwarding.</param>
        /// <returns>A call descriptor for the User Agent Client (UAC) call leg that will
        /// be bridged to the UAS leg.</returns>
        public async Task <SIPCallDescriptor> Lookup(UASInviteTransaction uasTx, ISIPAccount from)
        {
            var dialplan = await LoadDialPlan();

            //_logger.LogDebug($"Our dialplan last update {TrimMilliseconds(_dialplanLastUpdated).ToString("o")}, " +
            //    $"database last update {TrimMilliseconds(dialplan.LastUpdate).ToString("o")}.");

            if (dialplan != null && TrimMilliseconds(DateTime.Parse(dialplan.LastUpdate)) > TrimMilliseconds(_dialplanLastUpdated))
            {
                _logger.LogInformation($"SIP DialPlan Manager loading updated dialplan.");
                CompileDialPlan(dialplan.DialPlanScript, DateTime.Parse(dialplan.LastUpdate));
            }

            if (_dialPlanScriptRunner != null)
            {
                var result = await _dialPlanScriptRunner.Invoke(new SIPDialPlanGlobals { UasTx = uasTx, From = from });

                return(result as SIPCallDescriptor);
            }
            else
            {
                return(null);
            }
        }
예제 #24
0
        /// <summary>
        /// Attempts to lookup the caller based on the "To" header host. If the host matches a
        /// hosted domain then an attempt will be made to retrieve the SIP account for the caller.
        /// If a SIP account is not found for a hosted domain the call is rejected with a 403 response.
        /// If the host does not match then the caller is treated as a public non-authenticated caller.
        /// </summary>
        /// <returns>The caller's SIP account or null for non-hosted domains.</returns>
        private async Task <ISIPAccount> GetCaller(UASInviteTransaction uasTx)
        {
            var invReq = uasTx.TransactionRequest;

            if (invReq.Header.From == null || invReq.Header.From.FromURI == null)
            {
                uasTx.SendFinalResponse(SIPResponse.GetResponse(invReq, SIPResponseStatusCodesEnum.BadRequest, "From header malformed"));
                return(null);
            }
            else
            {
                string canonicalDomain = _sipDomainManager.GetCanonicalDomain(invReq.Header.From.FromURI.HostAddress);

                if (canonicalDomain == null)
                {
                    // The caller is from a non-hosted domain. Will be treated as a public non-authenticated caller.
                    return(null);
                }
                else
                {
                    Logger.LogDebug($"B2B incoming caller was for hosted domain {canonicalDomain}, looking up caller for {invReq.Header.From.FromURI.User}.");

                    var sipAccount = await _sipAccountDataLayer.GetSIPAccount(invReq.Header.From.FromURI.User, canonicalDomain);

                    if (sipAccount == null)
                    {
                        Logger.LogWarning($"B2B no SIP account found for caller {invReq.Header.From.FromURI.User}@{canonicalDomain}, rejecting.");
                        uasTx.SendFinalResponse(SIPResponse.GetResponse(invReq, SIPResponseStatusCodesEnum.Forbidden, null));

                        OnAcceptCallFailure?.Invoke(uasTx.TransactionRequest.RemoteSIPEndPoint, CallFailureEnum.NoSIPAccount, invReq);
                    }

                    return(sipAccount);
                }
            }
        }
예제 #25
0
        /// <summary>
        /// Handler for processing incomign SIP requests.
        /// </summary>
        /// <param name="localSIPEndPoint">The end point the request was received on.</param>
        /// <param name="remoteEndPoint">The end point the request came from.</param>
        /// <param name="sipRequest">The SIP request received.</param>
        private void SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            if (sipRequest.Method == SIPMethodsEnum.BYE)
            {
                if (m_uac != null && m_uac.SIPDialogue != null && sipRequest.Header.CallId == m_uac.SIPDialogue.CallId)
                {
                    // Call has been hungup by remote end.
                    StatusMessage("Call hungup by remote end.");
                    SIPNonInviteTransaction byeTransaction = m_sipTransport.CreateNonInviteTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null);
                    SIPResponse             byeResponse    = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                    byeTransaction.SendFinalResponse(byeResponse);
                    CallFinished();
                }
                else if (m_uas != null && m_uas.SIPDialogue != null && sipRequest.Header.CallId == m_uas.SIPDialogue.CallId)
                {
                    // Call has been hungup by remote end.
                    StatusMessage("Call hungup.");
                    m_uas.SIPDialogue.Hangup(m_sipTransport, null);
                    CallFinished();
                }
                else
                {
                    logger.Debug("Unmatched BYE request received for " + sipRequest.URI.ToString() + ".");
                    SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null);
                    m_sipTransport.SendResponse(noCallLegResponse);
                }
            }
            else if (sipRequest.Method == SIPMethodsEnum.INVITE)
            {
                StatusMessage("Incoming call request: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".");
                UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null);
                m_uas = new SIPServerUserAgent(m_sipTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction);
                m_uas.CallCancelled += UASCallCancelled;
                IncomingCall();
            }
            else if (sipRequest.Method == SIPMethodsEnum.CANCEL)
            {
                UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE));

                if (inviteTransaction != null)
                {
                    StatusMessage("Call was cancelled by remote end.");
                    SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction);
                    cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest);
                }
                else
                {
                    logger.Debug("No matching transaction was found for CANCEL to " + sipRequest.URI.ToString() + ".");
                    SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null);
                    m_sipTransport.SendResponse(noCallLegResponse);
                }

                CallFinished();
            }
            else
            {
                logger.Debug("SIP " + sipRequest.Method + " request received but no processing has been set up for it, rejecting.");
                SIPResponse notAllowedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null);
                m_sipTransport.SendResponse(notAllowedResponse);
            }
        }
예제 #26
0
        static void Main(string[] args)
        {
            Console.WriteLine("SIPSorcery user agent server example.");
            Console.WriteLine("Press h to hangup a call or ctrl-c to exit.");

            EnableConsoleLogger();

            IPAddress listenAddress     = IPAddress.Any;
            IPAddress listenIPv6Address = IPAddress.IPv6Any;

            if (args != null && args.Length > 0)
            {
                if (!IPAddress.TryParse(args[0], out var customListenAddress))
                {
                    Log.LogWarning($"Command line argument could not be parsed as an IP address \"{args[0]}\"");
                    listenAddress = IPAddress.Any;
                }
                else
                {
                    if (customListenAddress.AddressFamily == AddressFamily.InterNetwork)
                    {
                        listenAddress = customListenAddress;
                    }
                    if (customListenAddress.AddressFamily == AddressFamily.InterNetworkV6)
                    {
                        listenIPv6Address = customListenAddress;
                    }
                }
            }

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

            var localhostCertificate = new X509Certificate2("localhost.pfx");

            // IPv4 channels.
            sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(listenAddress, SIP_LISTEN_PORT)));
            sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(listenAddress, SIP_LISTEN_PORT)));
            sipTransport.AddSIPChannel(new SIPTLSChannel(localhostCertificate, new IPEndPoint(listenAddress, SIPS_LISTEN_PORT)));
            //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.Any, SIP_WEBSOCKET_LISTEN_PORT));
            //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.Any, SIP_SECURE_WEBSOCKET_LISTEN_PORT, localhostCertificate));

            // IPv6 channels.
            sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(listenIPv6Address, SIP_LISTEN_PORT)));
            sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(listenIPv6Address, SIP_LISTEN_PORT)));
            sipTransport.AddSIPChannel(new SIPTLSChannel(localhostCertificate, new IPEndPoint(listenIPv6Address, SIPS_LISTEN_PORT)));
            //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.IPv6Any, SIP_WEBSOCKET_LISTEN_PORT));
            //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.IPv6Any, SIP_SECURE_WEBSOCKET_LISTEN_PORT, localhostCertificate));

            EnableTraceLogs(sipTransport);

            string executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

            // To keep things a bit simpler this example only supports a single call at a time and the SIP server user agent
            // acts as a singleton
            SIPServerUserAgent      uas        = null;
            CancellationTokenSource rtpCts     = null; // Cancellation token to stop the RTP stream.
            RtpAVSession            rtpSession = null;

            // Because this is a server user agent the SIP transport must start listening for client user agents.
            sipTransport.SIPTransportRequestReceived += async(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) =>
            {
                try
                {
                    if (sipRequest.Method == SIPMethodsEnum.INVITE)
                    {
                        SIPSorcery.Sys.Log.Logger.LogInformation($"Incoming call request: {localSIPEndPoint}<-{remoteEndPoint} {sipRequest.URI}.");

                        // Check there's a codec we support in the INVITE offer.
                        var        offerSdp       = SDP.ParseSDPDescription(sipRequest.Body);
                        IPEndPoint dstRtpEndPoint = SDP.GetSDPRTPEndPoint(sipRequest.Body);

                        if (offerSdp.Media.Any(x => x.Media == SDPMediaTypesEnum.audio && x.HasMediaFormat((int)SDPMediaFormatsEnum.PCMU)))
                        {
                            Log.LogDebug($"Client offer contained PCMU audio codec.");
                            rtpSession = new RtpAVSession(
                                new AudioOptions {
                                AudioSource = AudioSourcesEnum.Music, SourceFile = executableDir + "/" + AUDIO_FILE_PCMU
                            }, null);
                            rtpSession.setRemoteDescription(new RTCSessionDescription {
                                type = RTCSdpType.offer, sdp = offerSdp
                            });
                        }

                        if (rtpSession == null)
                        {
                            // Didn't get a match on the codecs we support.
                            SIPResponse noMatchingCodecResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotAcceptableHere, null);
                            await sipTransport.SendResponseAsync(noMatchingCodecResponse);
                        }
                        else
                        {
                            // If there's already a call in progress hang it up. Of course this is not ideal for a real softphone or server but it
                            // means this example can be kept simpler.
                            if (uas?.IsHungup == false)
                            {
                                uas?.Hangup(false);
                            }
                            rtpCts?.Cancel();
                            rtpCts = new CancellationTokenSource();

                            UASInviteTransaction uasTransaction = new UASInviteTransaction(sipTransport, sipRequest, null);
                            uas = new SIPServerUserAgent(sipTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction);
                            uas.CallCancelled += (uasAgent) =>
                            {
                                rtpCts?.Cancel();
                                rtpSession.CloseSession(null);
                            };
                            rtpSession.OnRtpClosed += (reason) => uas?.Hangup(false);
                            uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null);
                            uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null);

                            var answerSdp = await rtpSession.createAnswer(null);

                            uas.Answer(SDP.SDP_MIME_CONTENTTYPE, answerSdp.ToString(), null, SIPDialogueTransferModesEnum.NotAllowed);

                            await rtpSession.Start();
                        }
                    }
                    else if (sipRequest.Method == SIPMethodsEnum.BYE)
                    {
                        SIPSorcery.Sys.Log.Logger.LogInformation("Call hungup.");
                        SIPResponse byeResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                        await sipTransport.SendResponseAsync(byeResponse);

                        uas?.Hangup(true);
                        rtpSession?.CloseSession(null);
                        rtpCts?.Cancel();
                    }
                    else if (sipRequest.Method == SIPMethodsEnum.SUBSCRIBE)
                    {
                        SIPResponse notAllowededResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null);
                        await sipTransport.SendResponseAsync(notAllowededResponse);
                    }
                    else if (sipRequest.Method == SIPMethodsEnum.OPTIONS || sipRequest.Method == SIPMethodsEnum.REGISTER)
                    {
                        SIPResponse optionsResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                        await sipTransport.SendResponseAsync(optionsResponse);
                    }
                }
                catch (Exception reqExcp)
                {
                    SIPSorcery.Sys.Log.Logger.LogWarning($"Exception handling {sipRequest.Method}. {reqExcp.Message}");
                }
            };

            ManualResetEvent exitMre = new ManualResetEvent(false);

            Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
            {
                e.Cancel = true;

                SIPSorcery.Sys.Log.Logger.LogInformation("Exiting...");

                Hangup(uas).Wait();

                rtpSession?.CloseSession(null);
                rtpCts?.Cancel();

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

                exitMre.Set();
            };

            // Task to handle user key presses.
            Task.Run(() =>
            {
                try
                {
                    while (!exitMre.WaitOne(0))
                    {
                        var keyProps = Console.ReadKey();
                        if (keyProps.KeyChar == 'h' || keyProps.KeyChar == 'q')
                        {
                            Console.WriteLine();
                            Console.WriteLine("Hangup requested by user...");

                            Hangup(uas).Wait();

                            rtpSession?.CloseSession(null);
                            rtpCts?.Cancel();
                        }

                        if (keyProps.KeyChar == 'q')
                        {
                            SIPSorcery.Sys.Log.Logger.LogInformation("Quitting...");

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

                            exitMre.Set();
                        }
                    }
                }
                catch (Exception excp)
                {
                    SIPSorcery.Sys.Log.Logger.LogError($"Exception Key Press listener. {excp.Message}.");
                }
            });

            exitMre.WaitOne();
        }
예제 #27
0
        /// <summary>
        /// Handler for when an in dialog request is received on an established call.
        /// Typical types of request will be re-INVITES for things like putting a call on or
        /// off hold and REFER requests for transfers. Some in dialog request types, such
        /// as re-INVITES have specific events so they can be bubbled up to the
        /// application to deal with.
        /// </summary>
        /// <param name="sipRequest">The in dialog request received.</param>
        private async Task DialogRequestReceivedAsync(SIPRequest sipRequest)
        {
            if (sipRequest.Method == SIPMethodsEnum.BYE)
            {
                logger.LogInformation($"Remote call party hungup {sipRequest.StatusLine}.");
                Dialogue.DialogueState = SIPDialogueStateEnum.Terminated;

                SIPNonInviteTransaction byeTx = new SIPNonInviteTransaction(m_transport, sipRequest, null);
                byeTx.SendResponse(SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null));

                CallEnded();
            }
            else if (sipRequest.Method == SIPMethodsEnum.INVITE)
            {
                logger.LogDebug($"Re-INVITE request received {sipRequest.StatusLine}.");

                UASInviteTransaction reInviteTransaction = new UASInviteTransaction(m_transport, sipRequest, m_outboundProxy);

                try
                {
                    MediaSession.setRemoteDescription(new RTCSessionDescription {
                        sdp = SDP.ParseSDPDescription(sipRequest.Body), type = RTCSdpType.offer
                    });

                    CheckRemotePartyHoldCondition(MediaSession.remoteDescription.sdp);

                    var answerSdp = await MediaSession.createAnswer(null).ConfigureAwait(false);

                    Dialogue.RemoteSDP  = sipRequest.Body;
                    Dialogue.SDP        = answerSdp.ToString();
                    Dialogue.RemoteCSeq = sipRequest.Header.CSeq;

                    var okResponse = reInviteTransaction.GetOkResponse(SDP.SDP_MIME_CONTENTTYPE, Dialogue.SDP);
                    reInviteTransaction.SendFinalResponse(okResponse);
                }
                catch (Exception ex)
                {
                    logger.LogError(ex, "MediaSession can't process the re-INVITE request.");

                    if (OnReinviteRequest == null)
                    {
                        // The application isn't prepared to accept re-INVITE requests and we can't work out what it was for.
                        // We'll reject as gently as we can to try and not lose the call.
                        SIPResponse notAcceptableResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotAcceptable, null);
                        reInviteTransaction.SendFinalResponse(notAcceptableResponse);
                    }
                    else
                    {
                        // The application is going to handle the re-INVITE request. We'll send a Trying response as a precursor.
                        SIPResponse tryingResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Trying, null);
                        await reInviteTransaction.SendProvisionalResponse(tryingResponse).ConfigureAwait(false);

                        OnReinviteRequest.Invoke(reInviteTransaction);
                    }
                }
            }
            else if (sipRequest.Method == SIPMethodsEnum.OPTIONS)
            {
                //Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "OPTIONS request for established dialogue " + dialogue.DialogueName + ".", dialogue.Owner));
                SIPResponse okResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                okResponse.Body = Dialogue.RemoteSDP;
                okResponse.Header.ContentLength = okResponse.Body.Length;
                okResponse.Header.ContentType   = m_sdpContentType;
                await SendResponseAsync(okResponse).ConfigureAwait(false);
            }
            else if (sipRequest.Method == SIPMethodsEnum.MESSAGE)
            {
                //Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "MESSAGE for call " + sipRequest.URI.ToString() + ": " + sipRequest.Body + ".", dialogue.Owner));
                SIPResponse okResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                await m_transport.SendResponseAsync(okResponse).ConfigureAwait(false);
            }
            else if (sipRequest.Method == SIPMethodsEnum.REFER)
            {
                if (sipRequest.Header.ReferTo.IsNullOrBlank())
                {
                    // A REFER request must have a Refer-To header.
                    //Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Bad REFER request, no Refer-To header.", dialogue.Owner));
                    SIPResponse invalidResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.BadRequest, "Missing mandatory Refer-To header");
                    await SendResponseAsync(invalidResponse).ConfigureAwait(false);
                }
                else
                {
                    //TODO: Add handling logic for in transfer requests from the remote call party.
                }
            }
            else if (sipRequest.Method == SIPMethodsEnum.NOTIFY)
            {
                SIPResponse okResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                await SendResponseAsync(okResponse).ConfigureAwait(false);

                if (sipRequest.Body?.Length > 0 && sipRequest.Header.ContentType?.Contains(m_sipReferContentType) == true)
                {
                    OnTransferNotify?.Invoke(sipRequest.Body);
                }
            }
        }
예제 #28
0
        public async Task HandleInvalidSdpPortOnPlaceCallUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            // This transport will act as the call receiver. It allows the test to provide a
            // tailored response to an incoming call.
            SIPTransport calleeTransport = new SIPTransport();

            // This transport will be used by the SIPUserAgent being tested to place the call.
            SIPTransport callerTransport = new SIPTransport();
            RTPSession   rtpSession      = new RTPSession(false, false, false);

            try
            {
                calleeTransport.AddSIPChannel(new SIPUDPChannel(IPAddress.Loopback, 0));
                calleeTransport.SIPTransportRequestReceived += async(lep, rep, req) =>
                {
                    if (req.Method != SIPMethodsEnum.INVITE)
                    {
                        SIPResponse notAllowedResponse = SIPResponse.GetResponse(req, SIPResponseStatusCodesEnum.MethodNotAllowed, null);
                        await calleeTransport.SendResponseAsync(notAllowedResponse);
                    }
                    else
                    {
                        UASInviteTransaction uasTransaction = new UASInviteTransaction(calleeTransport, req, null);
                        var uas = new SIPServerUserAgent(calleeTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction);
                        uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null);
                        uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null);

                        var answerSdp = @"
v=0
o=- 1838015445 0 IN IP4 127.0.0.1
s=-
c=IN IP4 127.0.0.1
t=0 0
m=audio 79762 RTP/AVP 0
a=rtpmap:0 PCMU/8000
a=sendrecv";
                        uas.Answer(SDP.SDP_MIME_CONTENTTYPE, answerSdp, null, SIPDialogueTransferModesEnum.NotAllowed);
                    }
                };

                SIPUserAgent userAgent = new SIPUserAgent(callerTransport, null);

                MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> {
                    new SDPMediaFormat(SDPMediaFormatsEnum.PCMU)
                });
                rtpSession.addTrack(audioTrack);

                SIPURI dstUri = new SIPURI(SIPSchemesEnum.sip, calleeTransport.GetSIPChannels().First().ListeningSIPEndPoint);
                var    result = await userAgent.Call(dstUri.ToString(), null, null, rtpSession);

                Assert.False(result);
            }
            finally
            {
                rtpSession?.Close("normal");
                callerTransport?.Shutdown();
                calleeTransport?.Shutdown();
            }
        }
예제 #29
0
        public async Task HangupUserAgentUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            SIPTransport   transport   = new SIPTransport(false, MockSIPDNSManager.Resolve);
            MockSIPChannel mockChannel = new MockSIPChannel(new System.Net.IPEndPoint(IPAddress.Any, 0));

            transport.AddSIPChannel(mockChannel);

            SIPUserAgent userAgent = new SIPUserAgent(transport, null);

            string inviteReqStr = "INVITE sip:192.168.11.50:5060 SIP/2.0" + m_CRLF +
                                  "Via: SIP/2.0/UDP 192.168.11.50:60163;rport;branch=z9hG4bKPj869f70960bdd4204b1352eaf242a3691" + m_CRLF +
                                  "To: <sip:[email protected]>;tag=ZUJSXRRGXQ" + m_CRLF +
                                  "From: <sip:[email protected]>;tag=4a60ce364b774258873ff199e5e39938" + m_CRLF +
                                  "Call-ID: 17324d6df8744d978008c8997bfd208d" + m_CRLF +
                                  "CSeq: 3532 INVITE" + m_CRLF +
                                  "Contact: <sip:[email protected]:60163;ob>" + m_CRLF +
                                  "Max-Forwards: 70" + m_CRLF +
                                  "User-Agent: MicroSIP/3.19.22" + m_CRLF +
                                  "Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS" + m_CRLF +
                                  "Supported: replaces, 100rel, timer, norefersub" + m_CRLF +
                                  "Content-Length: 343" + m_CRLF +
                                  "Content-Type: application/sdp" + m_CRLF +
                                  "Session-Expires: 1800" + m_CRLF +
                                  "Min-SE: 90" + m_CRLF +
                                  "" + m_CRLF +
                                  "v=0" + m_CRLF +
                                  "o=- 3785527268 3785527269 IN IP4 192.168.11.50" + m_CRLF +
                                  "s=pjmedia" + m_CRLF +
                                  "t=0 0" + m_CRLF +
                                  "m=audio 4032 RTP/AVP 0 101" + m_CRLF +
                                  "c=IN IP4 192.168.11.50" + m_CRLF +
                                  "a=rtpmap:0 PCMU/8000" + m_CRLF +
                                  "a=rtpmap:101 telephone-event/8000" + m_CRLF +
                                  "a=fmtp:101 0-16" + m_CRLF +
                                  "a=sendrecv";

            SIPEndPoint      dummySipEndPoint = new SIPEndPoint(new IPEndPoint(IPAddress.Loopback, 0));
            SIPMessageBuffer sipMessageBuffer = SIPMessageBuffer.ParseSIPMessage(inviteReqStr, dummySipEndPoint, dummySipEndPoint);
            SIPRequest       inviteReq        = SIPRequest.ParseSIPRequest(sipMessageBuffer);

            UASInviteTransaction uasTx   = new UASInviteTransaction(transport, inviteReq, null);
            SIPServerUserAgent   mockUas = new SIPServerUserAgent(transport, null, null, null, SIPCallDirection.In, null, null, null, uasTx);
            await userAgent.Answer(mockUas, CreateMediaSession());

            // Incremented Cseq and modified Via header from original request. Means the request is the same dialog but different tx.
            string inviteReqStr2 = "BYE sip:192.168.11.50:5060 SIP/2.0" + m_CRLF +
                                   "Via: SIP/2.0/UDP 192.168.11.50:60163;rport;branch=z9hG4bKPj869f70960bdd4204b1352eaf242a3700" + m_CRLF +
                                   "To: <sip:[email protected]>;tag=ZUJSXRRGXQ" + m_CRLF +
                                   "From: <sip:[email protected]>;tag=4a60ce364b774258873ff199e5e39938" + m_CRLF +
                                   "Call-ID: 17324d6df8744d978008c8997bfd208d" + m_CRLF +
                                   "CSeq: 3533 BYE" + m_CRLF +
                                   "Contact: <sip:[email protected]:60163;ob>" + m_CRLF +
                                   "Max-Forwards: 70" + m_CRLF +
                                   "User-Agent: MicroSIP/3.19.22" + m_CRLF +
                                   "Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS" + m_CRLF +
                                   "Supported: replaces, 100rel, timer, norefersub" + m_CRLF +
                                   "";

            mockChannel.FireMessageReceived(dummySipEndPoint, dummySipEndPoint, Encoding.UTF8.GetBytes(inviteReqStr2));
        }
예제 #30
0
        public void Call(SIPCallDescriptor sipCallDescriptor)
        {
            try
            {
                m_uacCallDescriptor = sipCallDescriptor;
                SIPRequest uacInviteRequest = GetInviteRequest(m_uacCallDescriptor.Uri, sipCallDescriptor.GetFromHeader());
                if (sipCallDescriptor.MangleResponseSDP && sipCallDescriptor.MangleIPAddress != null)
                {
                    uacInviteRequest.Header.ProxyReceivedFrom = sipCallDescriptor.MangleIPAddress.ToString();
                }
                uacInviteRequest.Body = sipCallDescriptor.Content;
                uacInviteRequest.Header.ContentType = sipCallDescriptor.ContentType;
                uacInviteRequest.LocalSIPEndPoint = m_blackhole;
                uacInviteRequest.RemoteSIPEndPoint = m_blackhole;

                // Now that we have a destination socket create a new UAC transaction for forwarded leg of the call.
                m_uacTransaction = m_sipTransport.CreateUACTransaction(uacInviteRequest, m_blackhole, m_blackhole, null);
                if (m_uacTransaction.CDR != null)
                {
                    m_uacTransaction.CDR.Owner = m_uacOwner;
                    m_uacTransaction.CDR.AdminMemberId = m_uacAdminMemberId;
                }

                //uacTransaction.UACInviteTransactionInformationResponseReceived += ServerInformationResponseReceived;
                //uacTransaction.UACInviteTransactionFinalResponseReceived += ServerFinalResponseReceived;
                //uacTransaction.UACInviteTransactionTimedOut += ServerTimedOut;
                //uacTransaction.TransactionTraceMessage += TransactionTraceMessage;

                m_uacTransaction.SendInviteRequest(m_blackhole, m_uacTransaction.TransactionRequest);

                SIPRequest uasInviteRequest = uacInviteRequest.Copy();
                uasInviteRequest.LocalSIPEndPoint = m_blackhole;
                uasInviteRequest.RemoteSIPEndPoint = m_blackhole;
                uasInviteRequest.Header.Vias.TopViaHeader.Branch = CallProperties.CreateBranchId();
                m_uasTransaction = m_sipTransport.CreateUASTransaction(uasInviteRequest, m_blackhole, m_blackhole, null);

                SetOwner(sipCallDescriptor.ToSIPAccount.Owner, sipCallDescriptor.ToSIPAccount.AdminMemberId);
                //m_uasTransaction.TransactionTraceMessage += TransactionTraceMessage;
                //m_uasTransaction.UASInviteTransactionTimedOut += ClientTimedOut;
                //m_uasTransaction.UASInviteTransactionCancelled += (t) => { };

                QueueNewCall_External(this);
            }
            catch (Exception excp)
            {
                logger.Error("Exception SIPB2BUserAgent Call. " + excp.Message);
            }
        }
예제 #31
0
        static void Main()
        {
            Console.WriteLine("SIPSorcery call hold example.");
            Console.WriteLine("Press ctrl-c to exit.");

            // Plumbing code to facilitate a graceful exit.
            CancellationTokenSource exitCts = new CancellationTokenSource(); // Cancellation token to stop the SIP transport and RTP stream.

            AddConsoleLogger();

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

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

            EnableTraceLogs(sipTransport);

            // Create two user agents. Each gets configured to answer an incoming call.
            var userAgent1 = new SIPUserAgent(sipTransport, null);
            var userAgent2 = new SIPUserAgent(sipTransport, null);

            // Only one of the user agents can use the microphone and speaker. The one designated
            // as the active agent gets the devices.
            SIPUserAgent    activeUserAgent  = null;
            RTPMediaSession activeRtpSession = null;

            // Get the default speaker.
            var(audioOutEvent, audioOutProvider) = GetAudioOutputDevice();
            m_audioOutProvider = audioOutProvider;
            WaveInEvent waveInEvent = GetAudioInputDevice();

            userAgent1.OnCallHungup        += () => Log.LogInformation($"UA1: Call hungup by remote party.");
            userAgent1.ServerCallCancelled += (uas) => Log.LogInformation("UA1: Incoming call cancelled by caller.");

            userAgent2.OnCallHungup        += () => Log.LogInformation($"UA2: Call hungup by remote party.");
            userAgent2.ServerCallCancelled += (uas) => Log.LogInformation("UA2: Incoming call cancelled by caller.");

            userAgent2.OnTransferNotify += (sipFrag) =>
            {
                if (!string.IsNullOrEmpty(sipFrag))
                {
                    Log.LogInformation($"UA2: Transfer status update: {sipFrag.Trim()}.");
                    if (sipFrag?.Contains("SIP/2.0 200") == true)
                    {
                        // The transfer attempt got a succesful answer. Can hangup the call.
                        userAgent2.Hangup();
                        exitCts.Cancel();
                    }
                }
            };

            sipTransport.SIPTransportRequestReceived += (locelEndPoint, remoteEndPoint, sipRequest) =>
            {
                if (sipRequest.Header.From != null &&
                    sipRequest.Header.From.FromTag != null &&
                    sipRequest.Header.To != null &&
                    sipRequest.Header.To.ToTag != null)
                {
                    // This is an in-dialog request that will be handled directly by a user agent instance.
                }
                else if (sipRequest.Method == SIPMethodsEnum.INVITE)
                {
                    if (!userAgent1.IsCallActive)
                    {
                        Log.LogInformation($"UA1: Incoming call request from {remoteEndPoint}: {sipRequest.StatusLine}.");
                        var incomingCall = userAgent1.AcceptCall(sipRequest);

                        var rtpMediaSession = new RTPMediaSession(SDPMediaTypesEnum.audio, new SDPMediaFormat(SDPMediaFormatsEnum.PCMU), AddressFamily.InterNetwork);
                        rtpMediaSession.RemotePutOnHold   += () => Log.LogInformation("UA1: Remote call party has placed us on hold.");
                        rtpMediaSession.RemoteTookOffHold += () => Log.LogInformation("UA1: Remote call party took us off hold.");

                        userAgent1.Answer(incomingCall, rtpMediaSession)
                        .ContinueWith(task =>
                        {
                            activeUserAgent  = userAgent1;
                            activeRtpSession = rtpMediaSession;
                            activeRtpSession.OnRtpPacketReceived += PlaySample;
                            waveInEvent.StartRecording();

                            Log.LogInformation($"UA1: Answered incoming call from {sipRequest.Header.From.FriendlyDescription()} at {remoteEndPoint}.");
                        }, exitCts.Token);
                    }
                    else if (!userAgent2.IsCallActive)
                    {
                        Log.LogInformation($"UA2: Incoming call request from {remoteEndPoint}: {sipRequest.StatusLine}.");

                        var incomingCall    = userAgent2.AcceptCall(sipRequest);
                        var rtpMediaSession = new RTPMediaSession(SDPMediaTypesEnum.audio, new SDPMediaFormat(SDPMediaFormatsEnum.PCMU), AddressFamily.InterNetwork);
                        rtpMediaSession.RemotePutOnHold   += () => Log.LogInformation("UA2: Remote call party has placed us on hold.");
                        rtpMediaSession.RemoteTookOffHold += () => Log.LogInformation("UA2: Remote call party took us off hold.");

                        userAgent2.Answer(incomingCall, rtpMediaSession)
                        .ContinueWith(task =>
                        {
                            activeRtpSession.OnRtpPacketReceived -= PlaySample;

                            activeUserAgent  = userAgent2;
                            activeRtpSession = rtpMediaSession;
                            activeRtpSession.PutOnHold();
                            activeRtpSession.OnRtpPacketReceived += PlaySample;

                            Log.LogInformation($"UA2: Answered incoming call from {sipRequest.Header.From.FriendlyDescription()} at {remoteEndPoint}.");
                        }, exitCts.Token);
                    }
                    else
                    {
                        // If both user agents are already on a call return a busy response.
                        Log.LogWarning($"Busy response returned for incoming call request from {remoteEndPoint}: {sipRequest.StatusLine}.");
                        UASInviteTransaction uasTransaction = new UASInviteTransaction(sipTransport, sipRequest, null);
                        SIPResponse          busyResponse   = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.BusyHere, null);
                        uasTransaction.SendFinalResponse(busyResponse);
                    }
                }
                else
                {
                    Log.LogDebug($"SIP {sipRequest.Method} request received but no processing has been set up for it, rejecting.");
                    SIPResponse notAllowedResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null);
                    return(sipTransport.SendResponseAsync(notAllowedResponse));
                }

                return(Task.FromResult(0));
            };

            // Wire up the RTP send session to the audio input device.
            uint rtpSendTimestamp = 0;

            waveInEvent.DataAvailable += (object sender, WaveInEventArgs args) =>
            {
                byte[] sample      = new byte[args.Buffer.Length / 2];
                int    sampleIndex = 0;

                for (int index = 0; index < args.BytesRecorded; index += 2)
                {
                    var ulawByte = NAudio.Codecs.MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(args.Buffer, index));
                    sample[sampleIndex++] = ulawByte;
                }

                if (activeRtpSession != null)
                {
                    activeRtpSession.SendAudioFrame(rtpSendTimestamp, (int)SDPMediaFormatsEnum.PCMU, sample);
                    rtpSendTimestamp += (uint)sample.Length;
                }
            };

            // At this point the call has been initiated and everything will be handled in an event handler.
            Task.Run(async() =>
            {
                try
                {
                    while (!exitCts.Token.WaitHandle.WaitOne(0))
                    {
                        var keyProps = Console.ReadKey();

                        if (keyProps.KeyChar == 't')
                        {
                            if (userAgent1.IsCallActive && userAgent2.IsCallActive)
                            {
                                bool result = await userAgent2.AttendedTransfer(userAgent1.Dialogue, TimeSpan.FromSeconds(TRANSFER_TIMEOUT_SECONDS), exitCts.Token);
                                if (!result)
                                {
                                    Log.LogWarning($"Attended transfer failed.");
                                }
                            }
                            else
                            {
                                Log.LogWarning("There need to be two active calls before the attended transfer can occur.");
                            }
                        }
                        else if (keyProps.KeyChar == 'q')
                        {
                            // Quit application.
                            exitCts.Cancel();
                        }
                    }
                }
                catch (Exception excp)
                {
                    SIPSorcery.Sys.Log.Logger.LogError($"Exception Key Press listener. {excp.Message}.");
                }
            });

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

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

            #region Cleanup.

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

            userAgent1?.Hangup();
            userAgent2?.Hangup();
            waveInEvent?.StopRecording();
            audioOutEvent?.Stop();

            // Give any BYE or CANCEL requests time to be transmitted.
            Log.LogInformation("Waiting 1s for calls to be cleaned up...");
            Task.Delay(1000).Wait();

            SIPSorcery.Net.DNSManager.Stop();

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

            #endregion
        }
예제 #32
0
        //private delegate void MediaSampleReadyDelegate(SDPMediaTypesEnum mediaType, uint duration, byte[] sample);
        //private static event MediaSampleReadyDelegate OnMediaFromSIPSampleReady;

        static void Main(string[] args)
        {
            Console.WriteLine("SIPSorcery SIP to WebRTC example.");
            Console.WriteLine("Press ctrl-c to exit.");

            // Plumbing code to facilitate a graceful exit.
            CancellationTokenSource exitCts = new CancellationTokenSource(); // Cancellation token to stop the SIP transport and RTP stream.

            Log = AddConsoleLogger();

            // Start web socket.
            Console.WriteLine("Starting web socket server...");
            _webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT, true);
            _webSocketServer.SslConfiguration.ServerCertificate          = new X509Certificate2(WEBSOCKET_CERTIFICATE_PATH);
            _webSocketServer.SslConfiguration.CheckCertificateRevocation = false;
            //_webSocketServer.Log.Level = WebSocketSharp.LogLevel.Debug;
            _webSocketServer.AddWebSocketService <SDPExchange>("/", (sdpExchanger) =>
            {
                sdpExchanger.WebSocketOpened   += SendSDPOffer;
                sdpExchanger.SDPAnswerReceived += SDPAnswerReceived;
            });
            _webSocketServer.Start();

            Console.WriteLine($"Waiting for browser web socket connection to {_webSocketServer.Address}:{_webSocketServer.Port}...");

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

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

            //EnableTraceLogs(sipTransport);

            RTPSession rtpSession = null;

            // Create a SIP user agent to receive a call from a remote SIP client.
            // Wire up event handlers for the different stages of the call.
            var userAgent = new SIPUserAgent(sipTransport, null);

            // We're only answering SIP calls, not placing them.
            userAgent.OnCallHungup += (dialog) =>
            {
                Log.LogInformation($"Call hungup by remote party.");
                exitCts.Cancel();
            };
            userAgent.ServerCallCancelled += (uas) => Log.LogInformation("Incoming call cancelled by caller.");

            sipTransport.SIPTransportRequestReceived += async(localEndPoint, remoteEndPoint, sipRequest) =>
            {
                if (sipRequest.Header.From != null &&
                    sipRequest.Header.From.FromTag != null &&
                    sipRequest.Header.To != null &&
                    sipRequest.Header.To.ToTag != null)
                {
                    // This is an in-dialog request that will be handled directly by a user agent instance.
                }
                else if (sipRequest.Method == SIPMethodsEnum.INVITE)
                {
                    if (userAgent?.IsCallActive == true)
                    {
                        Log.LogWarning($"Busy response returned for incoming call request from {remoteEndPoint}: {sipRequest.StatusLine}.");
                        // If we are already on a call return a busy response.
                        UASInviteTransaction uasTransaction = new UASInviteTransaction(sipTransport, sipRequest, null);
                        SIPResponse          busyResponse   = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.BusyHere, null);
                        uasTransaction.SendFinalResponse(busyResponse);
                    }
                    else
                    {
                        Log.LogInformation($"Incoming call request from {remoteEndPoint}: {sipRequest.StatusLine}.");
                        var incomingCall = userAgent.AcceptCall(sipRequest);

                        rtpSession = new RTPSession(false, false, false);
                        rtpSession.AcceptRtpFromAny = true;
                        MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> {
                            new SDPMediaFormat(SDPMediaFormatsEnum.PCMU)
                        });
                        rtpSession.addTrack(audioTrack);

                        await userAgent.Answer(incomingCall, rtpSession);

                        rtpSession.OnRtpPacketReceived += (ep, mediaType, rtpPacket) => ForwardMedia(mediaType, rtpPacket);

                        Log.LogInformation($"Answered incoming call from {sipRequest.Header.From.FriendlyDescription()} at {remoteEndPoint}.");
                    }
                }
                else
                {
                    Log.LogDebug($"SIP {sipRequest.Method} request received but no processing has been set up for it, rejecting.");
                    SIPResponse notAllowedResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null);
                    await sipTransport.SendResponseAsync(notAllowedResponse);
                }
            };

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

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

            #region Cleanup.

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

            rtpSession?.Close("app exit");

            if (userAgent != null)
            {
                if (userAgent.IsCallActive)
                {
                    Log.LogInformation($"Hanging up call to {userAgent?.CallDescriptor?.To}.");
                    userAgent.Hangup();
                }

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

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

            #endregion
        }