Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions TechnitiumLibrary.Net/Dns/DnsDatagram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ public static void SerializeDomainName(ReadOnlySpan<char> 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);
Expand Down Expand Up @@ -536,7 +536,7 @@ public static string DeserializeDomainName(Stream s, int maxDepth = 10, bool ign
Span<byte> 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;
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
}
Expand Down