diff --git a/TechnitiumLibrary.Net/Dns/DnsDatagram.cs b/TechnitiumLibrary.Net/Dns/DnsDatagram.cs index 4f22d1b5..30fb659c 100644 --- a/TechnitiumLibrary.Net/Dns/DnsDatagram.cs +++ b/TechnitiumLibrary.Net/Dns/DnsDatagram.cs @@ -481,7 +481,7 @@ public static void SerializeDomainName(ReadOnlySpan domain, Stream s, List domain = domain.Slice(i + 1); } - if (!Encoding.ASCII.TryGetBytes(label, labelBytes, out labelBytesLength)) + if (!Encoding.Latin1.TryGetBytes(label, labelBytes, out labelBytesLength)) throw new DnsClientException("Invalid domain name: label cannot exceed 63 bytes."); s.WriteByte((byte)labelBytesLength); @@ -536,7 +536,7 @@ public static string DeserializeDomainName(Stream s, int maxDepth = 10, bool ign Span label = buffer.Slice(0, labelLength); s.ReadExactly(label); - if (!Encoding.ASCII.TryGetChars(label, domain.Slice(domainPosition), out _)) + if (!Encoding.Latin1.TryGetChars(label, domain.Slice(domainPosition), out _)) throw new DnsClientException("Error while reading domain name: domain name length cannot exceed 255 bytes."); domainPosition += labelLength; @@ -566,6 +566,29 @@ public static string DeserializeDomainName(Stream s, int maxDepth = 10, bool ign return new string(domain.Slice(0, domainPosition)); } + //RFC 4034 section 6.2 canonical form requires lowercasing ASCII A-Z only; every other byte (including + //the Latin-1 supplement range 0xC0-0xDE) must pass through unchanged. string.ToLowerInvariant() does + //full Unicode case folding and would corrupt those bytes, so domain names use this instead wherever + //the result feeds into a signed hash or a canonical-order comparison. + internal static string ToLowerInvariantAscii(string domain) + { + char[] buffer = null; + + for (int i = 0; i < domain.Length; i++) + { + char c = domain[i]; + if ((c >= 'A') && (c <= 'Z')) + { + if (buffer is null) + buffer = domain.ToCharArray(); + + buffer[i] = (char)(c + 0x20); + } + } + + return buffer is null ? domain : new string(buffer); + } + public static int GetSerializeDomainNameLength(string domain) { if (domain.Length == 0) diff --git a/TechnitiumLibrary.Net/Dns/ResourceRecords/CanonicallySerializedResourceRecord.cs b/TechnitiumLibrary.Net/Dns/ResourceRecords/CanonicallySerializedResourceRecord.cs index 28d6f368..06866d68 100644 --- a/TechnitiumLibrary.Net/Dns/ResourceRecords/CanonicallySerializedResourceRecord.cs +++ b/TechnitiumLibrary.Net/Dns/ResourceRecords/CanonicallySerializedResourceRecord.cs @@ -56,7 +56,7 @@ public static CanonicallySerializedResourceRecord Create(string name, DnsResourc //serialize owner name | type | class | Original TTL | RDATA length buffer.SetLength(0); - DnsDatagram.SerializeDomainName(name.ToLowerInvariant(), buffer); + DnsDatagram.SerializeDomainName(DnsDatagram.ToLowerInvariantAscii(name), buffer); DnsDatagram.WriteUInt16NetworkOrder((ushort)type, buffer); DnsDatagram.WriteUInt16NetworkOrder((ushort)@class, buffer); DnsDatagram.WriteUInt32NetworkOrder(originalTtl, buffer); diff --git a/TechnitiumLibrary.Net/Dns/ResourceRecords/DnsNSECRecordData.cs b/TechnitiumLibrary.Net/Dns/ResourceRecords/DnsNSECRecordData.cs index 04111ab3..da6a102c 100644 --- a/TechnitiumLibrary.Net/Dns/ResourceRecords/DnsNSECRecordData.cs +++ b/TechnitiumLibrary.Net/Dns/ResourceRecords/DnsNSECRecordData.cs @@ -107,8 +107,8 @@ public static bool IsDomainCovered(string ownerName, string nextDomainName, stri public static int CanonicalComparison(string domain1, string domain2) { - string[] labels1 = domain1.ToLowerInvariant().Split('.'); - string[] labels2 = domain2.ToLowerInvariant().Split('.'); + string[] labels1 = DnsDatagram.ToLowerInvariantAscii(domain1).Split('.'); + string[] labels2 = DnsDatagram.ToLowerInvariantAscii(domain2).Split('.'); int minLength = labels1.Length; @@ -117,7 +117,7 @@ public static int CanonicalComparison(string domain1, string domain2) for (int i = 0; i < minLength; i++) { - int value = CanonicalComparison(Encoding.ASCII.GetBytes(labels1[labels1.Length - 1 - i]), Encoding.ASCII.GetBytes(labels2[labels2.Length - 1 - i])); + int value = CanonicalComparison(Encoding.Latin1.GetBytes(labels1[labels1.Length - 1 - i]), Encoding.Latin1.GetBytes(labels2[labels2.Length - 1 - i])); if (value != 0) return value; }