You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Socks5Message.cs 6.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. using System;
  2. using System.Diagnostics;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. namespace Shadowsocks.Protocol.Socks5
  7. {
  8. public abstract class Socks5Message : IProtocolMessage
  9. {
  10. public abstract int Serialize(Memory<byte> buffer);
  11. public abstract (bool success, int length) TryLoad(ReadOnlyMemory<byte> buffer);
  12. public abstract bool Equals(IProtocolMessage other);
  13. #region Socks5 constants
  14. public const byte AuthNone = 0;
  15. public const byte AuthGssApi = 1;
  16. public const byte AuthUserPass = 2;
  17. public const byte AuthChallengeHandshake = 3;
  18. public const byte AuthChallengeResponse = 5;
  19. public const byte AuthSsl = 6;
  20. public const byte AuthNds = 7;
  21. public const byte AuthMultiAuthenticationFramework = 8;
  22. public const byte AuthJsonParameterBlock = 9;
  23. public const byte AuthNoAcceptable = 0xff;
  24. public const byte AddressIPv4 = 1;
  25. public const byte AddressDomain = 3;
  26. public const byte AddressIPv6 = 4;
  27. public const byte CmdConnect = 1;
  28. public const byte CmdBind = 2;
  29. public const byte CmdUdpAssociation = 3;
  30. public const byte ReplySucceed = 0;
  31. public const byte ReplyFailure = 1;
  32. public const byte ReplyNotAllowed = 2;
  33. public const byte ReplyNetworkUnreachable = 3;
  34. public const byte ReplyHostUnreachable = 4;
  35. public const byte ReplyConnectionRefused = 5;
  36. public const byte ReplyTtlExpired = 6;
  37. public const byte ReplyCommandNotSupport = 7;
  38. public const byte ReplyAddressNotSupport = 8;
  39. #endregion
  40. private static readonly NotSupportedException _addressNotSupport =
  41. new NotSupportedException("Socks5 only support IPv4, IPv6, Domain name address");
  42. #region Address convert
  43. private static (byte high, byte low) ExpandPort(int port)
  44. {
  45. Debug.Assert(port >= 0 && port <= 65535);
  46. return ((byte) (port / 256), (byte) (port % 256));
  47. }
  48. private static int TransformPort(byte high, byte low) => high * 256 + low;
  49. protected static int NeededBytes(EndPoint endPoint)
  50. {
  51. switch (endPoint)
  52. {
  53. case IPEndPoint ipEndPoint when ipEndPoint.AddressFamily == AddressFamily.InterNetwork:
  54. return 7;
  55. case IPEndPoint ipEndPoint when ipEndPoint.AddressFamily == AddressFamily.InterNetworkV6:
  56. return 19;
  57. case DnsEndPoint dnsEndPoint:
  58. var host = Util.EncodeHostName(dnsEndPoint.Host);
  59. return host.Length + 4;
  60. default:
  61. throw _addressNotSupport;
  62. }
  63. }
  64. public static int SerializeAddress(Memory<byte> buffer, EndPoint endPoint)
  65. {
  66. switch (endPoint)
  67. {
  68. case IPEndPoint ipEndPoint when ipEndPoint.AddressFamily == AddressFamily.InterNetwork:
  69. {
  70. if (buffer.Length < 7) throw Util.BufferTooSmall(7, buffer.Length, nameof(buffer));
  71. buffer.Span[0] = AddressIPv4;
  72. Debug.Assert(ipEndPoint.Address.TryWriteBytes(buffer.Span[1..], out var l));
  73. Debug.Assert(l == 4);
  74. (var high, var low) = ExpandPort(ipEndPoint.Port);
  75. buffer.Span[5] = high;
  76. buffer.Span[6] = low;
  77. return 7;
  78. }
  79. case IPEndPoint ipEndPoint when ipEndPoint.AddressFamily == AddressFamily.InterNetworkV6:
  80. {
  81. if (buffer.Length < 19) throw Util.BufferTooSmall(19, buffer.Length, nameof(buffer));
  82. buffer.Span[0] = AddressIPv6;
  83. Debug.Assert(ipEndPoint.Address.TryWriteBytes(buffer.Span[1..], out var l));
  84. Debug.Assert(l == 16);
  85. (var high, var low) = ExpandPort(ipEndPoint.Port);
  86. buffer.Span[18] = low;
  87. buffer.Span[17] = high;
  88. return 19;
  89. }
  90. case DnsEndPoint dnsEndPoint:
  91. {
  92. // 3 lHost [Host] port port
  93. var host = Util.EncodeHostName(dnsEndPoint.Host);
  94. if (host.Length > 255) throw new NotSupportedException("Host name too long");
  95. if (buffer.Length < host.Length + 4)
  96. throw Util.BufferTooSmall(host.Length + 4, buffer.Length, nameof(buffer));
  97. buffer.Span[0] = AddressDomain;
  98. buffer.Span[1] = (byte) host.Length;
  99. Encoding.ASCII.GetBytes(host, buffer.Span[2..]);
  100. (var high, var low) = ExpandPort(dnsEndPoint.Port);
  101. buffer.Span[host.Length + 2] = high;
  102. buffer.Span[host.Length + 3] = low;
  103. return host.Length + 4;
  104. }
  105. default:
  106. throw _addressNotSupport;
  107. }
  108. }
  109. public static (bool success, int length) TryParseAddress(ReadOnlyMemory<byte> buffer,
  110. out EndPoint result)
  111. {
  112. result = default;
  113. if (buffer.Length < 1) return (false, 1);
  114. var addrType = buffer.Span[0];
  115. int len;
  116. switch (addrType)
  117. {
  118. case AddressIPv4:
  119. if (buffer.Length < 7) return (false, 7);
  120. var s = buffer[1..5];
  121. result = new IPEndPoint(
  122. new IPAddress(Util.GetArray(s)),
  123. TransformPort(buffer.Span[5], buffer.Span[6])
  124. );
  125. len = 7;
  126. break;
  127. case AddressDomain:
  128. if (buffer.Length < 2) return (false, 2);
  129. var nameLength = buffer.Span[1];
  130. if (buffer.Length < nameLength + 4) return (false, nameLength + 4);
  131. result = new DnsEndPoint(
  132. Encoding.ASCII.GetString(buffer.Span[2..(nameLength + 2)]),
  133. TransformPort(buffer.Span[nameLength + 2], buffer.Span[nameLength + 3])
  134. );
  135. len = nameLength + 4;
  136. break;
  137. case AddressIPv6:
  138. if (buffer.Length < 19) return (false, 19);
  139. result = new IPEndPoint(new IPAddress(Util.GetArray(buffer[1..17])),
  140. TransformPort(buffer.Span[17], buffer.Span[18]));
  141. len = 19;
  142. break;
  143. default:
  144. return (false, 0);
  145. }
  146. return (true, len);
  147. }
  148. #endregion
  149. }
  150. }