/// <summary>
        /// Adds or updates matching bindings.
        /// </summary>
        /// <param name="flow">SIP data flow what updates this binding. This value is null if binding was not added through network or
        /// flow has disposed.</param>
        /// <param name="callID">Call-ID header field value.</param>
        /// <param name="cseqNo">CSeq header field sequence number value.</param>
        /// <param name="contacts">Contacts to add or update.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>callID</b> or <b>contacts</b> is null reference.</exception>
        public void AddOrUpdateBindings(SIP_Flow flow,string callID,int cseqNo,SIP_t_ContactParam[] contacts)
        {
            if(callID == null){
                throw new ArgumentNullException("callID");
            }
            if(cseqNo < 0){
                throw new ArgumentException("Argument 'cseqNo' value must be >= 0.");
            }
            if(contacts == null){
                throw new ArgumentNullException("contacts");
            }

            lock(m_pLock){
                foreach(SIP_t_ContactParam contact in contacts){
                    SIP_RegistrationBinding binding = GetBinding(contact.Address.Uri);
                    // Add binding.
                    if(binding == null){
                        binding = new SIP_RegistrationBinding(this,contact.Address.Uri);
                        m_pBindings.Add(binding);
                    }

                    // Update binding.
                    binding.Update(
                        flow,
                        contact.Expires == -1 ? 3600 : contact.Expires,
                        contact.QValue == -1 ? 1.0 : contact.QValue,
                        callID,
                        cseqNo
                    );
                }
            }
        }
        /// <summary>
        /// Creates new SIP server transaction for specified request.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns added server transaction.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_ServerTransaction CreateServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            lock (m_pServerTransactions){
                SIP_ServerTransaction transaction = new SIP_ServerTransaction(m_pStack, flow, request);
                m_pServerTransactions.Add(transaction.Key, transaction);
                transaction.StateChanged += new EventHandler(delegate(object s, EventArgs e){
                    if (transaction.State == SIP_TransactionState.Terminated)
                    {
                        lock (m_pClientTransactions){
                            m_pServerTransactions.Remove(transaction.Key);
                        }
                    }
                });

                return(transaction);
            }
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 /// <param name="transaction">SIP server transaction which must be used to send response back to request maker.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack,SIP_Flow flow,SIP_Request request,SIP_ServerTransaction transaction)
 {
     m_pStack       = stack;
     m_pFlow        = flow;
     m_pRequest     = request;
     m_pTransaction = transaction;
 }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 /// <param name="transaction">SIP server transaction which must be used to send response back to request maker.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack, SIP_Flow flow, SIP_Request request, SIP_ServerTransaction transaction)
 {
     m_pStack       = stack;
     m_pFlow        = flow;
     m_pRequest     = request;
     m_pTransaction = transaction;
 }
Beispiel #5
0
        /// <summary>
        /// Sends specified request to the specified data flow.
        /// </summary>
        /// <param name="flow">SIP data flow.</param>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        private void SendToFlow(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            #region Contact (RFC 3261 8.1.1.8)

            /*
             *  The Contact header field provides a SIP or SIPS URI that can be used
             *  to contact that specific instance of the UA for subsequent requests.
             *  The Contact header field MUST be present and contain exactly one SIP
             *  or SIPS URI in any request that can result in the establishment of a
             *  dialog.  For the methods defined in this specification, that includes
             *  only the INVITE request.  For these requests, the scope of the
             *  Contact is global.  That is, the Contact header field value contains
             *  the URI at which the UA would like to receive requests, and this URI
             *  MUST be valid even if used in subsequent requests outside of any
             *  dialogs.
             *
             *  If the Request-URI or top Route header field value contains a SIPS
             *  URI, the Contact header field MUST contain a SIPS URI as well.
             */

            SIP_t_ContactParam contact = request.Contact.GetTopMostValue();

            // Add contact header If request-Method can establish dialog and contact header not present.
            if (SIP_Utils.MethodCanEstablishDialog(request.RequestLine.Method) && contact == null)
            {
                SIP_Uri from = (SIP_Uri)request.From.Address.Uri;

                request.Contact.Add((flow.IsSecure ? "sips:" : "sip:") + from.User + "@" + flow.LocalPublicEP.ToString());

                // REMOVE ME: 22.10.2010
                //request.Contact.Add((flow.IsSecure ? "sips:" : "sip:" ) + from.User + "@" + m_pStack.TransportLayer.GetContactHost(flow).ToString());
            }
            // If contact SIP URI and host = auto-allocate, allocate it as needed.
            else if (contact != null && contact.Address.Uri is SIP_Uri && ((SIP_Uri)contact.Address.Uri).Host == "auto-allocate")
            {
                ((SIP_Uri)contact.Address.Uri).Host = flow.LocalPublicEP.ToString();

                // REMOVE ME: 22.10.2010
                //((SIP_Uri)contact.Address.Uri).Host =  m_pStack.TransportLayer.GetContactHost(flow).ToString();
            }

            #endregion

            m_pTransaction = m_pStack.TransactionLayer.CreateClientTransaction(flow, request, true);
            m_pTransaction.ResponseReceived += new EventHandler <SIP_ResponseReceivedEventArgs>(ClientTransaction_ResponseReceived);
            m_pTransaction.TimedOut         += new EventHandler(ClientTransaction_TimedOut);
            m_pTransaction.TransportError   += new EventHandler <ExceptionEventArgs>(ClientTransaction_TransportError);

            // Start transaction processing.
            m_pTransaction.Start();
        }
        /// <summary>
        /// This method is called when REGISTER has finished.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pRegisterSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
        {
            m_pFlow = e.ClientTransaction.Flow;

            if (e.Response.StatusCodeType == SIP_StatusCodeType.Success)
            {
                SetState(SIP_UA_RegistrationState.Registered);

                OnRegistered();

                m_pFlow.SendKeepAlives = true;
            }
            else
            {
                SetState(SIP_UA_RegistrationState.Error);

                OnError(e);
            }

            // REMOVE ME:
            if (this.AutoFixContact && (m_pContact is SIP_Uri))
            {
                // If Via: received or rport paramter won't match to our sent-by, use received and rport to construct new contact value.

                SIP_Uri       cContact   = ((SIP_Uri)m_pContact);
                IPAddress     cContactIP = Net_Utils.IsIPAddress(cContact.Host) ? IPAddress.Parse(cContact.Host) : null;
                SIP_t_ViaParm via        = e.Response.Via.GetTopMostValue();
                if (via != null && cContactIP != null)
                {
                    IPEndPoint ep = new IPEndPoint(via.Received != null ? via.Received : cContactIP, via.RPort > 0 ? via.RPort : cContact.Port);
                    if (!cContactIP.Equals(ep.Address) || cContact.Port != via.RPort)
                    {
                        // Unregister old contact.
                        BeginUnregister(false);

                        // Fix contact.
                        cContact.Host = ep.Address.ToString();
                        cContact.Port = ep.Port;

                        m_pRegisterSender.Dispose();
                        m_pRegisterSender = null;

                        BeginRegister(m_AutoRefresh);

                        return;
                    }
                }
            }

            if (m_AutoRefresh)
            {
                // Set registration refresh timer.
                m_pTimer.Enabled = true;
            }

            m_pRegisterSender.Dispose();
            m_pRegisterSender = null;
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Owner SIP stack.</param>
 /// <param name="flow">SIP data flow which is used to send request.</param>
 /// <param name="request">SIP request that transaction will handle.</param>
 /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
 /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
 internal SIP_ClientTransaction(SIP_Stack stack,SIP_Flow flow,SIP_Request request) : base(stack,flow,request)
 {
     // Log
     if(this.Stack.Logger != null){
         this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] created.");
     }
    
     SetState(SIP_TransactionState.WaitingToStart);            
 }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">SIP data flow which received request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SIP_ServerTransaction(SIP_Stack stack,SIP_Flow flow,SIP_Request request) : base(stack,flow,request)
        {
            // Log
            if(this.Stack.Logger != null){
                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] created.");
            }

            Start();            
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="targetUri">Target request-URI.</param>
        /// <param name="flow">Data flow to try for forwarding..</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>targetUri</b> is null reference.</exception>
        public SIP_ProxyTarget(SIP_Uri targetUri,SIP_Flow flow)
        {
            if(targetUri == null){
                throw new ArgumentNullException("targetUri");
            }

            m_pTargetUri = targetUri;
            m_pFlow      = flow;
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">Transaction data flow.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_Transaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            m_pStack     = stack;
            m_pFlow      = flow;
            m_pRequest   = request;
            m_Method     = request.RequestLine.Method;
            m_CreateTime = DateTime.Now;
            m_pResponses = new List <SIP_Response>();

            // Validate Via:
            SIP_t_ViaParm via = request.Via.GetTopMostValue();

            if (via == null)
            {
                throw new ArgumentException("Via: header is missing !");
            }
            if (via.Branch == null)
            {
                throw new ArgumentException("Via: header 'branch' parameter is missing !");
            }

            m_ID = via.Branch;

            if (this is SIP_ServerTransaction)
            {
                /*
                 *  We use branch and sent-by as indexing key for transaction, the only special what we need to
                 *  do is to handle CANCEL, because it has same branch as transaction to be canceled.
                 *  For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
                 *  ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
                 */
                string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;
                if (request.RequestLine.Method == SIP_Methods.CANCEL)
                {
                    key += "-CANCEL";
                }
                m_Key = key;
            }
            else
            {
                m_Key = m_ID + "-" + request.RequestLine.Method;
            }
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">SIP data flow which is used to send request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        internal SIP_ClientTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) : base(stack, flow, request)
        {
            // Log
            if (this.Stack.Logger != null)
            {
                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] created.");
            }

            SetState(SIP_TransactionState.WaitingToStart);
        }
Beispiel #12
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">SIP data flow which received request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SIP_ServerTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) : base(stack, flow, request)
        {
            // Log
            if (this.Stack.Logger != null)
            {
                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] created.");
            }

            Start();
        }
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            SetState(SIP_TransactionState.Disposed);
            OnDisposed();

            m_pStack   = null;
            m_pFlow    = null;
            m_pRequest = null;

            this.StateChanged   = null;
            this.Disposed       = null;
            this.TimedOut       = null;
            this.TransportError = null;
        }
Beispiel #14
0
        /// <summary>
        /// Creates new client transaction.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to send request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <param name="addVia">If true, transaction will add <b>Via:</b> header, otherwise it's user responsibility.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        /// <returns>Returns created transaction.</returns>
        public SIP_ClientTransaction CreateClientTransaction(SIP_Flow flow, SIP_Request request, bool addVia)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            // Add Via:
            if (addVia)
            {
                SIP_t_ViaParm via = new SIP_t_ViaParm();
                via.ProtocolName      = "SIP";
                via.ProtocolVersion   = "2.0";
                via.ProtocolTransport = flow.Transport;
                via.SentBy            = new HostEndPoint("transport_layer_will_replace_it", -1);
                via.Branch            = SIP_t_ViaParm.CreateBranch();
                via.RPort             = 0;
                request.Via.AddToTop(via.ToStringValue());
            }

            lock (m_pClientTransactions){
                SIP_ClientTransaction transaction = new SIP_ClientTransaction(m_pStack, flow, request);
                m_pClientTransactions.Add(transaction.Key, transaction);
                transaction.StateChanged += new EventHandler(delegate(object s, EventArgs e){
                    if (transaction.State == SIP_TransactionState.Terminated)
                    {
                        lock (m_pClientTransactions){
                            m_pClientTransactions.Remove(transaction.Key);
                        }
                    }
                });

                SIP_Dialog dialog = MatchDialog(request);
                if (dialog != null)
                {
                    dialog.AddTransaction(transaction);
                }

                return(transaction);
            }
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="flow">Active data flow what to try before RFC 3261 [4](RFC 3263) methods to use to send request.
        /// This value can be null.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> or <b>request</b> is null.</exception>
        internal SIP_RequestSender(SIP_Stack stack,SIP_Request request,SIP_Flow flow)
        {
            if(stack == null){
                throw new ArgumentNullException("stack");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }

            m_pStack   = stack;
            m_pRequest = request;
            m_pFlow    = flow;

            m_pCredentials = new List<NetworkCredential>();
            m_pHops = new Queue<SIP_Hop>();
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">Transaction data flow.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_Transaction(SIP_Stack stack,SIP_Flow flow,SIP_Request request)
        {
            if(stack == null){
                throw new ArgumentNullException("stack");
            }
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }

            m_pStack     = stack;
            m_pFlow      = flow;
            m_pRequest   = request;
            m_Method     = request.RequestLine.Method;
            m_CreateTime = DateTime.Now;
            m_pResponses = new List<SIP_Response>();

            // Validate Via:
            SIP_t_ViaParm via = request.Via.GetTopMostValue();
            if(via == null){
                throw new ArgumentException("Via: header is missing !");
            }
            if(via.Branch == null){
                throw new ArgumentException("Via: header 'branch' parameter is missing !");
            }

            m_ID  = via.Branch;

            if(this is SIP_ServerTransaction){
                /*
                    We use branch and sent-by as indexing key for transaction, the only special what we need to
                    do is to handle CANCEL, because it has same branch as transaction to be canceled.
                    For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
                    ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
                */
                string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;
                if(request.RequestLine.Method == SIP_Methods.CANCEL){
                    key += "-CANCEL";
                }
                m_Key = key;
            }
            else{
                m_Key = m_ID + "-" + request.RequestLine.Method;
            }
        }
            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="owner">Owner proxy context.</param>
            /// <param name="flow">Data flow to use for sending. Value null means system will choose it.</param>
            /// <param name="targetUri">Target URI where to send request.</param>
            /// <param name="addRecordRoute">If true, handler will add Record-Route header to forwarded message.</param>
            /// <param name="isRecursed">If true then this target is redirected by proxy context.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>owner</b> or <b>targetURI</b> is null reference.</exception>
            public TargetHandler(SIP_ProxyContext owner,SIP_Flow flow,SIP_Uri targetUri,bool addRecordRoute,bool isRecursed)
            {
                if(owner == null){
                    throw new ArgumentNullException("owner");
                }
                if(targetUri == null){
                    throw new ArgumentNullException("targetUri");
                }

                m_pOwner         = owner; 
                m_pFlow          = flow;
                m_pTargetUri     = targetUri;
                m_AddRecordRoute = addRecordRoute;
                m_IsRecursed     = isRecursed;
                                
                m_pHops = new Queue<SIP_Hop>();  
            }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="flow">Active data flow what to try before RFC 3261 [4](RFC 3263) methods to use to send request.
        /// This value can be null.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> or <b>request</b> is null.</exception>
        internal SIP_RequestSender(SIP_Stack stack, SIP_Request request, SIP_Flow flow)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            m_pStack   = stack;
            m_pRequest = request;
            m_pFlow    = flow;

            m_pCredentials = new List <NetworkCredential>();
            m_pHops        = new Queue <SIP_Hop>();
        }
Beispiel #19
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="proxy">Owner SIP proxy server.</param>
        /// <param name="request">The request what is represented by this context.</param>
        /// <param name="flow">Data flow what received request.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>proxy</b>, <b>request</b> or <b>flow</b> is null reference.</exception>
        internal SIP_RequestContext(SIP_Proxy proxy,SIP_Request request,SIP_Flow flow)
        {
            if(proxy == null){
                throw new ArgumentNullException("proxy");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }
            if(flow == null){
                throw new ArgumentNullException("flow");
            }

            m_pProxy   = proxy;
            m_pRequest = request;
            m_pFlow    = flow;

            m_pTargets = new List<SIP_ProxyTarget>();
        }
        /// <summary>
        /// Ensures that specified request has matching server transaction. If server transaction doesn't exist,
        /// it will be created, otherwise existing transaction will be returned.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns matching transaction.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Is raised when request.Method is ACK request.</exception>
        public SIP_ServerTransaction EnsureServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (request.RequestLine.Method == SIP_Methods.ACK)
            {
                throw new InvalidOperationException("ACK request is transaction less request, can't create transaction for it.");
            }

            /*
             *  We use branch and sent-by as indexing key for transaction, the only special what we need to
             *  do is to handle CANCEL, because it has same branch as transaction to be canceled.
             *  For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
             *  ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
             */
            string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;

            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                key += "-CANCEL";
            }

            lock (m_pServerTransactions){
                SIP_ServerTransaction retVal = null;
                m_pServerTransactions.TryGetValue(key, out retVal);
                // We don't have transaction, create it.
                if (retVal == null)
                {
                    retVal = CreateServerTransaction(flow, request);
                }

                return(retVal);
            }
        }
        /// <summary>
        /// Updates specified binding.
        /// </summary>
        /// <param name="flow">SIP data flow what updates this binding. This value is null if binding was not added through network or
        /// flow has disposed.</param>
        /// <param name="expires">Time in seconds when binding will expire.</param>
        /// <param name="qvalue">Binding priority. Higher value means greater priority.</param>
        /// <param name="callID">Call-ID header field value which added/updated this binding.</param>
        /// <param name="cseqNo">CSeq header field sequence number value which added/updated this binding.</param>
        public void Update(SIP_Flow flow,int expires,double qvalue,string callID,int cseqNo)
        {
            if(expires < 0){
                throw new ArgumentException("Argument 'expires' value must be >= 0.");
            }
            if(qvalue < 0 || qvalue > 1){
                throw new ArgumentException("Argument 'qvalue' value must be >= 0.000 and <= 1.000");
            }
            if(callID == null){
                throw new ArgumentNullException("callID");
            }
            if(cseqNo < 0){
                throw new ArgumentException("Argument 'cseqNo' value must be >= 0.");
            }

            m_pFlow    = flow;
            m_Expires  = expires;
            m_QValue   = qvalue;
            m_CallID   = callID;
            m_CSeqNo   = cseqNo;

            m_LastUpdate = DateTime.Now;
        }
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            lock (m_pLock){
                if (this.State == SIP_DialogState.Disposed)
                {
                    return;
                }

                SetState(SIP_DialogState.Disposed, true);

                m_pStack        = null;
                m_CallID        = null;
                m_LocalTag      = null;
                m_RemoteTag     = null;
                m_pLocalUri     = null;
                m_pRemoteUri    = null;
                m_pLocalContact = null;
                m_pRemoteTarget = null;
                m_pRouteSet     = null;
                m_pFlow         = null;
                m_pLock         = null;
            }
        }
        /// <summary>
        /// Sends request to the specified flow.
        /// </summary>
        /// <param name="flow">Data flow.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="transaction">Owner client transaction or null if stateless sending.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments contains invalid value.</exception>
        internal void SendRequest(SIP_Flow flow,SIP_Request request,SIP_ClientTransaction transaction)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }
            if(request.Via.GetTopMostValue() == null){
                throw new ArgumentException("Argument 'request' doesn't contain required Via: header field.");
            }

            // Set sent-by
            SIP_t_ViaParm via = request.Via.GetTopMostValue();
            via.ProtocolTransport = flow.Transport;
            // Via sent-by is used only to send responses when request maker data flow is not active.
            // Normally this never used, so just report first local listening point as sent-by.
            HostEndPoint sentBy = null;
            foreach(IPBindInfo bind in this.BindInfo){
                if(flow.Transport == SIP_Transport.UDP && bind.Protocol == BindInfoProtocol.UDP){
                    if(!string.IsNullOrEmpty(bind.HostName)){
                        sentBy = new HostEndPoint(bind.HostName,bind.Port);
                    }
                    else{
                        sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(),bind.Port);
                    }
                    break;
                }
                else if(flow.Transport == SIP_Transport.TLS && bind.Protocol == BindInfoProtocol.TCP && bind.SslMode == SslMode.SSL){
                    if(!string.IsNullOrEmpty(bind.HostName)){
                        sentBy = new HostEndPoint(bind.HostName,bind.Port);
                    }
                    else{
                        sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(),bind.Port);
                    }
                    break;
                }
                else if(flow.Transport == SIP_Transport.TCP && bind.Protocol == BindInfoProtocol.TCP){
                    if(!string.IsNullOrEmpty(bind.HostName)){
                        sentBy = new HostEndPoint(bind.HostName,bind.Port);
                    }
                    else{
                        sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(),bind.Port);
                    }
                    break;
                }
            }
            // No local end point for sent-by, just use flow local end point for it.
            if(sentBy == null){
                via.SentBy = new HostEndPoint(flow.LocalEP);
            }
            else{
                via.SentBy = sentBy;
            }

            // Send request.
            flow.Send(request);

            // Log.
            if(m_pStack.Logger != null){
                byte[] requestData = request.ToByteData();

                m_pStack.Logger.AddWrite(
                    Guid.NewGuid().ToString(),
                    null,
                    0,
                    "Request [" + (transaction == null ? "" : "transactionID='" + transaction.ID + "';") + "method='" + request.RequestLine.Method + "'; cseq='" + request.CSeq.SequenceNumber + "'; " +
                    "transport='" + flow.Transport + "'; size='" + requestData.Length + "'; sent '" + flow.LocalEP + "' -> '" + flow.RemoteEP + "'.",
                    flow.LocalEP,
                    flow.RemoteEP,
                    requestData
                );
            }
        }
        /// <summary>
        /// Creates new SIP server transaction for specified request.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns added server transaction.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_ServerTransaction CreateServerTransaction(SIP_Flow flow,SIP_Request request)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }

            lock(m_pServerTransactions){
                SIP_ServerTransaction transaction = new SIP_ServerTransaction(m_pStack,flow,request);
                m_pServerTransactions.Add(transaction.Key,transaction);
                transaction.StateChanged += new EventHandler(delegate(object s,EventArgs e){
                    if(transaction.State == SIP_TransactionState.Terminated){
                        lock(m_pClientTransactions){
                            m_pServerTransactions.Remove(transaction.Key);
                        }
                    }
                });

                return transaction;
            }
        }
