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.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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue