diff --git a/EonaCat.Connections.Client/Program.cs b/EonaCat.Connections.Client/Program.cs
index 7dfdaf6..05041f5 100644
--- a/EonaCat.Connections.Client/Program.cs
+++ b/EonaCat.Connections.Client/Program.cs
@@ -1,27 +1,27 @@
-using EonaCat.Connections;
-using EonaCat.Connections.Models;
-using System.Text;
-
-namespace EonaCat.Connections.Client.Example
-{
- public class Program
- {
- private static NetworkClient _client;
-
- public static async Task Main(string[] args)
- {
- await CreateClientAsync().ConfigureAwait(false);
-
- while (true)
- {
- Console.Write("Enter message to send (or 'exit' to quit): ");
- var message = Console.ReadLine();
- if (!string.IsNullOrEmpty(message) && message.Equals("exit", StringComparison.OrdinalIgnoreCase))
- {
- await _client.DisconnectAsync().ConfigureAwait(false);
- break;
- }
-
+using EonaCat.Connections;
+using EonaCat.Connections.Models;
+using System.Text;
+
+namespace EonaCat.Connections.Client.Example
+{
+ public class Program
+ {
+ private static NetworkClient _client;
+
+ public static async Task Main(string[] args)
+ {
+ await CreateClientAsync().ConfigureAwait(false);
+
+ while (true)
+ {
+ Console.Write("Enter message to send (or 'exit' to quit): ");
+ var message = Console.ReadLine();
+ if (!string.IsNullOrEmpty(message) && message.Equals("exit", StringComparison.OrdinalIgnoreCase))
+ {
+ await _client.DisconnectAsync().ConfigureAwait(false);
+ break;
+ }
+
var jsonUrl = "https://microsoftedge.github.io/Demos/json-dummy-data/5MB-min.json";
try
@@ -35,46 +35,47 @@ namespace EonaCat.Connections.Client.Example
catch (Exception ex)
{
Console.WriteLine($"Failed to download large JSON file: {ex.Message}");
- }
-
- if (!string.IsNullOrEmpty(message))
- {
- await _client.SendAsync(message).ConfigureAwait(false);
- }
- }
- }
-
- private static async Task CreateClientAsync()
- {
- var config = new Configuration
- {
- Protocol = ProtocolType.TCP,
- Host = "127.0.0.1",
- Port = 1111,
- UseSsl = false,
- UseAesEncryption = false,
- //ServerCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2("client.pfx", "p@ss"),
- };
-
- _client = new NetworkClient(config);
-
- // Subscribe to events
- _client.OnConnected += (sender, e) =>
- Console.WriteLine($"Connected to server at {e.RemoteEndPoint}");
-
- _client.OnDataReceived += (sender, e) =>
- Console.WriteLine($"Server says: {(e.IsBinary ? $"{e.Data.Length} bytes" : e.StringData)}");
-
- _client.OnDisconnected += (sender, e) =>
- Console.WriteLine("Disconnected from server");
-
- await _client.ConnectAsync();
-
- // Send nickname
- await _client.SendNicknameAsync("TestUser");
-
- // Send a message
- await _client.SendAsync("Hello server!");
- }
- }
+ }
+
+ if (!string.IsNullOrEmpty(message))
+ {
+ await _client.SendAsync(message).ConfigureAwait(false);
+ }
+ }
+ }
+
+ private static async Task CreateClientAsync()
+ {
+ var config = new Configuration
+ {
+ Protocol = ProtocolType.TCP,
+ Host = "127.0.0.1",
+ Port = 1111,
+ UseSsl = false,
+ UseAesEncryption = true,
+ AesPassword = "p@ss",
+ //ServerCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2("client.pfx", "p@ss"),
+ };
+
+ _client = new NetworkClient(config);
+
+ // Subscribe to events
+ _client.OnConnected += (sender, e) =>
+ Console.WriteLine($"Connected to server at {e.RemoteEndPoint}");
+
+ _client.OnDataReceived += (sender, e) =>
+ Console.WriteLine($"Server says: {(e.IsBinary ? $"{e.Data.Length} bytes" : e.StringData)}");
+
+ _client.OnDisconnected += (sender, e) =>
+ Console.WriteLine("Disconnected from server");
+
+ await _client.ConnectAsync();
+
+ // Send nickname
+ await _client.SendNicknameAsync("TestUser");
+
+ // Send a message
+ await _client.SendAsync("Hello server!");
+ }
+ }
}
\ No newline at end of file
diff --git a/EonaCat.Connections.Server/Program.cs b/EonaCat.Connections.Server/Program.cs
index 98c32be..747cd82 100644
--- a/EonaCat.Connections.Server/Program.cs
+++ b/EonaCat.Connections.Server/Program.cs
@@ -37,8 +37,9 @@ namespace EonaCat.Connections.Server.Example
Protocol = ProtocolType.TCP,
Port = 1111,
UseSsl = false,
- UseAesEncryption = false,
+ UseAesEncryption = true,
MaxConnections = 100000,
+ AesPassword = "p@ss",
//ServerCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2("server.pfx", "p@ss"),
};
diff --git a/EonaCat.Connections/Helpers/AesKeyExchange.cs b/EonaCat.Connections/Helpers/AesKeyExchange.cs
index 80e8026..80ea79f 100644
--- a/EonaCat.Connections/Helpers/AesKeyExchange.cs
+++ b/EonaCat.Connections/Helpers/AesKeyExchange.cs
@@ -13,7 +13,7 @@ namespace EonaCat.Connections.Helpers
///
///
///
- public static async Task SendAesKeyAsync(Stream stream, Aes aes)
+ public static async Task SendAesKeyAsync(Stream stream, Aes aes, string password = null)
{
var rawKey = aes.Key;
var iv = aes.IV;
@@ -29,8 +29,12 @@ namespace EonaCat.Connections.Helpers
await WriteBytesWithLengthAsync(stream, salt);
await stream.FlushAsync();
- // Derive stronger key using PBKDF2-SHA256 + salt + pepper
- var derivedKey = PBKDF2_SHA256(Combine(rawKey, Encoding.UTF8.GetBytes(Pepper)), salt, 100_000, 32);
+ // Derive key using PBKDF2-SHA256 + salt + password + pepper
+ if (string.IsNullOrEmpty(password))
+ {
+ password = "EonaCat.Connections";
+ }
+ var derivedKey = PBKDF2_SHA256(Combine(Combine(rawKey, Encoding.UTF8.GetBytes(password)), Encoding.UTF8.GetBytes(Pepper)), salt, 100_000, 32);
aes.Key = derivedKey;
return aes;
@@ -41,13 +45,19 @@ namespace EonaCat.Connections.Helpers
///
///
///
- public static async Task ReceiveAesKeyAsync(Stream stream)
+ public static async Task ReceiveAesKeyAsync(Stream stream, string password = null)
{
var rawKey = await ReadBytesWithLengthAsync(stream);
var iv = await ReadBytesWithLengthAsync(stream);
var salt = await ReadBytesWithLengthAsync(stream);
- var derivedKey = PBKDF2_SHA256(Combine(rawKey, Encoding.UTF8.GetBytes(Pepper)), salt, 100_000, 32);
+ if (string.IsNullOrEmpty(password))
+ {
+ password = "EonaCat.Connections";
+ }
+
+ // Derived key using PBKDF2-SHA256 + salt + password + pepper
+ var derivedKey = PBKDF2_SHA256(Combine(Combine(rawKey, Encoding.UTF8.GetBytes(password)), Encoding.UTF8.GetBytes(Pepper)), salt, 100_000, 32);
Aes _aesEncryption = Aes.Create();
_aesEncryption.Key = derivedKey;
diff --git a/EonaCat.Connections/Models/Configuration.cs b/EonaCat.Connections/Models/Configuration.cs
index 51c623d..54ee2be 100644
--- a/EonaCat.Connections/Models/Configuration.cs
+++ b/EonaCat.Connections/Models/Configuration.cs
@@ -22,6 +22,7 @@ namespace EonaCat.Connections.Models
public bool EnableNagle { get; set; } = false;
// For testing purposes, allow self-signed certificates
- public bool IsSelfSignedEnabled { get; set; } = true;
+ public bool IsSelfSignedEnabled { get; set; } = true;
+ public string AesPassword { get; set; }
}
}
\ No newline at end of file
diff --git a/EonaCat.Connections/NetworkClient.cs b/EonaCat.Connections/NetworkClient.cs
index 2936634..d80fb02 100644
--- a/EonaCat.Connections/NetworkClient.cs
+++ b/EonaCat.Connections/NetworkClient.cs
@@ -1,148 +1,148 @@
-using EonaCat.Connections.EventArguments;
-using EonaCat.Connections.Helpers;
-using EonaCat.Connections.Models;
-using System.Collections.Concurrent;
-using System.Net;
-using System.Net.Security;
-using System.Net.Sockets;
-using System.Security.Authentication;
-using System.Security.Cryptography;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
-using ErrorEventArgs = EonaCat.Connections.EventArguments.ErrorEventArgs;
-
-namespace EonaCat.Connections
-{
- public class NetworkClient
- {
- private readonly Configuration _config;
- private TcpClient _tcpClient;
- private UdpClient _udpClient;
- private Stream _stream;
- private Aes _aesEncryption;
- private CancellationTokenSource _cancellation;
- private bool _isConnected;
-
- public event EventHandler OnConnected;
- public event EventHandler OnDataReceived;
- public event EventHandler OnDisconnected;
- public event EventHandler OnSslError;
- public event EventHandler OnEncryptionError;
- public event EventHandler OnGeneralError;
-
- public NetworkClient(Configuration config)
- {
- _config = config;
- }
-
- public async Task ConnectAsync()
- {
- _cancellation = new CancellationTokenSource();
-
- if (_config.Protocol == ProtocolType.TCP)
- {
- await ConnectTcpAsync();
- }
- else
- {
- await ConnectUdp();
- }
- }
-
- private async Task ConnectTcpAsync()
- {
- try
- {
- _tcpClient = new TcpClient();
- await _tcpClient.ConnectAsync(_config.Host, _config.Port);
-
- Stream stream = _tcpClient.GetStream();
-
- // Setup SSL if required
- if (_config.UseSsl)
- {
- try
- {
- var sslStream = new SslStream(stream, false, userCertificateValidationCallback:RemoteCertificateValidationCallback);
- await sslStream.AuthenticateAsClientAsync(_config.Host);
- stream = sslStream;
- }
- catch (Exception ex)
- {
- OnSslError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "SSL authentication failed" });
- return;
- }
- }
-
- // Setup AES encryption if required
- if (_config.UseAesEncryption)
- {
- try
- {
- _aesEncryption = await AesKeyExchange.ReceiveAesKeyAsync(stream);
- }
- catch (Exception ex)
- {
- OnEncryptionError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "AES setup failed" });
- return;
- }
- }
-
- _stream = stream;
- _isConnected = true;
-
- OnConnected?.Invoke(this, new ConnectionEventArgs { ClientId = "self", RemoteEndPoint = new IPEndPoint(IPAddress.Parse(_config.Host), _config.Port) });
-
- // Start receiving data
- _ = Task.Run(() => ReceiveDataAsync(), _cancellation.Token);
- }
- catch (Exception ex)
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Failed to connect" });
- }
- }
-
- private bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
- {
- if (_config.IsSelfSignedEnabled)
- {
- return true; // Accept self-signed certificates
- }
-
- if (sslPolicyErrors == SslPolicyErrors.None)
- {
- return true; // Certificate is valid
- }
-
- // Log or handle the SSL error as needed
- OnSslError?.Invoke(this, new ErrorEventArgs
- {
- Exception = new AuthenticationException("SSL certificate validation failed"),
- Message = $"SSL Policy Errors: {sslPolicyErrors}"
- });
- return false; // Reject the certificate
- }
-
- private async Task ConnectUdp()
- {
- try
- {
- _udpClient = new UdpClient();
- _udpClient.Connect(_config.Host, _config.Port);
- _isConnected = true;
-
- OnConnected?.Invoke(this, new ConnectionEventArgs { ClientId = "self", RemoteEndPoint = new IPEndPoint(IPAddress.Parse(_config.Host), _config.Port) });
-
- // Start receiving data
- _ = Task.Run(() => ReceiveUdpDataAsync(), _cancellation.Token);
- }
- catch (Exception ex)
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Failed to connect UDP" });
- }
- }
-
-
+using EonaCat.Connections.EventArguments;
+using EonaCat.Connections.Helpers;
+using EonaCat.Connections.Models;
+using System.Collections.Concurrent;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using ErrorEventArgs = EonaCat.Connections.EventArguments.ErrorEventArgs;
+
+namespace EonaCat.Connections
+{
+ public class NetworkClient
+ {
+ private readonly Configuration _config;
+ private TcpClient _tcpClient;
+ private UdpClient _udpClient;
+ private Stream _stream;
+ private Aes _aesEncryption;
+ private CancellationTokenSource _cancellation;
+ private bool _isConnected;
+
+ public event EventHandler OnConnected;
+ public event EventHandler OnDataReceived;
+ public event EventHandler OnDisconnected;
+ public event EventHandler OnSslError;
+ public event EventHandler OnEncryptionError;
+ public event EventHandler OnGeneralError;
+
+ public NetworkClient(Configuration config)
+ {
+ _config = config;
+ }
+
+ public async Task ConnectAsync()
+ {
+ _cancellation = new CancellationTokenSource();
+
+ if (_config.Protocol == ProtocolType.TCP)
+ {
+ await ConnectTcpAsync();
+ }
+ else
+ {
+ await ConnectUdp();
+ }
+ }
+
+ private async Task ConnectTcpAsync()
+ {
+ try
+ {
+ _tcpClient = new TcpClient();
+ await _tcpClient.ConnectAsync(_config.Host, _config.Port);
+
+ Stream stream = _tcpClient.GetStream();
+
+ // Setup SSL if required
+ if (_config.UseSsl)
+ {
+ try
+ {
+ var sslStream = new SslStream(stream, false, userCertificateValidationCallback:RemoteCertificateValidationCallback);
+ await sslStream.AuthenticateAsClientAsync(_config.Host);
+ stream = sslStream;
+ }
+ catch (Exception ex)
+ {
+ OnSslError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "SSL authentication failed" });
+ return;
+ }
+ }
+
+ // Setup AES encryption if required
+ if (_config.UseAesEncryption)
+ {
+ try
+ {
+ _aesEncryption = await AesKeyExchange.ReceiveAesKeyAsync(stream, _config.AesPassword);
+ }
+ catch (Exception ex)
+ {
+ OnEncryptionError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "AES setup failed" });
+ return;
+ }
+ }
+
+ _stream = stream;
+ _isConnected = true;
+
+ OnConnected?.Invoke(this, new ConnectionEventArgs { ClientId = "self", RemoteEndPoint = new IPEndPoint(IPAddress.Parse(_config.Host), _config.Port) });
+
+ // Start receiving data
+ _ = Task.Run(() => ReceiveDataAsync(), _cancellation.Token);
+ }
+ catch (Exception ex)
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Failed to connect" });
+ }
+ }
+
+ private bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
+ {
+ if (_config.IsSelfSignedEnabled)
+ {
+ return true; // Accept self-signed certificates
+ }
+
+ if (sslPolicyErrors == SslPolicyErrors.None)
+ {
+ return true; // Certificate is valid
+ }
+
+ // Log or handle the SSL error as needed
+ OnSslError?.Invoke(this, new ErrorEventArgs
+ {
+ Exception = new AuthenticationException("SSL certificate validation failed"),
+ Message = $"SSL Policy Errors: {sslPolicyErrors}"
+ });
+ return false; // Reject the certificate
+ }
+
+ private async Task ConnectUdp()
+ {
+ try
+ {
+ _udpClient = new UdpClient();
+ _udpClient.Connect(_config.Host, _config.Port);
+ _isConnected = true;
+
+ OnConnected?.Invoke(this, new ConnectionEventArgs { ClientId = "self", RemoteEndPoint = new IPEndPoint(IPAddress.Parse(_config.Host), _config.Port) });
+
+ // Start receiving data
+ _ = Task.Run(() => ReceiveUdpDataAsync(), _cancellation.Token);
+ }
+ catch (Exception ex)
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Failed to connect UDP" });
+ }
+ }
+
+
private async Task ReceiveDataAsync()
{
while (!_cancellation.Token.IsCancellationRequested && _isConnected)
@@ -209,29 +209,29 @@ namespace EonaCat.Connections
}
return offset;
}
-
-
- private async Task ReceiveUdpDataAsync()
- {
- while (!_cancellation.Token.IsCancellationRequested && _isConnected)
- {
- try
- {
- var result = await _udpClient.ReceiveAsync();
- await ProcessReceivedDataAsync(result.Buffer);
- }
- catch (Exception ex)
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Error receiving data" });
- _isConnected = false;
-
- // Start reconnect
- _ = Task.Run(() => AutoReconnectAsync());
- break;
- }
- }
- }
-
+
+
+ private async Task ReceiveUdpDataAsync()
+ {
+ while (!_cancellation.Token.IsCancellationRequested && _isConnected)
+ {
+ try
+ {
+ var result = await _udpClient.ReceiveAsync();
+ await ProcessReceivedDataAsync(result.Buffer);
+ }
+ catch (Exception ex)
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Error receiving data" });
+ _isConnected = false;
+
+ // Start reconnect
+ _ = Task.Run(() => AutoReconnectAsync());
+ break;
+ }
+ }
+ }
+
private async Task ProcessReceivedDataAsync(byte[] data)
{
try
@@ -269,8 +269,8 @@ namespace EonaCat.Connections
OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Error processing data" });
}
}
-
-
+
+
public async Task SendAsync(byte[] data)
{
if (!_isConnected) return;
@@ -310,99 +310,99 @@ namespace EonaCat.Connections
else
OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Error sending data" });
}
- }
-
-
- public async Task SendAsync(string message)
- {
- await SendAsync(Encoding.UTF8.GetBytes(message));
- }
-
- public async Task SendNicknameAsync(string nickname)
- {
- await SendAsync($"NICKNAME:{nickname}");
- }
-
- private async Task EncryptDataAsync(byte[] data, Aes aes)
- {
- using (var encryptor = aes.CreateEncryptor())
- using (var ms = new MemoryStream())
- using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
- {
- await cs.WriteAsync(data, 0, data.Length);
- cs.FlushFinalBlock();
- return ms.ToArray();
- }
- }
-
- private async Task DecryptDataAsync(byte[] data, Aes aes)
- {
- using (var decryptor = aes.CreateDecryptor())
- using (var ms = new MemoryStream(data))
- using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
- using (var result = new MemoryStream())
- {
- await cs.CopyToAsync(result);
- return result.ToArray();
- }
- }
-
- private async Task AutoReconnectAsync()
- {
- if (!_config.EnableAutoReconnect)
- {
- return;
- }
-
- int attempt = 0;
-
- while (!_isConnected && (_config.MaxReconnectAttempts == 0 || attempt < _config.MaxReconnectAttempts))
- {
- attempt++;
- try
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { Message = $"Attempting to reconnect (Attempt {attempt})" });
- await ConnectAsync();
-
- if (_isConnected)
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { Message = $"Reconnected successfully after {attempt} attempt(s)" });
- break;
- }
- }
- catch
- {
- // Ignore exceptions, we'll retry
- }
-
- await Task.Delay(_config.ReconnectDelayMs);
- }
-
- if (!_isConnected)
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { Message = "Failed to reconnect" });
- }
- }
-
-
- public async Task DisconnectAsync()
- {
- _isConnected = false;
- _cancellation?.Cancel();
- _tcpClient?.Close();
- _udpClient?.Close();
- _stream?.Dispose();
- _aesEncryption?.Dispose();
-
- OnDisconnected?.Invoke(this, new ConnectionEventArgs { ClientId = "self" });
-
- _ = Task.Run(() => AutoReconnectAsync());
- }
-
- public void Dispose()
- {
- DisconnectAsync().Wait();
- _cancellation?.Dispose();
- }
- }
+ }
+
+
+ public async Task SendAsync(string message)
+ {
+ await SendAsync(Encoding.UTF8.GetBytes(message));
+ }
+
+ public async Task SendNicknameAsync(string nickname)
+ {
+ await SendAsync($"NICKNAME:{nickname}");
+ }
+
+ private async Task EncryptDataAsync(byte[] data, Aes aes)
+ {
+ using (var encryptor = aes.CreateEncryptor())
+ using (var ms = new MemoryStream())
+ using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
+ {
+ await cs.WriteAsync(data, 0, data.Length);
+ cs.FlushFinalBlock();
+ return ms.ToArray();
+ }
+ }
+
+ private async Task DecryptDataAsync(byte[] data, Aes aes)
+ {
+ using (var decryptor = aes.CreateDecryptor())
+ using (var ms = new MemoryStream(data))
+ using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
+ using (var result = new MemoryStream())
+ {
+ await cs.CopyToAsync(result);
+ return result.ToArray();
+ }
+ }
+
+ private async Task AutoReconnectAsync()
+ {
+ if (!_config.EnableAutoReconnect)
+ {
+ return;
+ }
+
+ int attempt = 0;
+
+ while (!_isConnected && (_config.MaxReconnectAttempts == 0 || attempt < _config.MaxReconnectAttempts))
+ {
+ attempt++;
+ try
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { Message = $"Attempting to reconnect (Attempt {attempt})" });
+ await ConnectAsync();
+
+ if (_isConnected)
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { Message = $"Reconnected successfully after {attempt} attempt(s)" });
+ break;
+ }
+ }
+ catch
+ {
+ // Ignore exceptions, we'll retry
+ }
+
+ await Task.Delay(_config.ReconnectDelayMs);
+ }
+
+ if (!_isConnected)
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { Message = "Failed to reconnect" });
+ }
+ }
+
+
+ public async Task DisconnectAsync()
+ {
+ _isConnected = false;
+ _cancellation?.Cancel();
+ _tcpClient?.Close();
+ _udpClient?.Close();
+ _stream?.Dispose();
+ _aesEncryption?.Dispose();
+
+ OnDisconnected?.Invoke(this, new ConnectionEventArgs { ClientId = "self" });
+
+ _ = Task.Run(() => AutoReconnectAsync());
+ }
+
+ public void Dispose()
+ {
+ DisconnectAsync().Wait();
+ _cancellation?.Dispose();
+ }
+ }
}
\ No newline at end of file
diff --git a/EonaCat.Connections/NetworkServer.cs b/EonaCat.Connections/NetworkServer.cs
index 3350539..cec1f23 100644
--- a/EonaCat.Connections/NetworkServer.cs
+++ b/EonaCat.Connections/NetworkServer.cs
@@ -1,248 +1,248 @@
-using EonaCat.Connections.EventArguments;
-using EonaCat.Connections.Helpers;
-using EonaCat.Connections.Models;
-using System.Collections.Concurrent;
-using System.Net;
-using System.Net.Security;
-using System.Net.Sockets;
-using System.Security.Authentication;
-using System.Security.Cryptography;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
-using ErrorEventArgs = EonaCat.Connections.EventArguments.ErrorEventArgs;
-
-namespace EonaCat.Connections
-{
- public class NetworkServer
- {
- private readonly Configuration _config;
- private readonly Stats _stats;
- private readonly ConcurrentDictionary _clients;
- private TcpListener _tcpListener;
- private UdpClient _udpListener;
- private CancellationTokenSource _serverCancellation;
- private readonly object _statsLock = new object();
-
- public event EventHandler OnConnected;
- public event EventHandler OnConnectedWithNickname;
- public event EventHandler OnDataReceived;
- public event EventHandler OnDisconnected;
- public event EventHandler OnSslError;
- public event EventHandler OnEncryptionError;
- public event EventHandler OnGeneralError;
-
- public NetworkServer(Configuration config)
- {
- _config = config;
- _stats = new Stats { StartTime = DateTime.UtcNow };
- _clients = new ConcurrentDictionary();
- }
-
- public Stats GetStats()
- {
- lock (_statsLock)
- {
- _stats.ActiveConnections = _clients.Count;
- return _stats;
- }
- }
-
- public async Task StartAsync()
- {
- _serverCancellation = new CancellationTokenSource();
-
- if (_config.Protocol == ProtocolType.TCP)
- {
- await StartTcpServerAsync();
- }
- else
- {
- await StartUdpServerAsync();
- }
- }
-
- private async Task StartTcpServerAsync()
- {
- _tcpListener = new TcpListener(IPAddress.Parse(_config.Host), _config.Port);
- _tcpListener.Start();
-
- Console.WriteLine($"TCP Server started on {_config.Host}:{_config.Port}");
-
- while (!_serverCancellation.Token.IsCancellationRequested)
- {
- try
- {
- var tcpClient = await _tcpListener.AcceptTcpClientAsync();
- _ = Task.Run(() => HandleTcpClientAsync(tcpClient), _serverCancellation.Token);
- }
- catch (ObjectDisposedException)
- {
- break;
- }
- catch (Exception ex)
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Error accepting TCP client" });
- }
- }
- }
-
- private async Task StartUdpServerAsync()
- {
- _udpListener = new UdpClient(_config.Port);
- Console.WriteLine($"UDP Server started on {_config.Host}:{_config.Port}");
-
- while (!_serverCancellation.Token.IsCancellationRequested)
- {
- try
- {
- var result = await _udpListener.ReceiveAsync();
- _ = Task.Run(() => HandleUdpDataAsync(result), _serverCancellation.Token);
- }
- catch (ObjectDisposedException)
- {
- break;
- }
- catch (Exception ex)
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Error receiving UDP data" });
- }
- }
- }
-
- private async Task HandleTcpClientAsync(TcpClient tcpClient)
- {
- var clientId = Guid.NewGuid().ToString();
- var client = new Connection
- {
- Id = clientId,
- TcpClient = tcpClient,
- RemoteEndPoint = (IPEndPoint)tcpClient.Client.RemoteEndPoint,
- ConnectedAt = DateTime.UtcNow,
- CancellationToken = new CancellationTokenSource()
- };
-
- try
- {
- // Configure TCP client
- tcpClient.NoDelay = !_config.EnableNagle;
- if (_config.EnableKeepAlive)
- {
- tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
- }
-
- Stream stream = tcpClient.GetStream();
-
- // Setup SSL if required
- if (_config.UseSsl)
- {
- try
- {
- var sslStream = new SslStream(stream, false, userCertificateValidationCallback:RemoteCertificateValidationCallback);
- await sslStream.AuthenticateAsServerAsync(_config.ServerCertificate, false, SslProtocols.Tls12, false);
- stream = sslStream;
- client.IsSecure = true;
- }
- catch (Exception ex)
- {
- OnSslError?.Invoke(this, new ErrorEventArgs { ClientId = clientId, Exception = ex, Message = "SSL authentication failed" });
- return;
- }
- }
-
- // Setup AES encryption if required
- if (_config.UseAesEncryption)
- {
- try
- {
- client.AesEncryption = Aes.Create();
- client.AesEncryption.GenerateKey();
- client.AesEncryption.GenerateIV();
- client.IsEncrypted = true;
-
- // Securely send raw AES key + IV + salt
- await AesKeyExchange.SendAesKeyAsync(stream, client.AesEncryption);
- }
- catch (Exception ex)
- {
- OnEncryptionError?.Invoke(this, new ErrorEventArgs
- {
- ClientId = clientId,
- Exception = ex,
- Message = "AES setup failed"
- });
- return;
- }
- }
-
- client.Stream = stream;
- _clients[clientId] = client;
-
- lock (_statsLock)
- {
- _stats.TotalConnections++;
- }
-
- OnConnected?.Invoke(this, new ConnectionEventArgs { ClientId = clientId, RemoteEndPoint = client.RemoteEndPoint });
-
- // Handle client communication
- await HandleClientCommunicationAsync(client);
- }
- catch (Exception ex)
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { ClientId = clientId, Exception = ex, Message = "Error handling TCP client" });
- }
- finally
- {
- await DisconnectClientAsync(clientId);
- }
- }
-
- private bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
- {
- if (_config.IsSelfSignedEnabled)
- {
- return true; // Accept self-signed certificates
- }
-
- if (sslPolicyErrors == SslPolicyErrors.None)
- {
- return true; // Certificate is valid
- }
-
- // Log or handle the SSL error as needed
- OnSslError?.Invoke(this, new ErrorEventArgs
- {
- Exception = new AuthenticationException("SSL certificate validation failed"),
- Message = $"SSL Policy Errors: {sslPolicyErrors}"
- });
- return false; // Reject the certificate
- }
-
- private async Task HandleUdpDataAsync(UdpReceiveResult result)
- {
- var clientKey = result.RemoteEndPoint.ToString();
-
- if (!_clients.TryGetValue(clientKey, out var client))
- {
- client = new Connection
- {
- Id = clientKey,
- RemoteEndPoint = result.RemoteEndPoint,
- ConnectedAt = DateTime.UtcNow
- };
- _clients[clientKey] = client;
-
- lock (_statsLock)
- {
- _stats.TotalConnections++;
- }
-
- OnConnected?.Invoke(this, new ConnectionEventArgs { ClientId = clientKey, RemoteEndPoint = result.RemoteEndPoint });
- }
-
- await ProcessReceivedDataAsync(client, result.Buffer);
- }
-
+using EonaCat.Connections.EventArguments;
+using EonaCat.Connections.Helpers;
+using EonaCat.Connections.Models;
+using System.Collections.Concurrent;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using ErrorEventArgs = EonaCat.Connections.EventArguments.ErrorEventArgs;
+
+namespace EonaCat.Connections
+{
+ public class NetworkServer
+ {
+ private readonly Configuration _config;
+ private readonly Stats _stats;
+ private readonly ConcurrentDictionary _clients;
+ private TcpListener _tcpListener;
+ private UdpClient _udpListener;
+ private CancellationTokenSource _serverCancellation;
+ private readonly object _statsLock = new object();
+
+ public event EventHandler OnConnected;
+ public event EventHandler OnConnectedWithNickname;
+ public event EventHandler OnDataReceived;
+ public event EventHandler OnDisconnected;
+ public event EventHandler OnSslError;
+ public event EventHandler OnEncryptionError;
+ public event EventHandler OnGeneralError;
+
+ public NetworkServer(Configuration config)
+ {
+ _config = config;
+ _stats = new Stats { StartTime = DateTime.UtcNow };
+ _clients = new ConcurrentDictionary();
+ }
+
+ public Stats GetStats()
+ {
+ lock (_statsLock)
+ {
+ _stats.ActiveConnections = _clients.Count;
+ return _stats;
+ }
+ }
+
+ public async Task StartAsync()
+ {
+ _serverCancellation = new CancellationTokenSource();
+
+ if (_config.Protocol == ProtocolType.TCP)
+ {
+ await StartTcpServerAsync();
+ }
+ else
+ {
+ await StartUdpServerAsync();
+ }
+ }
+
+ private async Task StartTcpServerAsync()
+ {
+ _tcpListener = new TcpListener(IPAddress.Parse(_config.Host), _config.Port);
+ _tcpListener.Start();
+
+ Console.WriteLine($"TCP Server started on {_config.Host}:{_config.Port}");
+
+ while (!_serverCancellation.Token.IsCancellationRequested)
+ {
+ try
+ {
+ var tcpClient = await _tcpListener.AcceptTcpClientAsync();
+ _ = Task.Run(() => HandleTcpClientAsync(tcpClient), _serverCancellation.Token);
+ }
+ catch (ObjectDisposedException)
+ {
+ break;
+ }
+ catch (Exception ex)
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Error accepting TCP client" });
+ }
+ }
+ }
+
+ private async Task StartUdpServerAsync()
+ {
+ _udpListener = new UdpClient(_config.Port);
+ Console.WriteLine($"UDP Server started on {_config.Host}:{_config.Port}");
+
+ while (!_serverCancellation.Token.IsCancellationRequested)
+ {
+ try
+ {
+ var result = await _udpListener.ReceiveAsync();
+ _ = Task.Run(() => HandleUdpDataAsync(result), _serverCancellation.Token);
+ }
+ catch (ObjectDisposedException)
+ {
+ break;
+ }
+ catch (Exception ex)
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { Exception = ex, Message = "Error receiving UDP data" });
+ }
+ }
+ }
+
+ private async Task HandleTcpClientAsync(TcpClient tcpClient)
+ {
+ var clientId = Guid.NewGuid().ToString();
+ var client = new Connection
+ {
+ Id = clientId,
+ TcpClient = tcpClient,
+ RemoteEndPoint = (IPEndPoint)tcpClient.Client.RemoteEndPoint,
+ ConnectedAt = DateTime.UtcNow,
+ CancellationToken = new CancellationTokenSource()
+ };
+
+ try
+ {
+ // Configure TCP client
+ tcpClient.NoDelay = !_config.EnableNagle;
+ if (_config.EnableKeepAlive)
+ {
+ tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
+ }
+
+ Stream stream = tcpClient.GetStream();
+
+ // Setup SSL if required
+ if (_config.UseSsl)
+ {
+ try
+ {
+ var sslStream = new SslStream(stream, false, userCertificateValidationCallback:RemoteCertificateValidationCallback);
+ await sslStream.AuthenticateAsServerAsync(_config.ServerCertificate, false, SslProtocols.Tls12, false);
+ stream = sslStream;
+ client.IsSecure = true;
+ }
+ catch (Exception ex)
+ {
+ OnSslError?.Invoke(this, new ErrorEventArgs { ClientId = clientId, Exception = ex, Message = "SSL authentication failed" });
+ return;
+ }
+ }
+
+ // Setup AES encryption if required
+ if (_config.UseAesEncryption)
+ {
+ try
+ {
+ client.AesEncryption = Aes.Create();
+ client.AesEncryption.GenerateKey();
+ client.AesEncryption.GenerateIV();
+ client.IsEncrypted = true;
+
+ // Securely send raw AES key + IV + salt
+ await AesKeyExchange.SendAesKeyAsync(stream, client.AesEncryption, _config.AesPassword);
+ }
+ catch (Exception ex)
+ {
+ OnEncryptionError?.Invoke(this, new ErrorEventArgs
+ {
+ ClientId = clientId,
+ Exception = ex,
+ Message = "AES setup failed"
+ });
+ return;
+ }
+ }
+
+ client.Stream = stream;
+ _clients[clientId] = client;
+
+ lock (_statsLock)
+ {
+ _stats.TotalConnections++;
+ }
+
+ OnConnected?.Invoke(this, new ConnectionEventArgs { ClientId = clientId, RemoteEndPoint = client.RemoteEndPoint });
+
+ // Handle client communication
+ await HandleClientCommunicationAsync(client);
+ }
+ catch (Exception ex)
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { ClientId = clientId, Exception = ex, Message = "Error handling TCP client" });
+ }
+ finally
+ {
+ await DisconnectClientAsync(clientId);
+ }
+ }
+
+ private bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
+ {
+ if (_config.IsSelfSignedEnabled)
+ {
+ return true; // Accept self-signed certificates
+ }
+
+ if (sslPolicyErrors == SslPolicyErrors.None)
+ {
+ return true; // Certificate is valid
+ }
+
+ // Log or handle the SSL error as needed
+ OnSslError?.Invoke(this, new ErrorEventArgs
+ {
+ Exception = new AuthenticationException("SSL certificate validation failed"),
+ Message = $"SSL Policy Errors: {sslPolicyErrors}"
+ });
+ return false; // Reject the certificate
+ }
+
+ private async Task HandleUdpDataAsync(UdpReceiveResult result)
+ {
+ var clientKey = result.RemoteEndPoint.ToString();
+
+ if (!_clients.TryGetValue(clientKey, out var client))
+ {
+ client = new Connection
+ {
+ Id = clientKey,
+ RemoteEndPoint = result.RemoteEndPoint,
+ ConnectedAt = DateTime.UtcNow
+ };
+ _clients[clientKey] = client;
+
+ lock (_statsLock)
+ {
+ _stats.TotalConnections++;
+ }
+
+ OnConnected?.Invoke(this, new ConnectionEventArgs { ClientId = clientKey, RemoteEndPoint = result.RemoteEndPoint });
+ }
+
+ await ProcessReceivedDataAsync(client, result.Buffer);
+ }
+
private async Task HandleClientCommunicationAsync(Connection client)
{
var lengthBuffer = new byte[4]; // length prefix
@@ -312,8 +312,8 @@ namespace EonaCat.Connections
}
return offset;
}
-
-
+
+
private async Task ProcessReceivedDataAsync(Connection client, byte[] data)
{
try
@@ -367,36 +367,36 @@ namespace EonaCat.Connections
OnGeneralError?.Invoke(this, new ErrorEventArgs { ClientId = client.Id, Exception = ex, Message = "Error processing data" });
}
}
-
-
- public async Task SendToClientAsync(string clientId, byte[] data)
- {
- if (_clients.TryGetValue(clientId, out var client))
- {
- await SendDataAsync(client, data);
- }
- }
-
- public async Task SendToClientAsync(string clientId, string message)
- {
- await SendToClientAsync(clientId, Encoding.UTF8.GetBytes(message));
- }
-
- public async Task BroadcastAsync(byte[] data)
- {
- var tasks = new List();
- foreach (var client in _clients.Values)
- {
- tasks.Add(SendDataAsync(client, data));
- }
- await Task.WhenAll(tasks);
- }
-
- public async Task BroadcastAsync(string message)
- {
- await BroadcastAsync(Encoding.UTF8.GetBytes(message));
- }
-
+
+
+ public async Task SendToClientAsync(string clientId, byte[] data)
+ {
+ if (_clients.TryGetValue(clientId, out var client))
+ {
+ await SendDataAsync(client, data);
+ }
+ }
+
+ public async Task SendToClientAsync(string clientId, string message)
+ {
+ await SendToClientAsync(clientId, Encoding.UTF8.GetBytes(message));
+ }
+
+ public async Task BroadcastAsync(byte[] data)
+ {
+ var tasks = new List();
+ foreach (var client in _clients.Values)
+ {
+ tasks.Add(SendDataAsync(client, data));
+ }
+ await Task.WhenAll(tasks);
+ }
+
+ public async Task BroadcastAsync(string message)
+ {
+ await BroadcastAsync(Encoding.UTF8.GetBytes(message));
+ }
+
private async Task SendDataAsync(Connection client, byte[] data)
{
try
@@ -442,72 +442,72 @@ namespace EonaCat.Connections
else
OnGeneralError?.Invoke(this, new ErrorEventArgs { ClientId = client.Id, Exception = ex, Message = "Error sending data" });
}
- }
-
-
- private async Task EncryptDataAsync(byte[] data, Aes aes)
- {
- using (var encryptor = aes.CreateEncryptor())
- using (var ms = new MemoryStream())
- using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
- {
- await cs.WriteAsync(data, 0, data.Length);
- cs.FlushFinalBlock();
- return ms.ToArray();
- }
- }
-
- private async Task DecryptDataAsync(byte[] data, Aes aes)
- {
- using (var decryptor = aes.CreateDecryptor())
- using (var ms = new MemoryStream(data))
- using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
- using (var result = new MemoryStream())
- {
- await cs.CopyToAsync(result);
- return result.ToArray();
- }
- }
-
- private async Task DisconnectClientAsync(string clientId)
- {
- if (_clients.TryRemove(clientId, out var client))
- {
- try
- {
- client.CancellationToken?.Cancel();
- client.TcpClient?.Close();
- client.Stream?.Dispose();
- client.AesEncryption?.Dispose();
-
- OnDisconnected?.Invoke(this, new ConnectionEventArgs { ClientId = clientId, RemoteEndPoint = client.RemoteEndPoint });
- }
- catch (Exception ex)
- {
- OnGeneralError?.Invoke(this, new ErrorEventArgs { ClientId = clientId, Exception = ex, Message = "Error disconnecting client" });
- }
- }
- }
-
- public void Stop()
- {
- _serverCancellation?.Cancel();
- _tcpListener?.Stop();
- _udpListener?.Close();
-
- // Disconnect all clients
- var disconnectTasks = new List();
- foreach (var clientId in _clients.Keys.ToArray())
- {
- disconnectTasks.Add(DisconnectClientAsync(clientId));
- }
- Task.WaitAll(disconnectTasks.ToArray());
- }
-
- public void Dispose()
- {
- Stop();
- _serverCancellation?.Dispose();
- }
- }
+ }
+
+
+ private async Task EncryptDataAsync(byte[] data, Aes aes)
+ {
+ using (var encryptor = aes.CreateEncryptor())
+ using (var ms = new MemoryStream())
+ using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
+ {
+ await cs.WriteAsync(data, 0, data.Length);
+ cs.FlushFinalBlock();
+ return ms.ToArray();
+ }
+ }
+
+ private async Task DecryptDataAsync(byte[] data, Aes aes)
+ {
+ using (var decryptor = aes.CreateDecryptor())
+ using (var ms = new MemoryStream(data))
+ using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
+ using (var result = new MemoryStream())
+ {
+ await cs.CopyToAsync(result);
+ return result.ToArray();
+ }
+ }
+
+ private async Task DisconnectClientAsync(string clientId)
+ {
+ if (_clients.TryRemove(clientId, out var client))
+ {
+ try
+ {
+ client.CancellationToken?.Cancel();
+ client.TcpClient?.Close();
+ client.Stream?.Dispose();
+ client.AesEncryption?.Dispose();
+
+ OnDisconnected?.Invoke(this, new ConnectionEventArgs { ClientId = clientId, RemoteEndPoint = client.RemoteEndPoint });
+ }
+ catch (Exception ex)
+ {
+ OnGeneralError?.Invoke(this, new ErrorEventArgs { ClientId = clientId, Exception = ex, Message = "Error disconnecting client" });
+ }
+ }
+ }
+
+ public void Stop()
+ {
+ _serverCancellation?.Cancel();
+ _tcpListener?.Stop();
+ _udpListener?.Close();
+
+ // Disconnect all clients
+ var disconnectTasks = new List();
+ foreach (var clientId in _clients.Keys.ToArray())
+ {
+ disconnectTasks.Add(DisconnectClientAsync(clientId));
+ }
+ Task.WaitAll(disconnectTasks.ToArray());
+ }
+
+ public void Dispose()
+ {
+ Stop();
+ _serverCancellation?.Dispose();
+ }
+ }
}
\ No newline at end of file