From a1949130e24b1ff9dd68f9880bffa86d1040e02a Mon Sep 17 00:00:00 2001 From: Alan Edwardes Date: Sun, 11 Feb 2024 13:38:20 +0000 Subject: [PATCH] Adds new update packet style, now allow encoding "unknown" resources. --- src/Ae.Dns.Client/DnsUpdateClient.cs | 3 ++- .../Records/DnsUnknownResource.cs | 5 ++-- src/Ae.Dns.Protocol/Zone/DnsZone.cs | 6 +++++ .../Client/DnsUpdateClientTests.cs | 26 +++++++++++++++++++ tests/Ae.Dns.Tests/Protocol/DnsUpdateTests.cs | 6 +++++ tests/Ae.Dns.Tests/SampleDnsPackets.cs | 5 ++++ tests/Ae.Dns.Tests/Zone/DnsZoneTests.cs | 19 +++++++++++++- 7 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/Ae.Dns.Client/DnsUpdateClient.cs b/src/Ae.Dns.Client/DnsUpdateClient.cs index e95bfd9..ebef6cc 100644 --- a/src/Ae.Dns.Client/DnsUpdateClient.cs +++ b/src/Ae.Dns.Client/DnsUpdateClient.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -53,7 +54,7 @@ void ChangeRecords(ICollection records) } }; - if (query.Nameservers.Count > 0 && hostnames.All(x => x.ToString().EndsWith(_dnsZone.Origin))) + if (query.Nameservers.Count > 0 && hostnames.All(x => !Regex.IsMatch(x, @"\s")) && hostnames.All(x => x.ToString().EndsWith(_dnsZone.Origin))) { await _dnsZone.Update(ChangeRecords); return query.CreateAnswerMessage(DnsResponseCode.NoError, ToString()); diff --git a/src/Ae.Dns.Protocol/Records/DnsUnknownResource.cs b/src/Ae.Dns.Protocol/Records/DnsUnknownResource.cs index 446694f..18ae2b9 100644 --- a/src/Ae.Dns.Protocol/Records/DnsUnknownResource.cs +++ b/src/Ae.Dns.Protocol/Records/DnsUnknownResource.cs @@ -44,13 +44,14 @@ public void ReadBytes(ReadOnlyMemory bytes, ref int offset, int length) /// public string ToZone(IDnsZone zone) { - throw new NotImplementedException(); + return $"({Convert.ToBase64String(Raw.ToArray())})"; } /// public void FromZone(IDnsZone zone, string input) { - throw new NotImplementedException(); + var base64 = input.Trim().Trim(new char[] { '(', ')' }); + Raw = Convert.FromBase64String(base64); } /// diff --git a/src/Ae.Dns.Protocol/Zone/DnsZone.cs b/src/Ae.Dns.Protocol/Zone/DnsZone.cs index 93c1fd9..3804271 100644 --- a/src/Ae.Dns.Protocol/Zone/DnsZone.cs +++ b/src/Ae.Dns.Protocol/Zone/DnsZone.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -154,6 +155,11 @@ public string FromFormattedHost(string host) /// public string ToFormattedHost(string host) { + if (Regex.IsMatch(host, @"\s")) + { + throw new InvalidOperationException($"Hostname '{host}' contains invalid characters"); + } + if (host == Origin) { return "@"; diff --git a/tests/Ae.Dns.Tests/Client/DnsUpdateClientTests.cs b/tests/Ae.Dns.Tests/Client/DnsUpdateClientTests.cs index b193255..7b3a715 100644 --- a/tests/Ae.Dns.Tests/Client/DnsUpdateClientTests.cs +++ b/tests/Ae.Dns.Tests/Client/DnsUpdateClientTests.cs @@ -59,6 +59,32 @@ public async Task TestWrongZone() Assert.Empty(zone.Records); } + [Fact] + public async Task TestWhitespaceHostname() + { + var zone = new TestDnsZone(); + + var updateClient = new DnsUpdateClient(zone); + + var result = await updateClient.Query(new DnsMessage + { + Header = new DnsHeader { OperationCode = DnsOperationCode.UPDATE }, + Nameservers = new[] + { + new DnsResourceRecord + { + Type = DnsQueryType.A, + Class = DnsQueryClass.IN, + Host = "test me.example.com", + Resource = new DnsIpAddressResource { IPAddress = IPAddress.Parse("192.168.0.1") } + } + } + }); + Assert.Equal(DnsResponseCode.Refused, result.Header.ResponseCode); + + Assert.Empty(zone.Records); + } + [Fact] public async Task TestAddRecords() { diff --git a/tests/Ae.Dns.Tests/Protocol/DnsUpdateTests.cs b/tests/Ae.Dns.Tests/Protocol/DnsUpdateTests.cs index 32382e1..1d0a4b6 100644 --- a/tests/Ae.Dns.Tests/Protocol/DnsUpdateTests.cs +++ b/tests/Ae.Dns.Tests/Protocol/DnsUpdateTests.cs @@ -16,5 +16,11 @@ public void ReadUpdate2() { var message = DnsByteExtensions.FromBytes(SampleDnsPackets.Update2); } + + [Fact] + public void ReadUpdate3() + { + var message = DnsByteExtensions.FromBytes(SampleDnsPackets.Update3); + } } } diff --git a/tests/Ae.Dns.Tests/SampleDnsPackets.cs b/tests/Ae.Dns.Tests/SampleDnsPackets.cs index dab9ce8..05ff322 100644 --- a/tests/Ae.Dns.Tests/SampleDnsPackets.cs +++ b/tests/Ae.Dns.Tests/SampleDnsPackets.cs @@ -170,6 +170,11 @@ public static class SampleDnsPackets 177,183,40,0,0,1,0,2,0,1,0,0,4,104,111,109,101,0,0,6,0,1,13,88,82,49,56,45,48,70,45,66,50,45,52,69,192,12,0,1,0,254,0,0,0,0,0,0,192,22,0,28,0,254,0,0,0,0,0,0,192,22,0,16,0,254,0,0,0,0,0,35,34,48,48,101,51,53,98,97,56,49,52,53,50,57,99,57,98,98,99,100,51,100,101,52,101,57,98,55,100,50,51,101,57,54,55 }; + public static readonly byte[] Update3 = new byte[] + { + 197,1,40,0,0,1,0,1,0,2,0,0,4,104,111,109,101,0,0,6,0,1,12,101,117,102,121,32,82,111,98,111,86,97,99,192,12,0,255,0,254,0,0,0,0,0,0,192,22,0,1,0,1,0,0,14,16,0,4,192,168,178,243,192,22,0,49,0,1,0,0,14,16,0,35,0,0,1,71,63,137,34,61,192,202,183,33,145,161,38,88,247,91,91,17,134,221,161,136,142,110,218,234,187,149,18,126,121,106,33 + }; + private static IList ReadBatch(FileInfo fileInfo) { var answers = new List(); diff --git a/tests/Ae.Dns.Tests/Zone/DnsZoneTests.cs b/tests/Ae.Dns.Tests/Zone/DnsZoneTests.cs index 95a58d9..eb6e237 100644 --- a/tests/Ae.Dns.Tests/Zone/DnsZoneTests.cs +++ b/tests/Ae.Dns.Tests/Zone/DnsZoneTests.cs @@ -1,4 +1,5 @@ -using Ae.Dns.Protocol.Enums; +using Ae.Dns.Protocol; +using Ae.Dns.Protocol.Enums; using Ae.Dns.Protocol.Records; using Ae.Dns.Protocol.Zone; using System; @@ -194,6 +195,22 @@ public async Task TestLoadZone2() zone.DeserializeZone(await File.ReadAllTextAsync("Zone/test2.zone")); } + [Fact] + public async Task TestLoadZone3() + { + var message = DnsByteExtensions.FromBytes(SampleDnsPackets.Update3); + + // This packet has bad hostnames + message.Nameservers[0].Host = "eufyRoboVac.home"; + message.Nameservers[1].Host = "eufyRoboVac.home"; + + var zone = new DnsZone(message.Nameservers); + + var serialized = await RoundTripRecords(zone.Records.ToArray()); + + Assert.NotNull(serialized); + } + public async Task RoundTripRecords(params DnsResourceRecord[] records) { var originalZone = new DnsZone