Click here to Skip to main content
15,892,809 members
Articles / Programming Languages / C#

STUN Client

Rate me:
Please Sign up or sign in to vote.
4.83/5 (36 votes)
20 Apr 2007CPOL 327.9K   14.9K   85  
STUN client C# implementation with sample application
using System;
using System.Collections.Generic;
using System.Text;
using System.Timers;
using System.Net;

using LumiSoft.Net.SIP.Message;

namespace LumiSoft.Net.SIP.Stack
{
    /// <summary>
    /// Implements SIP server transaction. Defined in RFC 3261 17.2 Server Transaction.
    /// A transaction is a sequence of SIP messages exchanged between SIP network elements. 
    /// Server transaction is created when remote UA (client transaction) request is received by local UA.
    /// Server transaction provides way to send responses back to remote UA client transaction.
    /// </summary>
    /// <remarks>
    /// <img src="../images/SIP_ServerTransaction.gif" />
    /// </remarks>
    public class SIP_ServerTransaction : SIP_Transaction
    {
        private SIP_Stack                   m_pSipStack                = null;
        private string                      m_ID                       = "";
        private SIP_Request                 m_pRequest                 = null;
        private SIP_ServerTransactionState  m_TransactionState         = SIP_ServerTransactionState.Proceeding;
        private Timer                       m_pTransactionTimeoutTimer = null;  
        private Timer                       m_pTimerG                  = null;
        private Timer                       m_pTimerH                  = null;
        private Timer                       m_pTimerI                  = null;
        private Timer                       m_pTimerJ                  = null;
        private int                         m_T1                       = 500;
        private int                         m_T2                       = 4000;
        private int                         m_T4                       = 5000;
        private List<SIP_Response>          m_pResponses               = null;
        private SIP_Dialog                  m_pDialog                  = null;
        private object                      m_pTag                     = null;
        private bool                        m_Disposed                 = false;

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="sipStack">Reference to SIP stack.</param>
        /// <param name="request">SIP request what caused to create server transaction.</param>
        internal SIP_ServerTransaction(SIP_Stack sipStack,SIP_Request request)
        {
            m_pSipStack = sipStack;
            m_pRequest  = request;

            m_ID         = request.Via.GetTopMostValue().Branch;
            m_pResponses = new List<SIP_Response>();

            Begin();
        }

        #region method Dispose

        /// <summary>
        /// Disposes transaction and cleans up all resources.
        /// </summary>
        public override void Dispose()
        {
            if(m_Disposed){
                return;
            }

            try{
                m_TransactionState = SIP_ServerTransactionState.Terminated;

                base.Dispose();

                if(m_pTransactionTimeoutTimer != null){
                    m_pTransactionTimeoutTimer.Dispose();
                    m_pTransactionTimeoutTimer = null;
                }
                            
                if(m_pTimerG != null){
                    m_pTimerG.Dispose();
                    m_pTimerG = null;
                }

                if(m_pTimerH != null){
                    m_pTimerH.Dispose();
                    m_pTimerH = null;
                }

                if(m_pTimerI != null){
                    m_pTimerI.Dispose();
                    m_pTimerI = null;
                }

                if(m_pTimerJ != null){
                    m_pTimerJ.Dispose();
                    m_pTimerJ = null;
                }
                                                                
                // Log
                m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) disposed.");
            }
            finally{
                // Remove from transactions collection.
                m_pSipStack.TransactionLayer.RemoveServerTransaction(this);

                OnTerminated();
            }

            m_Disposed = true;
        }

        #endregion


        #region method Begin

        /// <summary>
        /// Starts processing server transaction.
        /// </summary>
        private void Begin()
        {
            // Log
            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) created.");

            // INVITE server transaction
            if(m_pRequest.Method == "INVITE"){
                /*  When a server transaction is constructed for a request, it enters the
                    "Proceeding" state. The server transaction MUST generate a 100 (Trying) response.
                */

                m_TransactionState = SIP_ServerTransactionState.Proceeding;                

                SendTrying();
            }
            // Non-INVITE server transaction
            else{
                /* When a server transaction is constructed for a request, it enters the "Trying" state.
                */

                m_TransactionState = SIP_ServerTransactionState.Trying;
            }

