Пример #1
0
        /// <summary>
        /// Sends specified response to flow remote end point.
        /// </summary>
        /// <param name="response">SIP response to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
        public void Send(SIP_Response response)
        {
            lock (m_pLock){
                if (m_IsDisposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (response == null)
                {
                    throw new ArgumentNullException("response");
                }

                SendInternal(response.ToByteData());
            }
        }
Пример #2
0
        /// <summary>
        /// Sends response to request maker using RFC 3261 18. rules.
        /// </summary>
        /// <param name="transaction">Owner server transaction. Can be null if stateless response sending.</param>
        /// <param name="response">SIP response to send.</param>
        /// <param name="localEP">Local IP end point to use for sending resposne. Value null means system will allocate it.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when stack ahs not been started and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        /// <exception cref="SIP_TransportException">Is raised when <b>response</b> sending has failed.</exception>
        private void SendResponseInternal(SIP_ServerTransaction transaction,SIP_Response response,IPEndPoint localEP)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(!m_IsRunning){
                throw new InvalidOperationException("Stack has not been started.");
            }
            if(response == null){
                throw new ArgumentNullException("response");
            }

            /* RFC 3261 18.2.2.
                The server transport uses the value of the top Via header field in
                order to determine where to send a response.  It MUST follow the
                following process:

                    o  If the "sent-protocol" is a reliable transport protocol such as
                       TCP or SCTP, or TLS over those, the response MUST be sent using
                       the existing connection to the source of the original request
                       that created the transaction, if that connection is still open.
                       This requires the server transport to maintain an association
                       between server transactions and transport connections.  If that
                       connection is no longer open, the server SHOULD open a
                       connection to the IP address in the "received" parameter, if
                       present, using the port in the "sent-by" value, or the default
                       port for that transport, if no port is specified.  If that
                       connection attempt fails, the server SHOULD use the procedures
                       in [4] for servers in order to determine the IP address and
                       port to open the connection and send the response to.

                    o  Otherwise, if the Via header field value contains a "maddr"
                       parameter, the response MUST be forwarded to the address listed
                       there, using the port indicated in "sent-by", or port 5060 if
                       none is present.  If the address is a multicast address, the
                       response SHOULD be sent using the TTL indicated in the "ttl"
                       parameter, or with a TTL of 1 if that parameter is not present.

                    o  Otherwise (for unreliable unicast transports), if the top Via
                       has a "received" parameter, the response MUST be sent to the
                       address in the "received" parameter, using the port indicated
                       in the "sent-by" value, or using port 5060 if none is specified
                       explicitly.  If this fails, for example, elicits an ICMP "port
                       unreachable" response, the procedures of Section 5 of [4]
                       SHOULD be used to determine where to send the response.

                    o  Otherwise, if it is not receiver-tagged, the response MUST be
                       sent to the address indicated by the "sent-by" value, using the
                       procedures in Section 5 of [4].
            */

            /* RFC 3581 4. (Adds new processing between RFC 3261 18.2.2. bullet 2 and 3)
                When a server attempts to send a response, it examines the topmost
                Via header field value of that response.  If the "sent-protocol"
                component indicates an unreliable unicast transport protocol, such as
                UDP, and there is no "maddr" parameter, but there is both a
                "received" parameter and an "rport" parameter, the response MUST be
                sent to the IP address listed in the "received" parameter, and the
                port in the "rport" parameter.  The response MUST be sent from the
                same address and port that the corresponding request was received on.
                This effectively adds a new processing step between bullets two and
                three in Section 18.2.2 of SIP [1].

                The response must be sent from the same address and port that the
                request was received on in order to traverse symmetric NATs.  When a
                server is listening for requests on multiple ports or interfaces, it
                will need to remember the one on which the request was received.  For
                a stateful proxy, storing this information for the duration of the
                transaction is not an issue.  However, a stateless proxy does not
                store state between a request and its response, and therefore cannot
                remember the address and port on which a request was received.  To
                properly implement this specification, a stateless proxy can encode
                the destination address and port of a request into the Via header
                field value that it inserts.  When the response arrives, it can
                extract this information and use it to forward the response.
            */

            SIP_t_ViaParm via = response.Via.GetTopMostValue();
            if(via == null){
                throw new ArgumentException("Argument 'response' does not contain required Via: header field.");
            }

            // TODO: If transport is not supported.
            //throw new SIP_TransportException("Not supported transport '" + via.ProtocolTransport + "'.");

            string logID         = Guid.NewGuid().ToString();
            string transactionID = transaction == null ? "" : transaction.ID;

            // Try to get local IP end point which we should use to send response back.
            if(transaction != null && transaction.Request.LocalEndPoint != null){
                localEP = transaction.Request.LocalEndPoint;
            }

            // TODO: no "localEP" at moment

            // TODO: Stateless should use flowID instead.

            // Our stateless proxy add 'localEP' parameter to Via: if so normally we can get it from there.
            else if(via.Parameters["localEP"] != null){
                localEP = Net_Utils.ParseIPEndPoint(via.Parameters["localEP"].Value);
            }

            byte[] responseData = response.ToByteData();

            #region Try existing flow first

            /* First try active flow to send response, thats not 100% as RFC says, but works better in any case.
               RFC says that for TCP and TLS only, we do it for any transport.
            */

            if(transaction != null){
                try{
                    SIP_Flow flow = transaction.Flow;
                    flow.Send(response);

                    if(m_pStack.Logger != null){
                            m_pStack.Logger.AddWrite(
                                logID,
                                null,
                                0,
                                "Response [flowReuse=true; transactionID='" + transactionID + "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + response.CSeq.SequenceNumber + "'; " +
                                    "transport='" + flow.Transport + "'; size='" + responseData.Length + "'; statusCode='" + response.StatusCode + "'; " +
                                    "reason='" + response.ReasonPhrase + "'; sent '" + flow.LocalEP + "' -> '" + flow.RemoteEP + "'.",
                                localEP,
                                flow.RemoteEP,
                                responseData
                            );
                        }

                    return;
                }
                catch{
                    // Do nothing, processing will continue.
                }
            }

            #endregion

            #region Reliable TCP,TLS, ...

            if(SIP_Utils.IsReliableTransport(via.ProtocolTransport)){
                // Get original request remote end point.
                IPEndPoint remoteEP = null;
                if(transaction != null && transaction.Request.RemoteEndPoint != null){
                    remoteEP = transaction.Request.RemoteEndPoint;
                }
                else if(via.Received != null){
                    remoteEP = new IPEndPoint(via.Received,via.SentBy.Port == -1 ? 5060 : via.SentBy.Port);
                }

                #region If original request connection alive, use it

                try{
                    SIP_Flow flow = null;

                    // Statefull
                    if(transaction != null){
                        if(transaction.Request.Flow != null && !transaction.Request.Flow.IsDisposed){
                            flow = transaction.Request.Flow;
                        }
                    }
                    // Stateless
                    else{
                        string flowID = via.Parameters["connectionID"].Value;
                        if(flowID != null){
                            flow = m_pFlowManager[flowID];
                        }
                    }

                    if(flow != null){
                        flow.Send(response);

                        if(m_pStack.Logger != null){
                            m_pStack.Logger.AddWrite(
                                logID,
                                null,
                                0,
                                "Response [flowReuse=true; transactionID='" + transactionID + "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + response.CSeq.SequenceNumber + "'; " +
                                    "transport='" + flow.Transport + "'; size='" + responseData.Length + "'; statusCode='" + response.StatusCode + "'; " +
                                    "reason='" + response.ReasonPhrase + "'; sent '" + flow.RemoteEP + "' -> '" + flow.LocalEP + "'.",
                                localEP,
                                remoteEP,
                                responseData
                            );
                        }

                        return;
                    }
                }
                catch{
                    // Do nothing, processing will continue.
                    // Override RFC, if there is any existing connection and it gives error, try always RFC 3261 18.2.2(recieved) and 3265 5.
                }

                #endregion

                #region Send RFC 3261 18.2.2(recieved)

                if(remoteEP != null){
                    try{
                        SendResponseToHost(logID,transactionID,null,remoteEP.Address.ToString(),remoteEP.Port,via.ProtocolTransport,response);
                    }
                    catch{
                        // Do nothing, processing will continue -> "RFC 3265 5.".
                    }
                }

                #endregion

                #region Send RFC 3265 5.

                SendResponse_RFC_3263_5(logID,transactionID,localEP,response);

                #endregion

            }

            #endregion

            #region UDP Via: maddr parameter

            else if(via.Maddr != null){
                throw new SIP_TransportException("Sending responses to multicast address(Via: 'maddr') is not supported.");
            }

            #endregion

            #region RFC 3581 4. UDP Via: received and rport parameters

            else if(via.Maddr == null && via.Received != null && via.RPort > 0){
                SendResponseToHost(logID,transactionID,localEP,via.Received.ToString(),via.RPort,via.ProtocolTransport,response);
            }

            #endregion

            #region UDP Via: received parameter

            else if(via.Received != null){
                SendResponseToHost(logID,transactionID,localEP,via.Received.ToString(),via.SentByPortWithDefault,via.ProtocolTransport,response);
            }

            #endregion

            #region UDP

            else{
                SendResponse_RFC_3263_5(logID,transactionID,localEP,response);
            }

            #endregion
        }
