using XFESystem.Network;
namespace XFESystem.Network.Tests;
public sealed class DnsTests
{
static byte[] Name(string value)
{
var bytes = new List<byte>();
foreach (string label in value.Split('.')) { bytes.Add((byte)label.Length); bytes.AddRange(System.Text.Encoding.ASCII.GetBytes(label)); }
bytes.Add(0); return bytes.ToArray();
}
static byte[] Query()
{ byte[] bytes = new byte[512]; int length = DnsMessage.WriteQuery(bytes, 1234, "www.bing.com"); return bytes[..length]; }
[Fact]
public void CompressedAnswerIsBoundToTheExactQuestion()
{
byte[] answer = PacketFixture.Answer(Query(), 0x01020304);
Assert.Equal(0x01020304u, DnsMessage.ReadAddress(answer, 1234, "WWW.BING.COM."));
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1235, "www.bing.com"));
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1234, "other.bing.com"));
}
[Fact]
public void CnameChainCanResolveAnAddressAppearingBeforeItsAlias()
{
byte[] query = Query(), alias = Name("edge.example.com");
byte[] answer = new byte[query.Length + alias.Length + 14 + 12 + alias.Length]; query.CopyTo(answer, 0);
NetworkPackets.Write16(answer, 2, 0x8180); NetworkPackets.Write16(answer, 6, 2); int at = query.Length;
// A record first; parser must bind its owner through the later CNAME.
alias.CopyTo(answer, at); at += alias.Length; NetworkPackets.Write16(answer, at, 1); NetworkPackets.Write16(answer, at + 2, 1);
NetworkPackets.Write32(answer, at + 4, 60); NetworkPackets.Write16(answer, at + 8, 4); NetworkPackets.Write32(answer, at + 10, 0x01020304); at += 14;
NetworkPackets.Write16(answer, at, 0xc00c); NetworkPackets.Write16(answer, at + 2, 5); NetworkPackets.Write16(answer, at + 4, 1);
NetworkPackets.Write32(answer, at + 6, 60); NetworkPackets.Write16(answer, at + 10, (ushort)alias.Length); alias.CopyTo(answer, at + 12);
Assert.Equal(0x01020304u, DnsMessage.ReadAddress(answer, 1234, "www.bing.com"));
}
[Fact]
public void TruncationAndCompressionCyclesFailWithoutUnboundedParsing()
{
byte[] answer = PacketFixture.Answer(Query(), 0x01020304);
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer[..^1], 1234, "www.bing.com"));
answer[12] = 0xc0; answer[13] = 12;
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1234, "www.bing.com"));
}
[Theory]
[InlineData(0x8380)]
[InlineData(0x8183)]
[InlineData(0x0180)]
public void TruncatedNxdomainAndNonresponseAreRejected(int flags)
{
byte[] answer = PacketFixture.Answer(Query(), 0x01020304); NetworkPackets.Write16(answer, 2, (ushort)flags);
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1234, "www.bing.com"));
}
[Fact]
public void UnrelatedAnswerCannotSubstituteAnotherHost()
{
byte[] answer = PacketFixture.Answer(Query(), 0x01020304); int at = Query().Length;
// Point the RR owner at 'bing.com', not the question's 'www.bing.com'.
NetworkPackets.Write16(answer, at, 0xc010);
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1234, "www.bing.com"));
}
[Theory]
[InlineData("bad..name")]
[InlineData("https://www.bing.com/")]
[InlineData("bad\r\nname")]
public void InvalidQueryNamesAreRejected(string host) => Assert.Throws<ArgumentException>(() => DnsMessage.WriteQuery(new byte[512], 1, host));
}
using XFESystem.Network;
namespace XFESystem.Network.Tests;
public sealed class DnsTests
{
static byte[] Name(string value)
{
var bytes = new List<byte>();
foreach (string label in value.Split('.')) { bytes.Add((byte)label.Length); bytes.AddRange(System.Text.Encoding.ASCII.GetBytes(label)); }
bytes.Add(0); return bytes.ToArray();
}
static byte[] Query()
{ byte[] bytes = new byte[512]; int length = DnsMessage.WriteQuery(bytes, 1234, "www.bing.com"); return bytes[..length]; }
[Fact]
public void CompressedAnswerIsBoundToTheExactQuestion()
{
byte[] answer = PacketFixture.Answer(Query(), 0x01020304);
Assert.Equal(0x01020304u, DnsMessage.ReadAddress(answer, 1234, "WWW.BING.COM."));
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1235, "www.bing.com"));
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1234, "other.bing.com"));
}
[Fact]
public void CnameChainCanResolveAnAddressAppearingBeforeItsAlias()
{
byte[] query = Query(), alias = Name("edge.example.com");
byte[] answer = new byte[query.Length + alias.Length + 14 + 12 + alias.Length]; query.CopyTo(answer, 0);
NetworkPackets.Write16(answer, 2, 0x8180); NetworkPackets.Write16(answer, 6, 2); int at = query.Length;
// A record first; parser must bind its owner through the later CNAME.
alias.CopyTo(answer, at); at += alias.Length; NetworkPackets.Write16(answer, at, 1); NetworkPackets.Write16(answer, at + 2, 1);
NetworkPackets.Write32(answer, at + 4, 60); NetworkPackets.Write16(answer, at + 8, 4); NetworkPackets.Write32(answer, at + 10, 0x01020304); at += 14;
NetworkPackets.Write16(answer, at, 0xc00c); NetworkPackets.Write16(answer, at + 2, 5); NetworkPackets.Write16(answer, at + 4, 1);
NetworkPackets.Write32(answer, at + 6, 60); NetworkPackets.Write16(answer, at + 10, (ushort)alias.Length); alias.CopyTo(answer, at + 12);
Assert.Equal(0x01020304u, DnsMessage.ReadAddress(answer, 1234, "www.bing.com"));
}
[Fact]
public void TruncationAndCompressionCyclesFailWithoutUnboundedParsing()
{
byte[] answer = PacketFixture.Answer(Query(), 0x01020304);
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer[..^1], 1234, "www.bing.com"));
answer[12] = 0xc0; answer[13] = 12;
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1234, "www.bing.com"));
}
[Theory]
[InlineData(0x8380)]
[InlineData(0x8183)]
[InlineData(0x0180)]
public void TruncatedNxdomainAndNonresponseAreRejected(int flags)
{
byte[] answer = PacketFixture.Answer(Query(), 0x01020304); NetworkPackets.Write16(answer, 2, (ushort)flags);
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1234, "www.bing.com"));
}
[Fact]
public void UnrelatedAnswerCannotSubstituteAnotherHost()
{
byte[] answer = PacketFixture.Answer(Query(), 0x01020304); int at = Query().Length;
// Point the RR owner at 'bing.com', not the question's 'www.bing.com'.
NetworkPackets.Write16(answer, at, 0xc010);
Assert.Throws<InvalidDataException>(() => DnsMessage.ReadAddress(answer, 1234, "www.bing.com"));
}
[Theory]
[InlineData("bad..name")]
[InlineData("https://www.bing.com/")]
[InlineData("bad\r\nname")]
public void InvalidQueryNamesAreRejected(string host) => Assert.Throws<ArgumentException>(() => DnsMessage.WriteQuery(new byte[512], 1, host));
}