using System; using System.Diagnostics; using System.Net; namespace Shadowsocks.Protocol.Socks5 { public class Socks5RequestReplyMessageBase : Socks5Message { // 5 cmdOrReply 0 aType [addr] port protected byte CmdByte; public EndPoint EndPoint =new IPEndPoint(IPAddress.Any, 0); public override int Serialize(Memory buffer) { var addrLen = NeededBytes(EndPoint); if (buffer.Length < addrLen + 3) throw Util.BufferTooSmall(addrLen + 3, buffer.Length, nameof(buffer)); buffer.Span[0] = 5; buffer.Span[1] = CmdByte; buffer.Span[2] = 0; return SerializeAddress(buffer[3..], EndPoint) + 3; } public override (bool success, int length) TryLoad(ReadOnlyMemory buffer) { if (buffer.Length < 4) return (false, 4); if (buffer.Span[0] != 5 || buffer.Span[2] != 0) return (false, 0); if (buffer.Span[3] == 3 && buffer.Length < 5) return (false, 5); var req = buffer.Span[3] switch { AddressIPv4 => 10, AddressDomain => buffer.Span[4] + 7, AddressIPv6 => 22, _ => 0, }; if (req == 0) return (false, 0); if (buffer.Length < req) return (false, req); (var state, var len) = TryParseAddress(buffer[3..], out var ep); Debug.Assert(state); Debug.Assert(len == req - 3); CmdByte = buffer.Span[1]; EndPoint = ep; return (true, req); } public override bool Equals(IProtocolMessage other) { if (other is null) return false; if (ReferenceEquals(this, other)) return true; if (other.GetType() != GetType()) return false; var msg = (Socks5RequestReplyMessageBase) other; return CmdByte == msg.CmdByte && EndPoint.Equals(msg.EndPoint); } } public class Socks5ReplyMessage : Socks5RequestReplyMessageBase { public byte Reply { get => CmdByte; set => CmdByte = value; } } public class Socks5RequestMessage : Socks5RequestReplyMessageBase { public byte Command { get => CmdByte; set => CmdByte = value; } } }