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<string> Messages { get; } = [];
public void Trace(string text) => Messages.Add(text);
}
/// <summary>Protocol peer only for host tests; production NativeNetwork never references this fixture.</summary>
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<byte[]> incoming = new();
public ReadOnlySpan<byte> MacAddress => mac;
public List<byte[]> Sent { get; } = [];
public List<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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));
}
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<string> Messages { get; } = [];
public void Trace(string text) => Messages.Add(text);
}
/// <summary>Protocol peer only for host tests; production NativeNetwork never references this fixture.</summary>
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<byte[]> incoming = new();
public ReadOnlySpan<byte> MacAddress => mac;
public List<byte[]> Sent { get; } = [];
public List<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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));
}