DnsRepository

This commit is contained in:
Jöran Malek 2023-12-27 16:30:34 +01:00
parent d101277c9a
commit 39020c75ec
5 changed files with 185 additions and 2 deletions

View file

@ -0,0 +1,20 @@
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Channels;
namespace pdns_dhcp.Dhcp;
public class DhcpLeaseQueue
{
private readonly Channel<DhcpLeaseChange> _pipe = Channel.CreateUnbounded<DhcpLeaseChange>();
}
public readonly record struct DhcpLeaseChange(IPAddress Address, string FQDN, DhcpLeaseIdentifier Identifier, TimeSpan Lifetime)
{
public AddressFamily LeaseType { get; } = Address.AddressFamily;
}
public record DhcpLeaseIdentifier;
public record DhcpLeaseClientIdentifier(string ClientId) : DhcpLeaseIdentifier;
public record DhcpLeaseHWAddrIdentifier(PhysicalAddress HWAddr) : DhcpLeaseIdentifier;

View file

@ -0,0 +1,154 @@
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using DotNext.Threading;
using pdns_dhcp.Dhcp;
using Timeout = System.Threading.Timeout;
namespace pdns_dhcp.Dns;
public class DnsRepository
{
private static ReadOnlySpan<int> Lifetimes => [600, 3600];
private readonly ReaderWriterLockSlim _recordLock = new();
private readonly List<DnsRecord> _records = [];
public List<DnsRecord> Find(Predicate<DnsRecord> query)
{
bool enteredLock = false;
try
{
enteredLock = _recordLock.TryEnterReadLock(Timeout.Infinite);
return _records.FindAll(query);
}
finally
{
if (enteredLock)
{
_recordLock.ExitReadLock();
}
}
}
public Task<List<DnsRecord>> FindAsync(Predicate<DnsRecord> query, CancellationToken cancellationToken = default)
{
return Task.Factory.StartNew(state => Find((Predicate<DnsRecord>)state!), query,
cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
public async ValueTask Record(DhcpLeaseChange leaseChange)
{
// just lock that thing.
using (await _recordLock.AcquireLockAsync(CancellationToken.None).ConfigureAwait(false))
{
RecordContinuation(leaseChange);
}
void RecordContinuation(DhcpLeaseChange leaseChange)
{
var search = Matches(leaseChange);
bool lockEntered = false;
try
{
lockEntered = _recordLock.TryEnterWriteLock(Timeout.Infinite);
DnsRecordIdentifier identifier = leaseChange.Identifier switch
{
DhcpLeaseClientIdentifier clientId => new DnsRecordClientIdentifier(clientId.ClientId),
DhcpLeaseHWAddrIdentifier hwAddr => new DnsRecordHWAddrIdentifier(hwAddr.HWAddr),
_ => throw new ArgumentException(nameof(leaseChange.Identifier))
};
TimeSpan lifetime = leaseChange.Lifetime.TotalSeconds switch
{
<= 1800 => TimeSpan.FromSeconds(Lifetimes[0]),
>= 10800 => TimeSpan.FromSeconds(Lifetimes[1]),
{ } seconds => TimeSpan.FromSeconds(seconds / 3)
};
var record = new DnsRecord(leaseChange.Address, leaseChange.FQDN, identifier, lifetime);
if (search.First is { } node)
{
search.RemoveFirst();
_records[node.Value] = record;
}
else
{
_records.Add(record);
}
while (search.Last is { } replace)
{
search.RemoveLast();
var last = _records.Count - 1;
if (replace.Value < last)
{
_records[replace.Value] = _records[last];
}
_records.RemoveAt(last);
}
}
finally
{
if (lockEntered)
{
_recordLock.ExitWriteLock();
}
}
}
LinkedList<int> Matches(DhcpLeaseChange query)
{
LinkedList<int> list = [];
for (int i = 0; i < _records.Count; i++)
{
var record = _records[i];
if (record.RecordType != query.LeaseType)
{
continue;
}
switch ((record.Identifier, query.Identifier))
{
case (DnsRecordClientIdentifier recordClientId, DhcpLeaseClientIdentifier queryClientId)
when StringComparer.InvariantCultureIgnoreCase.Equals(recordClientId.ClientId, queryClientId.ClientId):
case (DnsRecordHWAddrIdentifier recordHWAddr, DhcpLeaseHWAddrIdentifier queryHWAddr)
when EqualityComparer<PhysicalAddress>.Default.Equals(recordHWAddr.HWAddr, queryHWAddr.HWAddr):
list.AddLast(i);
continue;
}
if (EqualityComparer<IPAddress>.Default.Equals(record.Address, query.Address))
{
list.AddLast(i);
}
// Opt-In to disallow duplicate FQDN?
//else if (StringComparer.InvariantCultureIgnoreCase.Equals(record.FQDN, query.FQDN))
//{
// list.AddLast(i);
//}
}
return list;
}
}
}
// TODO Remove duplication
public record DnsRecordIdentifier;
public record DnsRecordClientIdentifier(string ClientId) : DnsRecordIdentifier;
public record DnsRecordHWAddrIdentifier(PhysicalAddress HWAddr) : DnsRecordIdentifier;
// /TODO
public record DnsRecord(IPAddress Address, string FQDN, DnsRecordIdentifier Identifier, TimeSpan Lifetime)
{
public AddressFamily RecordType { get; } = Address.AddressFamily;
}

View file

@ -5,10 +5,19 @@ using Microsoft.AspNetCore.Connections;
using Microsoft.Toolkit.HighPerformance;
using Microsoft.Toolkit.HighPerformance.Buffers;
using pdns_dhcp.Dns;
namespace pdns_dhcp.PowerDns;
public class PowerDnsHandler : ConnectionHandler
{
private readonly DnsRepository _repository;
public PowerDnsHandler(DnsRepository repository)
{
_repository = repository;
}
public override async Task OnConnectedAsync(ConnectionContext connection)
{
var input = connection.Transport.Input;

View file

@ -52,7 +52,7 @@ public class DhcpLeaseWatcher : IHostedService
var waitTask = Task.WhenAll(tasks);
TaskCompletionSource taskCompletionSource = new();
using var registration = cancellationToken.Register(s => (s as TaskCompletionSource)!.SetCanceled(), taskCompletionSource);
using var registration = cancellationToken.Register(s => ((TaskCompletionSource)s!).SetCanceled(), taskCompletionSource);
await Task.WhenAny(waitTask, taskCompletionSource.Task).ConfigureAwait(continueOnCapturedContext: false);
}
}

View file

@ -14,7 +14,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="DotNext" Version="4.15.2" />
<PackageReference Include="DotNext.Threading" Version="4.15.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Sep" Version="0.3.0" />