From 35fc73b58b09c1fc4ccbc802dd5b77027f24266d Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 25 Sep 2016 14:37:24 +1000 Subject: [PATCH] Add HTTP proxy support in forward proxy (#745) * Add HTTP proxy support in forward proxy Signed-off-by: noisyfox * Typo * Throw exception if forward proxy not supported. --- .../Controller/Service/TCPRelay.cs | 12 +- .../Controller/ShadowsocksController.cs | 3 +- shadowsocks-csharp/Data/cn.txt | 3 +- shadowsocks-csharp/Data/zh_tw.txt | 3 +- shadowsocks-csharp/Model/Configuration.cs | 5 + shadowsocks-csharp/Model/ProxyConfig.cs | 10 +- shadowsocks-csharp/Proxy/HttpProxy.cs | 199 +++++++++++++++ shadowsocks-csharp/Proxy/Socks5Proxy.cs | 12 +- shadowsocks-csharp/Util/Sockets/LineReader.cs | 231 ++++++++++++++++++ shadowsocks-csharp/View/MenuViewController.cs | 2 +- shadowsocks-csharp/View/ProxyForm.Designer.cs | 65 ++++- shadowsocks-csharp/View/ProxyForm.cs | 8 +- shadowsocks-csharp/shadowsocks-csharp.csproj | 2 + 13 files changed, 532 insertions(+), 23 deletions(-) create mode 100644 shadowsocks-csharp/Proxy/HttpProxy.cs create mode 100644 shadowsocks-csharp/Util/Sockets/LineReader.cs diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 405d2795..d3775fa7 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -420,7 +420,17 @@ namespace Shadowsocks.Controller EndPoint proxyEP; if (_config.proxy.useProxy) { - remote = new Socks5Proxy(); + switch (_config.proxy.proxyType) + { + case ProxyConfig.PROXY_SOCKS5: + remote = new Socks5Proxy(); + break; + case ProxyConfig.PROXY_HTTP: + remote = new HttpProxy(); + break; + default: + throw new NotSupportedException("Unknown forward proxy."); + } proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort); } else diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index d2df00fe..a26676f3 100644 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -221,9 +221,10 @@ namespace Shadowsocks.Controller SaveConfig(_config); } - public void EnableProxy(string proxy, int port) + public void EnableProxy(int type, string proxy, int port) { _config.proxy.useProxy = true; + _config.proxy.proxyType = type; _config.proxy.proxyServer = proxy; _config.proxy.proxyPort = port; SaveConfig(_config); diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index b8bc01d3..7030ff40 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -12,7 +12,7 @@ Servers=服务器 Edit Servers...=编辑服务器... Statistics Config...=统计配置... Start on Boot=开机启动 -SOCKS5 Proxy...=SOCKS5代理设置... +Forward Proxy...=正向代理设置... Allow Clients from LAN=允许来自局域网的连接 Local PAC=使用本地 PAC Online PAC=使用在线 PAC @@ -58,6 +58,7 @@ Move D&own=下移(&O) Edit Proxy=代理设置 Use Proxy=使用代理 +Proxy Type=代理类型 Proxy Addr=代理地址 Proxy Port=代理端口 diff --git a/shadowsocks-csharp/Data/zh_tw.txt b/shadowsocks-csharp/Data/zh_tw.txt index 493050a2..ea3f24ad 100644 --- a/shadowsocks-csharp/Data/zh_tw.txt +++ b/shadowsocks-csharp/Data/zh_tw.txt @@ -12,7 +12,7 @@ Servers=伺服器 Edit Servers...=編輯伺服器... Statistics Config...=統計配置... Start on Boot=開機啟動 -SOCKS5 Proxy...=SOCKS5代理設置... +Forward Proxy...=正向代理設置... Allow Clients from LAN=允許來自區域網路的連接 Local PAC=使用本地 PAC Online PAC=使用在線 PAC @@ -58,6 +58,7 @@ Move D&own=下移(&O) Edit Proxy=代理設置 Use Proxy=使用代理 +Proxy Type=代理類別 Proxy Addr=代理位址 Proxy Port=代理連接埠 diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index 78e63238..beaf3574 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -63,6 +63,11 @@ namespace Shadowsocks.Model config.proxy = new ProxyConfig(); if (config.hotkey == null) config.hotkey = new HotkeyConfig(); + + if (config.proxy.proxyType < ProxyConfig.PROXY_SOCKS5 || config.proxy.proxyType > ProxyConfig.PROXY_HTTP) + { + config.proxy.proxyType = ProxyConfig.PROXY_SOCKS5; + } return config; } catch (Exception e) diff --git a/shadowsocks-csharp/Model/ProxyConfig.cs b/shadowsocks-csharp/Model/ProxyConfig.cs index 0af17edb..1aa83ed6 100644 --- a/shadowsocks-csharp/Model/ProxyConfig.cs +++ b/shadowsocks-csharp/Model/ProxyConfig.cs @@ -1,20 +1,22 @@ -using Shadowsocks.View; -using System; -using System.Drawing; -using System.Windows.Forms; +using System; namespace Shadowsocks.Model { [Serializable] public class ProxyConfig { + public const int PROXY_SOCKS5 = 0; + public const int PROXY_HTTP = 1; + public bool useProxy; + public int proxyType; public string proxyServer; public int proxyPort; public ProxyConfig() { useProxy = false; + proxyType = PROXY_SOCKS5; proxyServer = ""; proxyPort = 0; } diff --git a/shadowsocks-csharp/Proxy/HttpProxy.cs b/shadowsocks-csharp/Proxy/HttpProxy.cs new file mode 100644 index 00000000..f67f9db9 --- /dev/null +++ b/shadowsocks-csharp/Proxy/HttpProxy.cs @@ -0,0 +1,199 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using Shadowsocks.Controller; +using Shadowsocks.Util.Sockets; + +namespace Shadowsocks.Proxy +{ + public class HttpProxy : IProxy + { + private class FakeAsyncResult : IAsyncResult + { + public readonly HttpState innerState; + + private readonly IAsyncResult r; + + public FakeAsyncResult(IAsyncResult orig, HttpState state) + { + r = orig; + innerState = state; + } + + public bool IsCompleted => r.IsCompleted; + public WaitHandle AsyncWaitHandle => r.AsyncWaitHandle; + public object AsyncState => innerState.AsyncState; + public bool CompletedSynchronously => r.CompletedSynchronously; + } + + private class HttpState + { + + public AsyncCallback Callback { get; set; } + + public object AsyncState { get; set; } + + public int BytesToRead; + + public Exception ex { get; set; } + } + + public EndPoint LocalEndPoint => _remote.LocalEndPoint; + public EndPoint ProxyEndPoint { get; private set; } + public EndPoint DestEndPoint { get; private set; } + + + private readonly WrappedSocket _remote = new WrappedSocket(); + + + public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) + { + ProxyEndPoint = remoteEP; + + _remote.BeginConnect(remoteEP, callback, state); + } + + public void EndConnectProxy(IAsyncResult asyncResult) + { + _remote.EndConnect(asyncResult); + _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + } + + private const string HTTP_CRLF = "\r\n"; + private const string HTTP_CONNECT_TEMPLATE = + "CONNECT {0} HTTP/1.1" + HTTP_CRLF + + "Host: {0}" + HTTP_CRLF + + "Proxy-Connection: keep-alive" + HTTP_CRLF + + "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" + HTTP_CRLF + + "" + HTTP_CRLF; // End with an empty line + + public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state) + { + DestEndPoint = destEndPoint; + string request = string.Format(HTTP_CONNECT_TEMPLATE, destEndPoint); + + var b = Encoding.UTF8.GetBytes(request); + + var st = new HttpState(); + st.Callback = callback; + st.AsyncState = state; + + _remote.BeginSend(b, 0, b.Length, 0, HttpRequestSendCallback, st); + } + + public void EndConnectDest(IAsyncResult asyncResult) + { + var state = ((FakeAsyncResult)asyncResult).innerState; + + if (state.ex != null) + { + throw state.ex; + } + } + + public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, + object state) + { + _remote.BeginSend(buffer, offset, size, socketFlags, callback, state); + } + + public int EndSend(IAsyncResult asyncResult) + { + return _remote.EndSend(asyncResult); + } + + public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, + object state) + { + _remote.BeginReceive(buffer, offset, size, socketFlags, callback, state); + } + + public int EndReceive(IAsyncResult asyncResult) + { + return _remote.EndReceive(asyncResult); + } + + public void Shutdown(SocketShutdown how) + { + _remote.Shutdown(how); + } + + public void Close() + { + _remote.Dispose(); + } + + private void HttpRequestSendCallback(IAsyncResult ar) + { + var state = (HttpState) ar.AsyncState; + try + { + _remote.EndSend(ar); + + // start line read + new LineReader(_remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, 1024, new FakeAsyncResult(ar, state)); + } + catch (Exception ex) + { + state.ex = ex; + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + } + } + + private void OnFinish(byte[] lastBytes, int index, int length, object state) + { + // TODO: save last bytes + var st = (FakeAsyncResult)state; + + if (!_established) + { + st.innerState.ex = new Exception(I18N.GetString("Proxy request failed")); + } + st.innerState.Callback?.Invoke(st); + } + + private void OnException(Exception ex, object state) + { + var st = (FakeAsyncResult) state; + + st.innerState.ex = ex; + st.innerState.Callback?.Invoke(st); + } + + private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$"); + private int _respondLineCount = 0; + private bool _established = false; + + private bool OnLineRead(string line, object state) + { + Logging.Debug(line); + + if (_respondLineCount == 0) + { + var m = HttpRespondHeaderRegex.Match(line); + if (m.Success) + { + var resultCode = m.Groups[2].Value; + if ("200" != resultCode) + { + return true; + } + _established = true; + } + } + else + { + if (line.IsNullOrEmpty()) + { + return true; + } + } + _respondLineCount++; + + return false; + } + } +} diff --git a/shadowsocks-csharp/Proxy/Socks5Proxy.cs b/shadowsocks-csharp/Proxy/Socks5Proxy.cs index 5227ae74..10ac3422 100644 --- a/shadowsocks-csharp/Proxy/Socks5Proxy.cs +++ b/shadowsocks-csharp/Proxy/Socks5Proxy.cs @@ -39,7 +39,7 @@ namespace Shadowsocks.Proxy public Exception ex { get; set; } } - private WrappedSocket _remote = new WrappedSocket(); + private readonly WrappedSocket _remote = new WrappedSocket(); private const int Socks5PktMaxSize = 4 + 16 + 2; private readonly byte[] _receiveBuffer = new byte[Socks5PktMaxSize]; @@ -124,7 +124,7 @@ namespace Shadowsocks.Proxy st.Callback = callback; st.AsyncState = state; - _remote?.BeginSend(request, 0, request.Length, 0, Socks5RequestSendCallback, st); + _remote.BeginSend(request, 0, request.Length, 0, Socks5RequestSendCallback, st); } public void EndConnectDest(IAsyncResult asyncResult) @@ -140,7 +140,7 @@ namespace Shadowsocks.Proxy public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state) { - _remote?.BeginSend(buffer, offset, size, socketFlags, callback, state); + _remote.BeginSend(buffer, offset, size, socketFlags, callback, state); } public int EndSend(IAsyncResult asyncResult) @@ -151,7 +151,7 @@ namespace Shadowsocks.Proxy public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state) { - _remote?.BeginReceive(buffer, offset, size, socketFlags, callback, state); + _remote.BeginReceive(buffer, offset, size, socketFlags, callback, state); } public int EndReceive(IAsyncResult asyncResult) @@ -161,12 +161,12 @@ namespace Shadowsocks.Proxy public void Shutdown(SocketShutdown how) { - _remote?.Shutdown(how); + _remote.Shutdown(how); } public void Close() { - _remote?.Dispose(); + _remote.Dispose(); } diff --git a/shadowsocks-csharp/Util/Sockets/LineReader.cs b/shadowsocks-csharp/Util/Sockets/LineReader.cs new file mode 100644 index 00000000..93626371 --- /dev/null +++ b/shadowsocks-csharp/Util/Sockets/LineReader.cs @@ -0,0 +1,231 @@ +using System; +using System.Text; + +namespace Shadowsocks.Util.Sockets +{ + public class LineReader + { + private readonly WrappedSocket _socket; + private readonly Func _onLineRead; + private readonly Action _onException; + private readonly Action _onFinish; + private readonly Encoding _encoding; + // private readonly string _delimiter; + private readonly byte[] _delimiterBytes; + private readonly int[] _delimiterSearchCharTable; + private readonly int[] _delimiterSearchOffsetTable; + private readonly object _state; + + private readonly byte[] _lineBuffer; + + private int _bufferIndex; + + public LineReader(WrappedSocket socket, Func onLineRead, Action onException, + Action onFinish, Encoding encoding, string delimiter, int maxLineBytes, object state) + { + if (socket == null) + { + throw new ArgumentNullException(nameof(socket)); + } + if (onLineRead == null) + { + throw new ArgumentNullException(nameof(onLineRead)); + } + if (encoding == null) + { + throw new ArgumentNullException(nameof(encoding)); + } + if (delimiter == null) + { + throw new ArgumentNullException(nameof(delimiter)); + } + + _socket = socket; + _onLineRead = onLineRead; + _onException = onException; + _onFinish = onFinish; + _encoding = encoding; + // _delimiter = delimiter; + _state = state; + + // decode delimiter + _delimiterBytes = encoding.GetBytes(delimiter); + + if (_delimiterBytes.Length == 0) + { + throw new ArgumentException("Too short!", nameof(delimiter)); + } + + if (maxLineBytes < _delimiterBytes.Length) + { + throw new ArgumentException("Too small!", nameof(maxLineBytes)); + } + + _delimiterSearchCharTable = MakeCharTable(_delimiterBytes); + _delimiterSearchOffsetTable = MakeOffsetTable(_delimiterBytes); + + _lineBuffer = new byte[maxLineBytes]; + + // start reading + socket.BeginReceive(_lineBuffer, 0, maxLineBytes, 0, ReceiveCallback, 0); + } + + private void ReceiveCallback(IAsyncResult ar) + { + int length = (int)ar.AsyncState; + try + { + var bytesRead = _socket.EndReceive(ar); + + if (bytesRead == 0) + { + OnFinish(length); + return; + } + + length += bytesRead; + + int i; + while ((i = IndexOf(_lineBuffer, _bufferIndex, length, _delimiterBytes, _delimiterSearchOffsetTable, + _delimiterSearchCharTable)) != -1) + { + var decodeLen = i - _bufferIndex; + string line = _encoding.GetString(_lineBuffer, _bufferIndex, decodeLen); + + _bufferIndex = i + _delimiterBytes.Length; + length -= decodeLen; + length -= _delimiterBytes.Length; + + var stop = _onLineRead(line, _state); + if (stop) + { + OnFinish(length); + return; + } + } + if (length == _lineBuffer.Length) + { + OnException(new IndexOutOfRangeException("LineBuffer full! Try increace maxLineBytes!")); + OnFinish(length); + + return; + } + + if (_bufferIndex > 0) + { + Buffer.BlockCopy(_lineBuffer, _bufferIndex, _lineBuffer, 0, length); + _bufferIndex = 0; + } + + _socket.BeginReceive(_lineBuffer, length, _lineBuffer.Length - length, 0, ReceiveCallback, length); + } + catch (Exception ex) + { + OnException(ex); + OnFinish(length); + } + } + + private void OnException(Exception ex) + { + _onException?.Invoke(ex, _state); + } + + private void OnFinish(int length) + { + _onFinish?.Invoke(_lineBuffer, _bufferIndex, length, _state); + } + + #region Boyer-Moore string search + + public static int IndexOf(byte[] haystack, int index, int length, byte[] needle, int[] offsetTable, int[] charTable) + { + var end = index + length; + for (int i = needle.Length - 1 + index, j; i < end;) + { + for (j = needle.Length - 1; needle[j] == haystack[i]; --i, --j) + { + if (j == 0) + { + return i; + } + } + // i += needle.length - j; // For naive method + i += Math.Max(offsetTable[needle.Length - 1 - j], charTable[haystack[i]]); + } + return -1; + } + + /** + * Makes the jump table based on the mismatched character information. + */ + private static int[] MakeCharTable(byte[] needle) + { + const int ALPHABET_SIZE = 256; + int[] table = new int[ALPHABET_SIZE]; + for (int i = 0; i < table.Length; ++i) + { + table[i] = needle.Length; + } + for (int i = 0; i < needle.Length - 1; ++i) + { + table[needle[i]] = needle.Length - 1 - i; + } + return table; + } + + /** + * Makes the jump table based on the scan offset which mismatch occurs. + */ + private static int[] MakeOffsetTable(byte[] needle) + { + int[] table = new int[needle.Length]; + int lastPrefixPosition = needle.Length; + for (int i = needle.Length - 1; i >= 0; --i) + { + if (IsPrefix(needle, i + 1)) + { + lastPrefixPosition = i + 1; + } + table[needle.Length - 1 - i] = lastPrefixPosition - i + needle.Length - 1; + } + for (int i = 0; i < needle.Length - 1; ++i) + { + int slen = SuffixLength(needle, i); + table[slen] = needle.Length - 1 - i + slen; + } + return table; + } + + /** + * Is needle[p:end] a prefix of needle? + */ + private static bool IsPrefix(byte[] needle, int p) + { + for (int i = p, j = 0; i < needle.Length; ++i, ++j) + { + if (needle[i] != needle[j]) + { + return false; + } + } + return true; + } + + /** + * Returns the maximum length of the substring ends at p and is a suffix. + */ + private static int SuffixLength(byte[] needle, int p) + { + int len = 0; + for (int i = p, j = needle.Length - 1; + i >= 0 && needle[i] == needle[j]; --i, --j) + { + len += 1; + } + return len; + } + + #endregion + } +} diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index dcce2324..ce63734d 100644 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -271,7 +271,7 @@ namespace Shadowsocks.View this.editGFWUserRuleItem = CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)), this.editOnlinePACItem = CreateMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)), }), - this.proxyItem = CreateMenuItem("SOCKS5 Proxy...", new EventHandler(this.proxyItem_Click)), + this.proxyItem = CreateMenuItem("Forward Proxy...", new EventHandler(this.proxyItem_Click)), new MenuItem("-"), this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), diff --git a/shadowsocks-csharp/View/ProxyForm.Designer.cs b/shadowsocks-csharp/View/ProxyForm.Designer.cs index 991da996..cb564d4e 100644 --- a/shadowsocks-csharp/View/ProxyForm.Designer.cs +++ b/shadowsocks-csharp/View/ProxyForm.Designer.cs @@ -38,9 +38,13 @@ this.ProxyServerTextBox = new System.Windows.Forms.TextBox(); this.ProxyPortLable = new System.Windows.Forms.Label(); this.ProxyPortTextBox = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); + this.ProxyTypeLabel = new System.Windows.Forms.Label(); + this.ProxyTypeComboBox = new System.Windows.Forms.ComboBox(); this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel3.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel4.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel1 @@ -49,16 +53,18 @@ this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.tableLayoutPanel1.ColumnCount = 1; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 0, 3); this.tableLayoutPanel1.Controls.Add(this.UseProxyCheckBox, 0, 0); - this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel4, 0, 1); this.tableLayoutPanel1.Location = new System.Drawing.Point(15, 15); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 3; + this.tableLayoutPanel1.RowCount = 4; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel1.Size = new System.Drawing.Size(395, 87); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(395, 123); this.tableLayoutPanel1.TabIndex = 0; // // tableLayoutPanel3 @@ -72,7 +78,7 @@ this.tableLayoutPanel3.Controls.Add(this.MyCancelButton, 1, 0); this.tableLayoutPanel3.Controls.Add(this.OKButton, 0, 0); this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Right; - this.tableLayoutPanel3.Location = new System.Drawing.Point(236, 58); + this.tableLayoutPanel3.Location = new System.Drawing.Point(236, 94); this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); this.tableLayoutPanel3.Name = "tableLayoutPanel3"; this.tableLayoutPanel3.RowCount = 1; @@ -129,10 +135,11 @@ this.tableLayoutPanel2.Controls.Add(this.ProxyServerTextBox, 1, 0); this.tableLayoutPanel2.Controls.Add(this.ProxyPortLable, 2, 0); this.tableLayoutPanel2.Controls.Add(this.ProxyPortTextBox, 3, 0); - this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 25); + this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 61); this.tableLayoutPanel2.Name = "tableLayoutPanel2"; this.tableLayoutPanel2.RowCount = 1; this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel2.Size = new System.Drawing.Size(389, 27); this.tableLayoutPanel2.TabIndex = 1; // @@ -174,6 +181,45 @@ this.ProxyPortTextBox.TabIndex = 3; this.ProxyPortTextBox.WordWrap = false; // + // tableLayoutPanel4 + // + this.tableLayoutPanel4.AutoSize = true; + this.tableLayoutPanel4.ColumnCount = 2; + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel4.Controls.Add(this.ProxyTypeLabel, 0, 0); + this.tableLayoutPanel4.Controls.Add(this.ProxyTypeComboBox, 1, 0); + this.tableLayoutPanel4.Location = new System.Drawing.Point(3, 25); + this.tableLayoutPanel4.Name = "tableLayoutPanel4"; + this.tableLayoutPanel4.RowCount = 1; + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel4.Size = new System.Drawing.Size(198, 30); + this.tableLayoutPanel4.TabIndex = 10; + // + // ProxyTypeLabel + // + this.ProxyTypeLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.ProxyTypeLabel.AutoSize = true; + this.ProxyTypeLabel.Location = new System.Drawing.Point(3, 9); + this.ProxyTypeLabel.Name = "ProxyTypeLabel"; + this.ProxyTypeLabel.Size = new System.Drawing.Size(65, 12); + this.ProxyTypeLabel.TabIndex = 1; + this.ProxyTypeLabel.Text = "Proxy Type"; + // + // ProxyTypeComboBox + // + this.ProxyTypeComboBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ProxyTypeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.ProxyTypeComboBox.FormattingEnabled = true; + this.ProxyTypeComboBox.Items.AddRange(new object[] { + "SOCKS5", + "HTTP"}); + this.ProxyTypeComboBox.Location = new System.Drawing.Point(74, 5); + this.ProxyTypeComboBox.Margin = new System.Windows.Forms.Padding(3, 5, 3, 5); + this.ProxyTypeComboBox.Name = "ProxyTypeComboBox"; + this.ProxyTypeComboBox.Size = new System.Drawing.Size(121, 20); + this.ProxyTypeComboBox.TabIndex = 2; + // // ProxyForm // this.AcceptButton = this.OKButton; @@ -182,7 +228,7 @@ this.AutoSize = true; this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.CancelButton = this.MyCancelButton; - this.ClientSize = new System.Drawing.Size(441, 149); + this.ClientSize = new System.Drawing.Size(441, 165); this.Controls.Add(this.tableLayoutPanel1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; @@ -197,6 +243,8 @@ this.tableLayoutPanel3.ResumeLayout(false); this.tableLayoutPanel2.ResumeLayout(false); this.tableLayoutPanel2.PerformLayout(); + this.tableLayoutPanel4.ResumeLayout(false); + this.tableLayoutPanel4.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -214,5 +262,8 @@ private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; private System.Windows.Forms.Button MyCancelButton; private System.Windows.Forms.Button OKButton; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4; + private System.Windows.Forms.Label ProxyTypeLabel; + private System.Windows.Forms.ComboBox ProxyTypeComboBox; } } \ No newline at end of file diff --git a/shadowsocks-csharp/View/ProxyForm.cs b/shadowsocks-csharp/View/ProxyForm.cs index 0f780c88..1a18f9c8 100644 --- a/shadowsocks-csharp/View/ProxyForm.cs +++ b/shadowsocks-csharp/View/ProxyForm.cs @@ -32,6 +32,7 @@ namespace Shadowsocks.View private void UpdateTexts() { UseProxyCheckBox.Text = I18N.GetString("Use Proxy"); + ProxyTypeLabel.Text = I18N.GetString("Proxy Type"); ProxyAddrLabel.Text = I18N.GetString("Proxy Addr"); ProxyPortLable.Text = I18N.GetString("Proxy Port"); OKButton.Text = I18N.GetString("OK"); @@ -50,6 +51,7 @@ namespace Shadowsocks.View UseProxyCheckBox.Checked = _modifiedConfiguration.useProxy; ProxyServerTextBox.Text = _modifiedConfiguration.proxyServer; ProxyPortTextBox.Text = _modifiedConfiguration.proxyPort.ToString(); + ProxyTypeComboBox.SelectedIndex = _modifiedConfiguration.proxyType; } private void OKButton_Click(object sender, EventArgs e) @@ -58,12 +60,13 @@ namespace Shadowsocks.View { try { + var type = ProxyTypeComboBox.SelectedIndex; var proxy = ProxyServerTextBox.Text; var port = int.Parse(ProxyPortTextBox.Text); Configuration.CheckServer(proxy); Configuration.CheckPort(port); - controller.EnableProxy(proxy, port); + controller.EnableProxy(type, proxy, port); } catch (FormatException) { @@ -82,6 +85,7 @@ namespace Shadowsocks.View } _modifiedConfiguration.useProxy = UseProxyCheckBox.Checked; + _modifiedConfiguration.proxyType = ProxyTypeComboBox.SelectedIndex; _modifiedConfiguration.proxyServer = ProxyServerTextBox.Text; var tmpProxyPort = 0; int.TryParse(ProxyPortTextBox.Text, out tmpProxyPort); @@ -112,6 +116,7 @@ namespace Shadowsocks.View { ProxyServerTextBox.Enabled = true; ProxyPortTextBox.Enabled = true; + ProxyTypeComboBox.Enabled = true; } else { @@ -119,6 +124,7 @@ namespace Shadowsocks.View ProxyPortTextBox.Clear(); ProxyServerTextBox.Enabled = false; ProxyPortTextBox.Enabled = false; + ProxyTypeComboBox.Enabled = false; } } } diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 44240bae..297c8965 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -146,6 +146,7 @@ + @@ -186,6 +187,7 @@ +