idk whats going on here

This commit is contained in:
Timerix 2025-05-08 21:48:57 +05:00
parent 6de712910f
commit acaf5fee14
23 changed files with 492 additions and 136 deletions

8
.gitignore vendored
View File

@ -21,3 +21,11 @@
#backups #backups
.old*/ .old*/
#secrets
*.pem
*.key
*.csr
*.crt
*.pfx
*.config.json

View File

@ -1,5 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<Version>1.0.0</Version> <Version>1.0.0</Version>
</PropertyGroup> </PropertyGroup>

View File

@ -4,6 +4,7 @@ global using System.Threading;
global using System.Threading.Tasks; global using System.Threading.Tasks;
global using Meum.Core; global using Meum.Core;
using System.Net; using System.Net;
using System.Net.Quic;
using System.Reflection; using System.Reflection;
using DTLib.Console; using DTLib.Console;
using DTLib.Demystifier; using DTLib.Demystifier;
@ -34,9 +35,15 @@ class Program
Console.OutputEncoding = StringConverter.UTF8; Console.OutputEncoding = StringConverter.UTF8;
Console.InputEncoding = StringConverter.UTF8; Console.InputEncoding = StringConverter.UTF8;
ColoredConsole.ResetColor(); ColoredConsole.ResetColor();
try
{
var v = Assembly.GetExecutingAssembly().GetName().Version; var v = Assembly.GetExecutingAssembly().GetName().Version;
string title = $"Meum CLI v{v?.ToString(3) ?? "Null"}"; string title = $"Meum CLI v{v?.ToString(3) ?? "Null"}";
Console.Title = title; Console.Title = title;
if (!QuicConnection.IsSupported)
throw new Exception("Quic is not supported, check for presence of libmsquic and openssl");
ColoredConsole.WriteTitle(title, '=', fg: ConsoleColor.Cyan); ColoredConsole.WriteTitle(title, '=', fg: ConsoleColor.Cyan);
ColoredConsole.WriteLine(greeting_art, fg: ConsoleColor.Magenta); ColoredConsole.WriteLine(greeting_art, fg: ConsoleColor.Magenta);
ColoredConsole.WriteHLine('=', fg: ConsoleColor.Cyan); ColoredConsole.WriteHLine('=', fg: ConsoleColor.Cyan);
@ -48,15 +55,17 @@ class Program
{ {
try try
{ {
if(userAddress == null) if (userAddress == null)
{ {
var addrstr = ColoredConsole.ReadLine("enter user address (name@server.xyz)", ConsoleColor.Blue); var addrstr =
ColoredConsole.ReadLine("enter user address (name@server.xyz)", ConsoleColor.Blue);
if (string.IsNullOrEmpty(addrstr)) if (string.IsNullOrEmpty(addrstr))
continue; continue;
userAddress = new(addrstr); userAddress = new(addrstr);
} }
Client client = new(userAddress);
if(serverEndPoint == null) Client client = new();
if (serverEndPoint == null)
{ {
serverAddress = ColoredConsole.ReadLine("enter server address (server.xyz)", ConsoleColor.Blue); serverAddress = ColoredConsole.ReadLine("enter server address (server.xyz)", ConsoleColor.Blue);
if (string.IsNullOrEmpty(serverAddress)) if (string.IsNullOrEmpty(serverAddress))
@ -64,12 +73,15 @@ class Program
ColoredConsole.WriteLine("null address", ConsoleColor.Red); ColoredConsole.WriteLine("null address", ConsoleColor.Red);
continue; continue;
} }
serverEndPoint = Network.ParseDnsEndPoint(serverAddress);
serverEndPoint = Functions.ParseDnsEndPoint(serverAddress);
ColoredConsole.WriteTitle(serverAddress, fg: ConsoleColor.Cyan); ColoredConsole.WriteTitle(serverAddress, fg: ConsoleColor.Cyan);
} }
ColoredConsole.WriteLine("connecting to the server...", ConsoleColor.Blue); ColoredConsole.WriteLine("connecting to the server...", ConsoleColor.Blue);
var conn = await client.ConnectToServerAsync(serverEndPoint); var conn = await client.ConnectToServerAsync(serverEndPoint);
ColoredConsole.WriteLine("Connected to server", ConsoleColor.Green);
await conn.PingAsync(); await conn.PingAsync();
await Task.Delay(-1); await Task.Delay(-1);
} }
@ -78,6 +90,14 @@ class Program
ColoredConsole.WriteLine(ex.ToStringDemystified(), ConsoleColor.Red); ColoredConsole.WriteLine(ex.ToStringDemystified(), ConsoleColor.Red);
} }
} }
// ColoredConsole.ResetColor(); }
catch (Exception ex)
{
ColoredConsole.WriteLine(ex.ToStringDemystified(), ConsoleColor.Red);
}
finally
{
Console.ResetColor();
}
} }
} }

