Skip to content

Commit

Permalink
Initial work for DTLS for connections
Browse files Browse the repository at this point in the history
  • Loading branch information
Extremelyd1 committed Jan 6, 2025
1 parent 3d21be4 commit 0647a58
Show file tree
Hide file tree
Showing 15 changed files with 624 additions and 143 deletions.
3 changes: 3 additions & 0 deletions HKMP/HKMP.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
<HintPath>$(References)\MonoMod.Utils.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="BouncyCastle.Cryptography">
<HintPath>$(References)\BouncyCastle.Cryptography.dll</HintPath>
</Reference>
</ItemGroup>

<!-- Create an item group for the source and destination files of the copy target. This way we can specify
Expand Down
49 changes: 49 additions & 0 deletions HKMP/Networking/Client/ClientDatagramTransport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Net.Sockets;
using Hkmp.Logging;
using Org.BouncyCastle.Tls;

namespace Hkmp.Networking.Client;

internal class ClientDatagramTransport : DatagramTransport {
private readonly Socket _socket;

public ClientDatagramTransport(Socket socket) {
_socket = socket;
}

public int GetReceiveLimit() {
// TODO: change to const defined somewhere
return 1400;
}

public int GetSendLimit() {
// TODO: change to const defined somewhere
return 1400;
}

public int Receive(byte[] buf, int off, int len, int waitMillis) {
try {
// _socket.ReceiveTimeout = waitMillis;
var numReceived = _socket.Receive(
buf,
off,
len,
SocketFlags.None
);
Logger.Debug($"Client socket receive: {numReceived}");
return numReceived;
} catch (SocketException e) {
Logger.Error($"UDP Socket exception:\n{e}");
}

return -1;
}

public void Send(byte[] buf, int off, int len) {
Logger.Debug($"Client sending {len} bytes of data");
_socket.Send(buf, off, len, SocketFlags.None);
}

public void Close() {
}
}
37 changes: 37 additions & 0 deletions HKMP/Networking/Client/ClientTlsClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Org.BouncyCastle.Tls;
using Org.BouncyCastle.Tls.Crypto;

namespace Hkmp.Networking.Client;

internal class ClientTlsClient(TlsCrypto crypto) : AbstractTlsClient(crypto) {
private static readonly int[] SupportedCipherSuites = [
CipherSuite.TLS_AES_128_GCM_SHA256,
CipherSuite.TLS_AES_256_GCM_SHA384,
CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
];

protected override ProtocolVersion[] GetSupportedVersions() {
return ProtocolVersion.DTLSv12.Only();
}

protected override int[] GetSupportedCipherSuites() {
return SupportedCipherSuites;
}

public override TlsAuthentication GetAuthentication() {
return new TlsAuthenticationImpl();
}

private class TlsAuthenticationImpl : TlsAuthentication {
public void NotifyServerCertificate(TlsServerCertificate serverCertificate) {
// TODO: check server certificate, throw error in case of invalid cert
}

public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) {
// TODO: provide means for a client to have certificate and return it in this method
return null;
}
}
}
12 changes: 2 additions & 10 deletions HKMP/Networking/Client/ClientUpdateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Hkmp.Math;
using Hkmp.Networking.Packet;
using Hkmp.Networking.Packet.Data;
using Org.BouncyCastle.Tls;

namespace Hkmp.Networking.Client;

Expand All @@ -18,16 +19,7 @@ internal class ClientUpdateManager : UdpUpdateManager<ServerUpdatePacket, Server
/// Construct the update manager with a UDP net client.
/// </summary>
/// <param name="udpSocket">The UDP socket for the local client.</param>
public ClientUpdateManager(Socket udpSocket) : base(udpSocket) {
}

