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(() => 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(() => 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(() => 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(() => 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(() => 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 _)); } }