Beispiel #25
0
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            lock(m_pLock){
                if(this.State == SIP_DialogState.Disposed){
                    return;
                }

                SetState(SIP_DialogState.Disposed,true);
   
                this.RequestReceived = null;
                m_pStack             = null;
                m_CallID             = null;
                m_LocalTag           = null;
                m_RemoteTag          = null;
                m_pLocalUri          = null;
                m_pRemoteUri         = null;
                m_pLocalContact      = null;
                m_pRemoteTarget      = null;
                m_pRouteSet          = null;
                m_pFlow              = null;           
            }
        }
        /// <summary>
        /// Processes specified response through this transaction.
        /// </summary>
        /// <param name="flow">SIP data flow what received response.</param>
        /// <param name="response">SIP response to process.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b>,<b>response</b> is null reference.</exception>
        internal void ProcessResponse(SIP_Flow flow, SIP_Response response)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            lock (this.SyncRoot){
                if (this.State == SIP_TransactionState.Disposed)
                {
                    return;
                }

                /* RFC 3261 9.1. CANCEL.
                 *) If provisional response, send CANCEL, we should get '478 Request terminated'.
                 *) If final response, skip canceling, nothing to cancel.
                 */
                else if (m_IsCanceling && response.StatusCodeType == SIP_StatusCodeType.Provisional)
                {
                    SendCancel();
                    return;
                }

                // Log
                if (this.Stack.Logger != null)
                {
                    byte[] responseData = response.ToByteData();

                    this.Stack.Logger.AddRead(
                        Guid.NewGuid().ToString(),
                        null,
                        0,
                        "Response [transactionID='" + this.ID + "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + response.CSeq.SequenceNumber + "'; " +
                        "transport='" + flow.Transport + "'; size='" + responseData.Length + "'; statusCode='" + response.StatusCode + "'; " +
                        "reason='" + response.ReasonPhrase + "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.",
                        flow.LocalEP,
                        flow.RemoteEP,
                        responseData
                        );
                }


                #region INVITE

                /* RFC 3261 17.1.1.2.
                 |INVITE from TU
                 *               Timer A fires     |INVITE sent
                 *               Reset A,          V                      Timer B fires
                 *               INVITE sent +-----------+                or Transport Err.
                 +---------|           |---------------+inform TU
                 |         |  Calling  |               |
                 +-------->|           |-------------->|
                 +-----------+ 2xx           |
                 |  |       2xx to TU     |
                 |  |1xx                  |
                 |      300-699 +---------------+  |1xx to TU            |
                 |     ACK sent |                  |                     |
                 |  resp. to TU |  1xx             V                     |
                 |  1xx to TU  -----------+               |
                 |  +---------|           |               |
                 |  |         |Proceeding |-------------->|
                 |  +-------->|           | 2xx           |
                 |            +-----------+ 2xx to TU     |
                 |       300-699    |                     |
                 |       ACK sent,  |                     |
                 |       resp. to TU|                     |
                 |                  |                     |      NOTE:
                 |  300-699         V                     |
                 |  ACK sent  +-----------+Transport Err. |  transitions
                 |  +---------|           |Inform TU      |  labeled with
                 |  |         | Completed |-------------->|  the event
                 |  +-------->|           |               |  over the action
                 |            +-----------+               |  to take
                 |              ^   |                     |
                 |              |   | Timer D fires       |
                 +--------------+   | -                   |
                 |                     |
                 |                                 V                     |
                 +-----------+               |
                 |           |               |
                 | Terminated|<--------------+
                 |           |
                 +-----------+
                 |
                 */

                if (this.Method == SIP_Methods.INVITE)
                {
                    #region Calling

                    if (this.State == SIP_TransactionState.Calling)
                    {
                        // Store response.
                        AddResponse(response);

                        // Stop timer A,B
                        if (m_pTimerA != null)
                        {
                            m_pTimerA.Dispose();
                            m_pTimerA = null;

                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer A(INVITE request retransmission) stopped.");
                            }
                        }
                        if (m_pTimerB != null)
                        {
                            m_pTimerB.Dispose();
                            m_pTimerB = null;

                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer B(INVITE calling state timeout) stopped.");
                            }
                        }

                        // 1xx response.
                        if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                        {
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Proceeding);
                        }
                        // 2xx response.
                        else if (response.StatusCodeType == SIP_StatusCodeType.Success)
                        {
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Terminated);
                        }
                        // 3xx - 6xx response.
                        else
                        {
                            SendAck(response);
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.1.2.
                             *  The client transaction SHOULD start timer D when it enters the "Completed" state,
                             *  with a value of at least 32 seconds for unreliable transports, and a value of zero
                             *  seconds for reliable transports.
                             */
                            m_pTimerD          = new TimerEx(this.Flow.IsReliable ? 0 : 32000, false);
                            m_pTimerD.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerD_Elapsed);
                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                    #endregion

                    #region Proceeding

                    else if (this.State == SIP_TransactionState.Proceeding)
                    {
                        // Store response.
                        AddResponse(response);

                        // 1xx response.
                        if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                        {
                            OnResponseReceived(response);
                        }
                        // 2xx response.
                        else if (response.StatusCodeType == SIP_StatusCodeType.Success)
                        {
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Terminated);
                        }
                        // 3xx - 6xx response.
                        else
                        {
                            SendAck(response);
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.1.2.
                             *  The client transaction SHOULD start timer D when it enters the "Completed" state,
                             *  with a value of at least 32 seconds for unreliable transports, and a value of zero
                             *  seconds for reliable transports.
                             */
                            m_pTimerD          = new TimerEx(this.Flow.IsReliable ? 0 : 32000, false);
                            m_pTimerD.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerD_Elapsed);
                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                    #endregion

                    #region Completed

                    else if (this.State == SIP_TransactionState.Completed)
                    {
                        // 3xx - 6xx
                        if (response.StatusCode >= 300)
                        {
                            SendAck(response);
                        }
                    }

                    #endregion

                    #region Terminated

                    else if (this.State == SIP_TransactionState.Terminated)
                    {
                        // We should never reach here, but if so, do nothing.
                    }

                    #endregion
                }

                #endregion

                #region Non-INVITE

                /* RFC 3251 17.1.2.2
                 |Request from TU
                 |send request
                 *         Timer E             V
                 *         send request  +-----------+
                 +---------|           |-------------------+
                 |         |  Trying   |  Timer F          |
                 +-------->|           |  or Transport Err.|
                 +-----------+  inform TU        |
                 |          200-699         |  |                         |
                 |          resp. to TU     |  |1xx                      |
                 +---------------+  |resp. to TU              |
                 |                  |                         |
                 |   Timer E        V       Timer F           |
                 |   send req +-----------+ or Transport Err. |
                 |  +---------|           | inform TU         |
                 |  |         |Proceeding |------------------>|
                 |  +-------->|           |-----+             |
                 |            +-----------+     |1xx          |
                 |              |      ^        |resp to TU   |
                 | 200-699      |      +--------+             |
                 | resp. to TU  |                             |
                 |              |                             |
                 |              V                             |
                 |            +-----------+                   |
                 |            |           |                   |
                 |            | Completed |                   |
                 |            |           |                   |
                 |            +-----------+                   |
                 |              ^   |                         |
                 |              |   | Timer K                 |
                 +--------------+   | -                       |
                 |                         |
                 |                             V                         |
                 |       NOTE:           +-----------+                   |
                 |           |                   |
                 |   transitions         | Terminated|<------------------+
                 |   labeled with        |           |
                 |   the event           +-----------+
                 |   over the action
                 |   to take
                 */

                else
                {
                    #region Trying

                    if (this.State == SIP_TransactionState.Trying)
                    {
                        // Store response.
                        AddResponse(response);

                        // Stop timer E
                        if (m_pTimerE != null)
                        {
                            m_pTimerE.Dispose();
                            m_pTimerE = null;

                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer E(Non-INVITE request retransmission) stopped.");
                            }
                        }

                        // 1xx response.
                        if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                        {
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Proceeding);
                        }
                        // 2xx - 6xx response.
                        else
                        {
                            // Stop timer F
                            if (m_pTimerF != null)
                            {
                                m_pTimerF.Dispose();
                                m_pTimerF = null;

                                // Log
                                if (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stopped.");
                                }
                            }

                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.2.2.
                             *  The client transaction enters the "Completed" state, it MUST set
                             *  Timer K to fire in T4 seconds for unreliable transports, and zero
                             *  seconds for reliable transports.
                             */
                            m_pTimerK          = new TimerEx(this.Flow.IsReliable ? 1 : SIP_TimerConstants.T4, false);
                            m_pTimerK.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerK_Elapsed);
                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                    #endregion

                    #region Proceeding

                    else if (this.State == SIP_TransactionState.Proceeding)
                    {
                        // Store response.
                        AddResponse(response);

                        // 1xx response.
                        if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                        {
                            OnResponseReceived(response);
                        }
                        // 2xx - 6xx response.
                        else
                        {
                            // Stop timer F
                            if (m_pTimerF != null)
                            {
                                m_pTimerF.Dispose();
                                m_pTimerF = null;

                                // Log
                                if (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stopped.");
                                }
                            }

                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.2.2.
                             *  The client transaction enters the "Completed" state, it MUST set
                             *  Timer K to fire in T4 seconds for unreliable transports, and zero
                             *  seconds for reliable transports.
                             */
                            m_pTimerK          = new TimerEx(this.Flow.IsReliable ? 0 : SIP_TimerConstants.T4, false);
                            m_pTimerK.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerK_Elapsed);
                            // Log
                            if (this.Stack.Logger != null)
                            {
                                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                    #endregion

                    #region Completed

                    else if (this.State == SIP_TransactionState.Completed)
                    {
                        // Eat retransmited response.
                    }

                    #endregion

                    #region Terminated

                    else if (this.State == SIP_TransactionState.Terminated)
                    {
                        // We should never reach here, but if so, do nothing.
                    }

                    #endregion
                }

                #endregion
            }
        }
