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.

HttpProxy.cs 6.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. using System.Threading;
  7. using Shadowsocks.Controller;
  8. using Shadowsocks.Util.Sockets;
  9. namespace Shadowsocks.Proxy
  10. {
  11. public class HttpProxy : IProxy
  12. {
  13. private class FakeAsyncResult : IAsyncResult
  14. {
  15. public readonly HttpState innerState;
  16. private readonly IAsyncResult r;
  17. public FakeAsyncResult(IAsyncResult orig, HttpState state)
  18. {
  19. r = orig;
  20. innerState = state;
  21. }
  22. public bool IsCompleted => r.IsCompleted;
  23. public WaitHandle AsyncWaitHandle => r.AsyncWaitHandle;
  24. public object AsyncState => innerState.AsyncState;
  25. public bool CompletedSynchronously => r.CompletedSynchronously;
  26. }
  27. private class HttpState
  28. {
  29. public AsyncCallback Callback { get; set; }
  30. public object AsyncState { get; set; }
  31. public int BytesToRead;
  32. public Exception ex { get; set; }
  33. }
  34. public EndPoint LocalEndPoint => _remote.LocalEndPoint;
  35. public EndPoint ProxyEndPoint { get; private set; }
  36. public EndPoint DestEndPoint { get; private set; }
  37. private readonly WrappedSocket _remote = new WrappedSocket();
  38. public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
  39. {
  40. ProxyEndPoint = remoteEP;
  41. _remote.BeginConnect(remoteEP, callback, state);
  42. }
  43. public void EndConnectProxy(IAsyncResult asyncResult)
  44. {
  45. _remote.EndConnect(asyncResult);
  46. _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
  47. }
  48. private const string HTTP_CRLF = "\r\n";
  49. private const string HTTP_CONNECT_TEMPLATE =
  50. "CONNECT {0} HTTP/1.1" + HTTP_CRLF +
  51. "Host: {0}" + HTTP_CRLF +
  52. "Proxy-Connection: keep-alive" + HTTP_CRLF +
  53. "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + HTTP_CRLF +
  54. "{1}" + // Proxy-Authorization if any
  55. "" + HTTP_CRLF; // End with an empty line
  56. private const string PROXY_AUTH_TEMPLATE = "Proxy-Authorization: Basic {0}" + HTTP_CRLF;
  57. public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state, NetworkCredential auth = null)
  58. {
  59. DestEndPoint = destEndPoint;
  60. String authInfo = "";
  61. if (auth != null)
  62. {
  63. string authKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(auth.UserName + ":" + auth.Password));
  64. authInfo = string.Format(PROXY_AUTH_TEMPLATE, authKey);
  65. }
  66. string request = string.Format(HTTP_CONNECT_TEMPLATE, destEndPoint, authInfo);
  67. var b = Encoding.UTF8.GetBytes(request);
  68. var st = new HttpState();
  69. st.Callback = callback;
  70. st.AsyncState = state;
  71. _remote.BeginSend(b, 0, b.Length, 0, HttpRequestSendCallback, st);
  72. }
  73. public void EndConnectDest(IAsyncResult asyncResult)
  74. {
  75. var state = ((FakeAsyncResult)asyncResult).innerState;
  76. if (state.ex != null)
  77. {
  78. throw state.ex;
  79. }
  80. }
  81. public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
  82. object state)
  83. {
  84. _remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
  85. }
  86. public int EndSend(IAsyncResult asyncResult)
  87. {
  88. return _remote.EndSend(asyncResult);
  89. }
  90. public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
  91. object state)
  92. {
  93. _remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
  94. }
  95. public int EndReceive(IAsyncResult asyncResult)
  96. {
  97. return _remote.EndReceive(asyncResult);
  98. }
  99. public void Shutdown(SocketShutdown how)
  100. {
  101. _remote.Shutdown(how);
  102. }
  103. public void Close()
  104. {
  105. _remote.Dispose();
  106. }
  107. private void HttpRequestSendCallback(IAsyncResult ar)
  108. {
  109. var state = (HttpState) ar.AsyncState;
  110. try
  111. {
  112. _remote.EndSend(ar);
  113. // start line read
  114. new LineReader(_remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, 1024, new FakeAsyncResult(ar, state));
  115. }
  116. catch (Exception ex)
  117. {
  118. state.ex = ex;
  119. state.Callback?.Invoke(new FakeAsyncResult(ar, state));
  120. }
  121. }
  122. private void OnFinish(byte[] lastBytes, int index, int length, object state)
  123. {
  124. var st = (FakeAsyncResult)state;
  125. if (st.innerState.ex == null)
  126. {
  127. if (!_established)
  128. {
  129. st.innerState.ex = new Exception(I18N.GetString("Proxy request failed"));
  130. }
  131. // TODO: save last bytes
  132. }
  133. st.innerState.Callback?.Invoke(st);
  134. }
  135. private void OnException(Exception ex, object state)
  136. {
  137. var st = (FakeAsyncResult) state;
  138. st.innerState.ex = ex;
  139. }
  140. private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$", RegexOptions.Compiled);
  141. private int _respondLineCount = 0;
  142. private bool _established = false;
  143. private bool OnLineRead(string line, object state)
  144. {
  145. Logging.Debug(line);
  146. if (_respondLineCount == 0)
  147. {
  148. var m = HttpRespondHeaderRegex.Match(line);
  149. if (m.Success)
  150. {
  151. var resultCode = m.Groups[2].Value;
  152. if ("200" != resultCode)
  153. {
  154. return true;
  155. }
  156. _established = true;
  157. }
  158. }
  159. else
  160. {
  161. if (line.IsNullOrEmpty())
  162. {
  163. return true;
  164. }
  165. }
  166. _respondLineCount++;
  167. return false;
  168. }
  169. }
  170. }