View File

@ -1,29 +1,58 @@
global using System; global using System;
global using System.Collections.Generic; global using System.Collections.Generic;
global using System.Threading;
global using System.Threading.Tasks; global using System.Threading.Tasks;
global using Meum.Core; global using Meum.Core;
using System.Net; using System.Net;
using System.Net.Quic;
using System.Net.Security;
using DTLib.Logging;
namespace Meum.Client; namespace Meum.Client;
public class Client public class Client
{ {
private readonly HashSet<ServerConnection> _connectedServers = new(); private readonly HashSet<ServerConnection> _connectedServers = new();
private readonly ILogger _logger;
public Client(ILogger logger)
{
_logger = logger;
}
public IReadOnlySet<ServerConnection> ConnectedServers => _connectedServers; public IReadOnlySet<ServerConnection> ConnectedServers => _connectedServers;
public UserAddress Address { get; } public UserAddress? Address { get; private set; }
public Client(UserAddress address) public Task RegisterAsync(UserAddress address)
{ {
return Task.CompletedTask;
}
public Task LogInAsync(UserAddress address)
{
if(Address != null)
throw new InvalidOperationException("Already logged in");
Address = address; Address = address;
return Task.CompletedTask;
} }
public async Task<ServerConnection> ConnectToServerAsync(DnsEndPoint serverEndPoint) public async Task<ServerConnection> ConnectToServerAsync(DnsEndPoint serverEndPoint)
{ {
var serv = new ServerConnection(serverEndPoint); var quicConn = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions
await serv.ConnectAsync(); {
_connectedServers.Add(serv); // TODO serverEndPoint
RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, Constants.ServerPortDefault),
DefaultStreamErrorCode = Constants.DefaultStreamErrorCode,
DefaultCloseErrorCode = Constants.DefaultCloseErrorCode,
ClientAuthenticationOptions = new SslClientAuthenticationOptions
{
ApplicationProtocols = Constants.ApplicationProtocols,
TargetHost = serverEndPoint.Host
}
});
var serv = new ServerConnection(quicConn, serverEndPoint, _logger);
if(!_connectedServers.Add(serv))
throw new Exception($"Is already connected to server '{serverEndPoint.Host}'");
return serv; return serv;
} }
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
@ -12,6 +12,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DTLib" Version="1.6.0" /> <PackageReference Include="DTLib" Version="1.6.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -2,46 +2,33 @@
using System.Net.Quic; using System.Net.Quic;
using System.Net.Security; using System.Net.Security;
using DTLib.Extensions; using DTLib.Extensions;
using DTLib.Logging;
namespace Meum.Client; namespace Meum.Client;
public class ServerConnection : IDisposable public class ServerConnection : IAsyncDisposable
{ {
private readonly QuicConnection _quicConnection;
private readonly ILogger _logger;
public DnsEndPoint ServerEndPoint { get; } public DnsEndPoint ServerEndPoint { get; }
private QuicConnection? _quicConnection;
public ServerConnection(DnsEndPoint serverEndPoint) public ServerConnection(QuicConnection quicConnection, DnsEndPoint serverEndPoint, ILogger logger)
{ {
ServerEndPoint = serverEndPoint; ServerEndPoint = serverEndPoint;
_quicConnection = quicConnection;
_logger = logger;
} }
public async Task ConnectAsync()
public override int GetHashCode()
{ {
_quicConnection = await QuicConnection.ConnectAsync(new QuicClientConnectionOptions return _quicConnection.RemoteEndPoint.GetHashCode();
{
RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, Network.ServerPortDefault),
DefaultStreamErrorCode = Network.DefaultStreamErrorCode,
DefaultCloseErrorCode = Network.DefaultCloseErrorCode,
ClientAuthenticationOptions = new SslClientAuthenticationOptions
{
ApplicationProtocols = Network.ApplicationProtocols.ToList(),
TargetHost = ServerEndPoint.Host
}
});
} }
public async Task PingAsync() public async ValueTask DisposeAsync()
{ {
var stream = await _quicConnection!.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); await _quicConnection.DisposeAsync();
await stream.WriteAsync("Ping\n".ToBytes());
StreamReader reader = new StreamReader(stream);
string line = await reader.ReadLineAsync() ?? String.Empty;
Console.WriteLine(line);
}
public void Dispose()
{
_quicConnection?.DisposeAsync();
} }
} }