Пример #3
0
        /// <summary>
        /// Sends response to the specified host.
        /// </summary>
        /// <param name="logID">Log ID.</param>
        /// <param name="transactionID">Transaction ID. If null, then stateless response sending.</param>
        /// <param name="localEP">UDP local end point to use for sending. If null, system will use default.</param>
        /// <param name="host">Host name or IP address where to send response.</param>
        /// <param name="port">Target host port.</param>
        /// <param name="transport">SIP transport to use.</param>
        /// <param name="response">SIP response to send.</param>
        private void SendResponseToHost(string logID,string transactionID,IPEndPoint localEP,string host,int port,string transport,SIP_Response response)
        {
            try{
                IPAddress[] targets = null;
                if(Net_Utils.IsIPAddress(host)){
                    targets = new IPAddress[]{IPAddress.Parse(host)};
                }
                else{
                    targets = m_pStack.Dns.GetHostAddresses(host);
                    if(targets.Length == 0){
                        throw new SIP_TransportException("Invalid Via: Sent-By host name '" + host + "' could not be resolved.");
                    }
                }

                byte[] responseData = response.ToByteData();

                for(int i=0;i<targets.Length;i++){
                    IPEndPoint remoteEP = new IPEndPoint(targets[i],port);
                    try{
                        SIP_Flow flow = GetOrCreateFlow(transport,localEP,remoteEP);
                        flow.Send(response);
                        // localEP = flow.LocalEP;

                        if(m_pStack.Logger != null){
                            m_pStack.Logger.AddWrite(
                                logID,
                                null,
                                0,
                                "Response [transactionID='" + transactionID + "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + response.CSeq.SequenceNumber + "'; " +
                                    "transport='" + transport + "'; size='" + responseData.Length + "'; statusCode='" + response.StatusCode + "'; " +
                                    "reason='" + response.ReasonPhrase + "'; sent '" + localEP + "' -> '" + remoteEP + "'.",
                                localEP,
                                remoteEP,
                                responseData
                            );
                        }

                        // If we reach so far, send succeeded.
                        return;
                    }
                    catch{
                        // Generate error, all IP addresses has failed.
                        if(i == (targets.Length - 1)){
                            if(m_pStack.Logger != null){
                                m_pStack.Logger.AddText(logID,"Failed to send response to host '" + host + "' IP end point '" + remoteEP + "'.");
                            }

                            throw new SIP_TransportException("Host '" + host + ":" + port + "' is not accessible.");
                        }
                        // For loop will try next IP address.
                        else{
                            if(m_pStack.Logger != null){
                                m_pStack.Logger.AddText(logID,"Failed to send response to host '" + host + "' IP end point '" + remoteEP + "', will try next A record.");
                            }
                        }
                    }
                }
            }
            catch(DNS_ClientException dnsX){
                throw new SIP_TransportException("Dns error: " + dnsX.ErrorCode.ToString());
            }
        }