Beispiel #27
0
        /// <summary>
        /// Initializes call from Calling state to active..
        /// </summary>
        /// <param name="dialog">SIP dialog.</param>
        /// <param name="localSDP">Local SDP.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>dialog</b> or <b>localSDP</b> is null reference.</exception>
        internal void InitCalling(SIP_Dialog dialog, SDP_Message localSDP)
        {
            if (dialog == null)
            {
                throw new ArgumentNullException("dialog");
            }
            if (localSDP == null)
            {
                throw new ArgumentNullException("localSDP");
            }

            m_pDialog = (SIP_Dialog_Invite)dialog;
            m_pFlow = dialog.Flow;
            m_pLocalSDP = localSDP;

            m_StartTime = DateTime.Now;
            dialog.StateChanged += new EventHandler(m_pDialog_StateChanged);

            SetState(SIP_CallState.Active);

            // Start ping timer.
            m_pKeepAliveTimer = new TimerEx(40000);
            m_pKeepAliveTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_pKeepAliveTimer_Elapsed);
            m_pKeepAliveTimer.Enabled = true;
        }
Beispiel #28
0
        /// <summary>
        /// Incoming call constructor.
        /// </summary>
        /// <param name="stack">Reference to SIP stack.</param>
        /// <param name="dialog">Reference SIP dialog.</param>
        /// <param name="session">Call RTP multimedia session.</param>
        /// <param name="localSDP">Local SDP.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>dialog</b>,<b>session</b> or <b>localSDP</b> is null reference.</exception>
        internal SIP_Call(SIP_Stack stack, SIP_Dialog dialog, RTP_MultimediaSession session, SDP_Message localSDP)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (dialog == null)
            {
                throw new ArgumentNullException("dialog");
            }
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (localSDP == null)
            {
                throw new ArgumentNullException("localSDP");
            }

            m_pStack = stack;
            m_pDialog = (SIP_Dialog_Invite)dialog;
            m_pRtpMultimediaSession = session;
            m_pLocalSDP = localSDP;

            m_StartTime = DateTime.Now;
            m_pFlow = dialog.Flow;
            dialog.StateChanged += new EventHandler(m_pDialog_StateChanged);

            SetState(SIP_CallState.Active);

            // Start ping timer.
            m_pKeepAliveTimer = new TimerEx(40000);
            m_pKeepAliveTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_pKeepAliveTimer_Elapsed);
            m_pKeepAliveTimer.Enabled = true;
        }
