コード例 #1
0
ファイル: WatsonTcpServer.cs プロジェクト: vf3k/WatsonTcp
        private async Task <WatsonMessage> MessageReadAsync(ClientMetadata client)
        {
            /*
             *
             * Do not catch exceptions, let them get caught by the data reader
             * to destroy the connection
             *
             */

            WatsonMessage msg = null;

            if (_Mode == Mode.Ssl)
            {
                msg = new WatsonMessage(client.SslStream, Debug);
                await msg.Build();
            }
            else if (_Mode == Mode.Tcp)
            {
                msg = new WatsonMessage(client.NetworkStream, Debug);
                await msg.Build();
            }
            else
            {
                throw new ArgumentException("Unknown mode: " + _Mode.ToString());
            }

            return(msg);
        }
コード例 #2
0
ファイル: WatsonTcpServer.cs プロジェクト: pha3z/WatsonTcp
        private async Task <bool> StartTls(ClientMetadata client)
        {
            try
            {
                // the two bools in this should really be contruction paramaters
                // maybe re-use mutualAuthentication and acceptInvalidCerts ?
                await client.SslStream.AuthenticateAsServerAsync(_SslCertificate, true, SslProtocols.Tls12, false);

                if (!client.SslStream.IsEncrypted)
                {
                    Log("*** StartTls stream from " + client.IpPort + " not encrypted");
                    client.Dispose();
                    return(false);
                }

                if (!client.SslStream.IsAuthenticated)
                {
                    Log("*** StartTls stream from " + client.IpPort + " not authenticated");
                    client.Dispose();
                    return(false);
                }

                if (MutuallyAuthenticate && !client.SslStream.IsMutuallyAuthenticated)
                {
                    Log("*** StartTls stream from " + client.IpPort + " failed mutual authentication");
                    client.Dispose();
                    return(false);
                }
            }
            catch (IOException ex)
            {
                // Some type of problem initiating the SSL connection
                switch (ex.Message)
                {
                case "Authentication failed because the remote party has closed the transport stream.":
                case "Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.":
                    Log("*** StartTls IOException " + client.IpPort + " closed the connection.");
                    break;

                case "The handshake failed due to an unexpected packet format.":
                    Log("*** StartTls IOException " + client.IpPort + " disconnected, invalid handshake.");
                    break;

                default:
                    Log("*** StartTls IOException from " + client.IpPort + Environment.NewLine + ex.ToString());
                    break;
                }

                client.Dispose();
                return(false);
            }
            catch (Exception ex)
            {
                Log("*** StartTls Exception from " + client.IpPort + Environment.NewLine + ex.ToString());
                client.Dispose();
                return(false);
            }

            return(true);
        }
コード例 #3
0
 private bool IsConnected(ClientMetadata client)
 {
     if (client.Tcp.Connected)
     {
         if ((client.Tcp.Client.Poll(0, SelectMode.SelectWrite)) && (!client.Tcp.Client.Poll(0, SelectMode.SelectError)))
         {
             byte[] buffer = new byte[1];
             if (client.Tcp.Client.Receive(buffer, SocketFlags.Peek) == 0)
             {
                 return(false);
             }
             else
             {
                 return(true);
             }
         }
         else
         {
             return(false);
         }
     }
     else
     {
         return(false);
     }
 }
コード例 #4
0
        private void FinalizeConnection(ClientMetadata client)
        {
            #region Add-to-Client-List

            if (!AddClient(client))
            {
                Log("*** FinalizeConnection unable to add client " + client.IpPort);
                client.Dispose();
                return;
            }

            // Do not decrement in this block, decrement is done by the connection reader
            int activeCount = Interlocked.Increment(ref _ActiveClients);

            #endregion

            #region Start-Data-Receiver

            Log("*** FinalizeConnection starting data receiver for " + client.IpPort + " (now " + activeCount + " clients)");
            if (_ClientConnected != null)
            {
                Task.Run(() => _ClientConnected(client.IpPort));
            }

            Task.Run(async() => await DataReceiver(client));

            #endregion
        }
コード例 #5
0
        private bool MessageWrite(ClientMetadata client, WatsonMessage msg, byte[] data)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }
            if (msg == null)
            {
                throw new ArgumentNullException(nameof(msg));
            }

            int dataLen = 0;

            if (data != null)
            {
                dataLen = data.Length;
            }

            byte[] headerBytes = msg.ToHeaderBytes(dataLen);

            _SendLock.Wait();

            try
            {
                if (_Mode == Mode.Tcp)
                {
                    client.NetworkStream.Write(headerBytes, 0, headerBytes.Length);
                    if (dataLen > 0)
                    {
                        client.NetworkStream.Write(data, 0, dataLen);
                    }
                    client.NetworkStream.Flush();
                }
                else if (_Mode == Mode.Ssl)
                {
                    client.SslStream.Write(headerBytes, 0, headerBytes.Length);
                    if (dataLen > 0)
                    {
                        client.SslStream.Write(data, 0, dataLen);
                    }
                    client.SslStream.Flush();
                }
                else
                {
                    throw new ArgumentException("Unknown mode: " + _Mode.ToString());
                }

                return(true);
            }
            catch (Exception)
            {
                Log("*** MessageWrite " + client.IpPort + " disconnected due to exception");
                return(false);
            }
            finally
            {
                _SendLock.Release();
            }
        }
コード例 #6
0
ファイル: WatsonTcpServer.cs プロジェクト: pha3z/WatsonTcp
        private bool RemoveClient(ClientMetadata client)
        {
            _Clients.TryRemove(client.IpPort, out ClientMetadata removedClient);
            _UnauthenticatedClients.TryRemove(client.IpPort, out DateTime dt);

            Log("*** RemoveClient removed client " + client.IpPort);
            return(true);
        }
コード例 #7
0
ファイル: WatsonTcpServer.cs プロジェクト: pha3z/WatsonTcp
        private bool AddClient(ClientMetadata client)
        {
            _Clients.TryRemove(client.IpPort, out ClientMetadata removedClient);
            _Clients.TryAdd(client.IpPort, client);

            Log("*** AddClient added client " + client.IpPort);
            return(true);
        }
コード例 #8
0
        private bool AddClient(ClientMetadata client)
        {
            if (!_Clients.TryRemove(client.IpPort, out ClientMetadata removedClient))
            {
                // do nothing, it probably did not exist anyway
            }

            _Clients.TryAdd(client.IpPort, client);
            Log("*** AddClient added client " + client.IpPort);
            return(true);
        }
コード例 #9
0
        private async Task <bool> MessageWriteAsync(ClientMetadata client, byte[] data)
        {
            try
            {
                #region Format-Message

                string header = "";
                byte[] headerBytes;
                byte[] message;

                if (data == null || data.Length < 1)
                {
                    header += "0:";
                }
                else
                {
                    header += data.Length + ":";
                }

                headerBytes = Encoding.UTF8.GetBytes(header);
                int messageLen = headerBytes.Length;
                if (data != null && data.Length > 0)
                {
                    messageLen += data.Length;
                }

                message = new byte[messageLen];
                Buffer.BlockCopy(headerBytes, 0, message, 0, headerBytes.Length);

                if (data != null && data.Length > 0)
                {
                    Buffer.BlockCopy(data, 0, message, headerBytes.Length, data.Length);
                }

                #endregion

                #region Send-Message-Async

                var clientStream = client.Tcp.GetStream();
                await clientStream.WriteAsync(message, 0, message.Length);

                await clientStream.FlushAsync();

                return(true);

                #endregion
            }
            catch (Exception)
            {
                Log("*** MessageWriteAsync " + client.IpPort() + " disconnected due to exception");
                return(false);
            }
        }
コード例 #10
0
 private bool RemoveClient(ClientMetadata client)
 {
     if (!_Clients.TryRemove(client.IpPort, out ClientMetadata removedClient))
     {
         Log("*** RemoveClient unable to remove client " + client.IpPort);
         return(false);
     }
     else
     {
         Log("*** RemoveClient removed client " + client.IpPort);
         return(true);
     }
 }
コード例 #11
0
        private bool AddClient(ClientMetadata client)
        {
            ClientMetadata removedClient;

            if (!Clients.TryRemove(client.IpPort(), out removedClient))
            {
                // do nothing, it probably did not exist anyway
            }

            Clients.TryAdd(client.IpPort(), client);
            Log("AddClient added client " + client.IpPort());
            return(true);
        }
コード例 #12
0
        private async Task DataReceiver(ClientMetadata client, CancellationToken?cancelToken = null)
        {
            try
            {
                #region Wait-for-Data

                while (true)
                {
                    cancelToken?.ThrowIfCancellationRequested();

                    try
                    {
                        if (!IsConnected(client))
                        {
                            break;
                        }

                        byte[] data = await MessageReadAsync(client);

                        if (data == null)
                        {
                            // no message available
                            await Task.Delay(30);

                            continue;
                        }

                        if (MessageReceived != null)
                        {
                            var unawaited = Task.Run(() => MessageReceived(client.IpPort(), data));
                        }
                    }
                    catch (Exception)
                    {
                        break;
                    }
                }

                #endregion
            }
            finally
            {
                ActiveClients--;
                RemoveClient(client);
                if (ClientDisconnected != null)
                {
                    var unawaited = Task.Run(() => ClientDisconnected(client.IpPort()));
                }
                Log("DataReceiver client " + client.IpPort() + " disconnected (now " + ActiveClients + " clients active)");
            }
        }
コード例 #13
0
        private async Task DataReceiver(ClientMetadata client)
        {
            try
            {
                #region Wait-for-Data

                while (true)
                {
                    try
                    {
                        if (!IsConnected(client))
                        {
                            break;
                        }

                        byte[] data = await MessageReadAsync(client);

                        if (data == null)
                        {
                            // no message available
                            await Task.Delay(30);

                            continue;
                        }

                        if (_MessageReceived != null)
                        {
                            Task <bool> unawaited = Task.Run(() => _MessageReceived(client.IpPort, data));
                        }
                    }
                    catch (Exception)
                    {
                        break;
                    }
                }

                #endregion
            }
            finally
            {
                int activeCount = Interlocked.Decrement(ref _ActiveClients);
                RemoveClient(client);
                if (_ClientDisconnected != null)
                {
                    Task <bool> unawaited = Task.Run(() => _ClientDisconnected(client.IpPort));
                }
                Log("*** DataReceiver client " + client.IpPort + " disconnected (now " + activeCount + " clients active)");

                client.Dispose();
            }
        }
コード例 #14
0
ファイル: WatsonTcpServer.cs プロジェクト: pha3z/WatsonTcp
        private async Task <bool> MessageWriteAsync(ClientMetadata client, WatsonMessage msg, byte[] data)
        {
            int          dataLen = 0;
            MemoryStream ms      = new MemoryStream();

            if (data != null && data.Length > 0)
            {
                dataLen = data.Length;
                ms.Write(data, 0, data.Length);
                ms.Seek(0, SeekOrigin.Begin);
            }

            return(await MessageWriteAsync(client, msg, dataLen, ms));
        }
コード例 #15
0
        private bool RemoveClient(ClientMetadata client)
        {
            ClientMetadata removedClient;

            if (!Clients.TryRemove(client.IpPort(), out removedClient))
            {
                Log("RemoveClient unable to remove client " + client.IpPort());
                return(false);
            }
            else
            {
                Log("RemoveClient removed client " + client.IpPort());
                return(true);
            }
        }
コード例 #16
0
ファイル: WatsonTcpServer.cs プロジェクト: vf3k/WatsonTcp
        private async Task <bool> MessageWriteAsync(ClientMetadata client, WatsonMessage msg)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }
            if (msg == null)
            {
                throw new ArgumentNullException(nameof(msg));
            }

            byte[] msgBytes = msg.ToBytes();

            await _SendLock.WaitAsync();

            try
            {
                if (_Mode == Mode.Tcp)
                {
                    await client.NetworkStream.WriteAsync(msgBytes, 0, msgBytes.Length);

                    await client.NetworkStream.FlushAsync();
                }
                else if (_Mode == Mode.Ssl)
                {
                    await client.SslStream.WriteAsync(msgBytes, 0, msgBytes.Length);

                    await client.SslStream.FlushAsync();
                }
                else
                {
                    throw new ArgumentException("Unknown mode: " + _Mode.ToString());
                }

                return(true);
            }
            catch (Exception)
            {
                Log("*** MessageWriteAsync " + client.IpPort + " disconnected due to exception");
                return(false);
            }
            finally
            {
                _SendLock.Release();
            }
        }
コード例 #17
0
ファイル: WatsonTcpServer.cs プロジェクト: pha3z/WatsonTcp
        private void FinalizeConnection(ClientMetadata client)
        {
            #region Add-to-Client-List

            if (!AddClient(client))
            {
                Log("*** FinalizeConnection unable to add client " + client.IpPort);
                client.Dispose();
                return;
            }

            // Do not decrement in this block, decrement is done by the connection reader
            int activeCount = Interlocked.Increment(ref _ActiveClients);

            #endregion

            #region Request-Authentication

            if (!String.IsNullOrEmpty(PresharedKey))
            {
                Log("*** FinalizeConnection soliciting authentication material from " + client.IpPort);
                _UnauthenticatedClients.TryAdd(client.IpPort, DateTime.Now);

                byte[]        data    = Encoding.UTF8.GetBytes("Authentication required");
                WatsonMessage authMsg = new WatsonMessage();
                authMsg.Status        = MessageStatus.AuthRequired;
                authMsg.Data          = null;
                authMsg.ContentLength = 0;
                MessageWrite(client, authMsg, null);
            }

            #endregion

            #region Start-Data-Receiver

            Log("*** FinalizeConnection starting data receiver for " + client.IpPort + " (now " + activeCount + " clients)");
            if (ClientConnected != null)
            {
                Task.Run(() => ClientConnected(client.IpPort));
            }

            Task.Run(async() => await DataReceiver(client));

            #endregion
        }
コード例 #18
0
        private bool MessageWrite(ClientMetadata client, WatsonMessage msg, byte[] data)
        {
            int          dataLen = 0;
            MemoryStream ms      = new MemoryStream();

            if (data != null && data.Length > 0)
            {
                dataLen = data.Length;
                ms.Write(data, 0, data.Length);
                ms.Seek(0, SeekOrigin.Begin);
            }
            else
            {
                ms = new MemoryStream(new byte[0]);
            }

            return(MessageWrite(client, msg, dataLen, ms));
        }
コード例 #19
0
        private void FinalizeConnection(ClientMetadata client)
        {
            #region Add-to-Client-List

            ClientMetadata removed = null;
            DateTime       ts;

            _Clients.TryRemove(client.IpPort, out removed);
            _ClientsLastSeen.TryRemove(client.IpPort, out ts);

            _Clients.TryAdd(client.IpPort, client);
            _ClientsLastSeen.TryAdd(client.IpPort, DateTime.Now);

            #endregion Add-to-Client-List

            #region Request-Authentication

            if (!String.IsNullOrEmpty(PresharedKey))
            {
                Log("*** FinalizeConnection soliciting authentication material from " + client.IpPort);
                _UnauthenticatedClients.TryAdd(client.IpPort, DateTime.Now);

                byte[]        data    = Encoding.UTF8.GetBytes("Authentication required");
                WatsonMessage authMsg = new WatsonMessage();
                authMsg.Status        = MessageStatus.AuthRequired;
                authMsg.Data          = null;
                authMsg.ContentLength = 0;
                MessageWrite(client, authMsg, null);
            }

            #endregion Request-Authentication

            #region Start-Data-Receiver

            Log("*** FinalizeConnection starting data receiver for " + client.IpPort);
            if (ClientConnected != null)
            {
                Task.Run(() => ClientConnected(client.IpPort));
            }

            Task.Run(async() => await DataReceiver(client, client.Token));

            #endregion Start-Data-Receiver
        }
コード例 #20
0
        private async Task AcceptConnections()
        {
            Listener.Start();
            while (true)
            {
                #region Accept-Connection

                Token.ThrowIfCancellationRequested();
                TcpClient client = await Listener.AcceptTcpClientAsync();

                client.LingerState.Enabled = false;

                #endregion

                #region Get-Tuple-and-Check-IP

                string clientIp   = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
                int    clientPort = ((IPEndPoint)client.Client.RemoteEndPoint).Port;

                if (PermittedIps != null && PermittedIps.Count > 0)
                {
                    if (!PermittedIps.Contains(clientIp))
                    {
                        Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)");
                        client.Close();
                        return;
                    }
                }

                Log("AcceptConnections accepted connection from " + clientIp + ":" + clientPort);

                #endregion

                var unawaited = Task.Run(() =>
                {
                    #region Add-to-Client-List

                    ActiveClients++;
                    // Do not decrement in this block, decrement is done by the connection reader

                    ClientMetadata currClient = new ClientMetadata(client);
                    if (!AddClient(currClient))
                    {
                        Log("*** AcceptConnections unable to add client " + currClient.IpPort());
                        client.Close();
                        return;
                    }

                    #endregion

                    #region Start-Data-Receiver

                    CancellationToken dataReceiverToken = default(CancellationToken);

                    Log("AcceptConnections starting data receiver for " + currClient.IpPort() + " (now " + ActiveClients + " clients)");
                    if (ClientConnected != null)
                    {
                        Task.Run(() => ClientConnected(currClient.IpPort()));
                    }

                    Task.Run(async() => await DataReceiver(currClient, dataReceiverToken), dataReceiverToken);

                    #endregion
                }, Token);
            }
        }
コード例 #21
0
        private bool IsConnected(ClientMetadata client)
        {
            if (client.TcpClient.Connected)
            {
                byte[] tmp        = new byte[1];
                bool   success    = false;
                bool   sendLocked = false;
                bool   readLocked = false;

                try
                {
                    client.WriteLock.Wait(1);
                    sendLocked = true;
                    client.TcpClient.Client.Send(tmp, 0, 0);
                    success = true;
                }
                catch (ObjectDisposedException)
                {
                }
                catch (IOException)
                {
                }
                catch (SocketException se)
                {
                    if (se.NativeErrorCode.Equals(10035))
                    {
                        success = true;
                    }
                }
                catch (Exception e)
                {
                    Log("*** IsConnected " + client.IpPort + " exception using send: " + e.Message);
                    success = false;
                }
                finally
                {
                    if (sendLocked)
                    {
                        client.WriteLock.Release();
                    }
                }

                if (success)
                {
                    return(true);
                }

                try
                {
                    client.ReadLock.Wait(1);
                    readLocked = true;

                    if ((client.TcpClient.Client.Poll(0, SelectMode.SelectWrite)) &&
                        (!client.TcpClient.Client.Poll(0, SelectMode.SelectError)))
                    {
                        byte[] buffer = new byte[1];
                        if (client.TcpClient.Client.Receive(buffer, SocketFlags.Peek) == 0)
                        {
                            return(false);
                        }
                        else
                        {
                            return(true);
                        }
                    }
                    else
                    {
                        return(false);
                    }
                }
                catch (Exception e)
                {
                    Log("*** IsConnected " + client.IpPort + " exception using poll/peek: " + e.Message);
                    return(false);
                }
                finally
                {
                    if (readLocked)
                    {
                        client.ReadLock.Release();
                    }
                }
            }
            else
            {
                return(false);
            }
        }
コード例 #22
0
        private async Task AcceptConnections()
        {
            _Listener.Start();

            while (!_Token.IsCancellationRequested)
            {
                string clientIpPort = String.Empty;

                try
                {
                    #region Accept-Connection-and-Validate-IP

                    TcpClient tcpClient = await _Listener.AcceptTcpClientAsync();

                    tcpClient.LingerState.Enabled = false;

                    string clientIp = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
                    if (PermittedIPs != null && PermittedIPs.Count > 0)
                    {
                        if (!PermittedIPs.Contains(clientIp))
                        {
                            Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)");
                            tcpClient.Close();
                            continue;
                        }
                    }

                    ClientMetadata client = new ClientMetadata(tcpClient);
                    clientIpPort = client.IpPort;

                    #endregion Accept-Connection-and-Validate-IP

                    if (_Mode == Mode.Tcp)
                    {
                        Task unawaited = Task.Run(() => FinalizeConnection(client), _Token);
                    }
                    else if (_Mode == Mode.Ssl)
                    {
                        if (AcceptInvalidCertificates)
                        {
                            client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(AcceptCertificate));
                        }
                        else
                        {
                            client.SslStream = new SslStream(client.NetworkStream, false);
                        }

                        Task unawaited = Task.Run(() =>
                        {
                            Task <bool> success = StartTls(client);
                            if (success.Result)
                            {
                                FinalizeConnection(client);
                            }
                        }, _Token);
                    }
                    else
                    {
                        throw new ArgumentException("Unknown mode: " + _Mode.ToString());
                    }

                    Log("*** AcceptConnections accepted connection from " + client.IpPort);
                }
                catch (Exception e)
                {
                    Log("*** AcceptConnections exception " + clientIpPort + " " + e.Message);
                }
            }
        }
コード例 #23
0
        private async Task <bool> MessageWriteAsync(ClientMetadata client, WatsonMessage msg, long contentLength, Stream stream)
        {
            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }
            if (msg == null)
            {
                throw new ArgumentNullException(nameof(msg));
            }

            if (contentLength > 0)
            {
                if (stream == null || !stream.CanRead)
                {
                    throw new ArgumentException("Cannot read from supplied stream.");
                }
            }

            byte[] headerBytes = msg.ToHeaderBytes(contentLength);

            int  bytesRead      = 0;
            long bytesRemaining = contentLength;

            byte[] buffer = new byte[_ReadStreamBufferSize];

            client.WriteLock.Wait(1);

            try
            {
                if (_Mode == Mode.Tcp)
                {
                    await client.NetworkStream.WriteAsync(headerBytes, 0, headerBytes.Length);

                    if (contentLength > 0)
                    {
                        while (bytesRemaining > 0)
                        {
                            bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);

                            if (bytesRead > 0)
                            {
                                await client.NetworkStream.WriteAsync(buffer, 0, bytesRead);

                                bytesRemaining -= bytesRead;
                            }
                        }
                    }

                    await client.NetworkStream.FlushAsync();
                }
                else if (_Mode == Mode.Ssl)
                {
                    await client.SslStream.WriteAsync(headerBytes, 0, headerBytes.Length);

                    if (contentLength > 0)
                    {
                        while (bytesRemaining > 0)
                        {
                            bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);

                            if (bytesRead > 0)
                            {
                                await client.SslStream.WriteAsync(buffer, 0, bytesRead);

                                bytesRemaining -= bytesRead;
                            }
                        }
                    }

                    await client.SslStream.FlushAsync();
                }

                return(true);
            }
            catch (Exception e)
            {
                Log("*** MessageWriteAsync " + client.IpPort + " disconnected due to exception: " + e.Message);
                return(false);
            }
            finally
            {
                client.WriteLock.Release();
            }
        }
コード例 #24
0
ファイル: WatsonTcpServer.cs プロジェクト: pha3z/WatsonTcp
        private async Task DataReceiver(ClientMetadata client)
        {
            try
            {
                #region Wait-for-Data

                while (true)
                {
                    try
                    {
                        if (!IsConnected(client))
                        {
                            break;
                        }

                        WatsonMessage msg = await MessageReadAsync(client);

                        if (msg == null)
                        {
                            // no message available
                            await Task.Delay(30);

                            continue;
                        }

                        if (!String.IsNullOrEmpty(PresharedKey))
                        {
                            if (_UnauthenticatedClients.ContainsKey(client.IpPort))
                            {
                                Log("*** DataReceiver message received from unauthenticated endpoint: " + client.IpPort);

                                if (msg.Status == MessageStatus.AuthRequested)
                                {
                                    // check preshared key
                                    if (msg.PresharedKey != null && msg.PresharedKey.Length > 0)
                                    {
                                        string clientPsk = Encoding.UTF8.GetString(msg.PresharedKey).Trim();
                                        if (PresharedKey.Trim().Equals(clientPsk))
                                        {
                                            if (Debug)
                                            {
                                                Log("DataReceiver accepted authentication from " + client.IpPort);
                                            }
                                            _UnauthenticatedClients.TryRemove(client.IpPort, out DateTime dt);
                                            byte[]        data    = Encoding.UTF8.GetBytes("Authentication successful");
                                            WatsonMessage authMsg = new WatsonMessage(data, Debug);
                                            authMsg.Status = MessageStatus.AuthSuccess;
                                            MessageWrite(client, authMsg, null);
                                            continue;
                                        }
                                        else
                                        {
                                            if (Debug)
                                            {
                                                Log("DataReceiver declined authentication from " + client.IpPort);
                                            }
                                            byte[]        data    = Encoding.UTF8.GetBytes("Authentication declined");
                                            WatsonMessage authMsg = new WatsonMessage(data, Debug);
                                            authMsg.Status = MessageStatus.AuthFailure;
                                            MessageWrite(client, authMsg, null);
                                            continue;
                                        }
                                    }
                                    else
                                    {
                                        if (Debug)
                                        {
                                            Log("DataReceiver no authentication material from " + client.IpPort);
                                        }
                                        byte[]        data    = Encoding.UTF8.GetBytes("No authentication material");
                                        WatsonMessage authMsg = new WatsonMessage(data, Debug);
                                        authMsg.Status = MessageStatus.AuthFailure;
                                        MessageWrite(client, authMsg, null);
                                        continue;
                                    }
                                }
                                else
                                {
                                    // decline the message
                                    if (Debug)
                                    {
                                        Log("DataReceiver no authentication material from " + client.IpPort);
                                    }
                                    byte[]        data    = Encoding.UTF8.GetBytes("Authentication required");
                                    WatsonMessage authMsg = new WatsonMessage(data, Debug);
                                    authMsg.Status = MessageStatus.AuthRequired;
                                    MessageWrite(client, authMsg, null);
                                    continue;
                                }
                            }
                        }

                        if (ReadDataStream)
                        {
                            if (MessageReceived != null)
                            {
                                Task <bool> unawaited = Task.Run(() => MessageReceived(client.IpPort, msg.Data));
                            }
                        }
                        else
                        {
                            if (StreamReceived != null)
                            {
                                StreamReceived(client.IpPort, msg.ContentLength, msg.DataStream);
                            }
                        }
                    }
                    catch (Exception)
                    {
                        break;
                    }
                }

                #endregion
            }
            finally
            {
                int activeCount = Interlocked.Decrement(ref _ActiveClients);
                RemoveClient(client);

                if (ClientDisconnected != null)
                {
                    Task <bool> unawaited = Task.Run(() => ClientDisconnected(client.IpPort));
                }

                Log("*** DataReceiver client " + client.IpPort + " disconnected (now " + activeCount + " clients active)");
                client.Dispose();
            }
        }
コード例 #25
0
        private async Task <byte[]> MessageReadAsync(ClientMetadata client)
        {
            /*
             *
             * Do not catch exceptions, let them get caught by the data reader
             * to destroy the connection
             *
             */

            #region Variables

            int  bytesRead      = 0;
            int  sleepInterval  = 25;
            int  maxTimeout     = 500;
            int  currentTimeout = 0;
            bool timeout        = false;

            NetworkStream ClientStream = client.Tcp.GetStream();

            byte[] headerBytes;
            string header = "";
            long   contentLength;
            byte[] contentBytes;

            if (!ClientStream.CanRead)
            {
                return(null);
            }
            if (!ClientStream.DataAvailable)
            {
                return(null);
            }

            #endregion

            #region Read-Header

            using (MemoryStream headerMs = new MemoryStream())
            {
                #region Read-Header-Bytes

                byte[] headerBuffer = new byte[1];
                timeout        = false;
                currentTimeout = 0;
                int read = 0;

                while ((read = await ClientStream.ReadAsync(headerBuffer, 0, headerBuffer.Length)) > 0)
                {
                    if (read > 0)
                    {
                        await headerMs.WriteAsync(headerBuffer, 0, read);

                        bytesRead     += read;
                        currentTimeout = 0;

                        if (bytesRead > 1)
                        {
                            if ((int)headerBuffer[0] == 58)
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        if (currentTimeout >= maxTimeout)
                        {
                            timeout = true;
                            break;
                        }
                        else
                        {
                            currentTimeout += sleepInterval;
                            await Task.Delay(sleepInterval);
                        }
                    }
                }

                if (timeout)
                {
                    Log("*** MessageReadAsync timeout " + currentTimeout + "ms/" + maxTimeout + "ms exceeded while reading header after reading " + bytesRead + " bytes");
                    return(null);
                }

                headerBytes = headerMs.ToArray();
                if (headerBytes == null || headerBytes.Length < 1)
                {
                    return(null);
                }

                #endregion

                #region Process-Header

                header = Encoding.UTF8.GetString(headerBytes);
                header = header.Replace(":", "");

                if (!Int64.TryParse(header, out contentLength))
                {
                    Log("*** MessageReadAsync malformed message from " + client.IpPort() + " (message header not an integer)");
                    return(null);
                }

                #endregion
            }

            #endregion

            #region Read-Data

            using (MemoryStream dataMs = new MemoryStream())
            {
                long bytesRemaining = contentLength;
                timeout        = false;
                currentTimeout = 0;

                int    read = 0;
                byte[] buffer;
                long   bufferSize = 2048;
                if (bufferSize > bytesRemaining)
                {
                    bufferSize = bytesRemaining;
                }
                buffer = new byte[bufferSize];

                while ((read = await ClientStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    if (read > 0)
                    {
                        dataMs.Write(buffer, 0, read);
                        bytesRead      = bytesRead + read;
                        bytesRemaining = bytesRemaining - read;
                        currentTimeout = 0;

                        // reduce buffer size if number of bytes remaining is
                        // less than the pre-defined buffer size of 2KB
                        if (bytesRemaining < bufferSize)
                        {
                            bufferSize = bytesRemaining;
                        }
                        buffer = new byte[bufferSize];

                        // check if read fully
                        if (bytesRemaining == 0)
                        {
                            break;
                        }
                        if (bytesRead == contentLength)
                        {
                            break;
                        }
                    }

                    if (!ClientStream.DataAvailable)
                    {
                        while (true)
                        {
                            if (currentTimeout >= maxTimeout)
                            {
                                timeout = true;
                                break;
                            }
                            else
                            {
                                currentTimeout += sleepInterval;
                                await Task.Delay(sleepInterval);
                            }
                        }

                        if (timeout)
                        {
                            break;
                        }
                    }
                }

                if (timeout)
                {
                    Log("*** MessageReadAsync timeout " + currentTimeout + "ms/" + maxTimeout + "ms exceeded while reading content after reading " + bytesRead + " bytes");
                    return(null);
                }

                contentBytes = dataMs.ToArray();
            }

            #endregion

            #region Check-Content-Bytes

            if (contentBytes == null || contentBytes.Length < 1)
            {
                Log("*** MessageReadAsync " + client.IpPort() + " no content read");
                return(null);
            }

            if (contentBytes.Length != contentLength)
            {
                Log("*** MessageReadAsync " + client.IpPort() + " content length " + contentBytes.Length + " bytes does not match header value " + contentLength + ", discarding");
                return(null);
            }

            #endregion

            return(contentBytes);
        }
コード例 #26
0
        private async Task DataReceiver(ClientMetadata client, CancellationToken token)
        {
            string header = "[" + client.IpPort + "]";

            while (true)
            {
                try
                {
                    token.ThrowIfCancellationRequested();

                    if (!IsConnected(client))
                    {
                        break;
                    }

                    WatsonMessage msg          = null;
                    bool          buildSuccess = false;

                    if (_Mode == Mode.Ssl)
                    {
                        msg = new WatsonMessage(client.SslStream, Debug);
                    }
                    else if (_Mode == Mode.Tcp)
                    {
                        msg = new WatsonMessage(client.NetworkStream, Debug);
                    }

                    if (_MessageReceived != null)
                    {
                        buildSuccess = await msg.Build();
                    }
                    else if (_StreamReceived != null)
                    {
                        buildSuccess = await msg.BuildStream();
                    }
                    else
                    {
                        break;
                    }

                    if (!buildSuccess)
                    {
                        break;
                    }

                    if (msg == null)
                    {
                        // no message available
                        await Task.Delay(30);

                        continue;
                    }

                    if (!String.IsNullOrEmpty(PresharedKey))
                    {
                        if (_UnauthenticatedClients.ContainsKey(client.IpPort))
                        {
                            Log(header + " message received from unauthenticated endpoint");

                            if (msg.Status == MessageStatus.AuthRequested)
                            {
                                // check preshared key
                                if (msg.PresharedKey != null && msg.PresharedKey.Length > 0)
                                {
                                    string clientPsk = Encoding.UTF8.GetString(msg.PresharedKey).Trim();
                                    if (PresharedKey.Trim().Equals(clientPsk))
                                    {
                                        Log(header + " accepted authentication");
                                        _UnauthenticatedClients.TryRemove(client.IpPort, out DateTime dt);
                                        byte[]        data    = Encoding.UTF8.GetBytes("Authentication successful");
                                        WatsonMessage authMsg = new WatsonMessage(data, Debug);
                                        authMsg.Status = MessageStatus.AuthSuccess;
                                        MessageWrite(client, authMsg, null);
                                        continue;
                                    }
                                    else
                                    {
                                        Log(header + " declined authentication");
                                        byte[]        data    = Encoding.UTF8.GetBytes("Authentication declined");
                                        WatsonMessage authMsg = new WatsonMessage(data, Debug);
                                        authMsg.Status = MessageStatus.AuthFailure;
                                        MessageWrite(client, authMsg, null);
                                        continue;
                                    }
                                }
                                else
                                {
                                    Log(header + " no authentication material");
                                    byte[]        data    = Encoding.UTF8.GetBytes("No authentication material");
                                    WatsonMessage authMsg = new WatsonMessage(data, Debug);
                                    authMsg.Status = MessageStatus.AuthFailure;
                                    MessageWrite(client, authMsg, null);
                                    continue;
                                }
                            }
                            else
                            {
                                // decline the message
                                Log(header + " no authentication material");
                                byte[]        data    = Encoding.UTF8.GetBytes("Authentication required");
                                WatsonMessage authMsg = new WatsonMessage(data, Debug);
                                authMsg.Status = MessageStatus.AuthRequired;
                                MessageWrite(client, authMsg, null);
                                continue;
                            }
                        }
                    }

                    if (msg.Status == MessageStatus.Disconnecting)
                    {
                        Log(header + " sent notification of disconnection");
                        break;
                    }

                    if (msg.Status == MessageStatus.Removed)
                    {
                        Log(header + " sent notification of removal");
                        break;
                    }

                    if (_MessageReceived != null)
                    {
                        // does not need to be awaited, because the stream has been fully read
                        Task unawaited = Task.Run(() => _MessageReceived(client.IpPort, msg.Data));
                    }
                    else if (_StreamReceived != null)
                    {
                        // must be awaited, the stream has not been fully read
                        await _StreamReceived(client.IpPort, msg.ContentLength, msg.DataStream);
                    }
                    else
                    {
                        break;
                    }

                    UpdateClientLastSeen(client.IpPort);
                }
                catch (Exception e)
                {
                    Log(Environment.NewLine +
                        "[" + client.IpPort + "] Data receiver exception:" +
                        Environment.NewLine +
                        e.ToString() +
                        Environment.NewLine);
                    break;
                }
            }

            Log(header + " data receiver terminated");

            if (ClientDisconnected != null)
            {
                Task unawaited = null;

                if (_ClientsKicked.ContainsKey(client.IpPort))
                {
                    unawaited = Task.Run(() => ClientDisconnected(client.IpPort, DisconnectReason.Kicked));
                }
                else if (_ClientsTimedout.ContainsKey(client.IpPort))
                {
                    unawaited = Task.Run(() => ClientDisconnected(client.IpPort, DisconnectReason.Timeout));
                }
                else
                {
                    unawaited = Task.Run(() => ClientDisconnected(client.IpPort, DisconnectReason.Normal));
                }
            }

            DateTime removedTs;

            _Clients.TryRemove(client.IpPort, out ClientMetadata removedClient);
            _ClientsLastSeen.TryRemove(client.IpPort, out removedTs);
            _ClientsKicked.TryRemove(client.IpPort, out removedTs);
            _ClientsTimedout.TryRemove(client.IpPort, out removedTs);
            _UnauthenticatedClients.TryRemove(client.IpPort, out removedTs);

            Log(header + " disposing");
            client.Dispose();
        }
コード例 #27
0
        private async Task AcceptConnections()
        {
            Listener.Start();
            while (true)
            {
                #region Accept-Connection

                Token.ThrowIfCancellationRequested();
                TcpClient tcpClient = await Listener.AcceptTcpClientAsync();

                tcpClient.LingerState.Enabled = false;

                #endregion

                #region Get-Tuple-and-Check-IP

                string clientIp   = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
                int    clientPort = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Port;

                if (PermittedIps != null && PermittedIps.Count > 0)
                {
                    if (!PermittedIps.Contains(clientIp))
                    {
                        Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)");
                        tcpClient.Close();
                        return;
                    }
                }

                Log("AcceptConnections accepted connection from " + clientIp + ":" + clientPort);

                #endregion

                #region Initialize-and-Authenticate

                SslStream sslStream = null;
                if (AcceptInvalidCerts)
                {
                    // accept invalid certs
                    sslStream = new SslStream(tcpClient.GetStream(), false, new RemoteCertificateValidationCallback(AcceptCertificate));
                }
                else
                {
                    // do not accept invalid SSL certificates
                    sslStream = new SslStream(tcpClient.GetStream(), false);
                }

                sslStream.AuthenticateAsServer(SslCertificate, true, SslProtocols.Tls12, false);

                if (!sslStream.IsEncrypted)
                {
                    Log("*** AcceptConnections stream from " + clientIp + " not encrypted");
                    tcpClient.Close();
                    return;
                }

                if (!sslStream.IsAuthenticated)
                {
                    Log("*** AcceptConnections stream from " + clientIp + " not authenticated");
                    tcpClient.Close();
                    return;
                }

                if (MutuallyAuthenticate && !sslStream.IsMutuallyAuthenticated)
                {
                    Log("*** AcceptConnections stream from " + clientIp + " failed mutual authentication");
                    tcpClient.Close();
                    return;
                }

                #endregion

                var unawaited = Task.Run(() =>
                {
                    #region Add-to-Client-List

                    ActiveClients++;
                    // Do not decrement in this block, decrement is done by the connection reader

                    ClientMetadata currClient = new ClientMetadata(tcpClient, sslStream);
                    if (!AddClient(currClient))
                    {
                        Log("*** AcceptConnections unable to add client " + currClient.IpPort());
                        tcpClient.Close();
                        return;
                    }

                    #endregion

                    #region Start-Data-Receiver

                    CancellationToken dataReceiverToken = default(CancellationToken);

                    Log("AcceptConnections starting data receiver for " + currClient.IpPort() + " (now " + ActiveClients + " clients)");
                    if (ClientConnected != null)
                    {
                        Task.Run(() => ClientConnected(currClient.IpPort()));
                    }

                    Task.Run(async() => await DataReceiver(currClient, dataReceiverToken), dataReceiverToken);

                    #endregion
                }, Token);
            }
        }
コード例 #28
0
        private async Task AcceptConnections()
        {
            _Listener.Start();
            while (!_Token.IsCancellationRequested)
            {
                string clientIpPort = String.Empty;

                try
                {
                    #region Accept-Connection

                    TcpClient tcpClient = await _Listener.AcceptTcpClientAsync();

                    tcpClient.LingerState.Enabled = false;

                    #endregion

                    #region Get-Tuple-and-Check-IP

                    string clientIp = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();

                    if (_PermittedIps != null && _PermittedIps.Count > 0)
                    {
                        if (!_PermittedIps.Contains(clientIp))
                        {
                            Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)");
                            tcpClient.Close();
                            continue;
                        }
                    }

                    #endregion

                    ClientMetadata client = new ClientMetadata(tcpClient);
                    clientIpPort = client.IpPort;

                    Log("*** AcceptConnections accepted connection from " + client.IpPort);

                    Task unawaited = Task.Run(() => {
                        FinalizeConnection(client);
                    }, _Token);
                }
                catch (ObjectDisposedException ex)
                {
                    // Listener stopped ? if so, clientIpPort will be empty
                    Log("*** AcceptConnections ObjectDisposedException from " + clientIpPort + Environment.NewLine + ex.ToString());
                }
                catch (SocketException ex)
                {
                    switch (ex.Message)
                    {
                    case "An existing connection was forcibly closed by the remote host":
                        Log("*** AcceptConnections SocketException " + clientIpPort + " closed the connection.");
                        break;

                    default:
                        Log("*** AcceptConnections SocketException from " + clientIpPort + Environment.NewLine + ex.ToString());
                        break;
                    }
                }
                catch (Exception ex)
                {
                    Log("*** AcceptConnections Exception from " + clientIpPort + Environment.NewLine + ex.ToString());
                }
            }
        }