20
Meum.Core/Constants.cs Normal file
View File

@ -0,0 +1,20 @@
global using System;
global using System.Collections.Generic;
global using System.Threading;
global using System.Threading.Tasks;
using System.Net;
using System.Net.Security;
namespace Meum.Core;
public static class Constants
{
public static readonly List<SslApplicationProtocol> ApplicationProtocols =
[
new("Meum-1")
];
public const int ServerPortDefault = 9320;
public const long DefaultStreamErrorCode = 0xA;
public const long DefaultCloseErrorCode = 0xB;
}

View File

@ -1,24 +1,9 @@
global using System; using System.Net;
global using System.Collections.Generic;
global using System.Threading;
global using System.Threading.Tasks;
using System.Net;
using System.Net.Security;
namespace Meum.Core; namespace Meum.Core;
public static class Network public static class Functions
{ {
public static readonly SslApplicationProtocol[] ApplicationProtocols =
[
new("Meum-1")
];
public const int ServerPortDefault = 9320;
public const long DefaultStreamErrorCode = 0xA;
public const long DefaultCloseErrorCode = 0xB;
public static bool IsValidDomainName(string name) public static bool IsValidDomainName(string name)
{ {
return Uri.CheckHostName(name) != UriHostNameType.Unknown; return Uri.CheckHostName(name) != UriHostNameType.Unknown;
@ -32,7 +17,7 @@ public static class Network
if (colon_index == -1) if (colon_index == -1)
{ {
host = address_str; host = address_str;
port = ServerPortDefault; port = Constants.ServerPortDefault;
} }
else else
{ {

View File

@ -0,0 +1,9 @@
using System.Runtime.InteropServices;
namespace Meum.Core.Messages;
[StructLayout(LayoutKind.Sequential)]
public record struct AuthorizationRequest(byte[] hash)
{
}

View File

@ -0,0 +1,28 @@
using System.Runtime.InteropServices;
namespace Meum.Core.Messages;
[StructLayout(LayoutKind.Sequential)]
public struct CodeMessage
{
// 0xb65d6d - meum in 6-bit ascii encoding
// 02 - CodeMessage
private const uint correct_magic = 0xb65d6d01;
/// warning: check with <see cref="ThrowIfInvalid"/>
public uint magic;
/// warning: can be any int
public MessageTypeCode type_code;
public CodeMessage(MessageTypeCode t)
{
magic = correct_magic;
type_code = t;
}
public void ThrowIfInvalid()
{
if(magic != correct_magic)
throw new Exception($"Invalid CodeMessage magic: {magic}");
}
}

View File

@ -0,0 +1,34 @@
using System.Runtime.InteropServices;
namespace Meum.Core.Messages;
[StructLayout(LayoutKind.Sequential)]
public struct DataMessageHeader
{
// 0xb65d6d - meum in 6-bit ascii encoding
// 02 - DataMessageHeader
private const uint correct_magic = 0xb65d6d02;
/// warning: check with <see cref="ThrowIfInvalid"/>
public uint magic;
/// warning: can be any int
public MessageTypeCode type_code;
/// warning: check with <see cref="ThrowIfInvalid"/>
public int data_size;
public DataMessageHeader(MessageTypeCode t, int size)
{
magic = correct_magic;
type_code = t;
data_size = size;
}
public void ThrowIfInvalid()
{
if(magic != correct_magic)
throw new Exception($"Invalid DataMessageHeader magic: {magic}");
if(data_size < 1)
throw new Exception($"Invalid DataMessageHeader data size: {data_size}");
}
}

View File

@ -0,0 +1,8 @@
namespace Meum.Core.Messages;
public enum MessageTypeCode
{
Ping, Pong,
RegistrationRequest, RegistrationResponse,
AuthorizationRequest, AuthorizationResponse,
}

View File

@ -1,4 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Unofficial.MsQuic" Version="2.4.6" /> <PackageReference Include="Unofficial.MsQuic" Version="2.4.6" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,85 @@
using System.Buffers;
using System.Net.Quic;
using System.Runtime.InteropServices;
using Meum.Core.Messages;
namespace Meum.Core;
public class QuicStreamWrapper : IAsyncDisposable
{
private QuicStream _stream;
public QuicStreamWrapper(QuicStream stream)
{
_stream = stream;
}
public async ValueTask<T> ReadStructAsync<T>(CancellationToken ct = default)
where T : struct
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(Marshal.SizeOf(typeof(T)));
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
await _stream.ReadExactlyAsync(buffer, ct);
return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T))!;
}
finally
{
handle.Free();
ArrayPool<byte>.Shared.Return(buffer);
}
}
public ValueTask WriteStructAsync<T>(T msg_struct, CancellationToken ct = default)
where T : struct
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(Marshal.SizeOf(typeof(T)));
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
Marshal.StructureToPtr(msg_struct, handle.AddrOfPinnedObject(), false);
return _stream.WriteAsync(buffer, ct);
}
finally
{
handle.Free();
ArrayPool<byte>.Shared.Return(buffer);
}
}
public ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken ct = default)
=> _stream.ReadAsync(buffer, ct);
public ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken ct = default)
=> _stream.WriteAsync(buffer, ct);
public ValueTask WriteCodeMessageAsync(MessageTypeCode messageTypeCode, CancellationToken ct = default)
{
CodeMessage m = new CodeMessage(messageTypeCode);
return WriteStructAsync(m, ct);
}
public async ValueTask<MessageTypeCode> ReadCodeMessageAsync(CancellationToken ct = default)
{
CodeMessage m = await ReadStructAsync<CodeMessage>(ct);
m.ThrowIfInvalid();
return m.type_code;
}
public async ValueTask<DataMessageHeader> ReadDataMessageHeaderAsync(CancellationToken ct = default)
{
var m = await ReadStructAsync<DataMessageHeader>(ct);
m.ThrowIfInvalid();
return m;
}
//TODO
// public async ValueTask<>
public async ValueTask DisposeAsync()
{
_stream.Close();
await _stream.DisposeAsync();
}
}