Beispiel #29
0
        /// <summary>
        /// Add or updates specified SIP registration info.
        /// </summary>
        /// <param name="aor">Registration address of record.</param>
        /// <param name="contacts">Registration address of record contacts to update.</param>
        /// <param name="flow">SIP proxy local data flow what accpeted this contact registration.</param>
        public void SetRegistration(string aor,SIP_t_ContactParam[] contacts,SIP_Flow flow)
        {
            lock(m_pRegistrations){
                SIP_Registration registration = m_pRegistrations[aor];
                if(registration == null){
                    registration = new SIP_Registration("system",aor);
                    m_pRegistrations.Add(registration);
                    OnAorRegistered(registration);
                }

                registration.AddOrUpdateBindings(flow,"",1,contacts);
            }
        }
        /// <summary>
        /// Initializes dialog.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="transaction">Owner transaction.</param>
        /// <param name="response">SIP response what caused dialog creation.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception>
        internal protected virtual void Init(SIP_Stack stack, SIP_Transaction transaction, SIP_Response response)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            m_pStack = stack;

            m_pUsages = new List <SIP_Dialog_Usage>();

            #region UAS

            /* RFC 3261 12.1.1.
             *  The UAS then constructs the state of the dialog.  This state MUST be
             *  maintained for the duration of the dialog.
             *
             *  If the request arrived over TLS, and the Request-URI contained a SIPS
             *  URI, the "secure" flag is set to TRUE.
             *
             *  The route set MUST be set to the list of URIs in the Record-Route
             *  header field from the request, taken in order and preserving all URI
             *  parameters.  If no Record-Route header field is present in the
             *  request, the route set MUST be set to the empty set.  This route set,
             *  even if empty, overrides any pre-existing route set for future
             *  requests in this dialog.  The remote target MUST be set to the URI
             *  from the Contact header field of the request.
             *
             *  The remote sequence number MUST be set to the value of the sequence
             *  number in the CSeq header field of the request.  The local sequence
             *  number MUST be empty.  The call identifier component of the dialog ID
             *  MUST be set to the value of the Call-ID in the request.  The local
             *  tag component of the dialog ID MUST be set to the tag in the To field
             *  in the response to the request (which always includes a tag), and the
             *  remote tag component of the dialog ID MUST be set to the tag from the
             *  From field in the request.  A UAS MUST be prepared to receive a
             *  request without a tag in the From field, in which case the tag is
             *  considered to have a value of null.
             *
             *      This is to maintain backwards compatibility with RFC 2543, which
             *      did not mandate From tags.
             *
             *  The remote URI MUST be set to the URI in the From field, and the
             *  local URI MUST be set to the URI in the To field.
             */

            if (transaction is SIP_ServerTransaction)
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure      = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet     = (SIP_t_AddressParam[])Net_Utils.ReverseArray(transaction.Request.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;
                m_RemoteSeqNo   = transaction.Request.CSeq.SequenceNumber;
                m_LocalSeqNo    = 0;
                m_CallID        = transaction.Request.CallID;
                m_LocalTag      = response.To.Tag;
                m_RemoteTag     = transaction.Request.From.Tag;
                m_pRemoteUri    = transaction.Request.From.Address.Uri;
                m_pLocalUri     = transaction.Request.To.Address.Uri;
                m_pLocalContact = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;

                List <string> allow = new List <string>();
                foreach (SIP_t_Method m in response.Allow.GetAllValues())
                {
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List <string> supported = new List <string>();
                foreach (SIP_t_OptionTag s in response.Supported.GetAllValues())
                {
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #endregion

            #region UAC

            /* RFC 3261 12.1.2.
             *  When a UAC receives a response that establishes a dialog, it
             *  constructs the state of the dialog.  This state MUST be maintained
             *  for the duration of the dialog.
             *
             *  If the request was sent over TLS, and the Request-URI contained a
             *  SIPS URI, the "secure" flag is set to TRUE.
             *
             *  The route set MUST be set to the list of URIs in the Record-Route
             *  header field from the response, taken in reverse order and preserving
             *  all URI parameters.  If no Record-Route header field is present in
             *  the response, the route set MUST be set to the empty set.  This route
             *  set, even if empty, overrides any pre-existing route set for future
             *  requests in this dialog.  The remote target MUST be set to the URI
             *  from the Contact header field of the response.
             *
             *  The local sequence number MUST be set to the value of the sequence
             *  number in the CSeq header field of the request.  The remote sequence
             *  number MUST be empty (it is established when the remote UA sends a
             *  request within the dialog).  The call identifier component of the
             *  dialog ID MUST be set to the value of the Call-ID in the request.
             *  The local tag component of the dialog ID MUST be set to the tag in
             *  the From field in the request, and the remote tag component of the
             *  dialog ID MUST be set to the tag in the To field of the response.  A
             *  UAC MUST be prepared to receive a response without a tag in the To
             *  field, in which case the tag is considered to have a value of null.
             *
             *      This is to maintain backwards compatibility with RFC 2543, which
             *      did not mandate To tags.
             *
             *  The remote URI MUST be set to the URI in the To field, and the local
             *  URI MUST be set to the URI in the From field.
             */

            else
            {
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure      = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet     = (SIP_t_AddressParam[])Net_Utils.ReverseArray(response.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;
                m_LocalSeqNo    = transaction.Request.CSeq.SequenceNumber;
                m_RemoteSeqNo   = 0;
                m_CallID        = transaction.Request.CallID;
                m_LocalTag      = transaction.Request.From.Tag;
                m_RemoteTag     = response.To.Tag;
                m_pRemoteUri    = transaction.Request.To.Address.Uri;
                m_pLocalUri     = transaction.Request.From.Address.Uri;
                m_pLocalContact = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;

                List <string> allow = new List <string>();
                foreach (SIP_t_Method m in response.Allow.GetAllValues())
                {
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List <string> supported = new List <string>();
                foreach (SIP_t_OptionTag s in response.Supported.GetAllValues())
                {
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #endregion

            m_pFlow = transaction.Flow;
        }
Beispiel #31
0
        /// <summary>
        /// This method is called when REGISTER has finished.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pRegisterSender_ResponseReceived(object sender,SIP_ResponseReceivedEventArgs e)
        {   
            m_pFlow = e.ClientTransaction.Flow;
       
            if(e.Response.StatusCodeType == SIP_StatusCodeType.Provisional){
                return;
            }
            else if(e.Response.StatusCodeType == SIP_StatusCodeType.Success){
                m_pContacts.Clear();
                foreach(SIP_t_ContactParam c in e.Response.Contact.GetAllValues()){
                    m_pContacts.Add(c.Address.Uri);
                }

                SetState(SIP_UA_RegistrationState.Registered);
                                             
                OnRegistered();

                m_pFlow.SendKeepAlives = true;
            }
            else{
                SetState(SIP_UA_RegistrationState.Error);

                OnError(e);
            }

            // REMOVE ME:
            if(this.AutoFixContact && (m_pContact is SIP_Uri)){
                // If Via: received or rport paramter won't match to our sent-by, use received and rport to construct new contact value.
                   
                SIP_Uri       cContact    = ((SIP_Uri)m_pContact);
                IPAddress     cContactIP  = Net_Utils.IsIPAddress(cContact.Host) ? IPAddress.Parse(cContact.Host) : null;
                SIP_t_ViaParm via         = e.Response.Via.GetTopMostValue();
                if(via != null && cContactIP != null){
                    IPEndPoint ep = new IPEndPoint(via.Received != null ? via.Received : cContactIP,via.RPort > 0 ? via.RPort : cContact.Port);
                    if(!cContactIP.Equals(ep.Address) || cContact.Port != via.RPort){
                        // Unregister old contact.
                        BeginUnregister(false);

                        // Fix contact.
                        cContact.Host = ep.Address.ToString();
                        cContact.Port = ep.Port;

                        m_pRegisterSender.Dispose();
                        m_pRegisterSender = null;
                                                
                        BeginRegister(m_AutoRefresh);

                        return;
                    }
                }
            }

            if(m_AutoRefresh){
                // Set registration refresh timer.
                m_pTimer.Enabled = true;
            }

            m_pRegisterSender.Dispose();
            m_pRegisterSender = null;
        }
        /// <summary>
        /// Is called when client transactions receives response.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void ClientTransaction_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
        {
            lock (m_pLock){
                m_pFlow = e.ClientTransaction.Request.Flow;

                if (e.Response.StatusCode == 401 || e.Response.StatusCode == 407)
                {
                    // Check if authentication failed(We sent authorization data and it's challenged again,
                    // probably user name or password inccorect)
                    bool hasFailedAuthorization = false;
                    foreach (SIP_t_Challenge challange in e.Response.WWWAuthenticate.GetAllValues())
                    {
                        foreach (SIP_t_Credentials credentials in m_pTransaction.Request.Authorization.GetAllValues())
                        {
                            if (new Auth_HttpDigest(challange.AuthData, "").Realm == new Auth_HttpDigest(credentials.AuthData, "").Realm)
                            {
                                hasFailedAuthorization = true;
                                break;
                            }
                        }
                    }
                    foreach (SIP_t_Challenge challange in e.Response.ProxyAuthenticate.GetAllValues())
                    {
                        foreach (SIP_t_Credentials credentials in m_pTransaction.Request.ProxyAuthorization.GetAllValues())
                        {
                            if (new Auth_HttpDigest(challange.AuthData, "").Realm == new Auth_HttpDigest(credentials.AuthData, "").Realm)
                            {
                                hasFailedAuthorization = true;
                                break;
                            }
                        }
                    }

                    // Authorization failed, pass response to UA.
                    if (hasFailedAuthorization)
                    {
                        OnResponseReceived(e.Response);
                    }
                    // Try to authorize challanges.
                    else
                    {
                        SIP_Request request = m_pRequest.Copy();

                        /* RFC 3261 22.2.
                         *  When a UAC resubmits a request with its credentials after receiving a
                         *  401 (Unauthorized) or 407 (Proxy Authentication Required) response,
                         *  it MUST increment the CSeq header field value as it would normally
                         *  when sending an updated request.
                         */
                        request.CSeq = new SIP_t_CSeq(m_pStack.ConsumeCSeq(), request.CSeq.RequestMethod);

                        // All challanges authorized, resend request.
                        if (Authorize(request, e.Response, this.Credentials.ToArray()))
                        {
                            SIP_Flow flow = m_pTransaction.Flow;
                            CleanUpActiveTransaction();
                            SendToFlow(flow, request);
                        }
                        // We don't have credentials for one or more challenges.
                        else
                        {
                            OnResponseReceived(e.Response);
                        }
                    }
                }
                else
                {
                    OnResponseReceived(e.Response);
                    if (e.Response.StatusCodeType != SIP_StatusCodeType.Provisional)
                    {
                        OnCompleted();
                    }
                }
            }
        }
        /// <summary>
        /// Creates new client transaction.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to send request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <param name="addVia">If true, transaction will add <b>Via:</b> header, otherwise it's user responsibility.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        /// <returns>Returns created transaction.</returns>
        public SIP_ClientTransaction CreateClientTransaction(SIP_Flow flow,SIP_Request request,bool addVia)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }

            // Add Via:
            if(addVia){
                SIP_t_ViaParm via = new SIP_t_ViaParm();
                via.ProtocolName = "SIP";
                via.ProtocolVersion = "2.0";
                via.ProtocolTransport = flow.Transport;
                via.SentBy = new HostEndPoint("transport_layer_will_replace_it",-1);
                via.Branch = SIP_t_ViaParm.CreateBranch();
                via.RPort = 0;
                request.Via.AddToTop(via.ToStringValue());
            }

            lock(m_pClientTransactions){
                SIP_ClientTransaction transaction = new SIP_ClientTransaction(m_pStack,flow,request);
                m_pClientTransactions.Add(transaction.Key,transaction);
                transaction.StateChanged += new EventHandler(delegate(object s,EventArgs e){
                    if(transaction.State == SIP_TransactionState.Terminated){
                        lock(m_pClientTransactions){
                            m_pClientTransactions.Remove(transaction.Key);
                        }
                    }
                });

                return transaction;
            }
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack, SIP_Flow flow, SIP_Request request) : this(stack, flow, request, null)
 {
 }
            /// <summary>
            /// Creates new flow from TCP server session.
            /// </summary>
            /// <param name="session">TCP server session.</param>
            /// <returns>Returns created flow.</returns>
            /// <exception cref="ArgumentNullException">Is raised when <b>session</b> is null reference.</exception>
            internal SIP_Flow CreateFromSession(TCP_ServerSession session)
            {
                if(session == null){
                    throw new ArgumentNullException("session");
                }

                string flowID = session.LocalEndPoint.ToString() + "-" + session.RemoteEndPoint.ToString() + "-" + (session.IsSecureConnection ? SIP_Transport.TLS : SIP_Transport.TCP);

                lock(m_pLock){
                    SIP_Flow flow = new SIP_Flow(m_pOwner.Stack,session);
                    m_pFlows.Add(flowID,flow);
                    flow.IsDisposing += new EventHandler(delegate(object s,EventArgs e){
                        lock(m_pLock){
                            m_pFlows.Remove(flowID);
                        }
                    });
                    flow.Start();

                    return flow;
                }
            }
Beispiel #36
0
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            SetState(SIP_TransactionState.Disposed);
            OnDisposed();

            m_pStack   = null;
            m_pFlow    = null;
            m_pRequest = null;

            this.StateChanged   = null;
            this.Disposed       = null;
            this.TimedOut       = null;
            this.TransportError = null;
        }
            /// <summary>
            /// Returns existing flow if exists, otherwise new created flow.
            /// </summary>
            /// <param name="isServer">Specifies if created flow is server or client flow. This has effect only if flow is created.</param>
            /// <param name="localEP">Local end point.</param>
            /// <param name="remoteEP">Remote end point.</param>
            /// <param name="transport">SIP transport.</param>
            /// <returns>Returns existing flow if exists, otherwise new created flow.</returns>
            /// <exception cref="ArgumentNullException">Is raised when <b>localEP</b>,<b>remoteEP</b> or <b>transport</b> is null reference.</exception>
            /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
            internal SIP_Flow GetOrCreateFlow(bool isServer,IPEndPoint localEP,IPEndPoint remoteEP,string transport)
            {
                if(localEP == null){
                    throw new ArgumentNullException("localEP");
                }
                if(remoteEP == null){
                    throw new ArgumentNullException("remoteEP");
                }
                if(transport == null){
                    throw new ArgumentNullException("transport");
                }

                string flowID = localEP.ToString() + "-" + remoteEP.ToString() + "-" + transport.ToString();

                lock(m_pLock){
                    SIP_Flow flow = null;
                    if(m_pFlows.TryGetValue(flowID,out flow)){
                        return flow;
                    }
                    else{
                        flow =  new SIP_Flow(m_pOwner.Stack,isServer,localEP,remoteEP,transport);
                        m_pFlows.Add(flow.ID,flow);
                        flow.IsDisposing += new EventHandler(delegate(object s,EventArgs e){
                            lock(m_pLock){
                                m_pFlows.Remove(flowID);
                            }
                        });
                        flow.Start();

                        return flow;
                    }
                }
            }
Beispiel #38
0
        /// <summary>
        /// Processes specified request through this transaction.
        /// </summary>
        /// <param name="flow">SIP data flow.</param>
        /// <param name="request">SIP request.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        internal void ProcessRequest(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            lock (this.SyncRoot){
                if (this.State == SIP_TransactionState.Disposed)
                {
                    return;
                }

                try{
                    // Log
                    if (this.Stack.Logger != null)
                    {
                        byte[] requestData = request.ToByteData();

                        this.Stack.Logger.AddRead(
                            Guid.NewGuid().ToString(),
                            null,
                            0,
                            "Request [transactionID='" + this.ID + "'; method='" + request.RequestLine.Method + "'; cseq='" + request.CSeq.SequenceNumber + "'; " +
                            "transport='" + flow.Transport + "'; size='" + requestData.Length + "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.",
                            flow.LocalEP,
                            flow.RemoteEP,
                            requestData
                            );
                    }

                    #region INVITE

                    if (this.Method == SIP_Methods.INVITE)
                    {
                        #region INVITE

                        if (request.RequestLine.Method == SIP_Methods.INVITE)
                        {
                            if (this.State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.1.
                                 *  If a request retransmission is received while in the "Proceeding" state, the most recent provisional
                                 *  response that was received from the TU MUST be passed to the transport layer for retransmission.
                                 */
                                SIP_Response response = this.LastProvisionalResponse;
                                if (response != null)
                                {
                                    this.Stack.TransportLayer.SendResponse(this, response);
                                }
                            }
                            else if (this.State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.1.
                                 *  While in the "Completed" state, if a request retransmission is received, the server SHOULD
                                 *  pass the response to the transport for retransmission.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.FinalResponse);
                            }
                        }

                        #endregion

                        #region ACK

                        else if (request.RequestLine.Method == SIP_Methods.ACK)
                        {
                            /* RFC 3261 17.2.1
                             *  If an ACK is received while the server transaction is in the "Completed" state, the server transaction
                             *  MUST transition to the "Confirmed" state.  As Timer G is ignored in this state, any retransmissions of the
                             *  response will cease.
                             *
                             *  When this state is entered, timer I is set to fire in T4 seconds for unreliable transports,
                             *  and zero seconds for reliable transports.
                             */

                            if (this.State == SIP_TransactionState.Completed)
                            {
                                SetState(SIP_TransactionState.Confirmed);

                                // Stop timers G,H
                                if (m_pTimerG != null)
                                {
                                    m_pTimerG.Dispose();
                                    m_pTimerG = null;

                                    // Log
                                    if (this.Stack.Logger != null)
                                    {
                                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) stoped.");
                                    }
                                }
                                if (m_pTimerH != null)
                                {
                                    m_pTimerH.Dispose();
                                    m_pTimerH = null;

                                    // Log
                                    if (this.Stack.Logger != null)
                                    {
                                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer H(INVITE ACK wait) stoped.");
                                    }
                                }

                                // Start timer I.
                                m_pTimerI          = new TimerEx((flow.IsReliable ? 0 : SIP_TimerConstants.T4), false);
                                m_pTimerI.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerI_Elapsed);
                                // Log
                                if (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer I(INVITE ACK retransission wait) started, will triger after " + m_pTimerI.Interval + ".");
                                }
                                m_pTimerI.Enabled = true;
                            }
                        }

                        #endregion
                    }

                    #endregion

                    #region Non-INVITE

                    else
                    {
                        // Non-INVITE transaction may have only request retransmission requests.
                        if (this.Method == request.RequestLine.Method)
                        {
                            if (this.State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.2.
                                 *  If a retransmission of the request is received while in the "Proceeding" state, the most
                                 *  recently sent provisional response MUST be passed to the transport layer for retransmission.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.LastProvisionalResponse);
                            }
                            else if (this.State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.2.
                                 *  While in the "Completed" state, the server transaction MUST pass the final response to the transport
                                 *  layer for retransmission whenever a retransmission of the request is received.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.FinalResponse);
                            }
                        }
                    }

                    #endregion
                }
                catch (SIP_TransportException x) {
                    // Log
                    if (this.Stack.Logger != null)
                    {
                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] transport exception: " + x.Message);
                    }

                    OnTransportError(x);
                    SetState(SIP_TransactionState.Terminated);
                }
            }
        }
 /// <summary>
 /// Sends request to the specified flow.
 /// </summary>
 /// <param name="flow">Data flow.</param>
 /// <param name="request">SIP request.</param>
 /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
 /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
 /// <exception cref="ArgumentException">Is raised when any of the arguments contains invalid value.</exception>
 public void SendRequest(SIP_Flow flow,SIP_Request request)
 {
     SendRequest(flow,request,null);
 }
Beispiel #40
0
        /// <summary>
        /// Cleans up any resource being used.
        /// </summary>
        public void Dispose()
        {
            lock (m_pLock)
            {
                if (this.State == SIP_CallState.Disposed)
                {
                    return;
                }
                SetState(SIP_CallState.Disposed);

                // TODO: Clean up
                m_pStack = null;
                m_pLocalSDP = null;
                if (m_pDialog != null)
                {
                    m_pDialog.Dispose();
                    m_pDialog = null;
                }
                m_pFlow = null;
                if (m_pKeepAliveTimer != null)
                {
                    m_pKeepAliveTimer.Dispose();
                    m_pKeepAliveTimer = null;
                }

                this.StateChanged = null;
            }
        }
        /// <summary>
        /// Gets contact URI <b>host</b> parameter suitable to the specified flow.
        /// </summary>
        /// <param name="flow">Data flow.</param>
        /// <returns>Returns contact URI <b>host</b> parameter suitable to the specified flow.</returns>
        internal HostEndPoint GetContactHost(SIP_Flow flow)
        {
            if(flow == null){
                throw new ArgumentNullException("flow");
            }

            HostEndPoint retVal = null;

            // Find suitable listening point for flow.
            foreach(IPBindInfo bind in this.BindInfo){
                if(bind.Protocol == BindInfoProtocol.UDP && flow.Transport == SIP_Transport.UDP){
                    // For UDP flow localEP is also listeining EP, so use it.
                    if(bind.IP.AddressFamily == flow.LocalEP.AddressFamily && bind.Port == flow.LocalEP.Port){
                        retVal = new HostEndPoint((string.IsNullOrEmpty(bind.HostName) ? flow.LocalEP.Address.ToString() : bind.HostName),bind.Port);
                        break;
                    }
                }
                else if(bind.Protocol == BindInfoProtocol.TCP && bind.SslMode == SslMode.SSL && flow.Transport == SIP_Transport.TLS){
                    // Just use first matching listening point.
                    //   TODO: Probably we should imporve it with load-balanched local end point.
                    if(bind.IP.AddressFamily == flow.LocalEP.AddressFamily){
                        if(bind.IP == IPAddress.Any || bind.IP == IPAddress.IPv6Any){
                            retVal = new HostEndPoint((string.IsNullOrEmpty(bind.HostName) ? flow.LocalEP.Address.ToString() : bind.HostName),bind.Port);
                        }
                        else{
                            retVal = new HostEndPoint((string.IsNullOrEmpty(bind.HostName) ? bind.IP.ToString() : bind.HostName),bind.Port);
                        }
                        break;
                    }
                }
                else if(bind.Protocol == BindInfoProtocol.TCP && flow.Transport == SIP_Transport.TCP){
                    // Just use first matching listening point.
                    //   TODO: Probably we should imporve it with load-balanched local end point.
                    if(bind.IP.AddressFamily == flow.LocalEP.AddressFamily){
                        if(bind.IP.Equals(IPAddress.Any) || bind.IP.Equals(IPAddress.IPv6Any)){
                            retVal = new HostEndPoint((string.IsNullOrEmpty(bind.HostName) ? flow.LocalEP.Address.ToString() : bind.HostName),bind.Port);
                        }
                        else{
                            retVal = new HostEndPoint((string.IsNullOrEmpty(bind.HostName) ? bind.IP.ToString() : bind.HostName),bind.Port);
                        }
                        break;
                    }
                }
            }

            // We don't have suitable listening point for active flow.
            // RFC 3261 forces to have, but for TCP based protocls + NAT, server can't connect to use anyway,
            // so just ignore it and report flow local EP.
            if(retVal == null){
                retVal = new HostEndPoint(flow.LocalEP);
            }

            // If flow remoteEP is public IP and our localEP is private IP, resolve localEP to public.
            if(retVal.IsIPAddress && Core.IsPrivateIP(IPAddress.Parse(retVal.Host)) && !Core.IsPrivateIP(flow.RemoteEP.Address)){
                retVal = new HostEndPoint(Resolve(flow));
            }

            return retVal;
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack,SIP_Flow flow,SIP_Request request)
     : this(stack,flow,request,null,null)
 {
 }
        /// <summary>
        /// Is called when specified SIP flow has got new SIP message.
        /// </summary>
        /// <param name="flow">SIP flow.</param>
        /// <param name="message">Received message.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>message</b> is null reference.</exception>
        internal void OnMessageReceived(SIP_Flow flow,byte[] message)
        {
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(message == null){
                throw new ArgumentNullException("message");
            }

            // TODO: Log

            try{

                #region Ping / pong

                // We have "ping"(CRLFCRLF) request, response with "pong".
                if(message.Length == 4){
                    if(this.Stack.Logger != null){
                        this.Stack.Logger.AddRead("",null,2,"Flow [id='" + flow.ID + "'] received \"ping\"",flow.LocalEP,flow.RemoteEP);
                    }

                    // Send "pong".
                    flow.SendInternal(new byte[]{(byte)'\r',(byte)'\n'});

                    if(this.Stack.Logger != null){
                        this.Stack.Logger.AddWrite("",null,2,"Flow [id='" + flow.ID + "'] sent \"pong\"",flow.LocalEP,flow.RemoteEP);
                    }

                    return;
                }
                // We have pong(CRLF), do nothing.
                else if(message.Length == 2){
                    if(this.Stack.Logger != null){
                        this.Stack.Logger.AddRead("",null,2,"Flow [id='" + flow.ID + "'] received \"pong\"",flow.LocalEP,flow.RemoteEP);
                    }

                    return;
                }

                #endregion

                #region Response

                if(Encoding.UTF8.GetString(message,0,3).ToUpper().StartsWith("SIP")){

                    #region Parse and validate response

                    SIP_Response response = null;
                    try{
                        response = SIP_Response.Parse(message);
                    }
                    catch(Exception x){
                        if(m_pStack.Logger != null){
                            m_pStack.Logger.AddText("Skipping message, parse error: " + x.ToString());
                        }

                        return;
                    }

                    try{
                        response.Validate();
                    }
                    catch(Exception x){
                        if(m_pStack.Logger != null){
                            m_pStack.Logger.AddText("Response validation failed: " + x.ToString());
                        }

                        return;
                    }

                    #endregion

                    /* RFC 3261 18.1.2 Receiving Responses.
                        When a response is received, the client transport examines the top
                        Via header field value.  If the value of the "sent-by" parameter in
                        that header field value does not correspond to a value that the
                        client transport is configured to insert into requests, the response
                        MUST be silently discarded.

                        If there are any client transactions in existence, the client
                        transport uses the matching procedures of Section 17.1.3 to attempt
                        to match the response to an existing transaction.  If there is a
                        match, the response MUST be passed to that transaction.  Otherwise,
                        the response MUST be passed to the core (whether it be stateless
                        proxy, stateful proxy, or UA) for further processing.  Handling of
                        these "stray" responses is dependent on the core (a proxy will
                        forward them, while a UA will discard, for example).
                    */

                    SIP_ClientTransaction transaction =  m_pStack.TransactionLayer.MatchClientTransaction(response);
                    // Allow client transaction to process response.
                    if(transaction != null){
                        transaction.ProcessResponse(flow,response);
                    }
                    else{
                        // Pass response to dialog.
                        SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(response);
                        if(dialog != null){
                            dialog.ProcessResponse(response);
                        }
                        // Pass response to core.
                        else{
                            m_pStack.OnResponseReceived(new SIP_ResponseReceivedEventArgs(m_pStack,null,response));
                        }
                    }
                }

                #endregion

                #region Request

                // SIP request.
                else{

                    #region Parse and validate request

                    SIP_Request request = null;
                    try{
                        request = SIP_Request.Parse(message);
                    }
                    catch(Exception x){
                        // Log
                        if(m_pStack.Logger != null){
                            m_pStack.Logger.AddText("Skipping message, parse error: " + x.Message);
                        }

                        return;
                    }

                    try{
                        request.Validate();
                    }
                    catch(Exception x){
                        if(m_pStack.Logger != null){
                            m_pStack.Logger.AddText("Request validation failed: " + x.ToString());
                        }

                        // Bad request, send error to request maker.
                        SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ". " + x.Message,request));

                        return;
                    }

                    #endregion

                    // TODO: Is that needed, core can reject message as it would like.
                    SIP_ValidateRequestEventArgs eArgs = m_pStack.OnValidateRequest(request,flow.RemoteEP);
                    // Request rejected, return response.
                    if(eArgs.ResponseCode != null){
                        SendResponse(m_pStack.CreateResponse(eArgs.ResponseCode,request));

                        return;
                    }

                    request.Flow = flow;
                    request.LocalEndPoint = flow.LocalEP;
                    request.RemoteEndPoint = flow.RemoteEP;

                    /* RFC 3261 18.2.1.
                        When the server transport receives a request over any transport, it
                        MUST examine the value of the "sent-by" parameter in the top Via
                        header field value.  If the host portion of the "sent-by" parameter
                        contains a domain name, or if it contains an IP address that differs
                        from the packet source address, the server MUST add a "received"
                        parameter to that Via header field value.  This parameter MUST
                        contain the source address from which the packet was received.  This
                        is to assist the server transport layer in sending the response,
                        since it must be sent to the source IP address from which the request
                        came.

                        Next, the server transport attempts to match the request to a server
                        transaction.  It does so using the matching rules described in
                        Section 17.2.3.  If a matching server transaction is found, the
                        request is passed to that transaction for processing.  If no match is
                        found, the request is passed to the core, which may decide to
                        construct a new server transaction for that request.  Note that when
                        a UAS core sends a 2xx response to INVITE, the server transaction is
                        destroyed.  This means that when the ACK arrives, there will be no
                        matching server transaction, and based on this rule, the ACK is
                        passed to the UAS core, where it is processed.
                    */

                    /* RFC 3581 4.
                        When a server compliant to this specification (which can be a proxy
                        or UAS) receives a request, it examines the topmost Via header field
                        value.  If this Via header field value contains an "rport" parameter
                        with no value, it MUST set the value of the parameter to the source
                        port of the request.  This is analogous to the way in which a server
                        will insert the "received" parameter into the topmost Via header
                        field value.  In fact, the server MUST insert a "received" parameter
                        containing the source IP address that the request came from, even if
                        it is identical to the value of the "sent-by" component.  Note that
                        this processing takes place independent of the transport protocol.
                    */

                    SIP_t_ViaParm via = request.Via.GetTopMostValue();
                    via.Received = flow.RemoteEP.Address;
                    if(via.RPort == 0){
                        via.RPort = flow.RemoteEP.Port;
                    }

                    bool processed = false;
                    SIP_ServerTransaction transaction = m_pStack.TransactionLayer.MatchServerTransaction(request);
                    // Pass request to matched server transaction.
                    if(transaction != null){
                        transaction.ProcessRequest(flow,request);

                        processed = true;
                    }
                    else{
                        SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(request);
                        // Pass request to dialog.
                        if(dialog != null){
                            processed = dialog.ProcessRequest(new SIP_RequestReceivedEventArgs(m_pStack,flow,request));
                        }
                    }

                    // Request not proecced by dialog or transaction, pass request to TU.
                    if(!processed){
                        // Log
                        if(m_pStack.Logger != null){
                            byte[] requestData = request.ToByteData();

                            m_pStack.Logger.AddRead(
                                Guid.NewGuid().ToString(),
                                null,
                                0,
                                "Request [method='" + request.RequestLine.Method + "'; cseq='" + request.CSeq.SequenceNumber + "'; " +
                                    "transport='" + flow.Transport + "'; size='" + requestData.Length + "'; " +
                                    "received '" + flow.RemoteEP + "' -> '" + flow.LocalEP + "'.",
                                flow.LocalEP,
                                flow.RemoteEP,
                                requestData
                            );
                        }

                        m_pStack.OnRequestReceived(new SIP_RequestReceivedEventArgs(m_pStack,flow,request));
                    }
                }

                #endregion

            }
            catch(SocketException s){
                // Skip all socket errors here
                string dummy = s.Message;
            }
            //catch(ArgumentException x){
            //    m_pStack.OnError(x);
            //}
            catch(Exception x){
                m_pStack.OnError(x);
            }
        }
        /// <summary>
        /// Processes specified response through this transaction.
        /// </summary>
        /// <param name="flow">SIP data flow what received response.</param>
        /// <param name="response">SIP response to process.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b>,<b>response</b> is null reference.</exception>
        internal void ProcessResponse(SIP_Flow flow,SIP_Response response)
        {
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(response == null){
                throw new ArgumentNullException("response");
            }

            lock(this.SyncRoot){
                if(this.State == SIP_TransactionState.Disposed){
                    return;
                }
                /* RFC 3261 9.1. CANCEL.
                    *) If provisional response, send CANCEL, we should get '478 Request terminated'.
                    *) If final response, skip canceling, nothing to cancel.
                */
                else if(m_IsCanceling && response.StatusCodeType == SIP_StatusCodeType.Provisional){
                    SendCancel();
                    return;
                }

                // Log
                if(this.Stack.Logger != null){
                    byte[] responseData = response.ToByteData();

                    this.Stack.Logger.AddRead(
                        Guid.NewGuid().ToString(),
                        null,
                        0,
                        "Response [transactionID='" +  this.ID + "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + response.CSeq.SequenceNumber + "'; " + 
                        "transport='" + flow.Transport + "'; size='" + responseData.Length + "'; statusCode='" + response.StatusCode + "'; " + 
                        "reason='" + response.ReasonPhrase + "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.",
                        flow.LocalEP,
                        flow.RemoteEP,
                        responseData
                    );
                }


                #region INVITE

                /* RFC 6026 7.2. INVITE client transaction. (Udpates RFC 3261)
                      +-----------+                        +-----------+
                      |           |                        |           |
                      |  Calling  |                        |  Calling  |
                      |           |----------->+           |           |-----------+
                      +-----------+ 2xx        |           +-----------+ 2xx       |
                                    2xx to TU  |                         2xx to TU |
                                               |                                   |
                                               |                                   |
                                               |                                   |
                                               |                                   |
                      +-----------+            |           +-----------+           |
                      |           |            |           |           |           |
                      |Proceeding |----------->|           |Proceeding |---------->|
                      |           | 2xx        |           |           | 2xx       |
                      +-----------+ 2xx to TU  |           +-----------+ 2xx to TU |
                                               |                                   |
                                               |                                   |
                                               |                                   |
                                               |                                   V
                                               |                            +-----------+
                                               |                            |           |
                                               |                            | Accepted  |
                                               |                        +---|           |
                                               |              2xx       |   +-----------+
                                               |              2xx to TU |     ^    |
                                               |                        |     |    |
                                               |                        +-----+    |
                                               |                                   |
                                               |                 +-----------------+
                                               |                 | Timer M fires
                                               |                 | -
                                               |                 V
                      +-----------+            |           +-----------+
                      |           |            |           |           |
                      | Terminated|<-----------+           | Terminated|
                      |           |                        |           |
                      +-----------+                        +-----------+


                */

                if(this.Method == SIP_Methods.INVITE){
                    #region Calling

                    if(this.State == SIP_TransactionState.Calling){
                        // Store response.
                        AddResponse(response);

                        // Stop timer A,B
                        if(m_pTimerA != null){
                            m_pTimerA.Dispose();
                            m_pTimerA = null;

                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer A(INVITE request retransmission) stopped.");
                            }
                        }
                        if(m_pTimerB != null){
                            m_pTimerB.Dispose();
                            m_pTimerB = null;

                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer B(INVITE calling state timeout) stopped.");
                            }
                        }

                        // 1xx response.
                        if(response.StatusCodeType == SIP_StatusCodeType.Provisional){
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Proceeding);
                        }
                        // 2xx response.
                        else if(response.StatusCodeType == SIP_StatusCodeType.Success){
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Accpeted);

                            /* RFC 6025 7.1.
                                When the "Accepted" state is entered, timer L MUST be set to fire in 64*T1.
                            */
                            m_pTimerM = new TimerEx(64 * SIP_TimerConstants.T1);
                            m_pTimerM.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerM_Elapsed);
                            m_pTimerM.Enabled = true;

                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer M(2xx retransmission wait) started, will trigger after " + m_pTimerM.Interval + ".");
                            }
                        }
                        // 3xx - 6xx response.
                        else{
                            SendAck(response);
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.1.2. 
                                The client transaction SHOULD start timer D when it enters the "Completed" state, 
                                with a value of at least 32 seconds for unreliable transports, and a value of zero 
                                seconds for reliable transports.
                            */
                            m_pTimerD = new TimerEx(this.Flow.IsReliable ? 0 : 32000,false);
                            m_pTimerD.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerD_Elapsed);
                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                    #endregion

                    #region Proceeding

                    else if(this.State == SIP_TransactionState.Proceeding){
                        // Store response.
                        AddResponse(response);

                        // 1xx response.
                        if(response.StatusCodeType == SIP_StatusCodeType.Provisional){
                            OnResponseReceived(response);
                        }
                        // 2xx response.
                        else if(response.StatusCodeType == SIP_StatusCodeType.Success){
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Accpeted);

                            /* RFC 6025 7.1.
                                When the "Accepted" state is entered, timer L MUST be set to fire in 64*T1.
                            */
                            m_pTimerM = new TimerEx(64 * SIP_TimerConstants.T1);
                            m_pTimerM.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerM_Elapsed);
                            m_pTimerM.Enabled = true;

                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer M(2xx retransmission wait) started, will trigger after " + m_pTimerM.Interval + ".");
                            }
                        }
                        // 3xx - 6xx response.
                        else{
                            SendAck(response);
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.1.2. 
                                The client transaction SHOULD start timer D when it enters the "Completed" state, 
                                with a value of at least 32 seconds for unreliable transports, and a value of zero 
                                seconds for reliable transports.
                            */
                            m_pTimerD = new TimerEx(this.Flow.IsReliable ? 0 : 32000,false);
                            m_pTimerD.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerD_Elapsed);
                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerD.Interval + ".");
                            }
                            m_pTimerD.Enabled = true;
                        }
                    }

                    #endregion

                    #region Accepted

                    else if(this.State == SIP_TransactionState.Accpeted){
                        if(response.StatusCodeType == SIP_StatusCodeType.Success){
                            OnResponseReceived(response);
                        }
                    }

                    #endregion

                    #region Completed

                    else if(this.State == SIP_TransactionState.Completed){
                        // 3xx - 6xx
                        if(response.StatusCode >= 300){
                            SendAck(response);
                        }
                    }

                    #endregion

                    #region Terminated

                    else if(this.State == SIP_TransactionState.Terminated){
                        // We should never reach here, but if so, do nothing.
                    }

                    #endregion
                }

                #endregion

                #region Non-INVITE

                /* RFC 3251 17.1.2.2
                                               |Request from TU
                                               |send request
                           Timer E             V
                           send request  +-----------+
                               +---------|           |-------------------+
                               |         |  Trying   |  Timer F          |
                               +-------->|           |  or Transport Err.|
                                         +-----------+  inform TU        |
                            200-699         |  |                         |
                            resp. to TU     |  |1xx                      |
                            +---------------+  |resp. to TU              |
                            |                  |                         |
                            |   Timer E        V       Timer F           |
                            |   send req +-----------+ or Transport Err. |
                            |  +---------|           | inform TU         |
                            |  |         |Proceeding |------------------>|
                            |  +-------->|           |-----+             |
                            |            +-----------+     |1xx          |
                            |              |      ^        |resp to TU   |
                            | 200-699      |      +--------+             |
                            | resp. to TU  |                             |
                            |              |                             |
                            |              V                             |
                            |            +-----------+                   |
                            |            |           |                   |
                            |            | Completed |                   |
                            |            |           |                   |
                            |            +-----------+                   |
                            |              ^   |                         |
                            |              |   | Timer K                 |
                            +--------------+   | -                       |
                                               |                         |
                                               V                         |
                         NOTE:           +-----------+                   |
                                         |           |                   |
                     transitions         | Terminated|<------------------+
                     labeled with        |           |
                     the event           +-----------+
                     over the action
                     to take
                */

                else{
                    #region Trying

                    if(this.State == SIP_TransactionState.Trying){
                        // Store response.
                        AddResponse(response);

                        // Stop timer E
                        if(m_pTimerE != null){
                            m_pTimerE.Dispose();
                            m_pTimerE = null;

                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer E(Non-INVITE request retransmission) stopped.");
                            }
                        }

                        // 1xx response.
                        if(response.StatusCodeType == SIP_StatusCodeType.Provisional){
                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Proceeding);
                        }
                        // 2xx - 6xx response.
                        else{
                            // Stop timer F
                            if(m_pTimerF != null){
                                m_pTimerF.Dispose();
                                m_pTimerF = null;

                                // Log
                                if(this.Stack.Logger != null){
                                    this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stopped.");
                                }
                            }

                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.2.2. 
                                The client transaction enters the "Completed" state, it MUST set
                                Timer K to fire in T4 seconds for unreliable transports, and zero
                                seconds for reliable transports.
                            */
                            m_pTimerK = new TimerEx(this.Flow.IsReliable ? 1 : SIP_TimerConstants.T4,false);
                            m_pTimerK.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerK_Elapsed);
                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                    #endregion

                    #region Proceeding

                    else if(this.State == SIP_TransactionState.Proceeding){
                        // Store response.
                        AddResponse(response);

                        // 1xx response.
                        if(response.StatusCodeType == SIP_StatusCodeType.Provisional){
                            OnResponseReceived(response);
                        }                        
                        // 2xx - 6xx response.
                        else{
                            // Stop timer F
                            if(m_pTimerF != null){
                                m_pTimerF.Dispose();
                                m_pTimerF = null;

                                // Log
                                if(this.Stack.Logger != null){
                                    this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stopped.");
                                }
                            }

                            OnResponseReceived(response);
                            SetState(SIP_TransactionState.Completed);

                            /* RFC 3261 17.1.2.2. 
                                The client transaction enters the "Completed" state, it MUST set
                                Timer K to fire in T4 seconds for unreliable transports, and zero
                                seconds for reliable transports.
                            */
                            m_pTimerK = new TimerEx(this.Flow.IsReliable ? 0 : SIP_TimerConstants.T4,false);
                            m_pTimerK.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerK_Elapsed);
                            // Log
                            if(this.Stack.Logger != null){
                                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will trigger after " + m_pTimerK.Interval + ".");
                            }
                            m_pTimerK.Enabled = true;
                        }
                    }

                    #endregion

                    #region Completed

                    else if(this.State == SIP_TransactionState.Completed){
                        // Eat retransmited response.
                    }

                    #endregion

                    #region Terminated

                    else if(this.State == SIP_TransactionState.Terminated){
                        // We should never reach here, but if so, do nothing.
                    }

                    #endregion
                }

                #endregion
            }
        }
        /// <summary>
        /// Resolves data flow local NATed IP end point to public IP end point.
        /// </summary>
        /// <param name="flow">Data flow.</param>
        /// <returns>Returns public IP end point of local NATed IP end point.</returns>
        /// <exception cref="ArgumentNullException">Is raised <b>flow</b> is null reference.</exception>
        internal IPEndPoint Resolve(SIP_Flow flow)
        {
            if(flow == null){
                throw new ArgumentNullException("flow");
            }

            IPEndPoint resolvedEP = null;
            AutoResetEvent completionWaiter = new AutoResetEvent(false);
            // Create OPTIONS request
            SIP_Request optionsRequest = m_pStack.CreateRequest(SIP_Methods.OPTIONS,new SIP_t_NameAddress("sip:[email protected]"),new SIP_t_NameAddress("sip:[email protected]"));
            SIP_ClientTransaction optionsTransaction = m_pStack.TransactionLayer.CreateClientTransaction(flow,optionsRequest,true);
            optionsTransaction.ResponseReceived += new EventHandler<SIP_ResponseReceivedEventArgs>(delegate(object s,SIP_ResponseReceivedEventArgs e){
                SIP_t_ViaParm via = e.Response.Via.GetTopMostValue();

                resolvedEP = new IPEndPoint(via.Received == null ? flow.LocalEP.Address : via.Received,via.RPort > 0 ? via.RPort : flow.LocalEP.Port);

                completionWaiter.Set();
            });
            optionsTransaction.StateChanged += new EventHandler(delegate(object s,EventArgs e){
                if(optionsTransaction.State == SIP_TransactionState.Terminated){
                    completionWaiter.Set();
                }
            });
            optionsTransaction.Start();

            // Wait OPTIONS request to complete.
            completionWaiter.WaitOne();

            if(resolvedEP != null){
                return resolvedEP;
            }
            else{
                return flow.LocalEP;
            }
        }
