Dhcp4 parser

This commit is contained in:
Jöran Malek 2023-11-29 22:59:12 +01:00
parent 5e69ae245e
commit 4c7671b396
5 changed files with 124 additions and 11 deletions

View file

@ -1,17 +1,71 @@
using System.Net;
using System.Net.NetworkInformation;
using nietras.SeparatedValues;
namespace pdns_dhcp.Kea;
// ref: https://github.com/isc-projects/kea/blob/Kea-2.5.3/src/lib/dhcpsrv/csv_lease_file4.h
public record struct KeaDhcp4Lease(
string Address,
string HWAddr,
IPAddress Address,
PhysicalAddress HWAddr,
string? ClientId,
uint ValidLifetime,
ulong Expire,
string SubnetId,
byte FqdnFwd,
byte FqdnRev,
DateTimeOffset Expire,
uint SubnetId,
bool FqdnFwd,
bool FqdnRev,
string Hostname,
uint State,
string UserContext,
uint PoolId
);
string? UserContext,
uint PoolId)
{
public static KeaDhcp4Lease Parse(in SepReader.Row row)
{
var address = IPAddress.Parse(row[0].Span);
PhysicalAddress hwaddr = PhysicalAddress.None;
if (row[1].Span is { IsEmpty: false } physical)
{
hwaddr = PhysicalAddress.Parse(physical);
}
string? clientId = row[2].ToString();
uint validLifetime = uint.Parse(row[3].Span);
DateTimeOffset expire = DateTimeOffset.FromUnixTimeSeconds(unchecked((long)ulong.Parse(row[4].Span)));
uint subnetId = uint.Parse(row[5].Span);
bool fqdnFwd = sbyte.Parse(row[6].Span) != 0;
bool fqdnRev = sbyte.Parse(row[7].Span) != 0;
string hostname = KeaDhcpLease.Unescape(row[8].Span);
uint state = 0;
if (row.ColCount > 9)
{
state = uint.Parse(row[9].Span);
}
string? userContext = default;
if (row.ColCount > 10)
{
userContext = KeaDhcpLease.Unescape(row[10].Span);
}
uint poolId = 0;
if (row.ColCount > 11)
{
poolId = uint.Parse(row[11].Span);
}
return new(
address,
hwaddr,
clientId,
validLifetime,
expire,
subnetId,
fqdnFwd,
fqdnRev,
hostname,
state,
userContext,
poolId);
}
}

View file

@ -4,5 +4,8 @@ namespace pdns_dhcp.Kea;
public class KeaDhcp4LeaseHandler : IKeaDhcpLeaseHandler
{
public void Handle(in SepReader.Row row) { }
public void Handle(in SepReader.Row row)
{
KeaDhcp4Lease lease = KeaDhcp4Lease.Parse(row);
}
}

View file

@ -0,0 +1,49 @@
using System.Buffers;
using System.Globalization;
using DotNext.Buffers;
namespace pdns_dhcp.Kea;
public static class KeaDhcpLease
{
private static ReadOnlySpan<char> EscapeTag => ['&', '#', 'x'];
public static string Unescape(in ReadOnlySpan<char> text)
{
int esc_pos = text.IndexOf(EscapeTag);
return esc_pos == -1 ? text.ToString() : SlowPath(esc_pos, text);
static string SlowPath(int esc_pos, in ReadOnlySpan<char> text)
{
SpanReader<char> reader = new(text);
ArrayBufferWriter<char> writer = new(text.Length);
while (reader.RemainingCount > 0)
{
writer.Write(reader.Read(esc_pos));
reader.Advance(EscapeTag.Length);
if (EscapeTag.Length <= reader.RemainingCount - 2)
{
var digits = reader.Read(2);
if (byte.TryParse(digits, NumberStyles.AllowHexSpecifier, null, out var escaped_char))
{
writer.Write((char)escaped_char);
}
else
{
writer.Write(EscapeTag);
writer.Write(digits);
}
}
esc_pos = reader.RemainingSpan.IndexOf(EscapeTag);
if (esc_pos == -1)
{
writer.Write(reader.ReadToEnd());
}
}
return writer.WrittenSpan.ToString();
}
}
}

View file

@ -163,7 +163,11 @@ public sealed class KeaDhcpLeaseWatcher<T> : IHostedService
{
if (reader is null)
{
reader = Sep.Reader().From(_pipe.Reader.AsStream());
reader = Sep.New(',').Reader(o => o with
{
DisableColCountCheck = true,
Unescape = false
}).From(_pipe.Reader.AsStream());
continue;
}
@ -172,6 +176,8 @@ public sealed class KeaDhcpLeaseWatcher<T> : IHostedService
// TODO Error state.
return;
}
_handler.Handle(reader.Current);
}
var memory = writer.GetMemory();

View file

@ -10,6 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNext" Version="4.15.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Sep" Version="0.3.0" />