View File

@ -26,7 +26,7 @@ public class UserAddress
throw new FormatException($"Invalid user name '{Name}' in address '{addrstr}'"); throw new FormatException($"Invalid user name '{Name}' in address '{addrstr}'");
string serverstr = addrstr.Substring(at_index + 1); string serverstr = addrstr.Substring(at_index + 1);
RegistrationServer = Network.ParseDnsEndPoint(serverstr); RegistrationServer = Functions.ParseDnsEndPoint(serverstr);
} }
public override string ToString() => Full; public override string ToString() => Full;

View File

@ -0,0 +1,61 @@
using System.IO;
using System.Net.Quic;
using DTLib.Logging;
using Meum.Core.Messages;
namespace Meum.Server;
public class ClientConnection : IAsyncDisposable
{
private readonly QuicConnection _quicConnection;
private QuicStreamWrapper? _systemStream;
private ILogger _logger;
public ClientConnection(QuicConnection quicConnection, ILogger logger)
{
_quicConnection = quicConnection;
_logger = logger;
}
private async Task<QuicStreamWrapper> AcceptStreamAsync(QuicStreamType streamType, CancellationToken ct = default)
{
var s = await _quicConnection.AcceptInboundStreamAsync(ct);
if (s.Type != streamType)
throw new Exception($"Accepted stream type is invalid: {s.Type} instead of {streamType}");
var w = new QuicStreamWrapper(s);
return w;
}
public async void HandleClientRequestsAsync(CancellationToken ct = default)
{
if(_systemStream != null)
throw new Exception("Already connected to client");
_systemStream = await AcceptStreamAsync(QuicStreamType.Bidirectional, ct);
// receive "Ping"
if(await _systemStream.ReadCodeMessageAsync(ct) != MessageTypeCode.Ping)
throw new Exception("Failed to test application protocol");
// send "Pong"
await _systemStream.WriteCodeMessageAsync(MessageTypeCode.Pong, ct);
DataMessageHeader header = await _systemStream.ReadDataMessageHeaderAsync(ct);
switch (header.type_code)
{
case MessageTypeCode.RegistrationRequest:
break;
case MessageTypeCode.AuthorizationRequest:
break;
default:
throw new Exception($"New connection sent unexpected message: {header.type_code}");
}
}
public async ValueTask DisposeAsync()
{
await _quicConnection.DisposeAsync();
}
}

