using System.Text;
using XFESystem.Network;
namespace XFESystem.Network.Tests;
public sealed class ProtocolTests
{
static (Ipv4Network Stack, PacketFixture Peer, TestPlatform Clock) Setup()
{ var peer = new PacketFixture(); var clock = new TestPlatform(); return (new(peer, clock, Ipv4Configuration.QemuUserNat), peer, clock); }
[Fact]
public void ActualPacketsPerformArpDnsTcpAndPreserveStreamBytes()
{
var (stack, peer, clock) = Setup(); using Stream stream = stack.OpenTcp("www.bing.com", 443, 1000);
stream.Write("client bytes"u8); Assert.Equal("client bytes", Encoding.ASCII.GetString(peer.Written.ToArray()));
peer.QueueData("server bytes", fin: true); using var result = new MemoryStream(); byte[] part = new byte[3]; int count;
while ((count = stream.Read(part)) != 0) result.Write(part, 0, count);
Assert.Equal("server bytes", Encoding.ASCII.GetString(result.ToArray()));
Assert.Contains(clock.Messages, line => line.Contains("XFE_NETWORK_ARP"));
Assert.Contains(clock.Messages, line => line.Contains("XFE_NETWORK_DNS|Host|www.bing.com|Address|1.2.3.4"));
Assert.Contains(clock.Messages, line => line.Contains("XFE_NETWORK_TCP|Address|1.2.3.4|Port|443|Established|1"));
Assert.All(peer.Sent.Where(frame => NetworkPackets.Read16(frame, 12) == 0x0800), frame =>
{
Assert.Equal(0, NetworkPackets.Checksum(frame.AsSpan(14, 20)));
int length = NetworkPackets.Read16(frame, 16) - 20;
Assert.Equal(0, NetworkPackets.TransportChecksum(NetworkPackets.Read32(frame, 26), NetworkPackets.Read32(frame, 30), frame[23], frame.AsSpan(34, length)));
});
}
[Fact]
public void LostSynAndDataAckRetransmitWithoutDuplicatingApplicationBytes()
{
var (stack, peer, clock) = Setup(); peer.DropFirstSyn = peer.DropFirstDataAck = true;
using Stream stream = stack.OpenTcp("1.2.3.4", 443, 2000); stream.Write("once"u8);
Assert.Equal(2, peer.SynCount); Assert.Equal(2, peer.DataSends);
Assert.Equal("once", Encoding.ASCII.GetString(peer.Written.ToArray())); Assert.True(clock.Milliseconds >= 600);
}
[Fact]
public void CorruptTcpChecksumNeverReachesTheStream()
{
var (stack, peer, _) = Setup(); using Stream stream = stack.OpenTcp("1.2.3.4", 443);
peer.QueueCorruptThenValid("correct"); byte[] bytes = new byte[20];
Assert.Equal(7, stream.Read(bytes)); Assert.Equal("correct", Encoding.ASCII.GetString(bytes, 0, 7));
}
[Fact]
public void OutOfOrderAndDuplicateDataRequireActualRetransmittedBytes()
{
var (stack, peer, _) = Setup(); using Stream stream = stack.OpenTcp("1.2.3.4", 443);
uint sequence = peer.PeerNext; peer.QueueDataAt(sequence + 5, "world"); peer.QueueDataAt(sequence, "hello");
peer.QueueDataAt(sequence, "hello"); peer.QueueDataAt(sequence + 5, "world");
byte[] bytes = new byte[20]; Assert.Equal(10, stream.Read(bytes)); Assert.Equal("helloworld", Encoding.ASCII.GetString(bytes, 0, 10));
}
[Fact]
public void RstRequiresExactNextSequence()
{
var (stack, peer, _) = Setup(); using Stream stream = stack.OpenTcp("1.2.3.4", 443);
peer.QueueReset(false); peer.QueueData("ok"); Assert.Equal(2, stream.Read(new byte[8]));
peer.QueueReset(true); Assert.Throws<IOException>(() => stream.Read(new byte[8]));
}
[Fact]
public void PeerSilenceIsBoundedAndDoesNotInventReadData()
{
var (stack, _, clock) = Setup(); using Stream stream = stack.OpenTcp("1.2.3.4", 443);
stream.ReadTimeout = 40; Assert.Throws<IOException>(() => stream.Read(new byte[10])); Assert.InRange(clock.Milliseconds, 40, 45);
}
[Fact]
public void FailedArpDnsAndTcpHaveBoundedTimeouts()
{
foreach (int layer in new[] { 0, 1, 2 })
{
var (stack, peer, clock) = Setup(); peer.AnswerArp = layer != 0; peer.AnswerDns = layer != 1; peer.AnswerTcp = layer != 2;
Assert.Throws<IOException>(() => stack.OpenTcp("www.bing.com", 443, 50)); Assert.InRange(clock.Milliseconds, 50, 55);
}
}
[Fact]
public void ActiveOwnerIsExclusiveAndDisposeAllowsTheNextRealConnection()
{
var (stack, _, _) = Setup(); using (Stream first = stack.OpenTcp("1.2.3.4", 443))
Assert.Throws<InvalidOperationException>(() => stack.OpenTcp("1.2.3.4", 443));
using Stream second = stack.OpenTcp("1.2.3.4", 80); Assert.True(second.CanRead);
}
[Theory]
[InlineData("0.0.0.0")]
[InlineData("224.0.0.1")]
[InlineData("255.255.255.255")]
[InlineData("10.0.2.15")]
public void UnusableTcpDestinationsAreRejected(string host)
{ var (stack, _, _) = Setup(); Assert.Throws<IOException>(() => stack.OpenTcp(host, 443)); }
[Fact]
public void OddLengthChecksumIncludesTheFinalHighByte()
{
Assert.Equal(0xfbfd, NetworkPackets.Checksum(new byte[] { 1, 2, 3 }));
Assert.True(NetworkPackets.TryParseAddress("10.0.2.15", out uint address)); Assert.Equal(0x0a00020fu, address);
Assert.False(NetworkPackets.TryParseAddress("10.0.2.256", out _));
}
}
using System.Text;
using XFESystem.Network;
namespace XFESystem.Network.Tests;
public sealed class ProtocolTests
{
static (Ipv4Network Stack, PacketFixture Peer, TestPlatform Clock) Setup()
{ var peer = new PacketFixture(); var clock = new TestPlatform(); return (new(peer, clock, Ipv4Configuration.QemuUserNat), peer, clock); }
[Fact]
public void ActualPacketsPerformArpDnsTcpAndPreserveStreamBytes()
{
var (stack, peer, clock) = Setup(); using Stream stream = stack.OpenTcp("www.bing.com", 443, 1000);
stream.Write("client bytes"u8); Assert.Equal("client bytes", Encoding.ASCII.GetString(peer.Written.ToArray()));
peer.QueueData("server bytes", fin: true); using var result = new MemoryStream(); byte[] part = new byte[3]; int count;
while ((count = stream.Read(part)) != 0) result.Write(part, 0, count);
Assert.Equal("server bytes", Encoding.ASCII.GetString(result.ToArray()));
Assert.Contains(clock.Messages, line => line.Contains("XFE_NETWORK_ARP"));
Assert.Contains(clock.Messages, line => line.Contains("XFE_NETWORK_DNS|Host|www.bing.com|Address|1.2.3.4"));
Assert.Contains(clock.Messages, line => line.Contains("XFE_NETWORK_TCP|Address|1.2.3.4|Port|443|Established|1"));
Assert.All(peer.Sent.Where(frame => NetworkPackets.Read16(frame, 12) == 0x0800), frame =>
{
Assert.Equal(0, NetworkPackets.Checksum(frame.AsSpan(14, 20)));
int length = NetworkPackets.Read16(frame, 16) - 20;
Assert.Equal(0, NetworkPackets.TransportChecksum(NetworkPackets.Read32(frame, 26), NetworkPackets.Read32(frame, 30), frame[23], frame.AsSpan(34, length)));
});
}
[Fact]
public void LostSynAndDataAckRetransmitWithoutDuplicatingApplicationBytes()
{
var (stack, peer, clock) = Setup(); peer.DropFirstSyn = peer.DropFirstDataAck = true;
using Stream stream = stack.OpenTcp("1.2.3.4", 443, 2000); stream.Write("once"u8);
Assert.Equal(2, peer.SynCount); Assert.Equal(2, peer.DataSends);
Assert.Equal("once", Encoding.ASCII.GetString(peer.Written.ToArray())); Assert.True(clock.Milliseconds >= 600);
}
[Fact]
public void CorruptTcpChecksumNeverReachesTheStream()
{
var (stack, peer, _) = Setup(); using Stream stream = stack.OpenTcp("1.2.3.4", 443);
peer.QueueCorruptThenValid("correct"); byte[] bytes = new byte[20];
Assert.Equal(7, stream.Read(bytes)); Assert.Equal("correct", Encoding.ASCII.GetString(bytes, 0, 7));
}
[Fact]
public void OutOfOrderAndDuplicateDataRequireActualRetransmittedBytes()
{
var (stack, peer, _) = Setup(); using Stream stream = stack.OpenTcp("1.2.3.4", 443);
uint sequence = peer.PeerNext; peer.QueueDataAt(sequence + 5, "world"); peer.QueueDataAt(sequence, "hello");
peer.QueueDataAt(sequence, "hello"); peer.QueueDataAt(sequence + 5, "world");
byte[] bytes = new byte[20]; Assert.Equal(10, stream.Read(bytes)); Assert.Equal("helloworld", Encoding.ASCII.GetString(bytes, 0, 10));
}
[Fact]
public void RstRequiresExactNextSequence()
{
var (stack, peer, _) = Setup(); using Stream stream = stack.OpenTcp("1.2.3.4", 443);
peer.QueueReset(false); peer.QueueData("ok"); Assert.Equal(2, stream.Read(new byte[8]));
peer.QueueReset(true); Assert.Throws<IOException>(() => stream.Read(new byte[8]));
}
[Fact]
public void PeerSilenceIsBoundedAndDoesNotInventReadData()
{
var (stack, _, clock) = Setup(); using Stream stream = stack.OpenTcp("1.2.3.4", 443);
stream.ReadTimeout = 40; Assert.Throws<IOException>(() => stream.Read(new byte[10])); Assert.InRange(clock.Milliseconds, 40, 45);
}
[Fact]
public void FailedArpDnsAndTcpHaveBoundedTimeouts()
{
foreach (int layer in new[] { 0, 1, 2 })
{
var (stack, peer, clock) = Setup(); peer.AnswerArp = layer != 0; peer.AnswerDns = layer != 1; peer.AnswerTcp = layer != 2;
Assert.Throws<IOException>(() => stack.OpenTcp("www.bing.com", 443, 50)); Assert.InRange(clock.Milliseconds, 50, 55);
}
}
[Fact]
public void ActiveOwnerIsExclusiveAndDisposeAllowsTheNextRealConnection()
{
var (stack, _, _) = Setup(); using (Stream first = stack.OpenTcp("1.2.3.4", 443))
Assert.Throws<InvalidOperationException>(() => stack.OpenTcp("1.2.3.4", 443));
using Stream second = stack.OpenTcp("1.2.3.4", 80); Assert.True(second.CanRead);
}
[Theory]
[InlineData("0.0.0.0")]
[InlineData("224.0.0.1")]
[InlineData("255.255.255.255")]
[InlineData("10.0.2.15")]
public void UnusableTcpDestinationsAreRejected(string host)
{ var (stack, _, _) = Setup(); Assert.Throws<IOException>(() => stack.OpenTcp(host, 443)); }
[Fact]
public void OddLengthChecksumIncludesTheFinalHighByte()
{
Assert.Equal(0xfbfd, NetworkPackets.Checksum(new byte[] { 1, 2, 3 }));
Assert.True(NetworkPackets.TryParseAddress("10.0.2.15", out uint address)); Assert.Equal(0x0a00020fu, address);
Assert.False(NetworkPackets.TryParseAddress("10.0.2.256", out _));
}
}