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 DotNext.Buffers;
|
||||||
|
|
||||||
|
using Microsoft.Toolkit.HighPerformance.Buffers;
|
||||||
|
|
||||||
namespace pdns_dhcp.Kea;
|
namespace pdns_dhcp.Kea;
|
||||||
|
|
||||||
public static class KeaDhcpLease
|
public static class KeaDhcpLease
|
||||||
|
|
@ -21,7 +23,7 @@ public static class KeaDhcpLease
|
||||||
static string SlowPath(int esc_pos, in ReadOnlySpan<char> text)
|
static string SlowPath(int esc_pos, in ReadOnlySpan<char> text)
|
||||||
{
|
{
|
||||||
SpanReader<char> reader = new(text);
|
SpanReader<char> reader = new(text);
|
||||||
ArrayBufferWriter<char> writer = new(text.Length);
|
using ArrayPoolBufferWriter<char> writer = new(text.Length);
|
||||||
while (reader.RemainingCount > 0)
|
while (reader.RemainingCount > 0)
|
||||||
{
|
{
|
||||||
writer.Write(reader.Read(esc_pos));
|
writer.Write(reader.Read(esc_pos));
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public sealed class KeaDhcpLeaseWatcher<T> : IHostedService
|
||||||
|
|
||||||
public KeaDhcpLeaseWatcher(KeaDhcpServerOptions options, T handler)
|
public KeaDhcpLeaseWatcher(KeaDhcpServerOptions options, T handler)
|
||||||
{
|
{
|
||||||
Options = options;
|
Options = options = options with { Leases = PathEx.ExpandPath(options.Leases) };
|
||||||
_handler = handler;
|
_handler = handler;
|
||||||
|
|
||||||
var leases = options.Leases.AsSpan();
|
var leases = options.Leases.AsSpan();
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,7 @@ using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace pdns_dhcp.PowerDns;
|
namespace pdns_dhcp.PowerDns;
|
||||||
|
|
||||||
public interface IMethod
|
public interface IMethod;
|
||||||
{
|
|
||||||
public abstract static string Method { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonPolymorphic(TypeDiscriminatorPropertyName = "method")]
|
[JsonPolymorphic(TypeDiscriminatorPropertyName = "method")]
|
||||||
[JsonDerivedType(typeof(InitializeMethod), "initialize")]
|
[JsonDerivedType(typeof(InitializeMethod), "initialize")]
|
||||||
|
|
@ -24,12 +21,6 @@ public abstract class Method<TSelf, TParam>(TParam parameters) : Method<TSelf> w
|
||||||
public TParam Parameters => parameters;
|
public TParam Parameters => parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class InitializeMethod : Method<InitializeMethod>, IMethod
|
public class InitializeMethod : Method<InitializeMethod>, IMethod;
|
||||||
{
|
|
||||||
public static string Method => "Initialize";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LookupMethod : Method<LookupMethod>, IMethod
|
public class LookupMethod : Method<LookupMethod>, IMethod;
|
||||||
{
|
|
||||||
public static string Method => "Lookup";
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
using Stl.Async;
|
|
||||||
|
|
||||||
namespace pdns_dhcp.PowerDns;
|
namespace pdns_dhcp.PowerDns;
|
||||||
|
|
||||||
public class PowerDnsStreamClient : IDisposable
|
public class PowerDnsStreamClient : IDisposable
|
||||||
|
|
@ -17,32 +15,63 @@ public class PowerDnsStreamClient : IDisposable
|
||||||
|
|
||||||
~PowerDnsStreamClient()
|
~PowerDnsStreamClient()
|
||||||
{
|
{
|
||||||
Dispose();
|
DisposeCore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
using (_cts)
|
_cts.Cancel();
|
||||||
using (_stream)
|
if (Interlocked.Exchange(ref _task, null!) is { } task)
|
||||||
{
|
{
|
||||||
_cts.Cancel();
|
task
|
||||||
_task.GetAwaiter().GetResult();
|
.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing)
|
||||||
|
.GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisposeCore();
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(CancellationToken stoppingToken)
|
public void Start(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
using var other = Interlocked.Exchange(ref _cts, CancellationTokenSource.CreateLinkedTokenSource(stoppingToken));
|
var cts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
|
||||||
_task = Run(_cts.Token);
|
using var old = Interlocked.Exchange(ref _cts, cts);
|
||||||
other.Cancel();
|
old.Cancel();
|
||||||
|
_task = Run(cts.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeCore()
|
||||||
|
{
|
||||||
|
_cts.Dispose();
|
||||||
|
_stream.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Run(CancellationToken stoppingToken)
|
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.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
using pdns_dhcp.Kea;
|
using pdns_dhcp.Kea;
|
||||||
using pdns_dhcp.Options;
|
using pdns_dhcp.Options;
|
||||||
|
|
@ -9,7 +9,7 @@ using pdns_dhcp.Services;
|
||||||
|
|
||||||
using Stl.Interception;
|
using Stl.Interception;
|
||||||
|
|
||||||
var builder = Host.CreateApplicationBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
builder.Services.Configure<DhcpOptions>(builder.Configuration.GetRequiredSection("Dhcp"));
|
builder.Services.Configure<DhcpOptions>(builder.Configuration.GetRequiredSection("Dhcp"));
|
||||||
builder.Services.Configure<PowerDnsOptions>(builder.Configuration.GetRequiredSection("PowerDns"));
|
builder.Services.Configure<PowerDnsOptions>(builder.Configuration.GetRequiredSection("PowerDns"));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,17 @@ public class PowerDnsBackend : BackgroundService
|
||||||
public PowerDnsBackend(IOptions<PowerDnsOptions> options, IPowerDnsFactory socketFactory)
|
public PowerDnsBackend(IOptions<PowerDnsOptions> options, IPowerDnsFactory socketFactory)
|
||||||
{
|
{
|
||||||
_factory = socketFactory;
|
_factory = socketFactory;
|
||||||
_socket = new(SocketType.Stream, ProtocolType.Unknown);
|
_socket = new(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
|
||||||
_socket.Bind(new UnixDomainSocketEndPoint(options.Value.Listener.Socket));
|
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)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
|
@ -29,4 +38,16 @@ public class PowerDnsBackend : BackgroundService
|
||||||
.Start(stoppingToken);
|
.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"
|
"Leases": "../../ext/kea/dhcp6.leases"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"PowerDns": {
|
||||||
|
"Listener": {
|
||||||
|
"Socket": "${XDG_RUNTIME_DIR}/pdns-dhcp/pdns.sock"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,6 +9,10 @@
|
||||||
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
|
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DotNext" Version="4.15.2" />
|
<PackageReference Include="DotNext" Version="4.15.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue