From 4c7671b396f01da0ccbf9f5e71343bf2e90690a8 Mon Sep 17 00:00:00 2001 From: AliveDevil Date: Wed, 29 Nov 2023 22:59:12 +0100 Subject: [PATCH] Dhcp4 parser --- src/pdns-dhcp/Kea/KeaDhcp4Lease.cs | 72 ++++++++++++++++++++--- src/pdns-dhcp/Kea/KeaDhcp4LeaseHandler.cs | 5 +- src/pdns-dhcp/Kea/KeaDhcpLease.cs | 49 +++++++++++++++ src/pdns-dhcp/Kea/KeaDhcpLeaseWatcher.cs | 8 ++- src/pdns-dhcp/pdns-dhcp.csproj | 1 + 5 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 src/pdns-dhcp/Kea/KeaDhcpLease.cs diff --git a/src/pdns-dhcp/Kea/KeaDhcp4Lease.cs b/src/pdns-dhcp/Kea/KeaDhcp4Lease.cs index c5186f4..fe5c437 100644 --- a/src/pdns-dhcp/Kea/KeaDhcp4Lease.cs +++ b/src/pdns-dhcp/Kea/KeaDhcp4Lease.cs @@ -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); + } +} diff --git a/src/pdns-dhcp/Kea/KeaDhcp4LeaseHandler.cs b/src/pdns-dhcp/Kea/KeaDhcp4LeaseHandler.cs index 202a3f4..5af3533 100644 --- a/src/pdns-dhcp/Kea/KeaDhcp4LeaseHandler.cs +++ b/src/pdns-dhcp/Kea/KeaDhcp4LeaseHandler.cs @@ -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); + } } diff --git a/src/pdns-dhcp/Kea/KeaDhcpLease.cs b/src/pdns-dhcp/Kea/KeaDhcpLease.cs new file mode 100644 index 0000000..ae24491 --- /dev/null +++ b/src/pdns-dhcp/Kea/KeaDhcpLease.cs @@ -0,0 +1,49 @@ +using System.Buffers; +using System.Globalization; + +using DotNext.Buffers; + +namespace pdns_dhcp.Kea; + +public static class KeaDhcpLease +{ + private static ReadOnlySpan EscapeTag => ['&', '#', 'x']; + + public static string Unescape(in ReadOnlySpan 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 text) + { + SpanReader reader = new(text); + ArrayBufferWriter 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(); + } + } +} diff --git a/src/pdns-dhcp/Kea/KeaDhcpLeaseWatcher.cs b/src/pdns-dhcp/Kea/KeaDhcpLeaseWatcher.cs index 116abcc..d13091c 100644 --- a/src/pdns-dhcp/Kea/KeaDhcpLeaseWatcher.cs +++ b/src/pdns-dhcp/Kea/KeaDhcpLeaseWatcher.cs @@ -163,7 +163,11 @@ public sealed class KeaDhcpLeaseWatcher : 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 : IHostedService // TODO Error state. return; } + + _handler.Handle(reader.Current); } var memory = writer.GetMemory(); diff --git a/src/pdns-dhcp/pdns-dhcp.csproj b/src/pdns-dhcp/pdns-dhcp.csproj index 38c7c18..3130929 100644 --- a/src/pdns-dhcp/pdns-dhcp.csproj +++ b/src/pdns-dhcp/pdns-dhcp.csproj @@ -10,6 +10,7 @@ +