View File

@ -1,11 +0,0 @@
using Meum.Core;
namespace Meum.Server;
public class Config
{
public string listener_ip { get; set; } = "127.0.0.1";
public int listener_port { get; set; } = Network.ServerPortDefault;
public string certificate_path { get; set; } = "self-signed.pem";
public string? key_path { get; set; } = "self-signed.key";
}

View File

@ -1,5 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
</PropertyGroup> </PropertyGroup>
@ -9,5 +10,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DTLib" Version="1.6.0" /> <PackageReference Include="DTLib" Version="1.6.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -8,57 +8,39 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.Quic; using System.Net.Quic;
using System.Net.Security; using System.Net.Security;
using System.Runtime.Serialization;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using DTLib.Console; using DTLib.Console;
using DTLib.Demystifier; using DTLib.Demystifier;
using DTLib.Extensions; using DTLib.Extensions;
using DTLib.Filesystem;
using DTLib.Logging;
namespace Meum.Server; namespace Meum.Server;
class Program static class Program
{ {
static readonly IOPath config_path = "Meum.Server.config.json";
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
try try
{ {
var config = new Config(); if (!QuicConnection.IsSupported)
var certificate = X509Certificate2.CreateFromPemFile(config.certificate_path, config.key_path); throw new Exception("Quic is not supported, check for presence of libmsquic and openssl");
if(!certificate.Verify()) var config = ServerConfig.LoadOrCreate(config_path);
throw new Exception("Certificate is not valid"); var logger = new ConsoleLogger();
var serverConnectionOptions = new QuicServerConnectionOptions var server = new Server(config, logger);
{ await server.ListenAsync();
DefaultStreamErrorCode = Network.DefaultStreamErrorCode,
DefaultCloseErrorCode = Network.DefaultCloseErrorCode,
ServerAuthenticationOptions = new SslServerAuthenticationOptions
{
ApplicationProtocols = Network.ApplicationProtocols.ToList(),
ServerCertificate = certificate,
ClientCertificateRequired = false
}
};
var listenerOptions = new QuicListenerOptions
{
ListenEndPoint = new IPEndPoint(IPAddress.Parse(config.listener_ip), config.listener_port),
ApplicationProtocols = Network.ApplicationProtocols.ToList(),
ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(serverConnectionOptions)
};
var listener = await QuicListener.ListenAsync(listenerOptions);
while (true) while (true)
{ {
try try
{ {
var conn = await listener.AcceptConnectionAsync(); var conn = await server.AcceptConnectionAsync();
var stream = await conn.AcceptInboundStreamAsync();
StreamReader reader = new(stream);
string line = await reader.ReadLineAsync() ?? "";
Console.WriteLine(line);
await stream.WriteAsync("Pong\n".ToBytes());
await conn.CloseAsync(Network.DefaultCloseErrorCode);
} }
catch (Exception ex) catch (Exception ex)
{ {
ColoredConsole.WriteLine(ex.ToStringDemystified(), ConsoleColor.Red); logger.LogError("Main", ex.ToStringDemystified());
} }
} }
} }
@ -66,5 +48,9 @@ class Program
{ {
ColoredConsole.WriteLine(ex.ToStringDemystified(), ConsoleColor.Red); ColoredConsole.WriteLine(ex.ToStringDemystified(), ConsoleColor.Red);
} }
finally
{
Console.ResetColor();
}
} }
} }

