/// <summary> /// Disconnects the specified client. /// </summary> /// <param name="ipPort">IP:port of the client.</param> public void DisconnectClient(string ipPort) { if (String.IsNullOrEmpty(ipPort)) { throw new ArgumentNullException(nameof(ipPort)); } ClientMetadata client = null; if (!_Clients.TryGetValue(ipPort, out client)) { Logger?.Invoke(_Header + "unable to find client: " + ipPort); } else { if (!_ClientsTimedout.ContainsKey(ipPort)) { Logger?.Invoke(_Header + "kicking: " + ipPort); _ClientsKicked.TryAdd(ipPort, DateTime.Now); } } if (client != null) { if (!client.TokenSource.IsCancellationRequested) { client.TokenSource.Cancel(); Logger?.Invoke(_Header + "requesting disposal of: " + ipPort); } client.Dispose(); } }
private async Task SendInternalAsync(string ipPort, long contentLength, Stream stream) { ClientMetadata client = null; if (!_Clients.TryGetValue(ipPort, out client)) { return; } if (client == null) { return; } long bytesRemaining = contentLength; int bytesRead = 0; byte[] buffer = new byte[_StreamBufferSize]; try { await client.SendLock.WaitAsync(); while (bytesRemaining > 0) { bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); if (bytesRead > 0) { if (!_Ssl) { await client.NetworkStream.WriteAsync(buffer, 0, bytesRead); } else { await client.SslStream.WriteAsync(buffer, 0, bytesRead); } bytesRemaining -= bytesRead; _Stats.SentBytes += bytesRead; } } if (!_Ssl) { await client.NetworkStream.FlushAsync(); } else { await client.SslStream.FlushAsync(); } } finally { if (client != null) { client.SendLock.Release(); } } }
/// <summary> /// Determines if a client is connected by its IP:port. /// </summary> /// <param name="ipPort">The client IP:port string.</param> /// <returns>True if connected.</returns> public bool IsConnected(string ipPort) { if (String.IsNullOrEmpty(ipPort)) { throw new ArgumentNullException(nameof(ipPort)); } ClientMetadata client = null; return(_Clients.TryGetValue(ipPort, out client)); }
private async Task <byte[]> DataReadAsync(ClientMetadata client, CancellationToken token) { byte[] buffer = new byte[_Settings.StreamBufferSize]; int read = 0; if (!_Ssl) { using (MemoryStream ms = new MemoryStream()) { while (true) { read = await client.NetworkStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false); if (read > 0) { await ms.WriteAsync(buffer, 0, read, token).ConfigureAwait(false); return(ms.ToArray()); } else { throw new SocketException(); } } } } else { using (MemoryStream ms = new MemoryStream()) { while (true) { read = await client.SslStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false); if (read > 0) { await ms.WriteAsync(buffer, 0, read, token).ConfigureAwait(false); return(ms.ToArray()); } else { throw new SocketException(); } } } } }
private async Task <bool> StartTls(ClientMetadata client) { try { await client.SslStream.AuthenticateAsServerAsync( _SslCertificate, MutuallyAuthenticate, SslProtocols.Tls12, !AcceptInvalidCertificates); if (!client.SslStream.IsEncrypted) { Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " not encrypted, disconnecting"); client.Dispose(); return(false); } if (!client.SslStream.IsAuthenticated) { Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " not SSL/TLS authenticated, disconnecting"); client.Dispose(); return(false); } if (MutuallyAuthenticate && !client.SslStream.IsMutuallyAuthenticated) { Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " failed mutual authentication, disconnecting"); client.Dispose(); return(false); } } catch (Exception e) { Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " SSL/TLS exception: " + Environment.NewLine + e.ToString()); client.Dispose(); return(false); } return(true); }
private async Task <bool> StartTls(ClientMetadata client) { try { await client.SslStream.AuthenticateAsServerAsync( _SslCertificate, MutuallyAuthenticate, SslProtocols.Tls12, !AcceptInvalidCertificates); if (!client.SslStream.IsEncrypted) { Log("[" + client.IpPort + "] not encrypted"); client.Dispose(); return(false); } if (!client.SslStream.IsAuthenticated) { Log("[" + client.IpPort + "] stream not authenticated"); client.Dispose(); return(false); } if (MutuallyAuthenticate && !client.SslStream.IsMutuallyAuthenticated) { Log("[" + client.IpPort + "] failed mutual authentication"); client.Dispose(); return(false); } } catch (Exception e) { Log("[" + client.IpPort + "] TLS exception" + Environment.NewLine + e.ToString()); client.Dispose(); return(false); } return(true); }
/// <summary> /// Send data to the specified client by IP:port. /// </summary> /// <param name="ipPort">The client IP:port string.</param> /// <param name="data">Byte array containing data to send.</param> public void Send(string ipPort, byte[] data) { if (String.IsNullOrEmpty(ipPort)) { throw new ArgumentNullException(nameof(ipPort)); } if (data == null || data.Length < 1) { throw new ArgumentNullException(nameof(data)); } ClientMetadata client = null; if (!_Clients.TryGetValue(ipPort, out client)) { return; } if (client == null) { return; } lock (client.SendLock) { if (!_Ssl) { client.NetworkStream.Write(data, 0, data.Length); client.NetworkStream.Flush(); } else { client.SslStream.Write(data, 0, data.Length); client.SslStream.Flush(); } } _Stats.SentBytes += data.Length; }
private async Task <byte[]> DataReadAsync(ClientMetadata client) { if (client.Token.IsCancellationRequested) { throw new OperationCanceledException(); } if (!client.NetworkStream.CanRead) { return(null); } if (!client.NetworkStream.DataAvailable) { return(null); } if (_Ssl && !client.SslStream.CanRead) { return(null); } byte[] buffer = new byte[_StreamBufferSize]; int read = 0; if (!_Ssl) { using (MemoryStream ms = new MemoryStream()) { while (true) { read = await client.NetworkStream.ReadAsync(buffer, 0, buffer.Length); if (read > 0) { ms.Write(buffer, 0, read); return(ms.ToArray()); } else { throw new SocketException(); } } } } else { using (MemoryStream ms = new MemoryStream()) { while (true) { read = await client.SslStream.ReadAsync(buffer, 0, buffer.Length); if (read > 0) { ms.Write(buffer, 0, read); return(ms.ToArray()); } else { throw new SocketException(); } } } } }
private async Task DataReceiver(ClientMetadata client) { Logger?.Invoke("[SimpleTcp.Server] Data receiver started for client " + client.IpPort); while (true) { try { if (client.Token.IsCancellationRequested || !IsClientConnected(client.Client)) { Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " disconnected"); break; } if (client.Token.IsCancellationRequested) { Logger?.Invoke("[SimpleTcp.Server] Cancellation requested (data receiver for client " + client.IpPort + ")"); break; } byte[] data = await DataReadAsync(client); if (data == null) { await Task.Delay(30); continue; } DataReceived?.Invoke(this, new DataReceivedFromClientEventArgs(client.IpPort, data)); _Stats.ReceivedBytes += data.Length; UpdateClientLastSeen(client.IpPort); } catch (SocketException) { Logger?.Invoke("[SimpleTcp.Server] Data receiver socket exception (disconnection) for " + client.IpPort); } catch (Exception e) { Logger?.Invoke("[SimpleTcp.Server] Data receiver exception for client " + client.IpPort + ":" + Environment.NewLine + e.ToString() + Environment.NewLine); break; } } Logger?.Invoke("[SimpleTcp.Server] Data receiver terminated for client " + client.IpPort); if (_ClientsKicked.ContainsKey(client.IpPort)) { ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Kicked)); } else if (_ClientsTimedout.ContainsKey(client.IpPort)) { ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Timeout)); } else { ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Normal)); } DateTime removedTs; _Clients.TryRemove(client.IpPort, out ClientMetadata destroyed); _ClientsLastSeen.TryRemove(client.IpPort, out removedTs); _ClientsKicked.TryRemove(client.IpPort, out removedTs); _ClientsTimedout.TryRemove(client.IpPort, out removedTs); client.Dispose(); }
private async void AcceptConnections() { while (!_Token.IsCancellationRequested) { ClientMetadata client = null; try { System.Net.Sockets.TcpClient tcpClient = await _Listener.AcceptTcpClientAsync(); string clientIp = tcpClient.Client.RemoteEndPoint.ToString(); client = new ClientMetadata(tcpClient); if (_Ssl) { if (AcceptInvalidCertificates) { client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(AcceptCertificate)); } else { client.SslStream = new SslStream(client.NetworkStream, false); } bool success = await StartTls(client); if (!success) { client.Dispose(); continue; } } _Clients.TryAdd(clientIp, client); _ClientsLastSeen.TryAdd(clientIp, DateTime.Now); Logger?.Invoke("[SimpleTcp.Server] Starting data receiver for: " + clientIp); ClientConnected?.Invoke(this, new ClientConnectedEventArgs(clientIp)); Task unawaited = Task.Run(() => DataReceiver(client), _Token); } catch (OperationCanceledException) { return; } catch (ObjectDisposedException) { if (client != null) { client.Dispose(); } continue; } catch (Exception e) { if (client != null) { client.Dispose(); } Logger?.Invoke("[SimpleTcp.Server] Exception while awaiting connections: " + e.ToString()); continue; } } }
private async Task DataReceiver(ClientMetadata client) { Logger?.Invoke(_Header + "data receiver started for client " + client.IpPort, TCPLogType.Debug); while (true) { try { if (client.Token.IsCancellationRequested || !IsClientConnected(client.Client)) { Logger?.Invoke(_Header + "client " + client.IpPort + " disconnected", TCPLogType.Debug); break; } if (client.Token.IsCancellationRequested) { Logger?.Invoke(_Header + "cancellation requested (data receiver for client " + client.IpPort + ")", TCPLogType.Debug); break; } byte[] m_Buffer = await DataReadAsync(client); if (m_Buffer == null) { await Task.Delay(30); continue; } int BufferStart = 0; int nBytesRec = m_Buffer.Length; // We support the PROXY protocol (currently v1) // PROXY TCP4 192.168.0.37 192.168.0.121 57307 16248\r\n // PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n // \x0D \x0A \x0D \x0A \x00 \x0D \x0A \x51 \x55 \x49 \x54 \x0A // Min 32 bytes, max 108 bytes if (nBytesRec > 32 && m_Buffer[0] == 'P' && m_Buffer[1] == 'R' && m_Buffer[2] == 'O' && m_Buffer[3] == 'X' && m_Buffer[4] == 'Y' && m_Buffer[5] == ' ') { try { string msg = Encoding.UTF8.GetString(m_Buffer, 0, Math.Min(108, nBytesRec)); // 108 is the max we need to parse string[] proxy = msg.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); string[] proxyparts = proxy[0].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); client.OriginalIP = IPAddress.Parse(proxyparts[2]); client.OriginalSrcPort = int.Parse(proxyparts[4]); client.OriginalDestPort = int.Parse(proxyparts[5]); client.IsProxied = true; int split = msg.IndexOf("\r\n"); if (split > 32) { nBytesRec -= split + 2; BufferStart = split + 2; } } catch (Exception) { } } byte[] package = new byte[nBytesRec]; Buffer.BlockCopy(m_Buffer, BufferStart, package, 0, nBytesRec); var args = new DataReceivedFromClientEventArgs(client.IpPort, package, client.IncomingProtocol, client.Client.Client.RemoteEndPoint); args.Data = package; args.Protocol = client.IncomingProtocol; if (client.IsWebSocket) { args.WebSocketFrame = DecodeWebSocketMessage(package); if (args.WebSocketFrame.OpCode == 0x9) { package[0] = (byte)((package[0] & 0xF0) + 0xA); Send(client.IpPort, package); return; } } if (client.IsProxied) { args.OriginalIP = client.OriginalIP; args.OriginalSrcPort = client.OriginalSrcPort; args.OriginalDestPort = client.OriginalDestPort; } _Events.HandleDataReceived(this, args); _Statistics.ReceivedBytes += package.Length; UpdateClientLastSeen(client.IpPort); } catch (SocketException) { Logger?.Invoke(_Header + "data receiver socket exception (disconnection) for " + client.IpPort, TCPLogType.Debug); } catch (Exception e) { Logger?.Invoke(_Header + "data receiver exception for client " + client.IpPort + ":" + Environment.NewLine + e.ToString() + Environment.NewLine, TCPLogType.Warn); break; } } Logger?.Invoke(_Header + "data receiver terminated for client " + client.IpPort, TCPLogType.Debug); if (_ClientsKicked.ContainsKey(client.IpPort)) { _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Kicked)); } else if (_ClientsTimedout.ContainsKey(client.IpPort)) { _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Timeout)); } else { _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Normal)); } DateTime removedTs; _Clients.TryRemove(client.IpPort, out ClientMetadata destroyed); _ClientsLastSeen.TryRemove(client.IpPort, out removedTs); _ClientsKicked.TryRemove(client.IpPort, out removedTs); _ClientsTimedout.TryRemove(client.IpPort, out removedTs); client.Dispose(); }
private async Task DataReceiver(ClientMetadata client) { Logger?.Invoke(_Header + "data receiver started for client " + client.IpPort); CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_Token, client.Token); while (true) { try { if (!IsClientConnected(client.Client)) { Logger?.Invoke(_Header + "client " + client.IpPort + " disconnected"); break; } if (client.Token.IsCancellationRequested) { Logger?.Invoke(_Header + "cancellation requested (data receiver for client " + client.IpPort + ")"); break; } byte[] data = await DataReadAsync(client, linkedCts.Token).ConfigureAwait(false); if (data == null) { await Task.Delay(10).ConfigureAwait(false); continue; } Task unawaited = Task.Run(() => _Events.HandleDataReceived(this, new DataReceivedFromClientEventArgs(client.IpPort, data)), linkedCts.Token); _Statistics.ReceivedBytes += data.Length; UpdateClientLastSeen(client.IpPort); } catch (IOException) { Logger?.Invoke(_Header + "data receiver canceled, peer disconnected [" + client.IpPort + "]"); } catch (SocketException) { Logger?.Invoke(_Header + "data receiver canceled, peer disconnected [" + client.IpPort + "]"); } catch (TaskCanceledException) { Logger?.Invoke(_Header + "data receiver task canceled [" + client.IpPort + "]"); } catch (ObjectDisposedException) { Logger?.Invoke(_Header + "data receiver canceled due to disposal [" + client.IpPort + "]"); } catch (Exception e) { Logger?.Invoke(_Header + "data receiver exception [" + client.IpPort + "]:" + Environment.NewLine + e.ToString() + Environment.NewLine); break; } } Logger?.Invoke(_Header + "data receiver terminated for client " + client.IpPort); if (_ClientsKicked.ContainsKey(client.IpPort)) { _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Kicked)); } else if (_ClientsTimedout.ContainsKey(client.IpPort)) { _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Timeout)); } else { _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Normal)); } DateTime removedTs; _Clients.TryRemove(client.IpPort, out ClientMetadata destroyed); _ClientsLastSeen.TryRemove(client.IpPort, out removedTs); _ClientsKicked.TryRemove(client.IpPort, out removedTs); _ClientsTimedout.TryRemove(client.IpPort, out removedTs); client.Dispose(); }
private async Task AcceptConnections() { _IsListening = true; _Listener.Start(); while (!_Token.IsCancellationRequested) { ClientMetadata client = null; try { System.Net.Sockets.TcpClient tcpClient = await _Listener.AcceptTcpClientAsync().ConfigureAwait(false); string clientIp = tcpClient.Client.RemoteEndPoint.ToString(); client = new ClientMetadata(tcpClient); if (_Ssl) { if (_Settings.AcceptInvalidCertificates) { client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(AcceptCertificate)); } else { client.SslStream = new SslStream(client.NetworkStream, false); } bool success = await StartTls(client).ConfigureAwait(false); if (!success) { client.Dispose(); continue; } } _Clients.TryAdd(clientIp, client); _ClientsLastSeen.TryAdd(clientIp, DateTime.Now); Logger?.Invoke(_Header + "starting data receiver for: " + clientIp); _Events.HandleClientConnected(this, new ClientConnectedEventArgs(clientIp)); CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(client.Token, _Token); Task unawaited = Task.Run(() => DataReceiver(client), linkedCts.Token); } catch (TaskCanceledException) { _IsListening = false; if (client != null) { client.Dispose(); } return; } catch (OperationCanceledException) { _IsListening = false; if (client != null) { client.Dispose(); } return; } catch (ObjectDisposedException) { if (client != null) { client.Dispose(); } continue; } catch (Exception e) { if (client != null) { client.Dispose(); } Logger?.Invoke(_Header + "exception while awaiting connections: " + e.ToString()); continue; } } _IsListening = false; }
private async Task SendInternalAsync(string ipPort, long contentLength, Stream stream, CancellationToken token) { ClientMetadata client = null; try { if (!_Clients.TryGetValue(ipPort, out client)) { return; } if (client == null) { return; } long bytesRemaining = contentLength; int bytesRead = 0; byte[] buffer = new byte[_Settings.StreamBufferSize]; await client.SendLock.WaitAsync(token).ConfigureAwait(false); while (bytesRemaining > 0) { bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false); if (bytesRead > 0) { if (!_Ssl) { await client.NetworkStream.WriteAsync(buffer, 0, bytesRead, token).ConfigureAwait(false); } else { await client.SslStream.WriteAsync(buffer, 0, bytesRead, token).ConfigureAwait(false); } bytesRemaining -= bytesRead; _Statistics.SentBytes += bytesRead; } } if (!_Ssl) { await client.NetworkStream.FlushAsync(token).ConfigureAwait(false); } else { await client.SslStream.FlushAsync(token).ConfigureAwait(false); } } catch (TaskCanceledException) { } catch (OperationCanceledException) { } finally { if (client != null) { client.SendLock.Release(); } } }
private async Task DataReceiver(ClientMetadata client) { string header = "[" + client.IpPort + "]"; Log(header + " data receiver started"); while (true) { try { if (!IsClientConnected(client.Client)) { Log(header + " client no longer connected"); break; } if (client.Token.IsCancellationRequested) { Log(header + " cancellation requested"); break; } byte[] data = await DataReadAsync(client); if (data == null) { await Task.Delay(30); continue; } if (DataReceived != null) { Task unawaited = Task.Run(() => DataReceived(client.IpPort, data)); } UpdateClientLastSeen(client.IpPort); } catch (Exception e) { Log( Environment.NewLine + header + " 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 destroyed); _ClientsLastSeen.TryRemove(client.IpPort, out removedTs); _ClientsKicked.TryRemove(client.IpPort, out removedTs); _ClientsTimedout.TryRemove(client.IpPort, out removedTs); client.Dispose(); }
private async void AcceptConnections() { while (!_Token.IsCancellationRequested) { ClientMetadata client = null; try { System.Net.Sockets.TcpClient tcpClient = await _Listener.AcceptTcpClientAsync(); string clientIp = tcpClient.Client.RemoteEndPoint.ToString(); client = new ClientMetadata(tcpClient); if (_Ssl) { if (AcceptInvalidCertificates) { client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(AcceptCertificate)); } else { client.SslStream = new SslStream(client.NetworkStream, false); } bool success = await StartTls(client); if (!success) { client.Dispose(); continue; } } _Clients.TryAdd(clientIp, client); _ClientsLastSeen.TryAdd(clientIp, DateTime.Now); Log("[" + clientIp + "] starting data receiver"); if (ClientConnected != null) { await Task.Run(() => ClientConnected(clientIp)); } Task dataRecv = Task.Run(() => DataReceiver(client), _Token); } catch (OperationCanceledException) { return; } catch (ObjectDisposedException) { if (client != null) { client.Dispose(); } continue; } catch (Exception e) { if (client != null) { client.Dispose(); } Log("*** AcceptConnections exception: " + e.ToString()); continue; } finally { } } }