/// <inheritdoc />
protected override void SendPacket(Packet.Packet packet) {
if (!UdpSocket.Connected) {
return;
}

UdpSocket?.SendAsync(new ArraySegment<byte>(packet.ToArray()), SocketFlags.None);
public ClientUpdateManager(DtlsTransport dtlsTransport) : base(dtlsTransport) {
}

/// <inheritdoc />
Expand Down
104 changes: 104 additions & 0 deletions HKMP/Networking/Client/DtlsClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using Hkmp.Logging;
using Org.BouncyCastle.Tls;
using Org.BouncyCastle.Tls.Crypto.Impl.BC;

namespace Hkmp.Networking.Client;

internal class DtlsClient {
private Socket _socket;
private ClientTlsClient _tlsClient;
private ClientDatagramTransport _clientDatagramTransport;

private CancellationTokenSource _updateTaskTokenSource;

public DtlsTransport DtlsTransport { get; set; }
public event Action<byte[], int> DataReceivedEvent;

public void Connect(string address, int port) {
if (_socket != null ||
_tlsClient != null ||
_clientDatagramTransport != null ||
DtlsTransport != null ||
_updateTaskTokenSource != null
) {
Disconnect();
}

_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// _socket.DualMode = true;

try {
_socket.Connect(address, port);
} catch (SocketException e) {
Logger.Error($"Socket exception when connecting UDP socket:\n{e}");

_socket.Close();

throw;
}

var clientProtocol = new DtlsClientProtocol();
_tlsClient = new ClientTlsClient(new BcTlsCrypto());
_clientDatagramTransport = new ClientDatagramTransport(_socket);

try {
DtlsTransport = clientProtocol.Connect(_tlsClient, _clientDatagramTransport);
} catch (IOException e) {
Logger.Error($"IO exception when connecting DTLS client:\n{e}");

_clientDatagramTransport.Close();

throw;
}

Logger.Debug($"Successfully connected DTLS client to endpoint: {address}:{port}");

_updateTaskTokenSource = new CancellationTokenSource();
var cancellationToken = _updateTaskTokenSource.Token;
new Thread(() => ReceiveLoop(cancellationToken)).Start();
}

public void Disconnect() {
_updateTaskTokenSource?.Cancel();
_updateTaskTokenSource?.Dispose();
_updateTaskTokenSource = null;

DtlsTransport?.Close();
DtlsTransport = null;

_clientDatagramTransport?.Close();
_clientDatagramTransport = null;

_tlsClient?.Cancel();
_tlsClient = null;

_socket?.Close();
_socket = null;
}

public void SendPacket(Packet.Packet packet) {
if (DtlsTransport == null) {
Logger.Error("DTLS transport instance is null, cannot send packet");
return;
}

var buffer = packet.ToArray();

DtlsTransport.Send(buffer, 0, buffer.Length);
}

private void ReceiveLoop(CancellationToken cancellationToken) {
while (!cancellationToken.IsCancellationRequested && DtlsTransport != null) {
// TODO: change to const define somewhere central
var buffer = new byte[1400];
var length = DtlsTransport.Receive(buffer, 0, buffer.Length, 5);
if (length >= 0) {
DataReceivedEvent?.Invoke(buffer, length);
}
}
}
}
57 changes: 28 additions & 29 deletions HKMP/Networking/Client/NetClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using Hkmp.Api.Client;
Expand All @@ -26,11 +27,6 @@ internal class NetClient : INetClient {
/// </summary>
private readonly PacketManager _packetManager;

/// <summary>
/// The underlying UDP net client for networking.
/// </summary>
private readonly UdpNetClient _udpNetClient;

/// <summary>
/// The client update manager for this net client.
/// </summary>
Expand Down Expand Up @@ -71,17 +67,23 @@ internal class NetClient : INetClient {
/// </summary>
private CancellationTokenSource _updateTaskTokenSource;

private DtlsClient _dtlsClient;

/// <summary>
/// Byte array containing received data that was not included in a packet object yet.
/// </summary>
private byte[] _leftoverData;

/// <summary>
/// Construct the net client with the given packet manager.
/// </summary>
/// <param name="packetManager">The packet manager instance.</param>
public NetClient(PacketManager packetManager) {
_packetManager = packetManager;

_udpNetClient = new UdpNetClient();
_dtlsClient = new DtlsClient();

// Register the same function for both TCP and UDP receive callbacks
_udpNetClient.RegisterOnReceive(OnReceiveData);
_dtlsClient.DataReceivedEvent += OnReceiveData;
}

/// <summary>
Expand Down Expand Up @@ -132,7 +134,12 @@ private void OnConnectFailed(ConnectFailedResult result) {
/// Callback method for when the net client receives data.
/// </summary>
/// <param name="packets">A list of raw received packets.</param>
private void OnReceiveData(List<Packet.Packet> packets) {
private void OnReceiveData(byte[] buffer, int length) {
// TODO: check if this is the correct place to make this call
UpdateManager.ProcessUpdate();

var packets = PacketManager.HandleReceivedData(buffer, length, ref _leftoverData);

foreach (var packet in packets) {
// Create a ClientUpdatePacket from the raw packet instance,
// and read the values into it
Expand Down Expand Up @@ -209,38 +216,30 @@ public void Connect(
List<AddonData> addonData
) {
IsConnecting = true;

try {
_udpNetClient.Connect(address, port);
_dtlsClient.Connect(address, port);
} catch (SocketException e) {
Logger.Error($"Failed to connect due to SocketException:\n{e}");

OnConnectFailed(new ConnectFailedResult {
Type = ConnectFailedResult.FailType.SocketException
});
return;
} catch (IOException e) {
Logger.Error($"Failed to connect due to IOException:\n{e}");

OnConnectFailed(new ConnectFailedResult {
Type = ConnectFailedResult.FailType.SocketException
});
return;
}

UpdateManager = new ClientUpdateManager(_udpNetClient.UdpSocket);
UpdateManager = new ClientUpdateManager(_dtlsClient.DtlsTransport);
// During the connection process we register the connection failed callback if we time out
UpdateManager.OnTimeout += OnConnectTimedOut;
UpdateManager.StartUpdates();

// Start a thread that will process the updates for the update manager
// Also make a cancellation token source so we can cancel the thread on demand
_updateTaskTokenSource = new CancellationTokenSource();
var cancellationToken = _updateTaskTokenSource.Token;
new Thread(() => {
while (!cancellationToken.IsCancellationRequested) {
UpdateManager.ProcessUpdate();

// TODO: figure out a good way to get rid of the sleep here
// some way to signal when clients should be updated again would suffice
// also see NetServer#StartClientUpdates
Thread.Sleep(5);
}
}).Start();

UpdateManager.SetLoginRequestData(username, authKey, addonData);
Logger.Debug("Sending login request");
}
Expand All @@ -250,8 +249,8 @@ List<AddonData> addonData
/// </summary>
public void Disconnect() {
UpdateManager.StopUpdates();

_udpNetClient.Disconnect();
_dtlsClient.Disconnect();

IsConnected = false;

Expand Down
Loading

0 comments on commit 0647a58

Please sign in to comment.