DnsRepository
This commit is contained in:
parent
d101277c9a
commit
39020c75ec
5 changed files with 185 additions and 2 deletions
20
src/pdns-dhcp/Dhcp/DhcpLeaseQueue.cs
Normal file
20
src/pdns-dhcp/Dhcp/DhcpLeaseQueue.cs
Normal 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;
|
||||||
154
src/pdns-dhcp/Dns/DnsRepository.cs
Normal file
154
src/pdns-dhcp/Dns/DnsRepository.cs
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -5,10 +5,19 @@ using Microsoft.AspNetCore.Connections;
|
||||||
using Microsoft.Toolkit.HighPerformance;
|
using Microsoft.Toolkit.HighPerformance;
|
||||||
using Microsoft.Toolkit.HighPerformance.Buffers;
|
using Microsoft.Toolkit.HighPerformance.Buffers;
|
||||||
|
|
||||||
|
using pdns_dhcp.Dns;
|
||||||
|
|
||||||
namespace pdns_dhcp.PowerDns;
|
namespace pdns_dhcp.PowerDns;
|
||||||
|
|
||||||
public class PowerDnsHandler : ConnectionHandler
|
public class PowerDnsHandler : ConnectionHandler
|
||||||
{
|
{
|
||||||
|
private readonly DnsRepository _repository;
|
||||||
|
|
||||||
|
public PowerDnsHandler(DnsRepository repository)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
var input = connection.Transport.Input;
|
var input = connection.Transport.Input;
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ public class DhcpLeaseWatcher : IHostedService
|
||||||
|
|
||||||
var waitTask = Task.WhenAll(tasks);
|
var waitTask = Task.WhenAll(tasks);
|
||||||
TaskCompletionSource taskCompletionSource = new();
|
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);
|
await Task.WhenAny(waitTask, taskCompletionSource.Task).ConfigureAwait(continueOnCapturedContext: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<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.Configuration.Binder" Version="8.0.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
<PackageReference Include="Sep" Version="0.3.0" />
|
<PackageReference Include="Sep" Version="0.3.0" />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue