XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
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));
}