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.

Socks5Service.cs 4.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO.Pipelines;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace Shadowsocks.Protocol.Socks5
  8. {
  9. public class Socks5Service : IStreamService
  10. {
  11. public Socks5Service()
  12. {
  13. }
  14. public Socks5Service(Dictionary<string, string> passwords)
  15. {
  16. enablePassword = true;
  17. this.passwords = passwords;
  18. }
  19. private readonly bool enablePassword;
  20. private readonly Dictionary<string, string> passwords = new Dictionary<string, string>();
  21. public static int ReadTimeout = 120000;
  22. public async Task<bool> IsMyClient(IDuplexPipe pipe)
  23. {
  24. var result = await pipe.Input.ReadAsync();
  25. pipe.Input.AdvanceTo(result.Buffer.Start);
  26. var buffer = result.Buffer;
  27. if (buffer.Length < 3) return false;
  28. if (buffer.First.Span[0] != 5) return false;
  29. if (buffer.First.Span[1] == 0) return false;
  30. // ver 5, has auth method
  31. return true;
  32. }
  33. public async Task<IDuplexPipe> Handle(IDuplexPipe pipe)
  34. {
  35. var pmp = new ProtocolMessagePipe(pipe);
  36. var hs = await pmp.ReadAsync<Socks5VersionIdentifierMessage>();
  37. var selected = Socks5Message.AuthNoAcceptable;
  38. if (enablePassword)
  39. {
  40. foreach (var a in Util.GetArray(hs.Auth))
  41. {
  42. if (a == Socks5Message.AuthUserPass)
  43. {
  44. selected = Socks5Message.AuthUserPass;
  45. break;
  46. }
  47. if (a == Socks5Message.AuthNone)
  48. {
  49. selected = Socks5Message.AuthNone;
  50. }
  51. }
  52. }
  53. else
  54. {
  55. if (Util.GetArray(hs.Auth).Any(a => a == Socks5Message.AuthNone))
  56. {
  57. selected = Socks5Message.AuthNone;
  58. }
  59. }
  60. await pmp.WriteAsync(new Socks5MethodSelectionMessage()
  61. {
  62. SelectedAuth = selected,
  63. });
  64. switch (selected)
  65. {
  66. case Socks5Message.AuthNoAcceptable:
  67. default:
  68. await pipe.Output.CompleteAsync();
  69. return null;
  70. case Socks5Message.AuthNone:
  71. break;
  72. case Socks5Message.AuthUserPass:
  73. var token = await pmp.ReadAsync<Socks5UserPasswordRequestMessage>();
  74. var user = Encoding.UTF8.GetString(token.User.Span);
  75. var password = Encoding.UTF8.GetString(token.Password.Span);
  76. var ar = new Socks5UserPasswordResponseMessage();
  77. var success =
  78. passwords.TryGetValue(user, out var expectPassword)
  79. && expectPassword == password;
  80. ar.Success = success;
  81. await pmp.WriteAsync(ar);
  82. if (!success)
  83. {
  84. await pipe.Output.CompleteAsync();
  85. return null;
  86. }
  87. break;
  88. }
  89. var req = await pmp.ReadAsync<Socks5RequestMessage>();
  90. var resp = new Socks5ReplyMessage();
  91. switch (req.Command)
  92. {
  93. case Socks5Message.CmdBind:
  94. case Socks5Message.CmdUdpAssociation: // not support yet
  95. resp.Reply = Socks5Message.ReplyCommandNotSupport;
  96. break;
  97. case Socks5Message.CmdConnect:
  98. Console.WriteLine(req.EndPoint);
  99. // TODO: route and dial outbound
  100. resp.Reply = Socks5Message.ReplySucceed;
  101. break;
  102. }
  103. // TODO: write response, hand out connection
  104. await pmp.WriteAsync(resp);
  105. if (req.Command != Socks5Message.CmdConnect) return null;
  106. return pipe;
  107. }
  108. }
  109. }