feyris-tan ef86554f9a Import
2025-05-12 22:09:16 +02:00

145 lines
3.2 KiB
C#

using skyscraper5.DNS.Protocol.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace skyscraper5.DNS.Protocol
{
public class Domain : IComparable<Domain>
{
public static Domain FromArray(byte[] message, int offset, out int endOffset)
{
IList<byte[]> labels = new List<byte[]>();
bool endOffsetAssigned = false;
endOffset = 0;
byte lengthOrPointer;
HashSet<int> visitedOffsetPointers = new HashSet<int>();
while ((lengthOrPointer = message[offset++]) > 0)
{
// Two highest bits are set (pointer)
if (lengthOrPointer.GetBitValueAt(6, 2) == 3)
{
if (!endOffsetAssigned)
{
endOffsetAssigned = true;
endOffset = offset + 1;
}
ushort pointer = lengthOrPointer.GetBitValueAt(0, 6);
offset = (pointer << 8) | message[offset];
if (visitedOffsetPointers.Contains(offset))
{
throw new ArgumentException("Compression pointer loop detected");
}
visitedOffsetPointers.Add(offset);
continue;
}
if (lengthOrPointer.GetBitValueAt(6, 2) != 0)
{
throw new IndexOutOfRangeException("Unexpected bit pattern in label length");
}
byte length = lengthOrPointer;
byte[] label = new byte[length];
Array.Copy(message, offset, label, 0, length);
labels.Add(label);
offset += length;
}
if (!endOffsetAssigned)
{
endOffset = offset;
}
return new Domain(labels.ToArray());
}
public int CompareTo(Domain? other)
{
int length = Math.Min(labels.Length, other.labels.Length);
for (int i = 0; i < length; i++)
{
int v = CompareTo(this.labels[i], other.labels[i]);
if (v != 0) return v;
}
return this.labels.Length - other.labels.Length;
}
private static int CompareTo(byte[] a, byte[] b)
{
int length = Math.Min(a.Length, b.Length);
for (int i = 0; i < length; i++)
{
int v = CompareTo(a[i], b[i]);
if (v != 0) return v;
}
return a.Length - b.Length;
}
private static int CompareTo(byte a, byte b)
{
if (IsASCIIAlphabet(a) && IsASCIIAlphabet(b))
{
a &= ASCII_UPPERCASE_MASK;
b &= ASCII_UPPERCASE_MASK;
}
return a - b;
}
private static bool IsASCIIAlphabet(byte b)
{
return (ASCII_UPPERCASE_FIRST <= b && b <= ASCII_UPPERCASE_LAST) ||
(ASCII_LOWERCASE_FIRST <= b && b <= ASCII_LOWERCASE_LAST);
}
private const byte ASCII_UPPERCASE_FIRST = 65;
private const byte ASCII_UPPERCASE_LAST = 90;
private const byte ASCII_LOWERCASE_FIRST = 97;
private const byte ASCII_LOWERCASE_LAST = 122;
private const byte ASCII_UPPERCASE_MASK = 223;
public Domain(byte[][] labels)
{
this.labels = labels;
}
private byte[][] labels;
public int Size
{
get { return labels.Sum(l => l.Length) + labels.Length + 1; }
}
public static Domain FromArray(byte[] message, int offset)
{
return FromArray(message, offset, out offset);
}
[DebuggerStepThrough]
public string ToString(Encoding encoding)
{
return string.Join(".", labels.Select(label => encoding.GetString(label)));
}
[DebuggerStepThrough]
public override string ToString()
{
return ToString(Encoding.ASCII);
}
}
}