Beispiel #46
0
        /// <summary>
        /// Initializes dialog.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="transaction">Owner transaction.</param>
        /// <param name="response">SIP response what caused dialog creation.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception>
        internal protected virtual void Init(SIP_Stack stack,SIP_Transaction transaction,SIP_Response response)
        {
            if(stack == null){
                throw new ArgumentNullException("stack");
            }
            if(transaction == null){
                throw new ArgumentNullException("transaction");
            }
            if(response == null){
                throw new ArgumentNullException("response");
            }

            m_pStack = stack;
                        
            #region UAS

            /* RFC 3261 12.1.1.
                The UAS then constructs the state of the dialog.  This state MUST be
                maintained for the duration of the dialog.

                If the request arrived over TLS, and the Request-URI contained a SIPS
                URI, the "secure" flag is set to TRUE.

                The route set MUST be set to the list of URIs in the Record-Route
                header field from the request, taken in order and preserving all URI
                parameters.  If no Record-Route header field is present in the
                request, the route set MUST be set to the empty set.  This route set,
                even if empty, overrides any pre-existing route set for future
                requests in this dialog.  The remote target MUST be set to the URI
                from the Contact header field of the request.

                The remote sequence number MUST be set to the value of the sequence
                number in the CSeq header field of the request.  The local sequence
                number MUST be empty.  The call identifier component of the dialog ID
                MUST be set to the value of the Call-ID in the request.  The local
                tag component of the dialog ID MUST be set to the tag in the To field
                in the response to the request (which always includes a tag), and the
                remote tag component of the dialog ID MUST be set to the tag from the
                From field in the request.  A UAS MUST be prepared to receive a
                request without a tag in the From field, in which case the tag is
                considered to have a value of null.

                    This is to maintain backwards compatibility with RFC 2543, which
                    did not mandate From tags.

                The remote URI MUST be set to the URI in the From field, and the
                local URI MUST be set to the URI in the To field.
            */

            if(transaction is SIP_ServerTransaction){
                m_IsSecure = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet = (SIP_t_AddressParam[])Net_Utils.ReverseArray(transaction.Request.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;
                m_RemoteSeqNo = transaction.Request.CSeq.SequenceNumber;
                m_LocalSeqNo = 0;
                m_CallID = transaction.Request.CallID;
                m_LocalTag = response.To.Tag;
                m_RemoteTag = transaction.Request.From.Tag;
                m_pRemoteUri = transaction.Request.From.Address.Uri;
                m_pLocalUri = transaction.Request.To.Address.Uri;
                m_pLocalContact = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;

                List<string> allow = new List<string>();
                foreach(SIP_t_Method m in response.Allow.GetAllValues()){
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List<string> supported = new List<string>();
                foreach(SIP_t_OptionTag s in response.Supported.GetAllValues()){
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #endregion

            #region UAC

            /* RFC 3261 12.1.2.
                When a UAC receives a response that establishes a dialog, it
                constructs the state of the dialog.  This state MUST be maintained
                for the duration of the dialog.

                If the request was sent over TLS, and the Request-URI contained a
                SIPS URI, the "secure" flag is set to TRUE.

                The route set MUST be set to the list of URIs in the Record-Route
                header field from the response, taken in reverse order and preserving
                all URI parameters.  If no Record-Route header field is present in
                the response, the route set MUST be set to the empty set.  This route
                set, even if empty, overrides any pre-existing route set for future
                requests in this dialog.  The remote target MUST be set to the URI
                from the Contact header field of the response.

                The local sequence number MUST be set to the value of the sequence
                number in the CSeq header field of the request.  The remote sequence
                number MUST be empty (it is established when the remote UA sends a
                request within the dialog).  The call identifier component of the
                dialog ID MUST be set to the value of the Call-ID in the request.
                The local tag component of the dialog ID MUST be set to the tag in
                the From field in the request, and the remote tag component of the
                dialog ID MUST be set to the tag in the To field of the response.  A
                UAC MUST be prepared to receive a response without a tag in the To
                field, in which case the tag is considered to have a value of null.

                    This is to maintain backwards compatibility with RFC 2543, which
                    did not mandate To tags.

                The remote URI MUST be set to the URI in the To field, and the local
                URI MUST be set to the URI in the From field.
            */

            else{
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure  = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet = (SIP_t_AddressParam[])Net_Utils.ReverseArray(response.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;                
                m_LocalSeqNo = transaction.Request.CSeq.SequenceNumber;
                m_RemoteSeqNo = 0;
                m_CallID = transaction.Request.CallID;
                m_LocalTag = transaction.Request.From.Tag;
                m_RemoteTag = response.To.Tag;
                m_pRemoteUri = transaction.Request.To.Address.Uri;
                m_pLocalUri = transaction.Request.From.Address.Uri;
                m_pLocalContact = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;
                
                List<string> allow = new List<string>();
                foreach(SIP_t_Method m in response.Allow.GetAllValues()){
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List<string> supported = new List<string>();
                foreach(SIP_t_OptionTag s in response.Supported.GetAllValues()){
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #endregion            

            m_pFlow = transaction.Flow;
            AddTransaction(transaction);
        }
        /// <summary>
        /// Ensures that specified request has matching server transaction. If server transaction doesn't exist, 
        /// it will be created, otherwise existing transaction will be returned.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns matching transaction.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Is raised when request.Method is ACK request.</exception>
        public SIP_ServerTransaction EnsureServerTransaction(SIP_Flow flow,SIP_Request request)
        {
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }
            if(request.RequestLine.Method == SIP_Methods.ACK){
                throw new InvalidOperationException("ACK request is transaction less request, can't create transaction for it.");
            }

            /*
                We use branch and sent-by as indexing key for transaction, the only special what we need to
                do is to handle CANCEL, because it has same branch as transaction to be canceled.
                For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
                ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
            */
            string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;
            if(request.RequestLine.Method == SIP_Methods.CANCEL){
                key += "-CANCEL";
            }

            lock(m_pServerTransactions){
                SIP_ServerTransaction retVal = null;
                m_pServerTransactions.TryGetValue(key,out retVal);
                // We don't have transaction, create it.
                if(retVal == null){
                    retVal = CreateServerTransaction(flow,request);
                }

                return retVal;
            }
        }