using System.Text; using XFESystem.Network; namespace XFESystem.Network.Tests; internal sealed class TestPlatform : INetworkPlatform { public long Milliseconds { get; private set; } uint random = 0x91827364; public void Wait(int milliseconds) => Milliseconds += milliseconds; public uint Random32() => ++random; public List Messages { get; } = []; public void Trace(string text) => Messages.Add(text); } /// Protocol peer only for host tests; production NativeNetwork never references this fixture. internal sealed class PacketFixture : IEthernetDevice { public const uint Local = 0x0a00020f, Remote = 0x01020304, Gateway = 0x0a000202, Dns = 0x0a000203; readonly byte[] mac = [0x52, 0x54, 0, 0x12, 0x34, 0x56]; readonly byte[] peerMac = [0x52, 0x55, 0x0a, 0, 2, 2]; readonly Queue incoming = new(); public ReadOnlySpan MacAddress => mac; public List Sent { get; } = []; public List Written { get; } = []; public bool AnswerArp = true, AnswerDns = true, AnswerTcp = true, DropFirstSyn, DropFirstDataAck; public bool AcknowledgeData = true; public Action? DataReceived; public ushort ClientPort, ServerPort; public uint PeerNext = 0x10203041; public int SynCount, DataSends; uint clientNext; public int Receive(Span output) { if (!incoming.TryDequeue(out byte[]? frame)) return 0; if (frame.Length > output.Length) return -1; frame.CopyTo(output); return frame.Length; } public bool Send(ReadOnlySpan data, long deadlineMilliseconds) { byte[] frame = data.ToArray(); Sent.Add(frame); if (NetworkPackets.Read16(frame, 12) == 0x0806 && AnswerArp) { byte[] reply = new byte[60]; mac.CopyTo(reply, 0); peerMac.CopyTo(reply, 6); NetworkPackets.Write16(reply, 12, 0x0806); NetworkPackets.Write16(reply, 14, 1); NetworkPackets.Write16(reply, 16, 0x0800); reply[18] = 6; reply[19] = 4; NetworkPackets.Write16(reply, 20, 2); peerMac.CopyTo(reply, 22); NetworkPackets.Write32(reply, 28, NetworkPackets.Read32(frame, 38)); mac.CopyTo(reply, 32); NetworkPackets.Write32(reply, 38, Local); incoming.Enqueue(reply); return true; } if (NetworkPackets.Read16(frame, 12) != 0x0800) return true; int ipLength = NetworkPackets.Read16(frame, 16); ReadOnlySpan segment = frame.AsSpan(34, ipLength - 20); if (frame[23] == 17 && AnswerDns) { byte[] answer = Answer(segment[8..], Remote); byte[] udp = new byte[8 + answer.Length]; NetworkPackets.Write16(udp, 0, 53); NetworkPackets.Write16(udp, 2, NetworkPackets.Read16(segment, 0)); NetworkPackets.Write16(udp, 4, (ushort)udp.Length); answer.CopyTo(udp, 8); NetworkPackets.Write16(udp, 6, NetworkPackets.TransportChecksum(Dns, Local, 17, udp)); incoming.Enqueue(Ip(Dns, 17, udp)); return true; } if (frame[23] != 6 || !AnswerTcp) return true; uint sequence = NetworkPackets.Read32(segment, 4); byte flags = segment[13]; int header = (segment[12] >> 4) * 4; if ((flags & 2) != 0) { ClientPort = NetworkPackets.Read16(segment, 0); ServerPort = NetworkPackets.Read16(segment, 2); clientNext = unchecked(sequence + 1); SynCount++; if (!(DropFirstSyn && SynCount == 1)) incoming.Enqueue(Tcp(PeerNext - 1, clientNext, 0x12, [])); } else if (segment.Length > header) { DataSends++; ReadOnlySpan bytes = segment[header..]; if (sequence == clientNext) { Written.AddRange(bytes.ToArray()); clientNext += (uint)bytes.Length; } DataReceived?.Invoke(); if (AcknowledgeData && !(DropFirstDataAck && DataSends == 1)) incoming.Enqueue(Tcp(PeerNext, clientNext, 0x10, [])); } else if ((flags & 1) != 0) incoming.Enqueue(Tcp(PeerNext, sequence + 1, 0x10, [])); return true; } public static byte[] Answer(ReadOnlySpan query, uint address) { byte[] result = new byte[query.Length + 16]; query.CopyTo(result); NetworkPackets.Write16(result, 2, 0x8180); NetworkPackets.Write16(result, 6, 1); int at = query.Length; NetworkPackets.Write16(result, at, 0xc00c); NetworkPackets.Write16(result, at + 2, 1); NetworkPackets.Write16(result, at + 4, 1); NetworkPackets.Write32(result, at + 6, 60); NetworkPackets.Write16(result, at + 10, 4); NetworkPackets.Write32(result, at + 12, address); return result; } byte[] Ip(uint source, byte protocol, byte[] data) { byte[] frame = new byte[34 + data.Length]; mac.CopyTo(frame, 0); peerMac.CopyTo(frame, 6); NetworkPackets.Write16(frame, 12, 0x0800); frame[14] = 0x45; NetworkPackets.Write16(frame, 16, (ushort)(20 + data.Length)); frame[22] = 64; frame[23] = protocol; NetworkPackets.Write32(frame, 26, source); NetworkPackets.Write32(frame, 30, Local); NetworkPackets.Write16(frame, 24, NetworkPackets.Checksum(frame.AsSpan(14, 20))); data.CopyTo(frame, 34); return frame; } byte[] Tcp(uint sequence, uint acknowledgement, byte flags, byte[] data, ushort window = 65535) { byte[] segment = new byte[20 + data.Length]; NetworkPackets.Write16(segment, 0, ServerPort); NetworkPackets.Write16(segment, 2, ClientPort); NetworkPackets.Write32(segment, 4, sequence); NetworkPackets.Write32(segment, 8, acknowledgement); segment[12] = 0x50; segment[13] = flags; NetworkPackets.Write16(segment, 14, window); data.CopyTo(segment, 20); NetworkPackets.Write16(segment, 16, NetworkPackets.TransportChecksum(Remote, Local, 6, segment)); return Ip(Remote, 6, segment); } public void QueueData(string value, bool fin = false) { byte[] bytes = Encoding.ASCII.GetBytes(value); incoming.Enqueue(Tcp(PeerNext, clientNext, fin ? (byte)0x19 : (byte)0x18, bytes)); PeerNext += (uint)bytes.Length + (fin ? 1u : 0u); } public void QueueDataAt(uint sequence, string value) => incoming.Enqueue(Tcp(sequence, clientNext, 0x18, Encoding.ASCII.GetBytes(value))); public void QueueCorruptThenValid(string value) { byte[] frame = Tcp(PeerNext, clientNext, 0x18, Encoding.ASCII.GetBytes(value)); frame[^1] ^= 1; incoming.Enqueue(frame); QueueData(value); } public void QueueReset(bool correctSequence) => incoming.Enqueue(Tcp(correctSequence ? PeerNext : PeerNext + 100, clientNext, 0x14, [])); public void QueueAcknowledgementAt(uint sequence, ushort window) => incoming.Enqueue(Tcp(sequence, clientNext, 0x10, [], window)); }