Пример #4
0
        /// <summary>
        /// Sends specified response to flow remote end point.
        /// </summary>
        /// <param name="response">SIP response to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
        public void Send(SIP_Response response)
        {
            lock(m_pLock){
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if(response == null){
                    throw new ArgumentNullException("response");
                }

                SendInternal(response.ToByteData());
                m_LastPing = DateTime.Now;
            }
        }
        /// <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
            }
        }
        /// <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>
        /// Processes SIP data (request or response).
        /// </summary>
        /// <param name="socket">Socket what accepted specified SIP message.</param>
        /// <param name="data">SIP message or may be junk data too.</param>
        /// <param name="remoteEndPoint">Remote IP end point what sent SIP message.</param>
        /// <param name="transport">SIP transport what received message.</param>
        private void Process(string transport,SocketEx socket,byte[] data,IPEndPoint remoteEndPoint)
        {
            try{
                // Log
                m_pSipStack.Logger.AddRead(data.Length,"Received (" + data.Length + " bytes): " + socket.LocalEndPoint.ToString() + " <- " + remoteEndPoint.ToString() + "\r\n" + System.Text.Encoding.UTF8.GetString(data) + "\r\n");

                // Bad request or response.
                if(data.Length < 10){
                    return;
                }

                // TODO: Check that advertised transport matches actual received transport.

                // Dedect if sip request or reponse.
                // Response
                if(System.Text.Encoding.UTF8.GetString(data,0,10).ToUpper().StartsWith("SIP")){
                    try{
                        SIP_Response response = SIP_Response.Parse(data);
                        response.Validate();

                        ProcessResponse(response);
                    }
                    catch(Exception x){
                        // We have bad response, just skip it.

                        // Log
                        if(m_pSipStack.Logger != null){
                            m_pSipStack.Logger.AddDebug("Skipping message, parse error: " + x.Message);
                        }
                    }
                }
                // Request
                else{
                    SIP_Request request = null;
                    try{
                        request = SIP_Request.Parse(data);
                        try{
                            request.Validate();
                        }
                        catch(Exception x){
                            // Log
                            if(m_pSipStack.Logger != null){
                                m_pSipStack.Logger.AddDebug("Invalid request: " + x.Message);
                            }

                            // Bad request, send error to request maker.
                            SendResponse(socket,request.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ". " + x.Message));
                            return;
                        }
                    }
                    catch(Exception x){
                        // Log
                        if(m_pSipStack.Logger != null){
                            m_pSipStack.Logger.AddDebug("Invalid request: " + x.Message);
                        }

                        // We have bad request, try to send bad request error to request maker.
                        SIP_Response badRequestResponse = new SIP_Response();
                        badRequestResponse.StatusCode_ReasonPhrase = SIP_ResponseCodes.x400_Bad_Request;
                        socket.SendTo(badRequestResponse.ToByteData(),remoteEndPoint);
                        return;
                    }

                    SIP_ValidateRequestEventArgs eArgs = m_pSipStack.OnValidateRequest(request,remoteEndPoint);
                    // Request validated, allow transport layer to handle it.
                    if(eArgs.ResponseCode == null){
                        ProcessRequest(request,socket,remoteEndPoint);
                    }
                    // Request rejected, return response.
                    else{
                        SendResponse(socket,request.CreateResponse(eArgs.ResponseCode));
                    }
                }
            }
            catch(SocketException s){
                // Skip all socket errors here
                string dummy = s.Message;
            }
            catch(Exception x){
                m_pSipStack.OnError(x);
            }
        }
        /// <summary>
        /// Sends response to request maker.
        /// </summary>
        /// <param name="socket">Socket which to use to send response.</param>
        /// <param name="remoteEndPoint">Remote end point where to send response. 
        /// If this value is null, Via header is used to get remote end point.
        /// </param>
        /// <param name="response">Response to send.</param>
        public void SendResponse(SocketEx socket,IPEndPoint remoteEndPoint,SIP_Response response)
        {
            /* RFC 3581 4.  Server Behavior.
                When a server attempts to send a response, it examines the topmost
                Via header field value of that response.  If the "sent-protocol"
                component indicates an unreliable unicast transport protocol, such as
                UDP, and there is no "maddr" parameter, but there is both a
                "received" parameter and an "rport" parameter, the response MUST be
                sent to the IP address listed in the "received" parameter, and the
                port in the "rport" parameter.  The response MUST be sent from the
                same address and port that the corresponding request was received on
                in order to traverse symmetric NATs.

            */

            /* RFC 3261 18.2.2 Sending Responses.
                The server transport uses the value of the top Via header field in
                order to determine where to send a response.  It MUST follow the
                following process:

                  o  If the "sent-protocol" is a reliable transport protocol such as
                     TCP or SCTP, or TLS over those, the response MUST be sent using
                     the existing connection to the source of the original request
                     that created the transaction, if that connection is still open.
                     This requires the server transport to maintain an association
                     between server transactions and transport connections.  If that
                     connection is no longer open, the server SHOULD open a
                     connection to the IP address in the "received" parameter, if
                     present, using the port in the "sent-by" value, or the default
                     port for that transport, if no port is specified.  If that
                     connection attempt fails, the server SHOULD use the procedures
                     in [4] for servers in order to determine the IP address and
                     port to open the connection and send the response to.

                  o  Otherwise, if the Via header field value contains a "maddr"
                     parameter, the response MUST be forwarded to the address listed
                     there, using the port indicated in "sent-by", or port 5060 if
                     none is present.  If the address is a multicast address, the
                     response SHOULD be sent using the TTL indicated in the "ttl"
                     parameter, or with a TTL of 1 if that parameter is not present.

                  o  Otherwise (for unreliable unicast transports), if the top Via
                     has a "received" parameter, the response MUST be sent to the
                     address in the "received" parameter, using the port indicated
                     in the "sent-by" value, or using port 5060 if none is specified
                     explicitly.  If this fails, for example, elicits an ICMP "port
                     unreachable" response, the procedures of Section 5 of [4]
                     SHOULD be used to determine where to send the response.

                  o  Otherwise, if it is not receiver-tagged, the response MUST be
                     sent to the address indicated by the "sent-by" value, using the
                     procedures in Section 5 of [4].
            */

            // TODO: Probably we can use local endpoint instead of socket. Because then we can
            //       Search right UDP connection or TCP/TLS connection.

            try{
                SIP_t_ViaParm via = response.Via.GetTopMostValue();

                // End point not specified, get it from Via.
                if(remoteEndPoint == null){
                    string host = null;
                    int    port = 5060;

                    // Use received host.
                    if(via.Received != null){
                        host = via.Received;
                    }
                    // Get sent-by host
                    else{
                        host = via.SentBy.Split(':')[0];
                    }

                    // Use rport if recevived is specified too
                    if(via.Received != null && via.RPort > 0){
                        port = via.RPort;
                    }
                    // Get port from sent-by
                    else{
                        string[] host_port = via.SentBy.Split(':');
                        if(host_port.Length == 2){
                            port = Convert.ToInt32(host_port[1]);
                        }
                    }

                    remoteEndPoint = new IPEndPoint(System.Net.Dns.GetHostAddresses(host)[0],port);
                }

                byte[] data = response.ToByteData();

                // Log
                m_pSipStack.Logger.AddWrite(data.Length,"Sending (" + data.Length + " bytes): " + socket.LocalEndPoint.ToString() + " -> " + remoteEndPoint.ToString() + "\r\n<begin>\r\n" + System.Text.Encoding.UTF8.GetString(data) + "<end>\r\n");

                // We don't have any more that socket what accepted request which response it is.
                // There are 2 known cases when no socket:
                //   1) Stateless proxy.
                //   2) Statefull proxy, but response didn't match any transaction.
                if(socket == null){
                    // UDP Multicast
                    if(via.ProtocolTransport.ToUpper() == SIP_Transport.UDP && via.Maddr != null){
                        throw new SIP_TransportException("UDP Multicast not implemented !");
                    }
                    // UDP
                    else if(via.ProtocolTransport.ToUpper() == SIP_Transport.UDP){
                        foreach(SipListeningPoint listeningPoint in m_pListeningPoints){
                            if(listeningPoint.Protocol == BindInfoProtocol.UDP){
                                listeningPoint.Socket.SendTo(data,remoteEndPoint);
                                return;
                            }
                        }

                        throw new SIP_TransportException("No UDP transport available, this never should happen !");
                    }
                    // TCP
                    else if(via.ProtocolTransport.ToUpper() == SIP_Transport.TCP){
                        SipTcpPipe pipe = GetTcpPipe(SIP_Transport.TCP,remoteEndPoint);
                        // Not existing connection, create it.
                        if(pipe == null){
                            pipe = new SipTcpPipe(this,remoteEndPoint,true);
                        }
                        pipe.SendMessage(data);
                    }
                    // TCP TLS
                    else if(via.ProtocolTransport.ToUpper() == SIP_Transport.TLS){
                        SipTcpPipe pipe = GetTcpPipe(SIP_Transport.TLS,remoteEndPoint);
                        // Not existing connection, create it.
                        if(pipe == null){
                            pipe = new SipTcpPipe(this,remoteEndPoint,true);
                        }
                        pipe.SendMessage(data);
                    }
                }
                // We have existing socket, use it.
                else{
                    if(via.ProtocolTransport.ToUpper() == SIP_Transport.UDP){
                        socket.SendTo(data,remoteEndPoint);
                    }
                    else{
                        socket.Write(data);
                    }
                }
            }
            catch(Exception x){
                throw new SIP_TransportException(x.Message);
            }
        }