            // This is just timout timer and ensures that transaction ends or will terminated.
            m_pTransactionTimeoutTimer = new Timer(120 * m_T1);
            m_pTransactionTimeoutTimer.Elapsed += new ElapsedEventHandler(m_pTransactionTimeoutTimer_Elapsed);
            m_pTransactionTimeoutTimer.Enabled = true;
            // Log
            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Transaction timeout Timer started, will triger after " + m_pTransactionTimeoutTimer.Interval + ".");
        }

        #endregion

        #region method Cancel

        /// <summary>
        /// Cancels transaction. NOTE: Only INVITE transaction can be canceled, for other methods cancel is skipped.
        /// If If final response is already sent, then cancel has no effect and will be skipped.
        /// </summary>
        public override void Cancel()
        {   
            /* RFC 3261 9.2.
                If we have sent final response, skip cancel, because it has no effect. If the UAS has not 
                issued a final response for the original request, its behavior depends on the method of the 
                original request. If the original request was an INVITE, the UAS SHOULD immediately respond to 
                the INVITE with a 487 (Request Terminated). A CANCEL request has no impact on the processing of 
                transactions with any other method defined in this specification.
            */

            lock(this){
                // If final response sent, skip cancel.
                if(GetFinalResponse() != null){
                    return;
                }
                else{
                    // Only INVITE can be canceled.
                    if(m_pRequest.Method == SIP_Methods.INVITE){
                        SendResponse(m_pRequest.CreateResponse(SIP_ResponseCodes.x487_Request_Terminated));
                        // We no normally must get ACK now, if not timeout timers will 
                        // take care of disposing this transaction.
                                                   
                        OnCanceled();

                        // Log
                        m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) canceled.");
                    }
                }
            }
        }

        #endregion

        #region method SendResponse
        
        /// <summary>
        /// Sends a SIP response message to the client transaction whose request initiated this server transaction.
        /// The response value must be SIP_ServerTransaction.Request.CreateResponse value.
        /// </summary>
        /// <param name="response">SIP response to send.</param>
        public void SendResponse(SIP_Response response)
        {
            ProcessResponse(response);
        }

        #endregion
                        
        
        #region method m_pTimerG_Elapsed

        /// <summary>
        /// Is called when INVITE response retransission must be done.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void m_pTimerG_Elapsed(object sender,ElapsedEventArgs e)
        { 
            /* RFC 3261 17.2.1.
                When timer G fires, the server transaction MUST retransmit the response by passing 
                it to the transport layer, and MUST reset the timer with a value of MIN(2*T1,T2).
                For the default values of T1 and T2, this results in intervals of 500 ms, 1 s, 2 s, 4 s, 4 s, etc.
            */

            // Log.
            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer G(INVITE response retransmission interval) triggered,next transmit after " + m_pTimerG.Interval + " ms.");
    
            // Retransmits response.
            m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,GetFinalResponse());

            // Update interval.
            m_pTimerG.Interval = Math.Min(m_pTimerG.Interval * 2,m_T2);
        }

        #endregion

        #region method m_pTimerH_Elapsed

        /// <summary>
        /// Is called when INVITE completed state ACK wait time reached.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void m_pTimerH_Elapsed(object sender,ElapsedEventArgs e)
        {
            /* RFC 3261 17.2.1.
                If timer H fires while in the "Completed" state, it implies that the
                ACK was never received.  In this case, the server transaction MUST
                transition to the "Terminated" state, and MUST indicate to the TU
                that a transaction failure has occurred.
            */

            // Log.
            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer H(Wait time for ACK receipt) triggered.");

            if(m_TransactionState == SIP_ServerTransactionState.Completed){
                Dispose();

                // Notify TU
                OnTransactionError("ACK never received !");
            }
        }

        #endregion

        #region merhod m_pTimerI_Elapsed

        /// <summary>
        /// Is called when INVITE Confirmed linger time completed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void m_pTimerI_Elapsed(object sender,ElapsedEventArgs e)
        {
            // Log.
            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer I(Wait time for ACK retransmissions) triggered.");

            Dispose();
        }

        #endregion

        #region mehtod m_pTimerJ_Elapsed

        /// <summary>
        /// Is called when non-INVITE Confirmed linger time completed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void m_pTimerJ_Elapsed(object sender,ElapsedEventArgs e)
        {
            /* RFC 3261 17.2.2
                Timer J fires, at which point it MUST transition to the "Terminated" state.
            */

            // Log.
            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer J(Wait time for retransmissions of non-INVITE requests) triggered.");

            Dispose();
        }

        #endregion

        #region method m_pTransactionTimeoutTimer_Elapsed

        /// <summary>
        /// Is called when transaction has timed out.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void m_pTransactionTimeoutTimer_Elapsed(object sender,ElapsedEventArgs e)
        { 
            // Log.
            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Transaction timeout timer triggered.");

            OnTimedOut();           
            Dispose();
        }

        #endregion

                       
        #region method ProcessRequest

        /// <summary>
        /// Processes transaction request.
        /// </summary>
        /// <param name="request">SIP request.</param>
        internal void ProcessRequest(SIP_Request request)
        {   
            // TODO: We MAY accept only ACK,CANCEL and request retransmission.

            // Log
            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) got request '" + request.Method +  "'.");

            lock(this){
            
                #region ACK

                if(request.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.
                
                        Also kill timer H(ACK wait timeout timer) because we got ACK.
                    */
                    if(m_TransactionState == SIP_ServerTransactionState.Completed){
                        m_TransactionState = SIP_ServerTransactionState.Confirmed;

                        // If there is timer G (response retransmit timer), stop it,because we got ACK.
                        if(m_pTimerG != null){
                            m_pTimerG.Dispose();
                            m_pTimerG = null;

                            // Log
                            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer G (response retransmit timer) stopped.");
                        }
                        // If there is timer H (ACK wait timeout timer), stop it because we got ACK.
                        if(m_pTimerH != null){
                            m_pTimerH.Dispose();
                            m_pTimerH = null;

                            // Log
                            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer H (ACK wait timeout timer) stopped.");
                        }

                        /* RFC 3261 17.2.1.
                            The purpose of the "Confirmed" state is to absorb any additional ACK
                            messages that arrive, triggered from retransmissions of the final response.
                            When confirmed state is entered, timer I is set to fire in T4
                            seconds for unreliable transports, and zero seconds for reliable transports.
                        */
                        if(request.Via.GetTopMostValue().ProtocolTransport.ToUpper() == "UDP"){
                            m_pTimerI = new Timer(m_T4);
                            m_pTimerI.AutoReset = false;
                            m_pTimerI.Elapsed += new ElapsedEventHandler(m_pTimerI_Elapsed);

                            // Log
                            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer I (additional ACK absorb) started, will triger after " + m_pTimerI.Interval + ".");
                        }
                        else{
                            Dispose();
                        }
                    }
                }

                #endregion

                #region CANCEL

                else if(request.Method == SIP_Methods.CANCEL){
                    // Do new server transaction for cancel.
                    // We bind RFC here, rfc says UAS needs to respond canel, we hide cancel stuff from user. 
                    // If somebody knows why we can't do so, let me know.
                    m_pSipStack.TransactionLayer.CreateServerTransaction(request).SendResponse(request.CreateResponse(SIP_ResponseCodes.x200_Ok));

                    Cancel();
                }

                #endregion

                #region INVITE

                // INVITE server transaction
                else if(request.Method == SIP_Methods.INVITE){
                    /*
                        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. 
                  
                        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.  
                    */

                    if(m_TransactionState == SIP_ServerTransactionState.Proceeding){
                        SIP_Response lastProvisionalResponse = GetLastProvisionalResponse();
                        if(lastProvisionalResponse != null){
                            m_pSipStack.TransportLayer.SendResponse(null,lastProvisionalResponse);
                        }
                    }
                    else if(m_TransactionState == SIP_ServerTransactionState.Completed){
                        m_pSipStack.TransportLayer.SendResponse(null,GetFinalResponse());
                    }
                }

                #endregion

                #region Non-INVITE

                // Non-INVITE server transaction
                else{
                    /*
                        In the "Trying" state, any further request retransmissions are discarded. 
                  
                        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. 
                  
                        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.               
                    */

                    if(m_TransactionState == SIP_ServerTransactionState.Trying){
                        // Do nothing
                    }
                    else if(m_TransactionState == SIP_ServerTransactionState.Proceeding){
                        m_pSipStack.TransportLayer.SendResponse(null,GetLastProvisionalResponse());
                    }
                    else if(m_TransactionState == SIP_ServerTransactionState.Completed){
                        m_pSipStack.TransportLayer.SendResponse(null,GetFinalResponse());
                    }
                }

                #endregion

            }
        }
                
        #endregion

        #region method ProcessResponse

        /// <summary>
        /// Processes specified SIP response through this transaction.
        /// </summary>
        /// <param name="response">SIP response to process.</param>
        internal void ProcessResponse(SIP_Response response)
        {     
            #region INVITE

            // INVITE server transaction
            if(m_pRequest.Method == "INVITE"){

                #region Proceeding

                // Proceeding state
                if(m_TransactionState == SIP_ServerTransactionState.Proceeding){
                    // 1xx response
                    if(response.StausCodeType == SIP_StatusCodeType.Provisional){
                        // We don't forward 100 Trying, skip it
                        if(response.StatusCode == 100){
                            return;
                        }

                        m_pResponses.Add(response);

                        EnsureDialog(response);
                        PassResponseToDialog(response);

                        // Send unreliably (Pass to tranport layer)
                        m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,response);
                    }
                    // 2xx response
                    else if(response.StausCodeType == SIP_StatusCodeType.Success){
                        EnsureDialog(response);
                        PassResponseToDialog(response);

                        // Send unreliably (Pass to tranport layer)
                        m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,response);

                        m_TransactionState = SIP_ServerTransactionState.Terminated;
                        Dispose();
                    }
                    // 300-699 response
                    else{
                        m_TransactionState = SIP_ServerTransactionState.Completed;
                        m_pResponses.Add(response);

                        // Send unreliably (Pass to tranport layer)
                        m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,response);
                        
                        /* RFC 3261 17.2.1.
                            When the "Completed" state is entered, timer H MUST be set to fire in
                            64*T1 seconds for all transports.  Timer H determines when the server
                            transaction abandons retransmitting the response.
                        */
                        m_pTimerH = new Timer(64 * m_T1);
                        m_pTimerH.AutoReset = false;
                        m_pTimerH.Elapsed += new ElapsedEventHandler(m_pTimerH_Elapsed);
                        // Log
                        m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer H(response retransmit timeout timer) started, will triger after " + m_pTimerH.Interval + ".");
  
                        /* RFC 3261 17.2.1.
                            For unreliable transports, timer G is set to fire in T1 seconds, 
                            and is not set to fire for reliable transports.
                            (INVITE final response retransmit timer)
                        */
                        if(response.Via.GetTopMostValue().ProtocolTransport.ToUpper() == "UDP"){
                            m_pTimerG = new Timer(m_T1);
                            m_pTimerG.Elapsed += new ElapsedEventHandler(m_pTimerG_Elapsed);
                            m_pTimerG.Enabled = true;

                            // Log
                            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer G(INVITE final response retransmit timer) started, will triger after " + m_pTimerG.Interval + ".");
                        }
                    }
                }

                #endregion

                #region Completed

                // Completed state
                else if(m_TransactionState == SIP_ServerTransactionState.Completed){
                    // We do nothing here, we just wait ACK to arrive.
                }

                #endregion

                #region Confirmed

                else if(m_TransactionState == SIP_ServerTransactionState.Confirmed){
                    // We do nothing, just wait ACK retransmissions while linger time.
                }

                #endregion

                #region Terminated

                else if(m_TransactionState == SIP_ServerTransactionState.Terminated){
                   // We should never rreach here, but if so, skip it.
                }

                #endregion
            }

            #endregion

            #region Non-INVITE

            // Non-INVITE server transaction
            else{

                #region Trying

                // Trying state
                if(m_TransactionState == SIP_ServerTransactionState.Trying){
                    // 1xx response
                    if(response.StausCodeType == SIP_StatusCodeType.Provisional){
                        m_TransactionState = SIP_ServerTransactionState.Proceeding;
                        m_pResponses.Add(response);

                        // Send unreliably (Pass to tranport layer)
                        m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,response);                        
                    }
                    // 200-699 response
                    else{
                        m_TransactionState = SIP_ServerTransactionState.Completed;
                        m_pResponses.Add(response);

                        // Send unreliably (Pass to tranport layer)
                        m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,response);

                        /* RFC 3261 17.2.2.
                            When the server transaction enters the "Completed" state, it MUST set
                            Timer J to fire in 64*T1 seconds for unreliable transports.
                            (wait time for retransmissions of non-INVITE requests)
                        */
                        if(response.Via.GetTopMostValue().ProtocolTransport.ToUpper() == "UDP"){
                            m_pTimerJ = new Timer(64 * m_T1);
                            m_pTimerJ.AutoReset = false;
                            m_pTimerJ.Elapsed += new ElapsedEventHandler(m_pTimerJ_Elapsed);
                            m_pTimerJ.Enabled = true;

                            // Log
                            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer J(wait time for retransmissions of non-INVITE requests) started, will triger after " + m_pTimerJ.Interval + ".");
                        }
                        else{
                            Dispose();
                        }
                    }
                }

                #endregion

                #region Proceeding

                // Proceeding state
                else if(m_TransactionState == SIP_ServerTransactionState.Proceeding){
                    // 1xx response
                    if(response.StausCodeType == SIP_StatusCodeType.Provisional){
                        m_pResponses.Add(response);

                        // Send unreliably (Pass to tranport layer)
                        m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,response);
                    }
                    // 200-699 response
                    else{
                        m_TransactionState = SIP_ServerTransactionState.Completed;
                        m_pResponses.Add(response);

                        // Send unreliably (Pass to tranport layer)
                        m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,response);

                        /* RFC 3261 17.2.2.
                            When the server transaction enters the "Completed" state, it MUST set
                            Timer J to fire in 64*T1 seconds for unreliable transports.
                            (wait time for retransmissions of non-INVITE requests)
                        */
                        if(response.Via.GetTopMostValue().ProtocolTransport.ToUpper() == "UDP"){
                            m_pTimerJ = new Timer(64 * m_T1);
                            m_pTimerJ.AutoReset = false;
                            m_pTimerJ.Elapsed += new ElapsedEventHandler(m_pTimerJ_Elapsed);
                            m_pTimerJ.Enabled = true;

                            // Log
                            m_pSipStack.Logger.AddDebug("Transaction(id='" + m_ID + "' method=" + m_pRequest.Method + " server=true) Timer J(wait time for retransmissions of non-INVITE requests) started, will triger after " + m_pTimerJ.Interval + ".");
                        }
                    }
                }

                #endregion

                #region Completed

                // Completed state
                else if(m_TransactionState == SIP_ServerTransactionState.Completed){
                    // Just resend last response.
                    m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,GetFinalResponse());
                }

                #endregion

                #region Terminated

                // Terminated state
                else if(m_TransactionState == SIP_ServerTransactionState.Terminated){
                    // We should never rreach here, but if so, skip it.
                }

                #endregion
            }

            #endregion
        }
                                               
        #endregion


        #region method SendTrying

        /// <summary>
        /// Sends trying response to request maker.
        /// </summary>
        private void SendTrying()
        {
            /* RFC 3261 8.2.6.1 Sending a Provisional Response.
                When a 100 (Trying) response is generated, any Timestamp header field
                present in the request MUST be copied into this 100 (Trying)
                response.  If there is a delay in generating the response, the UAS
                SHOULD add a delay value into the Timestamp value in the response.
            */

            SIP_Response sipTryingResponse = new SIP_Response();                        
            sipTryingResponse.StatusCode_ReasonPhrase = SIP_ResponseCodes.x100_Trying;
            sipTryingResponse.Via.AddToTop(m_pRequest.Via.GetTopMostValue().ToStringValue());
            sipTryingResponse.To = m_pRequest.To;
            sipTryingResponse.From = m_pRequest.From;
            sipTryingResponse.CallID = m_pRequest.CallID;
            sipTryingResponse.CSeq = m_pRequest.CSeq;

            // Add time stamp, because we send Trying at once, we don't have delay.
            if(m_pRequest.Timestamp != null){
                sipTryingResponse.Timestamp = new SIP_t_Timestamp(m_pRequest.Timestamp.Time,0);
            }

            // Send response
            m_pSipStack.TransportLayer.SendResponse(m_pRequest.Socket,m_pRequest.RemoteEndPoint,sipTryingResponse);
        }

        #endregion

        #region method PassResponseToDialog

        /// <summary>
        /// Passes specified response to SIP dialog. If dialog doesn't exist, nothing is done.
        /// </summary>
        /// <param name="response">SIP response.</param>
        private void PassResponseToDialog(SIP_Response response)
        {
            lock(this){
                if(this.Dialog != null){
                    this.Dialog.ProcessResponse(response);
                }
            }
        }

        #endregion

        #region method EnsureDialog

        /// <summary>
        /// Ensures that SIP dialog exists. If not, creates SIP dialog, if exists, updates dialog state.
        /// </summary>
        /// <param name="response">SIP response what causes dialog creation.</param>
        private void EnsureDialog(SIP_Response response)
        {
            lock(this){
                // Create dialog
                if(this.CanCreateDialog && this.Dialog == null){
                    m_pDialog = m_pSipStack.TransactionLayer.CreateDialog(this,m_pRequest,response);
                }
            }
        }

        #endregion
                

        #region Properties Implementation

        /// <summary>
        /// Gets transaction ID (Via: branch parameter value).
        /// </summary>
        public override string ID
        {
            get{ return m_ID; }
        }

        /// <summary>
        /// Gets current transaction state.
        /// </summary>
        public SIP_ServerTransactionState TransactionState
        {
            get{ return m_TransactionState; }
        }

        /// <summary>
        /// Gets request what created this server transaction.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
        public override SIP_Request Request
        {
            get{ 
                if(m_Disposed){
                    throw new ObjectDisposedException("SIP_ServerTransaction");
                }

                return m_pRequest; 
            }
        }

        /// <summary>
        /// Gets transaction related responses.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
        public override SIP_Response[] Responses
        {
            get{ 
                if(m_Disposed){
                    throw new ObjectDisposedException("SIP_ServerTransaction");
                }

                return m_pResponses.ToArray(); 
            }
        }

        /// <summary>
        /// Gets transaction dialog. Returns null if no dialog available.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this property is accessed.</exception>
        public override SIP_Dialog Dialog
        {
            get{ 
                if(m_Disposed){
                    throw new ObjectDisposedException("SIP_ServerTransaction");
                }

                return m_pDialog; 
            }
        }

        /// <summary>
        /// Gets or sets user data.
        /// </summary>
        public object Tag
        {
            get{ return m_pTag; }

            set{ m_pTag = value; }
        }

        #endregion

        #region Events Implementation
                
        #endregion

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Estonia Estonia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions