From f84f088fef8a9ed4e594a1325f62de1733b8f598 Mon Sep 17 00:00:00 2001 From: ArsalanH Date: Thu, 24 Apr 2025 21:07:46 +0330 Subject: [PATCH 1/4] add: AllowAnanymousIps to server settings in order to prevent ananymous connections even if PermittedIps is null or empty --- src/SuperSimpleTcp/SimpleTcpServer.cs | 2 +- src/SuperSimpleTcp/SimpleTcpServerSettings.cs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/SuperSimpleTcp/SimpleTcpServer.cs b/src/SuperSimpleTcp/SimpleTcpServer.cs index d21e3ed..7087529 100644 --- a/src/SuperSimpleTcp/SimpleTcpServer.cs +++ b/src/SuperSimpleTcp/SimpleTcpServer.cs @@ -775,7 +775,7 @@ private async Task AcceptConnections() int clientPort = 0; Common.ParseIpPort(clientIpPort, out clientIp, out clientPort); - if (_settings.PermittedIPs.Count > 0 && !_settings.PermittedIPs.Contains(clientIp)) + if (!_settings.AllowAnanymousIPs && !_settings.PermittedIPs.Contains(clientIp)) { Logger?.Invoke($"{_header}rejecting connection from {clientIp} (not permitted)"); tcpClient.Close(); diff --git a/src/SuperSimpleTcp/SimpleTcpServerSettings.cs b/src/SuperSimpleTcp/SimpleTcpServerSettings.cs index d8068b8..e951048 100644 --- a/src/SuperSimpleTcp/SimpleTcpServerSettings.cs +++ b/src/SuperSimpleTcp/SimpleTcpServerSettings.cs @@ -155,6 +155,18 @@ public List BlockedIPs } } + public bool AllowAnanymousIPs + { + get + { + return _allowAnanimousIPs; + } + set + { + _allowAnanimousIPs = value; + } + } + #endregion #region Private-Members @@ -166,6 +178,7 @@ public List BlockedIPs private int _idleClientEvaluationIntervalMs = 5000; private List _permittedIPs = new List(); private List _blockedIPs = new List(); + private bool _allowAnanimousIPs = true; #endregion From ef915e924e7bca1887b02c29529ced7546bd4b72 Mon Sep 17 00:00:00 2001 From: ArsalanH Date: Thu, 24 Apr 2025 21:20:55 +0330 Subject: [PATCH 2/4] rename: wrong spelling of 'anonymous' --- src/SuperSimpleTcp/SimpleTcpServer.cs | 2 +- src/SuperSimpleTcp/SimpleTcpServerSettings.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/SuperSimpleTcp/SimpleTcpServer.cs b/src/SuperSimpleTcp/SimpleTcpServer.cs index 7087529..51d6522 100644 --- a/src/SuperSimpleTcp/SimpleTcpServer.cs +++ b/src/SuperSimpleTcp/SimpleTcpServer.cs @@ -775,7 +775,7 @@ private async Task AcceptConnections() int clientPort = 0; Common.ParseIpPort(clientIpPort, out clientIp, out clientPort); - if (!_settings.AllowAnanymousIPs && !_settings.PermittedIPs.Contains(clientIp)) + if (!_settings.AllowAnonymousIPs && !_settings.PermittedIPs.Contains(clientIp)) { Logger?.Invoke($"{_header}rejecting connection from {clientIp} (not permitted)"); tcpClient.Close(); diff --git a/src/SuperSimpleTcp/SimpleTcpServerSettings.cs b/src/SuperSimpleTcp/SimpleTcpServerSettings.cs index e951048..695df64 100644 --- a/src/SuperSimpleTcp/SimpleTcpServerSettings.cs +++ b/src/SuperSimpleTcp/SimpleTcpServerSettings.cs @@ -155,15 +155,15 @@ public List BlockedIPs } } - public bool AllowAnanymousIPs + public bool AllowAnonymousIPs { get { - return _allowAnanimousIPs; + return _allowAnonymousIPs; } set { - _allowAnanimousIPs = value; + _allowAnonymousIPs = value; } } @@ -178,7 +178,7 @@ public bool AllowAnanymousIPs private int _idleClientEvaluationIntervalMs = 5000; private List _permittedIPs = new List(); private List _blockedIPs = new List(); - private bool _allowAnanimousIPs = true; + private bool _allowAnonymousIPs = true; #endregion From 67f5291e8f0bbb3425677e0b313558173969f14e Mon Sep 17 00:00:00 2001 From: ArsalanH Date: Sat, 26 Apr 2025 09:57:57 +0330 Subject: [PATCH 3/4] add: description comment on the AllowAnonymousIPs flag --- src/SuperSimpleTcp/SimpleTcpServerSettings.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/SuperSimpleTcp/SimpleTcpServerSettings.cs b/src/SuperSimpleTcp/SimpleTcpServerSettings.cs index 695df64..54f88b1 100644 --- a/src/SuperSimpleTcp/SimpleTcpServerSettings.cs +++ b/src/SuperSimpleTcp/SimpleTcpServerSettings.cs @@ -155,6 +155,9 @@ public List BlockedIPs } } + /// + /// The AllowAnonymousIPs flag to know if permitted IPs list should be checked or not. + /// public bool AllowAnonymousIPs { get From 4c3940ef90ca5927fc7858aaafc4e4cdc4440564 Mon Sep 17 00:00:00 2001 From: ArsalanH Date: Tue, 29 Apr 2025 13:00:07 +0330 Subject: [PATCH 4/4] fix: the PermittedIPs list will work as it was for old users and to use with AllowAnonymous flag you need to change the setting PermitType property --- src/SuperSimpleTcp/PermitType.cs | 20 +++++ src/SuperSimpleTcp/SimpleTcpServer.cs | 84 ++++++++++--------- src/SuperSimpleTcp/SimpleTcpServerSettings.cs | 16 ++++ src/SuperSimpleTcp/SuperSimpleTcp.xml | 26 ++++++ 4 files changed, 108 insertions(+), 38 deletions(-) create mode 100644 src/SuperSimpleTcp/PermitType.cs diff --git a/src/SuperSimpleTcp/PermitType.cs b/src/SuperSimpleTcp/PermitType.cs new file mode 100644 index 0000000..ee15a7e --- /dev/null +++ b/src/SuperSimpleTcp/PermitType.cs @@ -0,0 +1,20 @@ + +namespace SuperSimpleTcp +{ + /// + /// PermitType's used to clarify the way to validate permitted IPs + /// + public enum PermitType : byte + { + /// + /// Only PermittedList is used when only the PermittedIPs list decides + /// + OnlyPermittedList = 0, + + /// + /// FlagAndPermittedList is used to check the flag AllowAnonymousIPs first + /// and if the flag was false then checks the PermittedIPs list + /// + FlagAndPermittedList = 1 + } +} \ No newline at end of file diff --git a/src/SuperSimpleTcp/SimpleTcpServer.cs b/src/SuperSimpleTcp/SimpleTcpServer.cs index 51d6522..16f3b10 100644 --- a/src/SuperSimpleTcp/SimpleTcpServer.cs +++ b/src/SuperSimpleTcp/SimpleTcpServer.cs @@ -206,7 +206,7 @@ public SimpleTcpServer(string ipPort) { _ipAddress = Dns.GetHostEntry(_listenerIp).AddressList[0]; _listenerIp = _ipAddress.ToString(); - } + } } _isListening = false; @@ -236,16 +236,16 @@ public SimpleTcpServer(string listenerIp, int port) _listenerIp = listenerIp; } else - { + { if (!IPAddress.TryParse(_listenerIp, out _ipAddress)) { _ipAddress = Dns.GetHostEntry(listenerIp).AddressList[0]; _listenerIp = _ipAddress.ToString(); - } + } } - + _isListening = false; - _token = _tokenSource.Token; + _token = _tokenSource.Token; } /// @@ -299,7 +299,7 @@ public SimpleTcpServer(string ipPort, bool ssl, string pfxCertFilename, string p { _sslCertificate }; - } + } } /// @@ -311,7 +311,7 @@ public SimpleTcpServer(string ipPort, bool ssl, string pfxCertFilename, string p /// The filename of the PFX certificate file. /// The password to the PFX certificate file. public SimpleTcpServer(string listenerIp, int port, bool ssl, string pfxCertFilename, string pfxPassword) - { + { if (port < 0) throw new ArgumentException("Port must be zero or greater."); _listenerIp = listenerIp; @@ -324,7 +324,7 @@ public SimpleTcpServer(string listenerIp, int port, bool ssl, string pfxCertFile } else if (_listenerIp == "*" || _listenerIp == "+") { - _ipAddress = IPAddress.Any; + _ipAddress = IPAddress.Any; } else { @@ -334,7 +334,7 @@ public SimpleTcpServer(string listenerIp, int port, bool ssl, string pfxCertFile _listenerIp = _ipAddress.ToString(); } } - + _ssl = ssl; _isListening = false; _token = _tokenSource.Token; @@ -354,7 +354,7 @@ public SimpleTcpServer(string listenerIp, int port, bool ssl, string pfxCertFile { _sslCertificate }; - } + } } /// @@ -431,7 +431,7 @@ public void Start() _listenerToken = _listenerTokenSource.Token; _statistics = new SimpleTcpStatistics(); - + if (_idleClientMonitor == null) { _idleClientMonitor = Task.Run(() => IdleClientMonitor(), _token); @@ -673,7 +673,7 @@ protected virtual void Dispose(bool disposing) { curr.Value.Dispose(); Logger?.Invoke($"{_header}disconnected client: {curr.Key}"); - } + } } if (_tokenSource != null) @@ -707,7 +707,7 @@ protected virtual void Dispose(bool disposing) Logger?.Invoke($"{_header}disposed"); } } - + private bool IsClientConnected(TcpClient client) { if (client == null) return false; @@ -775,7 +775,15 @@ private async Task AcceptConnections() int clientPort = 0; Common.ParseIpPort(clientIpPort, out clientIp, out clientPort); - if (!_settings.AllowAnonymousIPs && !_settings.PermittedIPs.Contains(clientIp)) + if (Settings.PermitType == PermitType.OnlyPermittedList + && (_settings.PermittedIPs.Count > 0 && !_settings.PermittedIPs.Contains(clientIp))) + { + Logger?.Invoke($"{_header}rejecting connection from {clientIp} (not permitted)"); + tcpClient.Close(); + continue; + + } + else if (!_settings.AllowAnonymousIPs && !_settings.PermittedIPs.Contains(clientIp)) { Logger?.Invoke($"{_header}rejecting connection from {clientIp} (not permitted)"); tcpClient.Close(); @@ -797,7 +805,7 @@ private async Task AcceptConnections() { client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(AcceptCertificate)); } - else if(_settings.CertificateValidationCallback != null) + else if (_settings.CertificateValidationCallback != null) { client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(_settings.CertificateValidationCallback)); } @@ -927,7 +935,7 @@ private async Task DataReceiver(ClientMetadata client) while (true) { try - { + { if (!IsClientConnected(client.Client)) { Logger?.Invoke($"{_header}client {ipPort} disconnected"); @@ -938,7 +946,7 @@ private async Task DataReceiver(ClientMetadata client) { Logger?.Invoke($"{_header}cancellation requested (data receiver for client {ipPort})"); break; - } + } var data = await DataReadAsync(client, linkedCts.Token).ConfigureAwait(false); if (data == null) @@ -982,7 +990,7 @@ private async Task DataReceiver(ClientMetadata client) } catch (Exception e) { - Logger?.Invoke($"{_header}data receiver exception [{ipPort}]:{ Environment.NewLine}{e}{Environment.NewLine}"); + Logger?.Invoke($"{_header}data receiver exception [{ipPort}]:{Environment.NewLine}{e}{Environment.NewLine}"); break; } } @@ -1005,13 +1013,13 @@ private async Task DataReceiver(ClientMetadata client) _clients.TryRemove(ipPort, out _); _clientsLastSeen.TryRemove(ipPort, out _); _clientsKicked.TryRemove(ipPort, out _); - _clientsTimedout.TryRemove(ipPort, out _); + _clientsTimedout.TryRemove(ipPort, out _); if (client != null) client.Dispose(); } - + private async Task> DataReadAsync(ClientMetadata client, CancellationToken token) - { + { byte[] buffer = new byte[_settings.StreamBufferSize]; int read = 0; @@ -1054,23 +1062,23 @@ private async Task> DataReadAsync(ClientMetadata client, Canc } } } - } + } } private async Task IdleClientMonitor() { while (!_token.IsCancellationRequested) - { + { await Task.Delay(_settings.IdleClientEvaluationIntervalMs, _token).ConfigureAwait(false); if (_settings.IdleClientTimeoutMs == 0) continue; try - { + { DateTime idleTimestamp = DateTime.Now.AddMilliseconds(-1 * _settings.IdleClientTimeoutMs); foreach (KeyValuePair curr in _clientsLastSeen) - { + { if (curr.Value < idleTimestamp) { _clientsTimedout.TryAdd(curr.Key, DateTime.Now); @@ -1085,14 +1093,14 @@ private async Task IdleClientMonitor() } } } - + private void UpdateClientLastSeen(string ipPort) { if (_clientsLastSeen.ContainsKey(ipPort)) { _clientsLastSeen.TryRemove(ipPort, out _); } - + _clientsLastSeen.TryAdd(ipPort, DateTime.Now); } @@ -1114,8 +1122,8 @@ private void SendInternal(string ipPort, long contentLength, Stream stream) bytesRead = stream.Read(buffer, 0, buffer.Length); if (bytesRead > 0) { - if (!_ssl) client.NetworkStream.Write(buffer, 0, bytesRead); - else client.SslStream.Write(buffer, 0, bytesRead); + if (!_ssl) client.NetworkStream.Write(buffer, 0, bytesRead); + else client.SslStream.Write(buffer, 0, bytesRead); bytesRemaining -= bytesRead; _statistics.SentBytes += bytesRead; @@ -1195,19 +1203,19 @@ private void EnableKeepalives() #elif NETFRAMEWORK - byte[] keepAlive = new byte[12]; + byte[] keepAlive = new byte[12]; - // Turn keepalive on - Buffer.BlockCopy(BitConverter.GetBytes((uint)1), 0, keepAlive, 0, 4); + // Turn keepalive on + Buffer.BlockCopy(BitConverter.GetBytes((uint)1), 0, keepAlive, 0, 4); - // Set TCP keepalive time - Buffer.BlockCopy(BitConverter.GetBytes((uint)_keepalive.TcpKeepAliveTimeMilliseconds), 0, keepAlive, 4, 4); + // Set TCP keepalive time + Buffer.BlockCopy(BitConverter.GetBytes((uint)_keepalive.TcpKeepAliveTimeMilliseconds), 0, keepAlive, 4, 4); - // Set TCP keepalive interval - Buffer.BlockCopy(BitConverter.GetBytes((uint)_keepalive.TcpKeepAliveIntervalMilliseconds), 0, keepAlive, 8, 4); + // Set TCP keepalive interval + Buffer.BlockCopy(BitConverter.GetBytes((uint)_keepalive.TcpKeepAliveIntervalMilliseconds), 0, keepAlive, 8, 4); - // Set keepalive settings on the underlying Socket - _listener.Server.IOControl(IOControlCode.KeepAliveValues, keepAlive, null); + // Set keepalive settings on the underlying Socket + _listener.Server.IOControl(IOControlCode.KeepAliveValues, keepAlive, null); #elif NETSTANDARD diff --git a/src/SuperSimpleTcp/SimpleTcpServerSettings.cs b/src/SuperSimpleTcp/SimpleTcpServerSettings.cs index 54f88b1..0b2aa78 100644 --- a/src/SuperSimpleTcp/SimpleTcpServerSettings.cs +++ b/src/SuperSimpleTcp/SimpleTcpServerSettings.cs @@ -155,6 +155,21 @@ public List BlockedIPs } } + /// + /// This property indicates which way to validate IPs + /// + public PermitType PermitType + { + get + { + return this._permitType; + } + set + { + this._permitType = value; + } + } + /// /// The AllowAnonymousIPs flag to know if permitted IPs list should be checked or not. /// @@ -181,6 +196,7 @@ public bool AllowAnonymousIPs private int _idleClientEvaluationIntervalMs = 5000; private List _permittedIPs = new List(); private List _blockedIPs = new List(); + private PermitType _permitType = PermitType.OnlyPermittedList; private bool _allowAnonymousIPs = true; #endregion diff --git a/src/SuperSimpleTcp/SuperSimpleTcp.xml b/src/SuperSimpleTcp/SuperSimpleTcp.xml index cece589..b5c9610 100644 --- a/src/SuperSimpleTcp/SuperSimpleTcp.xml +++ b/src/SuperSimpleTcp/SuperSimpleTcp.xml @@ -74,6 +74,22 @@ The connection was not disconnected. + + + PermitType's used to clarify the way to validate permitted IPs + + + + + Only PermittedList is used when only the PermittedIPs list decides + + + + + FlagAndPermittedList is used to check the flag AllowAnonymousIPs first + and if the flag was false then checks the PermittedIPs list + + SimpleTcp client with SSL support. @@ -760,6 +776,16 @@ The list of blocked IP addresses from which connections will be declined. + + + This property indicates in which way validate IPs + + + + + The AllowAnonymousIPs flag to know if permitted IPs list should be checked or not. + + Instantiate the object.