View File

@ -1,4 +1,4 @@
## Create self-signed certificate ## Create self-signed certificate
```sh ```sh
dotnet dev-certs https -ep bin/Debug/net8.0/self-signed.pfx --trust --format PEM --no-password dotnet dev-certs https -ep bin/Debug/net8.0/self-signed.pem --trust --format PEM --no-password
``` ```

62
Meum.Server/Server.cs Normal file
View File

@ -0,0 +1,62 @@
using System.Net;
using System.Net.Quic;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using DTLib.Logging;
namespace Meum.Server;
public class Server
{
private readonly ServerConfig _config;
private readonly X509Certificate _certificate;
private readonly ILogger _logger;
private QuicListener? _listener;
public Server(ServerConfig config, ILogger logger, X509Certificate? certificate = null)
{
_config = config;
_logger = logger;
_certificate = certificate ??
X509Certificate2.CreateFromPemFile(config.certificate_path, config.key_path);
}
public async ValueTask ListenAsync()
{
var serverConnectionOptions = new QuicServerConnectionOptions
{
DefaultStreamErrorCode = Constants.DefaultStreamErrorCode,
DefaultCloseErrorCode = Constants.DefaultCloseErrorCode,
ServerAuthenticationOptions = new SslServerAuthenticationOptions
{
ApplicationProtocols = Constants.ApplicationProtocols,
ServerCertificate = _certificate,
ClientCertificateRequired = false
}
};
var listenerOptions = new QuicListenerOptions
{
ListenEndPoint = new IPEndPoint(IPAddress.Parse(_config.listener_ip), _config.listener_port),
ApplicationProtocols = Constants.ApplicationProtocols,
ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(serverConnectionOptions)
};
_listener = await QuicListener.ListenAsync(listenerOptions);
}
public async Task<ClientConnection> AcceptConnectionAsync(CancellationToken ct = default)
{
if (_listener == null)
throw new Exception("Server is not listening");
while (true)
{
ct.ThrowIfCancellationRequested();
var quicConnection = await _listener.AcceptConnectionAsync(ct);
var clientConnection = new ClientConnection(quicConnection, _logger);
await clientConnection.HandleClientRequestsAsync(ct);
return clientConnection;
}
}
}

View File

@ -0,0 +1,36 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using DTLib.Filesystem;
using Newtonsoft.Json;
namespace Meum.Server;
public class ServerConfig
{
public string listener_ip { get; set; } = "127.0.0.1";
public int listener_port { get; set; } = Constants.ServerPortDefault;
public string certificate_path { get; set; } = "self-signed.pem";
public string? key_path { get; set; } = "self-signed.key";
public void Save(IOPath file_path)
{
string serialized = JsonConvert.SerializeObject(this);
if (string.IsNullOrEmpty(serialized))
throw new Exception("can't serialize config");
File.WriteAllText(file_path, serialized);
}
public static ServerConfig LoadOrCreate(IOPath file_path)
{
if(File.Exists(file_path))
{
string serialized = File.ReadAllText(file_path);
return JsonConvert.DeserializeObject<ServerConfig>(serialized)
?? throw new Exception("can't deserialize config");
}
var c = new ServerConfig();
c.Save(file_path);
return c;
}
}

View File

@ -9,6 +9,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution_items", "solution_items", "{9375C0E5-DB78-4E77-869B-F9F7DC2651D1}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution_items", "solution_items", "{9375C0E5-DB78-4E77-869B-F9F7DC2651D1}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props Directory.Build.props = Directory.Build.props
.gitignore = .gitignore
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meum.Client", "Meum.Client\Meum.Client.csproj", "{6DADE8A1-B363-4888-BB9B-72282B9AC769}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meum.Client", "Meum.Client\Meum.Client.csproj", "{6DADE8A1-B363-4888-BB9B-72282B9AC769}"