PowerDNS Socket (to be changed from custom-implementation to Kestrel with ConnectionHandler)
This commit is contained in:
parent
58be1ee178
commit
7f9dc36473
9 changed files with 196 additions and 31 deletions
|
|
@ -3,6 +3,8 @@ using System.Globalization;
|
|||
|
||||
using DotNext.Buffers;
|
||||
|
||||
using Microsoft.Toolkit.HighPerformance.Buffers;
|
||||
|
||||
namespace pdns_dhcp.Kea;
|
||||
|
||||
public static class KeaDhcpLease
|
||||
|
|
@ -21,7 +23,7 @@ public static class KeaDhcpLease
|
|||
static string SlowPath(int esc_pos, in ReadOnlySpan<char> text)
|
||||
{
|
||||
SpanReader<char> reader = new(text);
|
||||
ArrayBufferWriter<char> writer = new(text.Length);
|
||||
using ArrayPoolBufferWriter<char> writer = new(text.Length);
|
||||
while (reader.RemainingCount > 0)
|
||||
{
|
||||
writer.Write(reader.Read(esc_pos));
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public sealed class KeaDhcpLeaseWatcher<T> : IHostedService
|
|||
|
||||
public KeaDhcpLeaseWatcher(KeaDhcpServerOptions options, T handler)
|
||||
{
|
||||
Options = options;
|
||||
Options = options = options with { Leases = PathEx.ExpandPath(options.Leases) };
|
||||
_handler = handler;
|
||||
|
||||
var leases = options.Leases.AsSpan();
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@ using System.Text.Json.Serialization;
|
|||
|
||||
namespace pdns_dhcp.PowerDns;
|
||||
|
||||
public interface IMethod
|
||||
{
|
||||
public abstract static string Method { get; }
|
||||
}
|
||||
public interface IMethod;
|
||||
|
||||
[JsonPolymorphic(TypeDiscriminatorPropertyName = "method")]
|
||||
[JsonDerivedType(typeof(InitializeMethod), "initialize")]
|
||||
|
|
@ -24,12 +21,6 @@ public abstract class Method<TSelf, TParam>(TParam parameters) : Method<TSelf> w
|
|||
public TParam Parameters => parameters;
|
||||
}
|
||||
|
||||
public class InitializeMethod : Method<InitializeMethod>, IMethod
|
||||
{
|
||||
public static string Method => "Initialize";
|
||||
}
|
||||
public class InitializeMethod : Method<InitializeMethod>, IMethod;
|
||||
|
||||
public class LookupMethod : Method<LookupMethod>, IMethod
|
||||
{
|
||||
public static string Method => "Lookup";
|
||||
}
|
||||
public class LookupMethod : Method<LookupMethod>, IMethod;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
using System.Text.Json;
|
||||
|
||||
using Stl.Async;
|
||||
|
||||
namespace pdns_dhcp.PowerDns;
|
||||
|
||||
public class PowerDnsStreamClient : IDisposable
|
||||
|
|
@ -17,32 +15,63 @@ public class PowerDnsStreamClient : IDisposable
|
|||
|
||||
~PowerDnsStreamClient()
|
||||
{
|
||||
Dispose();
|
||||
DisposeCore();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
using (_cts)
|
||||
using (_stream)
|
||||
_cts.Cancel();
|
||||
if (Interlocked.Exchange(ref _task, null!) is { } task)
|
||||
{
|
||||
_cts.Cancel();
|
||||
_task.GetAwaiter().GetResult();
|
||||
task
|
||||
.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing)
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
DisposeCore();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Start(CancellationToken stoppingToken)
|
||||
{
|
||||
using var other = Interlocked.Exchange(ref _cts, CancellationTokenSource.CreateLinkedTokenSource(stoppingToken));
|
||||
_task = Run(_cts.Token);
|
||||
other.Cancel();
|
||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
|
||||
using var old = Interlocked.Exchange(ref _cts, cts);
|
||||
old.Cancel();
|
||||
_task = Run(cts.Token);
|
||||
}
|
||||
|
||||
private void DisposeCore()
|
||||
{
|
||||
_cts.Dispose();
|
||||
_stream.Dispose();
|
||||
}
|
||||
|
||||
private async Task Run(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
try
|
||||
{
|
||||
await JsonSerializer.DeserializeAsync<Method>(_stream, cancellationToken: stoppingToken);
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
switch (await JsonSerializer.DeserializeAsync<Method>(_stream, cancellationToken: stoppingToken).ConfigureAwait(false))
|
||||
{
|
||||
case InitializeMethod init:
|
||||
break;
|
||||
|
||||
case LookupMethod lookup:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Interlocked.Exchange(ref _task, null!) is not null)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using pdns_dhcp.Kea;
|
||||
using pdns_dhcp.Options;
|
||||
|
|
@ -9,7 +9,7 @@ using pdns_dhcp.Services;
|
|||
|
||||
using Stl.Interception;
|
||||
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.Configure<DhcpOptions>(builder.Configuration.GetRequiredSection("Dhcp"));
|
||||
builder.Services.Configure<PowerDnsOptions>(builder.Configuration.GetRequiredSection("PowerDns"));
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,17 @@ public class PowerDnsBackend : BackgroundService
|
|||
public PowerDnsBackend(IOptions<PowerDnsOptions> options, IPowerDnsFactory socketFactory)
|
||||
{
|
||||
_factory = socketFactory;
|
||||
_socket = new(SocketType.Stream, ProtocolType.Unknown);
|
||||
_socket.Bind(new UnixDomainSocketEndPoint(options.Value.Listener.Socket));
|
||||
_socket = new(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
|
||||
var path = PathEx.ExpandPath(options.Value.Listener.Socket);
|
||||
FileInfo file = new(path);
|
||||
file.Directory!.Create();
|
||||
file.Delete();
|
||||
_socket.Bind(new UnixDomainSocketEndPoint(path));
|
||||
}
|
||||
|
||||
~PowerDnsBackend()
|
||||
{
|
||||
DisposeCore();
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
|
|
@ -29,4 +38,16 @@ public class PowerDnsBackend : BackgroundService
|
|||
.Start(stoppingToken);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
DisposeCore();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void DisposeCore()
|
||||
{
|
||||
_socket.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
113
src/pdns-dhcp/System/IO/Paths.cs
Normal file
113
src/pdns-dhcp/System/IO/Paths.cs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#if !(LINUX || MACOS || WINDOWS)
|
||||
#define TARGET_ANY
|
||||
#endif
|
||||
|
||||
#if TARGET_ANY || LINUX
|
||||
#define TARGET_LINUX
|
||||
#endif
|
||||
#if TARGET_ANY || MACOS
|
||||
#define TARGET_MACOS
|
||||
#endif
|
||||
#if TARGET_ANY || WINDOWS
|
||||
#define TARGET_WINDOWS
|
||||
#endif
|
||||
|
||||
using System.Buffers;
|
||||
|
||||
using Microsoft.Toolkit.HighPerformance.Buffers;
|
||||
|
||||
namespace System.IO;
|
||||
|
||||
public static class PathEx
|
||||
{
|
||||
public static string ExpandPath(string path)
|
||||
{
|
||||
#if TARGET_ANY
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
{
|
||||
#endif
|
||||
#if TARGET_LINUX || TARGET_MACOS
|
||||
return ExpandPathUnixImpl(path);
|
||||
#endif
|
||||
#if TARGET_ANY
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
#endif
|
||||
#if TARGET_WINDOWS
|
||||
return ExpandPathWindowsImpl(path);
|
||||
#endif
|
||||
#if TARGET_ANY
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TARGET_LINUX || TARGET_MACOS
|
||||
private static string ExpandPathUnixImpl(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
var reader = new SequenceReader<char>(new(path.AsMemory()));
|
||||
if (!reader.TryReadTo(out ReadOnlySpan<char> read, '$'))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
using ArrayPoolBufferWriter<char> result = new();
|
||||
while (true)
|
||||
{
|
||||
result.Write(read);
|
||||
|
||||
int skip = 0;
|
||||
if (reader.UnreadSpan[0] == '{' && reader.UnreadSpan.IndexOf('}') is not -1 and int s)
|
||||
{
|
||||
read = reader.UnreadSpan[1..s];
|
||||
skip = s + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var propertyReader = new SequenceReader<char>(reader.UnreadSequence);
|
||||
while (!propertyReader.End && propertyReader.UnreadSpan[0] is char c && (c == '_' || char.IsAsciiLetterOrDigit(c)))
|
||||
{
|
||||
propertyReader.Advance(1);
|
||||
}
|
||||
|
||||
read = reader.UnreadSpan[..(int)propertyReader.Consumed];
|
||||
skip = read.Length;
|
||||
}
|
||||
|
||||
if (skip != 0 && Environment.GetEnvironmentVariable(read.ToString()) is { } env)
|
||||
{
|
||||
result.Write(env);
|
||||
reader.Advance(skip);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Write(['$']);
|
||||
}
|
||||
|
||||
if (!reader.TryReadTo(out read, '$'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.Write(reader.UnreadSpan);
|
||||
return result.WrittenSpan.ToString();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TARGET_WINDOWS
|
||||
private static string ExpandPathWindowsImpl(string path)
|
||||
{
|
||||
return Environment.ExpandEnvironmentVariables(path);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -8,5 +8,10 @@
|
|||
"Leases": "../../ext/kea/dhcp6.leases"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PowerDns": {
|
||||
"Listener": {
|
||||
"Socket": "${XDG_RUNTIME_DIR}/pdns-dhcp/pdns.sock"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,10 @@
|
|||
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DotNext" Version="4.15.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue