Ejemplo n.º 1
0
        /// <summary>
        /// Attempts to send data to the remote end point over a reliable connection. If an existing
        /// connection exists it will be used otherwise an attempt will be made to establish a new connection.
        /// </summary>
        /// <param name="dstEndPoint">The remote end point to send the reliable data to.</param>
        /// <param name="buffer">The data to send.</param>
        /// <param name="serverCertificateName">Optional. Only relevant for SSL streams. The common name
        /// that is expected for the remote SSL server.</param>
        /// <param name="connectionIDHint">Optional. The ID of the specific TCP connection to try and the send the message on.</param>
        /// <returns>If no errors SocketError.Success otherwise an error value.</returns>
        public override async Task <SocketError> SendSecureAsync(IPEndPoint dstEndPoint, byte[] buffer, string serverCertificateName, string connectionIDHint)
        {
            try
            {
                if (dstEndPoint == null)
                {
                    throw new ArgumentException("dstEndPoint", "An empty destination was specified to Send in SIPTCPChannel.");
                }
                if (buffer == null || buffer.Length == 0)
                {
                    throw new ApplicationException("An empty buffer was specified to Send in SIPTCPChannel.");
                }
                else if (DisableLocalTCPSocketsCheck == false && m_localTCPSockets.Contains(dstEndPoint.ToString()))
                {
                    logger.LogWarning($"SIP {ProtDescr} Channel blocked Send to {dstEndPoint} as it was identified as a locally hosted {ProtDescr} socket.\r\n" + Encoding.UTF8.GetString(buffer));
                    throw new ApplicationException($"A Send call was blocked in SIP {ProtDescr} Channel due to the destination being another local TCP socket.");
                }
                else
                {
                    // Lookup a client socket that is connected to the destination. If it does not exist attempt to connect a new one.
                    SIPStreamConnection sipStreamConn = null;

                    if (connectionIDHint != null)
                    {
                        m_connections.TryGetValue(connectionIDHint, out sipStreamConn);
                    }

                    if (sipStreamConn == null && HasConnection(dstEndPoint))
                    {
                        sipStreamConn = m_connections.Where(x => x.Value.RemoteEndPoint.Equals(dstEndPoint)).First().Value;
                    }

                    if (sipStreamConn != null)
                    {
                        SendOnConnected(sipStreamConn, buffer);
                        return(SocketError.Success);
                    }
                    else
                    {
                        return(await ConnectClientAsync(dstEndPoint, buffer, serverCertificateName));
                    }
                }
            }
            catch (SocketException sockExcp)
            {
                return(sockExcp.SocketErrorCode);
            }
            catch (ApplicationException)
            {
                throw;
            }
            catch (Exception excp)
            {
                logger.LogError("Exception (" + excp.GetType().ToString() + ") SIPTCPChannel Send (sendto=>" + dstEndPoint + "). " + excp.Message);
                throw;
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Send event handler for the newer SendAsync socket call.
        /// </summary>
        /// <param name="e">The socket args for the completed send operation.</param>
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            SIPStreamConnection streamConn = (SIPStreamConnection)e.UserToken;

            if (e.BytesTransferred == 0 || e.SocketError != SocketError.Success)
            {
                // There was an error processing the last message send. Remove the disconnected socket.
                logger.LogWarning($"SIP {ProtDescr} Channel Socket send to {e.RemoteEndPoint} failed with socket error {e.SocketError}, removing connection.");
                OnSIPStreamDisconnected(streamConn, e.SocketError);
            }
        }
Ejemplo n.º 3
0
 /// <summary>
 /// Sends data using the connected SSL stream.
 /// </summary>
 /// <param name="sipStreamConn">The stream connection object that holds the SSL stream.</param>
 /// <param name="buffer">The data to send.</param>
 protected override Task SendOnConnected(SIPStreamConnection sipStreamConn, byte[] buffer)
 {
     try
     {
         return(sipStreamConn.SslStream.WriteAsync(buffer, 0, buffer.Length));
     }
     catch (SocketException sockExcp)
     {
         logger.LogWarning(sockExcp, $"SocketException SIP TLS Channel sending to {sipStreamConn.RemoteSIPEndPoint}. ErrorCode {sockExcp.SocketErrorCode}. {sockExcp}");
         OnSIPStreamDisconnected(sipStreamConn, sockExcp.SocketErrorCode);
         throw;
     }
 }
Ejemplo n.º 4
0
        /// <summary>
        /// Callback for read operations on the SSL stream.
        /// </summary>
        private void OnReadCallback(IAsyncResult ar)
        {
            SIPStreamConnection sipStreamConnection = (SIPStreamConnection)ar.AsyncState;

            try
            {
                int bytesRead = sipStreamConnection.SslStream.EndRead(ar);

                if (bytesRead == 0)
                {
                    // SSL stream was disconnected by the remote end point sending a FIN or RST.
                    logger.LogDebug($"TLS socket disconnected by {sipStreamConnection.RemoteEndPoint}.");
                    OnSIPStreamDisconnected(sipStreamConnection, SocketError.ConnectionReset);
                }
                else
                {
                    sipStreamConnection.ExtractSIPMessages(this, sipStreamConnection.SslStreamBuffer, bytesRead);
                    sipStreamConnection.SslStream.BeginRead(sipStreamConnection.SslStreamBuffer,
                                                            sipStreamConnection.RecvEndPosn,
                                                            sipStreamConnection.SslStreamBuffer.Length - sipStreamConnection.RecvEndPosn,
                                                            new AsyncCallback(OnReadCallback), sipStreamConnection);
                }
            }
            catch (SocketException sockExcp) // Occurs if the remote end gets disconnected.
            {
                OnSIPStreamDisconnected(sipStreamConnection, sockExcp.SocketErrorCode);
            }
            catch (IOException ioExcp)
            {
                if (ioExcp.InnerException is SocketException)
                {
                    OnSIPStreamDisconnected(sipStreamConnection,
                                            (ioExcp.InnerException as SocketException).SocketErrorCode);
                }
                else if (ioExcp.InnerException is ObjectDisposedException)
                {
                    // This exception is expected when the TLS connection is closed and this method is waiting for a receive.
                    OnSIPStreamDisconnected(sipStreamConnection, SocketError.Disconnecting);
                }
                else
                {
                    logger.LogWarning($"IOException SIPTLSChannel OnReadCallback. {ioExcp.Message}");
                    OnSIPStreamDisconnected(sipStreamConnection, SocketError.Fault);
                }
            }
            catch (Exception excp)
            {
                logger.LogWarning($"Exception SIPTLSChannel OnReadCallback. {excp.Message}");
                OnSIPStreamDisconnected(sipStreamConnection, SocketError.Fault);
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Attempts to send data to the remote end point over a reliable connection. If an existing
        /// connection exists it will be used otherwise an attempt will be made to establish a new connection.
        /// </summary>
        /// <param name="dstEndPoint">The remote end point to send the reliable data to.</param>
        /// <param name="buffer">The data to send.</param>
        /// <param name="serverCertificateName">Optional. Only relevant for SSL streams. The common name
        /// that is expected for the remote SSL server.</param>
        public override async Task <SocketError> SendAsync(IPEndPoint dstEndPoint, byte[] buffer, string serverCertificateName)
        {
            try
            {
                if (buffer == null || buffer.Length == 0)
                {
                    throw new ApplicationException("An empty buffer was specified to Send in SIPTCPChannel.");
                }
                else if (DisableLocalTCPSocketsCheck == false && LocalTCPSockets.Contains(dstEndPoint.ToString()))
                {
                    logger.LogWarning($"SIPTCPChannel blocked Send to {dstEndPoint} as it was identified as a locally hosted TCP socket.\r\n" + Encoding.UTF8.GetString(buffer));
                    throw new ApplicationException("A Send call was blocked in SIPTCPChannel due to the destination being another local TCP socket.");
                }
                else if (m_connectionFailures.ContainsKey(dstEndPoint.ToString()))
                {
                    throw new ApplicationException($"SIP TCP channel connect attempt to {dstEndPoint} failed.");
                }
                else
                {
                    SIPStreamConnection sipStreamConn = null;

                    // Lookup a client socket that is connected to the destination. If it does not exist attempt to connect a new one.
                    if (m_connectedSockets.ContainsKey(dstEndPoint.ToString()))
                    {
                        sipStreamConn = m_connectedSockets[dstEndPoint.ToString()];
                        SendOnConnected(sipStreamConn, buffer);
                        return(SocketError.Success);
                    }
                    else
                    {
                        await ConnectClientAsync(dstEndPoint, buffer, serverCertificateName);

                        return(SocketError.Success);
                    }
                }
            }
            catch (SocketException sockExcp)
            {
                return(sockExcp.SocketErrorCode);
            }
            catch (ApplicationException)
            {
                throw;
            }
            catch (Exception excp)
            {
                logger.LogError("Exception (" + excp.GetType().ToString() + ") SIPTCPChannel Send (sendto=>" + dstEndPoint + "). " + excp.Message);
                throw;
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Processes the socket accepts from the channel's socket listener.
        /// </summary>
        private void AcceptConnections()
        {
            logger.LogDebug(
                $"SIP {ProtDescr} Channel socket on {m_tcpServerListener.Server.LocalEndPoint} accept connections thread started.");

            while (!Closed)
            {
                try
                {
                    Socket clientSocket = m_tcpServerListener.AcceptSocketAsync().Result;

                    if (!Closed)
                    {
                        logger.LogDebug(
                            $"SIP {ProtDescr} Channel connection accepted from {clientSocket.RemoteEndPoint} by {clientSocket.LocalEndPoint}.");

                        clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                        clientSocket.LingerState = new LingerOption(true, 0);

                        SIPStreamConnection sipStmConn = new SIPStreamConnection(clientSocket,
                                                                                 clientSocket.RemoteEndPoint as IPEndPoint, SIPProtocol);
                        sipStmConn.SIPMessageReceived += SIPTCPMessageReceived;

                        m_connections.TryAdd(sipStmConn.ConnectionID, sipStmConn);

                        OnAccept(sipStmConn).Wait();
                    }
                }
                catch (ObjectDisposedException)
                {
                    // This is a result of the transport channel being closed. Safe to ignore.
                    //logger.LogDebug($"SIP {ProtDescr} Channel accepts for {ListeningEndPoint} cancelled.");
                }
                catch (SocketException acceptSockExcp) when(acceptSockExcp.SocketErrorCode == SocketError.Interrupted)
                {
                    // This is a result of the transport channel being closed and WSACancelBlockingCall being called in WinSock2. Safe to ignore.
                    //logger.LogDebug($"SIP {ProtDescr} Channel accepts for {ListeningEndPoint} cancelled.");
                }
                catch (AggregateException)
                {
                    // This is a result of the transport channel being closed. Safe to ignore.
                }
                catch (Exception acceptExcp)
                {
                    // This exception gets thrown if the remote end disconnects during the socket accept.
                    logger.LogWarning($"Exception SIP {ProtDescr} Channel accepting socket (" + acceptExcp.GetType() +
                                      "). " + acceptExcp.Message);
                }
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// For TCP channel no special action is required when a new outgoing client connection is established.
        /// Can start receiving immeidately.
        /// </summary>
        /// <param name="streamConnection">The stream connection holding the newly connected client socket.</param>
        /// <param name="buffer">Optional parameter that contains the data that still needs to be sent once the connection is established.</param>
        protected virtual void OnClientConnect(SIPStreamConnection streamConnection, byte[] buffer, string certificateName)
        {
            SocketAsyncEventArgs recvArgs = streamConnection.RecvSocketArgs;

            recvArgs.AcceptSocket = streamConnection.StreamSocket;
            recvArgs.UserToken    = streamConnection;
            recvArgs.Completed   += IO_Completed;

            bool willRaise = streamConnection.StreamSocket.ReceiveAsync(recvArgs);

            if (!willRaise)
            {
                ProcessReceive(recvArgs);
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// For the TLS channel once the TCP client socket is connected it needs to be wrapped up in an SSL stream.
        /// </summary>
        /// <param name="streamConnection">The stream connection holding the newly connected client socket.</param>
        /// <param name="serverCertificateName">The expected common name on the SSL certificate supplied by the server.</param>
        protected override async Task OnClientConnect(SIPStreamConnection streamConnection, string serverCertificateName)
        {
            NetworkStream networkStream = new NetworkStream(streamConnection.StreamSocket, true);
            SslStream     sslStream     = new SslStream(networkStream, false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
            //DisplayCertificateInformation(sslStream);

            await sslStream.AuthenticateAsClientAsync(serverCertificateName);

            streamConnection.SslStream       = sslStream;
            streamConnection.SslStreamBuffer = new byte[2 * SIPStreamConnection.MaxSIPTCPMessageSize];

            logger.LogDebug($"SIP TLS Channel successfully upgraded client connection to SSL stream for {ListeningEndPoint}->{streamConnection.StreamSocket.RemoteEndPoint}.");

            sslStream.BeginRead(streamConnection.SslStreamBuffer, 0, SIPStreamConnection.MaxSIPTCPMessageSize, new AsyncCallback(OnReadCallback), streamConnection);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// For TCP channel no special action is required when accepting a new client connection. Can start receiving immediately.
        /// </summary>
        /// <param name="streamConnection">The stream connection holding the newly accepted client socket.</param>
        protected virtual void OnAccept(SIPStreamConnection streamConnection)
        {
            SocketAsyncEventArgs args = streamConnection.RecvSocketArgs;

            args.AcceptSocket = streamConnection.StreamSocket;
            args.UserToken    = streamConnection;
            args.Completed   += IO_Completed;

            bool willRaise = streamConnection.StreamSocket.ReceiveAsync(args);

            if (!willRaise)
            {
                ProcessReceive(args);
            }
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Sends data using the connected SSL stream.
        /// </summary>
        /// <param name="sipStreamConn">The stream connection object that holds the SSL stream.</param>
        /// <param name="buffer">The data to send.</param>
        protected override async void SendOnConnected(SIPStreamConnection sipStreamConn, byte[] buffer)
        {
            IPEndPoint dstEndPoint = sipStreamConn.RemoteEndPoint;

            try
            {
                await sipStreamConn.SslStream.WriteAsync(buffer, 0, buffer.Length);
            }
            catch (SocketException sockExcp)
            {
                logger.LogWarning($"SocketException SIP TLS Channel sending to {dstEndPoint}. ErrorCode {sockExcp.SocketErrorCode}. {sockExcp}");
                OnSIPStreamDisconnected(sipStreamConn, sockExcp.SocketErrorCode);
                throw;
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Event handler for a reliable SIP stream socket being disconnected.
        /// </summary>
        /// <param name="connection">The disconnected stream.</param>
        /// <param name="socketError">The cause of the disconnect.</param>
        protected void OnSIPStreamDisconnected(SIPStreamConnection connection, SocketError socketError)
        {
            try
            {
                if (connection != null)
                {
                    if (socketError == SocketError.ConnectionReset)
                    {
                        // Connection reset seems to be the way normal closures are reported.
                        logger.LogDebug($"SIP {ProtDescr} stream disconnected {connection.RemoteSIPEndPoint} {socketError}.");
                    }
                    else
                    {
                        logger.LogWarning($"SIP {ProtDescr} stream disconnected {connection.RemoteSIPEndPoint} {socketError}.");
                    }

                    if (m_connections.TryRemove(connection.ConnectionID, out _))
                    {
                        var socket = connection.StreamSocket;

                        // Important: Due to the way TCP works the end of the connection that initiates the close
                        // is meant to go into a TIME_WAIT state. On Windows that results in the same pair of sockets
                        // being unable to reconnect for 30s. SIP can deal with stray and duplicate messages at the
                        // application layer so the TIME_WAIT is not that useful. In fact it TIME_WAIT is a major annoyance for SIP
                        // as if a connection is dropped for whatever reason, such as a parser error or inactivity, it will
                        // prevent the connection being re-established for the TIME_WAIT period.
                        //
                        // For this reason this implementation uses a hard RST close for client initiated socket closes. This
                        // results in a TCP RST packet instead of the graceful FIN-ACK sequence. Two things are necessary with
                        // WinSock2 to force the hard RST:
                        //
                        // - the Linger option must be set on the raw socket before binding as Linger option {1, 0}.
                        // - the close method must be called on the socket without shutting down the stream.

                        // Linux (WSL) note: This mechanism does not work. Calling socket close does not send the RST and instead
                        // sends the graceful FIN-ACK.
                        // TODO: Research if there is a way to force a socket reset with dotnet on LINUX.

                        socket.Close();
                    }
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception OnSIPStreamDisconnected. " + excp.Message);
            }
        }
Ejemplo n.º 12
0
        /// <summary>
        /// For TCP channel no special action is required when a new outgoing client connection is established.
        /// Can start receiving immediately.
        /// </summary>
        /// <param name="streamConnection">The stream connection holding the newly connected client socket.</param>
        protected virtual Task <SocketError> OnClientConnect(SIPStreamConnection streamConnection, string certificateName)
        {
            SocketAsyncEventArgs recvArgs = streamConnection.RecvSocketArgs;

            recvArgs.AcceptSocket = streamConnection.StreamSocket;
            recvArgs.UserToken    = streamConnection;
            recvArgs.Completed   += IO_Completed;

            bool willRaise = streamConnection.StreamSocket.ReceiveAsync(recvArgs);

            if (!willRaise)
            {
                ProcessReceive(recvArgs);
            }

            return(Task.FromResult(SocketError.Success));
        }
Ejemplo n.º 13
0
        /// <summary>
        /// For TCP channel no special action is required when a new outgoing client connection is established.
        /// Can start receiving immediately.
        /// </summary>
        /// <param name="streamConnection">The stream connection holding the newly connected client socket.</param>
        protected virtual Task OnClientConnect(SIPStreamConnection streamConnection, string certificateName)
        {
            SocketAsyncEventArgs recvArgs = streamConnection.RecvSocketArgs;

            recvArgs.AcceptSocket = streamConnection.StreamSocket;
            recvArgs.UserToken    = streamConnection;
            recvArgs.Completed   += IO_Completed;

            bool willRaise = streamConnection.StreamSocket.ReceiveAsync(recvArgs);

            if (!willRaise)
            {
                ProcessReceive(recvArgs);
            }

            // Task.IsCompleted not available for net452.
            return(Task.FromResult(0));
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Processes the socket accepts from the channel's socket listener.
        /// </summary>
        private void AcceptConnections()
        {
            Thread.CurrentThread.Name = m_acceptThreadName + m_localSIPEndPoint.Port;

            logger.LogDebug("SIPTCPChannel socket on " + m_localSIPEndPoint + " accept connections thread started.");

            while (!Closed)
            {
                try
                {
                    Socket clientSocket = m_tcpServerListener.AcceptSocket();
                    logger.LogDebug($"SIP TCP Channel connection accepted from {clientSocket.RemoteEndPoint}.");

                    if (!Closed)
                    {
                        clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                        clientSocket.LingerState = new LingerOption(true, 0);

                        SIPStreamConnection sipStmConn = new SIPStreamConnection(clientSocket, clientSocket.RemoteEndPoint as IPEndPoint, m_localSIPEndPoint.Protocol, SIPConnectionsEnum.Listener);
                        sipStmConn.SIPMessageReceived += SIPTCPMessageReceived;

                        lock (m_connectedSockets)
                        {
                            m_connectedSockets.Add(clientSocket.RemoteEndPoint.ToString(), sipStmConn);
                        }

                        OnAccept(sipStmConn);
                    }
                }
                catch (SocketException acceptSockExcp) when(acceptSockExcp.SocketErrorCode == SocketError.Interrupted)
                {
                    // This is a result of the transport channel being closed and WSACancelBlockingCall being called in WinSock2. Safe to ignore.
                    logger.LogDebug($"SIPTCPChannel accepts for {m_localSIPEndPoint.ToString()} cancelled.");
                }
                catch (Exception acceptExcp)
                {
                    // This exception gets thrown if the remote end disconnects during the socket accept.
                    logger.LogWarning("Exception SIPTCPChannel accepting socket (" + acceptExcp.GetType() + "). " + acceptExcp.Message);
                }
            }
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Callback for read operations on the SSL stream.
        /// </summary>
        private void OnReadCallback(IAsyncResult ar)
        {
            SIPStreamConnection sipStreamConnection = (SIPStreamConnection)ar.AsyncState;

            try
            {
                int bytesRead = sipStreamConnection.SslStream.EndRead(ar);

                if (bytesRead == 0)
                {
                    // SSL stream was disconnected by the remote end pont sending a FIN or RST.
                    logger.LogDebug("TLS socket disconnected by {sipStreamConnection.ConnectionProps.RemoteEndPoint}.");
                    OnSIPStreamDisconnected(sipStreamConnection.RemoteEndPoint, SocketError.ConnectionReset);
                }
                sipStreamConnection.ExtractSIPMessages(this, sipStreamConnection.SslStreamBuffer, bytesRead);
                sipStreamConnection.SslStream.BeginRead(sipStreamConnection.SslStreamBuffer, sipStreamConnection.RecvEndPosn, sipStreamConnection.SslStreamBuffer.Length - sipStreamConnection.RecvEndPosn, new AsyncCallback(OnReadCallback), sipStreamConnection);
            }
            catch (SocketException sockExcp)  // Occurs if the remote end gets disconnected.
            {
                //logger.LogWarning($"SocketException SIPTLSChannel ReceiveCallback. Error code {sockExcp.SocketErrorCode}. {sockExcp}");
                OnSIPStreamDisconnected(sipStreamConnection.RemoteEndPoint, sockExcp.SocketErrorCode);
            }
            catch (IOException ioExcp)
            {
                if (ioExcp.InnerException is SocketException)
                {
                    OnSIPStreamDisconnected(sipStreamConnection.RemoteEndPoint, (ioExcp.InnerException as SocketException).SocketErrorCode);
                }
                else
                {
                    logger.LogWarning($"IOException SIPTLSChannel ReceiveCallback. {ioExcp.Message}");
                    OnSIPStreamDisconnected(sipStreamConnection.RemoteEndPoint, SocketError.Fault);
                }
            }
            catch (Exception excp)
            {
                logger.LogWarning($"Exception SIPTLSChannel ReceiveCallback. {excp.Message}");
                OnSIPStreamDisconnected(sipStreamConnection.RemoteEndPoint, SocketError.Fault);
            }
        }
Ejemplo n.º 16
0
        /// <summary>
        /// Event handler for a reliable SIP stream socket being disconnected.
        /// </summary>
        /// <param name="connection">The disconnected stream.</param>
        /// <param name="socketError">The cause of the disconnect.</param>
        protected void OnSIPStreamDisconnected(SIPStreamConnection connection, SocketError socketError)
        {
            try
            {
                if (connection != null)
                {
                    logger.LogDebug($"SIP stream disconnected {m_localSIPEndPoint.Protocol}:{connection.RemoteEndPoint} {socketError}.");

                    lock (m_connections)
                    {
                        if (m_connections.TryRemove(connection.ConnectionID, out _))
                        {
                            var socket = connection.StreamSocket;

                            // Important: Due to the way TCP works the end of the connection that initiates the close
                            // is meant to go into a TIME_WAIT state. On Windows that results in the same pair of sockets
                            // being unable to reconnect for 30s. SIP can deal with stray and duplicate messages at the
                            // appliction layer so the TIME_WAIT is not that useful. While not useful it is also a major annoyance
                            // as if a connection is dropped for whatever reason, such as a parser error or inactivity, it will
                            // prevent the connection being re-established.
                            //
                            // For this reason this implementation uses a hard RST close for client initiated socket closes. This
                            // results in a TCP RST packet instead of the graceful FIN-ACK sequence. Two things are necessary with
                            // WinSock2 to force the hard RST:
                            //
                            // - the Linger option must be set on the raw socket before binding as Linger option {1, 0}.
                            // - the close method must be called on teh socket without shutting down the stream.

                            socket.Close();
                        }
                    }
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception OnSIPStreamDisconnected. " + excp.Message);
            }
        }
Ejemplo n.º 17
0
        /// <summary>
        /// For the TLS channel once the TCP client socket is connected it needs to be wrapped up in an SSL stream.
        /// </summary>
        /// <param name="streamConnection">The stream connection holding the newly connected client socket.</param>
        /// <param name="serverCertificateName">The expected common name on the SSL certificate supplied by the server.</param>
        protected override async Task <SocketError> OnClientConnect(SIPStreamConnection streamConnection, string serverCertificateName)
        {
            NetworkStream networkStream = new NetworkStream(streamConnection.StreamSocket, true);
            SslStream     sslStream     = new SslStream(networkStream, false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
            //DisplayCertificateInformation(sslStream);

            var timeoutTask   = Task.Delay(TLS_ATTEMPT_CONNECT_TIMEOUT);
            var sslStreamTask = m_clientCertificates != null?sslStream.AuthenticateAsClientAsync(serverCertificateName, m_clientCertificates, SslProtocols.None, false) : sslStream.AuthenticateAsClientAsync(serverCertificateName);

            await Task.WhenAny(sslStreamTask, timeoutTask).ConfigureAwait(false);

            if (sslStreamTask.IsCompleted)
            {
                if (!sslStream.IsAuthenticated)
                {
                    logger.LogWarning($"SIP TLS channel failed to establish SSL stream with {streamConnection.RemoteSIPEndPoint}.");
                    networkStream.Close(CLOSE_CONNECTION_TIMEOUT);
                    return(SocketError.ProtocolNotSupported);
                }
                else
                {
                    streamConnection.SslStream       = sslStream;
                    streamConnection.SslStreamBuffer = new byte[2 * SIPStreamConnection.MaxSIPTCPMessageSize];

                    logger.LogDebug($"SIP TLS Channel successfully upgraded client connection to SSL stream for {ListeningSIPEndPoint}->{streamConnection.RemoteSIPEndPoint}.");

                    sslStream.BeginRead(streamConnection.SslStreamBuffer, 0, SIPStreamConnection.MaxSIPTCPMessageSize, new AsyncCallback(OnReadCallback), streamConnection);

                    return(SocketError.Success);
                }
            }
            else
            {
                logger.LogWarning($"SIP TLS channel timed out attempting to establish SSL stream with {streamConnection.RemoteSIPEndPoint}.");
                networkStream.Close(CLOSE_CONNECTION_TIMEOUT);
                return(SocketError.TimedOut);
            }
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Receive event handler for the newer ReceiveAsync socket call.
        /// </summary>
        /// <param name="e">The socket args for the completed receive operation.</param>
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            SIPStreamConnection streamConn = (SIPStreamConnection)e.UserToken;

            try
            {
                if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                {
                    byte[] buffer = streamConn.RecvSocketArgs.Buffer;
                    streamConn.ExtractSIPMessages(this, buffer, e.BytesTransferred);

                    streamConn.RecvSocketArgs.SetBuffer(buffer, streamConn.RecvEndPosn,
                                                        buffer.Length - streamConn.RecvEndPosn);

                    bool willRaiseEvent = streamConn.StreamSocket.ReceiveAsync(e);
                    if (!willRaiseEvent)
                    {
                        ProcessReceive(e);
                    }
                }
                else
                {
                    OnSIPStreamDisconnected(streamConn, e.SocketError);
                }
            }
            catch (SocketException sockExcp)
            {
                OnSIPStreamDisconnected(streamConn, sockExcp.SocketErrorCode);
            }
            catch (Exception excp)
            {
                // There was an error processing the last message received. Remove the disconnected socket.
                logger.LogError(
                    $"Exception processing SIP {ProtDescr} stream receive on read from {e.RemoteEndPoint} closing connection. {excp.Message}");
                OnSIPStreamDisconnected(streamConn, SocketError.Fault);
            }
        }
Ejemplo n.º 19
0
        /// <summary>
        /// For the TLS channel the SSL stream must be created and any authentication actions undertaken.
        /// </summary>
        /// <param name="streamConnection">The stream connection holding the newly accepted client socket.</param>
        protected override async Task OnAccept(SIPStreamConnection streamConnection)
        {
            NetworkStream networkStream = new NetworkStream(streamConnection.StreamSocket, true);
            SslStream     sslStream     = new SslStream(networkStream, false);

            await sslStream.AuthenticateAsServerAsync(m_serverCertificate);

            logger.LogDebug($"SIP TLS Channel successfully upgraded accepted client to SSL stream for {ListeningEndPoint}->{streamConnection.StreamSocket.RemoteEndPoint}.");

            //// Display the properties and settings for the authenticated stream.
            ////DisplaySecurityLevel(sslStream);
            ////DisplaySecurityServices(sslStream);
            ////DisplayCertificateInformation(sslStream);
            ////DisplayStreamProperties(sslStream);

            //// Set timeouts for the read and write to 5 seconds.
            //sslStream.ReadTimeout = 5000;
            //sslStream.WriteTimeout = 5000;

            streamConnection.SslStream       = sslStream;
            streamConnection.SslStreamBuffer = new byte[2 * SIPStreamConnection.MaxSIPTCPMessageSize];

            sslStream.BeginRead(streamConnection.SslStreamBuffer, 0, SIPStreamConnection.MaxSIPTCPMessageSize, new AsyncCallback(OnReadCallback), streamConnection);
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Periodically checks the established connections and closes any that have not had a transmission for a specified
        /// period or where the number of connections allowed per IP address has been exceeded.
        /// </summary>
        private void PruneConnections()
        {
            try
            {
                Task.Delay(PRUNE_CONNECTIONS_INTERVAL, m_cts.Token).Wait();

                while (!Closed)
                {
                    bool checkComplete = false;

                    while (!checkComplete)
                    {
                        try
                        {
                            SIPStreamConnection inactiveConnection = null;

                            lock (m_connections)
                            {
                                var inactiveConnectionKey = (from connection in m_connections
                                                             where connection.Value.LastTransmission <
                                                             DateTime.Now.AddMinutes(PRUNE_NOTRANSMISSION_MINUTES * -1)
                                                             select connection.Key).FirstOrDefault();

                                if (inactiveConnectionKey != null)
                                {
                                    inactiveConnection = m_connections[inactiveConnectionKey];
                                    m_connections.TryRemove(inactiveConnectionKey, out _);
                                }
                            }

                            if (inactiveConnection != null)
                            {
                                logger.LogDebug(
                                    $"Pruning inactive connection on {ProtDescr} {ListeningEndPoint} to remote end point {inactiveConnection.RemoteEndPoint}.");
                                inactiveConnection.StreamSocket.Close();
                            }
                            else
                            {
                                checkComplete = true;
                            }
                        }
                        catch (SocketException sockExcp)
                        {
                            // Will be thrown if the socket is already closed.
                            logger.LogWarning(
                                $"Socket error in PruneConnections. {sockExcp.Message} ({sockExcp.ErrorCode}).");
                        }
                        catch (Exception pruneExcp)
                        {
                            logger.LogError("Exception PruneConnections (pruning). " + pruneExcp.Message);
                            checkComplete = true;
                        }
                    }

                    Task.Delay(PRUNE_CONNECTIONS_INTERVAL, m_cts.Token).Wait();
                    checkComplete = false;
                }

                logger.LogDebug($"SIP {ProtDescr} Channel socket on {ListeningEndPoint} pruning connections halted.");
            }
            catch (OperationCanceledException)
            {
            }
            catch (AggregateException)
            {
            } // This gets thrown if task is cancelled.
            catch (Exception excp)
            {
                logger.LogError($"Exception SIP {ProtDescr} Channel PruneConnections. " + excp.Message);
            }
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Attempts to create a client TCP socket connection to a remote end point.
        /// </summary>
        /// <param name="dstEndPoint">The remote TCP end point to attempt to connect to.</param>
        /// <param name="buffer">An optional buffer that if set can contain data to transmit immediately after connecting.</param>
        /// <returns>If successful a connected client socket or null if not.</returns>
        internal async Task <SocketError> ConnectClientAsync(IPEndPoint dstEndPoint, byte[] buffer,
                                                             string serverCertificateName)
        {
            try
            {
                // No existing TCP connection to the destination. Attempt a new socket connection.
                IPAddress localAddress = ListeningIPAddress;
                if (ListeningIPAddress == IPAddress.Any)
                {
                    localAddress = NetServices.GetLocalAddressForRemote(dstEndPoint.Address);
                }

                IPEndPoint localEndPoint = new IPEndPoint(localAddress, Port);

                logger.LogDebug(
                    $"ConnectAsync SIP {ProtDescr} Channel local end point of {localEndPoint} selected for connection to {dstEndPoint}.");

                Socket clientSocket = new Socket(dstEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                clientSocket.LingerState = new LingerOption(true, 0);
                clientSocket.Bind(localEndPoint);

                SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs
                {
                    RemoteEndPoint = dstEndPoint
                };

                // NOTE: The approach of setting the buffer on the connect args worked properly on Windows BUT
                // not so on Linux. Since it's such a tiny saving skip setting the buffer on the connect and
                // do the send once the sockets are connected (use SendOnConnected).
                // If this is a TCP channel can take a shortcut and set the first send payload on the connect args.
                //if (buffer != null && buffer.Length > 0 && serverCertificateName == null)
                //{
                //    connectArgs.SetBuffer(buffer, 0, buffer.Length);
                //}

                logger.LogDebug($"Attempting TCP connection from {localEndPoint} to {dstEndPoint}.");

                // Attempt to connect.
                TaskCompletionSource <SocketError> connectTcs =
                    new TaskCompletionSource <SocketError>(TaskCreationOptions.RunContinuationsAsynchronously);
                connectArgs.Completed += (sender, sockArgs) =>
                {
                    if (sockArgs.LastOperation == SocketAsyncOperation.Connect)
                    {
                        connectTcs.SetResult(sockArgs.SocketError);
                    }
                };
                bool willRaiseEvent = clientSocket.ConnectAsync(connectArgs);
                if (!willRaiseEvent)
                {
                    if (connectArgs.LastOperation == SocketAsyncOperation.Connect)
                    {
                        connectTcs.SetResult(connectArgs.SocketError);
                    }
                }

                var connectResult = await connectTcs.Task.ConfigureAwait(false);

                logger.LogDebug(
                    $"ConnectAsync SIP {ProtDescr} Channel connect completed result for {localEndPoint}->{dstEndPoint} {connectResult}.");

                if (connectResult != SocketError.Success)
                {
                    logger.LogWarning(
                        $"SIP {ProtDescr} Channel send to {dstEndPoint} failed. Attempt to create a client socket failed.");
                }
                else
                {
                    SIPStreamConnection sipStmConn = new SIPStreamConnection(clientSocket,
                                                                             clientSocket.RemoteEndPoint as IPEndPoint, SIPProtocol);
                    sipStmConn.SIPMessageReceived += SIPTCPMessageReceived;

                    m_connections.TryAdd(sipStmConn.ConnectionID, sipStmConn);

                    await OnClientConnect(sipStmConn, serverCertificateName).ConfigureAwait(false);

                    await SendOnConnected(sipStmConn, buffer).ConfigureAwait(false);
                }

                return(connectResult);
            }
            catch (Exception excp)
            {
                logger.LogError($"Exception ConnectClientAsync. {excp.Message}");
                return(SocketError.Fault);
            }
        }
Ejemplo n.º 22
0
        /// <summary>
        /// Attempts to send data to the remote end point over a reliable connection. If an existing
        /// connection exists it will be used otherwise an attempt will be made to establish a new connection.
        /// </summary>
        /// <param name="dstSIPEndPoint">The remote SIP end point to send the reliable data to.</param>
        /// <param name="buffer">The data to send.</param>
        /// <param name="serverCertificateName">Optional. Only relevant for SSL streams. The common name
        /// that is expected for the remote SSL server.</param>
        /// <param name="canInitiateConnection">Indicates whether this send should initiate a connection if needed.
        /// The typical case is SIP requests can initiate new connections but responses should not. Responses should
        /// only be sent on the same TCP or TLS connection that the original request was received on.</param>
        /// <param name="connectionIDHint">Optional. The ID of the specific TCP connection to try and the send the message on.</param>
        /// <returns>If no errors SocketError.Success otherwise an error value.</returns>
        public override Task <SocketError> SendSecureAsync(
            SIPEndPoint dstSIPEndPoint,
            byte[] buffer,
            string serverCertificateName,
            bool canInitiateConnection,
            string connectionIDHint)
        {
            try
            {
                if (dstSIPEndPoint == null)
                {
                    throw new ArgumentException(nameof(dstSIPEndPoint), "An empty destination was specified to Send in SIPTCPChannel.");
                }
                if (buffer == null || buffer.Length == 0)
                {
                    throw new ApplicationException("An empty buffer was specified to Send in SIPTCPChannel.");
                }
                else if (!DisableLocalTCPSocketsCheck && NetServices.LocalIPAddresses.Contains(dstSIPEndPoint.Address) && Port == dstSIPEndPoint.Port)
                {
                    logger.LogWarning($"SIP {ProtDescr} Channel blocked Send to {dstSIPEndPoint} as it was identified as a locally hosted {ProtDescr} socket.\r\n" + SIPConstants.DEFAULT_ENCODING.GetString(buffer));
                    throw new ApplicationException($"A Send call was blocked in SIP {ProtDescr} Channel due to the destination being another local TCP socket.");
                }
                else
                {
                    IPEndPoint dstEndPoint = dstSIPEndPoint.GetIPEndPoint(m_isDualMode);

                    // Lookup a client socket that is connected to the destination. If it does not exist attempt to connect a new one.
                    SIPStreamConnection sipStreamConn = null;

                    if (connectionIDHint != null)
                    {
                        m_connections.TryGetValue(connectionIDHint, out sipStreamConn);
                    }

                    if (sipStreamConn == null && HasConnection(dstSIPEndPoint))
                    {
                        sipStreamConn = m_connections.Where(x => x.Value.RemoteSIPEndPoint.IsSocketEqual(dstSIPEndPoint)).First().Value;
                    }

                    if (sipStreamConn != null)
                    {
                        SendOnConnected(sipStreamConn, buffer);
                        return(Task.FromResult(SocketError.Success));
                    }
                    else if (canInitiateConnection)
                    {
                        return(ConnectClientAsync(dstEndPoint, buffer, serverCertificateName));
                    }
                    else
                    {
                        logger.LogWarning($"SIP {ProtDescr} Channel did not have an existing connection for send to {dstSIPEndPoint} and requested not to initiate a connection.");
                        return(Task.FromResult(SocketError.NotConnected));
                    }
                }
            }
            catch (SocketException sockExcp)
            {
                return(Task.FromResult(sockExcp.SocketErrorCode));
            }
            catch (ApplicationException)
            {
                throw;
            }
            catch (Exception excp)
            {
                logger.LogError($"Exception SIPTCPChannel Send (sendto=>{dstSIPEndPoint}). {excp}");
                throw;
            }
        }
Ejemplo n.º 23
0
        /// <summary>
        /// Attempts to create a client TCP socket connection to a remote end point.
        /// </summary>
        /// <param name="dstEndPoint">The remote TCP end point to attempt to connect to.</param>
        /// <param name="buffer">An optional buffer that if set can contain data to transmit immediately after connecting.</param>
        /// <returns>If successful a connected client socket or null if not.</returns>
        public async Task ConnectClientAsync(IPEndPoint dstEndPoint, byte[] buffer, string serverCertificateName)
        {
            // No existing TCP connection to the destination. Attempt a new socket connection.
            IPEndPoint localEndPoint = m_localSIPEndPoint.GetIPEndPoint();

            Socket clientSocket = new Socket(dstEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            clientSocket.LingerState = new LingerOption(true, 0);
            clientSocket.Bind(localEndPoint);

            SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs
            {
                AcceptSocket   = clientSocket,
                RemoteEndPoint = dstEndPoint
            };

            // If this is a TCP channel can take a shortcut and set the first send payload on the connect args.
            if (buffer != null && buffer.Length > 0 && serverCertificateName == null)
            {
                connectArgs.SetBuffer(buffer, 0, buffer.Length);
            }

            // Attempt to connect.
            TaskCompletionSource <SocketError> connectTcs = new TaskCompletionSource <SocketError>();

            connectArgs.Completed += (sender, sockArgs) => { if (sockArgs.LastOperation == SocketAsyncOperation.Connect)
                                                             {
                                                                 connectTcs.SetResult(sockArgs.SocketError);
                                                             }
            };
            bool willRaiseEvent = clientSocket.ConnectAsync(connectArgs);

            if (!willRaiseEvent)
            {
                if (connectArgs.LastOperation == SocketAsyncOperation.Connect)
                {
                    connectTcs.SetResult(connectArgs.SocketError);
                }
            }

            var connectResult = await connectTcs.Task;

            logger.LogDebug($"ConnectAsync SIP TCP Channel connect completed result for {localEndPoint}->{dstEndPoint} {connectResult}.");

            if (connectResult != SocketError.Success)
            {
                logger.LogWarning($"SIP TCP Channel sent to {dstEndPoint} failed. Attempt to create a client socket failed.");
                lock (m_connectionFailures)
                {
                    m_connectionFailures.Add(dstEndPoint.ToString(), DateTime.Now);
                }
                throw new ApplicationException($"Failed to establish TCP connection to {dstEndPoint}.");
            }
            else
            {
                SIPStreamConnection sipStmConn = new SIPStreamConnection(clientSocket, clientSocket.RemoteEndPoint as IPEndPoint, m_localSIPEndPoint.Protocol, SIPConnectionsEnum.Caller);
                sipStmConn.SIPMessageReceived += SIPTCPMessageReceived;

                lock (m_connectedSockets)
                {
                    m_connectedSockets.Add(clientSocket.RemoteEndPoint.ToString(), sipStmConn);
                }

                OnClientConnect(sipStmConn, buffer, serverCertificateName);
            }
        }
Ejemplo n.º 24
0
        /// <summary>
        /// Periodically checks the established connections and closes any that have not had a transmission for a specified
        /// period or where the number of connections allowed per IP address has been exceeded. Only relevant for connection
        /// oriented channels such as TCP and TLS.
        /// </summary>
        protected void PruneConnections(string threadName)
        {
            try
            {
                Thread.CurrentThread.Name = threadName;

                Thread.Sleep(INITIALPRUNE_CONNECTIONS_DELAY);

                while (!Closed)
                {
                    bool checkComplete = false;

                    while (!checkComplete)
                    {
                        try
                        {
                            SIPStreamConnection inactiveConnection = null;
                            Dictionary <string, SIPStreamConnection> connections = GetConnectionsList();

                            lock (connections)
                            {
                                var inactiveConnectionKey = (from connection in connections
                                                             where connection.Value.LastTransmission < DateTime.Now.AddMinutes(PRUNE_NOTRANSMISSION_MINUTES * -1)
                                                             select connection.Key).FirstOrDefault();

                                if (inactiveConnectionKey != null)
                                {
                                    inactiveConnection = connections[inactiveConnectionKey];
                                    connections.Remove(inactiveConnectionKey);
                                }
                            }

                            if (inactiveConnection != null)
                            {
                                logger.LogDebug($"Pruning inactive connection on {SIPChannelContactURI}to remote end point {inactiveConnection.RemoteEndPoint}.");
                                inactiveConnection.StreamSocket.Close();
                            }
                            else
                            {
                                checkComplete = true;
                            }
                        }
                        catch (SocketException)
                        {
                            // Will be thrown if the socket is already closed.
                        }
                        catch (Exception pruneExcp)
                        {
                            logger.LogError("Exception PruneConnections (pruning). " + pruneExcp.Message);
                            checkComplete = true;
                        }
                    }

                    Thread.Sleep(PRUNE_CONNECTIONS_INTERVAL);
                    checkComplete = false;
                }

                logger.LogDebug("SIPChannel socket on " + m_localSIPEndPoint.ToString() + " pruning connections halted.");
            }
            catch (Exception excp)
            {
                logger.LogError("Exception SIPChannel PruneConnections. " + excp.Message);
            }
        }
Ejemplo n.º 25
0
        /// <summary>
        /// Attempts to create a client TCP socket connection to a remote end point.
        /// </summary>
        /// <param name="dstEndPoint">The remote TCP end point to attempt to connect to.</param>
        /// <param name="buffer">An optional buffer that if set can contain data to transmit immediately after connecting.</param>
        /// <returns>If successful a connected client socket or null if not.</returns>
        internal async Task <SocketError> ConnectClientAsync(IPEndPoint dstEndPoint, byte[] buffer, string serverCertificateName)
        {
            try
            {
                // Map IPv4 to IPv6 for dual mode socket sends.
                if (dstEndPoint.AddressFamily == AddressFamily.InterNetwork && m_isDualMode)
                {
                    dstEndPoint = new IPEndPoint(dstEndPoint.Address.MapToIPv6(), dstEndPoint.Port);
                }

                logger.LogDebug($"ConnectClientAsync SIP {ProtDescr} Channel local end point of {ListeningSIPEndPoint} selected for connection to {dstEndPoint}.");

                Socket clientSocket = new Socket(ListeningIPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                clientSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                clientSocket.LingerState = new LingerOption(true, 0);

                if (m_isDualMode && ListeningIPAddress.AddressFamily == AddressFamily.InterNetworkV6)
                {
                    clientSocket.DualMode = true;
                }

                clientSocket.Bind(ListeningEndPoint);

                SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs
                {
                    RemoteEndPoint = dstEndPoint
                };

                // NOTE: The approach of setting the buffer on the connect args worked properly on Windows BUT
                // not so on Linux. Since it's such a tiny saving skip setting the buffer on the connect and
                // do the send once the sockets are connected (use SendOnConnected).
                // If this is a TCP channel can take a shortcut and set the first send payload on the connect args.
                //if (buffer != null && buffer.Length > 0 && serverCertificateName == null)
                //{
                //    connectArgs.SetBuffer(buffer, 0, buffer.Length);
                //}

                logger.LogDebug($"Attempting TCP connection from {ListeningSIPEndPoint} to {dstEndPoint}.");

                // Attempt to connect.
                TaskCompletionSource <SocketError> connectTcs = new TaskCompletionSource <SocketError>(TaskCreationOptions.RunContinuationsAsynchronously);
                connectArgs.Completed += (sender, sockArgs) =>
                {
                    if (sockArgs.LastOperation == SocketAsyncOperation.Connect)
                    {
                        connectTcs.SetResult(sockArgs.SocketError);
                    }
                };
                bool willRaiseEvent = clientSocket.ConnectAsync(connectArgs);
                if (!willRaiseEvent && connectArgs.LastOperation == SocketAsyncOperation.Connect)
                {
                    connectTcs.SetResult(connectArgs.SocketError);
                }

                var timeoutTask   = Task.Delay(TCP_ATTEMPT_CONNECT_TIMEOUT);
                var connectResult = await Task.WhenAny(connectTcs.Task, timeoutTask).ConfigureAwait(false);

                if (timeoutTask.IsCompleted)
                {
                    logger.LogWarning($"SIP {ProtDescr} channel timed out attempting to establish a connection to {dstEndPoint}.");
                    return(SocketError.TimedOut);
                }
                else if (connectTcs.Task.Result != SocketError.Success)
                {
                    logger.LogWarning($"SIP {ProtDescr} Channel send to {dstEndPoint} failed. Attempt to create a client socket failed with {connectTcs.Task.Result}.");
                    return(connectTcs.Task.Result);
                }
                else
                {
                    logger.LogDebug($"ConnectAsync SIP {ProtDescr} Channel connect completed result for {ListeningSIPEndPoint}->{dstEndPoint} {connectTcs.Task.Result}.");

                    var remoteSIPEndPoint          = new SIPEndPoint(SIPProtocol, clientSocket.RemoteEndPoint as IPEndPoint);
                    SIPStreamConnection sipStmConn = new SIPStreamConnection(clientSocket, SIPEncoding, SIPBodyEncoding, remoteSIPEndPoint, SIPProtocol);
                    sipStmConn.SIPMessageReceived += SIPTCPMessageReceived;

                    var postConnectResult = await OnClientConnect(sipStmConn, serverCertificateName).ConfigureAwait(false);

                    if (postConnectResult != SocketError.Success)
                    {
                        logger.LogWarning($"SIP {ProtDescr} Channel send to {dstEndPoint} failed. Attempt to connect to server at {dstEndPoint} failed with {postConnectResult}.");
                    }
                    else
                    {
                        m_connections.TryAdd(sipStmConn.ConnectionID, sipStmConn);
                        await SendOnConnected(sipStmConn, buffer).ConfigureAwait(false);
                    }

                    return(postConnectResult);
                }
            }
            catch (Exception excp)
            {
                logger.LogError($"Exception ConnectClientAsync. {excp}");
                return(SocketError.Fault);
            }
        }