diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 00edc00f..300c65dd 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -8,6 +8,7 @@ using System.Timers; using Shadowsocks.Controller.Strategy; using Shadowsocks.Encryption; using Shadowsocks.Model; +using Shadowsocks.Proxy; namespace Shadowsocks.Controller { @@ -87,16 +88,17 @@ namespace Shadowsocks.Controller public IEncryptor encryptor; public Server server; // Client socket. - public Socket remote; + public IProxy remote; public Socket connection; public ShadowsocksController controller; public TCPRelay tcprelay; public DateTime lastActivity; - private const int _maxRetry = 4; + private const int MaxRetry = 4; private int _retryCount = 0; - private bool _connected; + private bool _proxyConnected; + private bool _destConnected; private byte _command; private byte[] _firstPacket; @@ -126,8 +128,8 @@ namespace Shadowsocks.Controller public TCPHandler(TCPRelay tcprelay, Configuration config) { - _tcprelay = tcprelay; - _config = config; + this._tcprelay = tcprelay; + this._config = config; } public void CreateRemote() @@ -300,7 +302,7 @@ namespace Shadowsocks.Controller if (ar.AsyncState != null) { connection.EndSend(ar); - Logging.Debug(remote, RecvSize, "TCP Relay"); + Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else @@ -308,7 +310,7 @@ namespace Shadowsocks.Controller int bytesRead = connection.EndReceive(ar); if (bytesRead > 0) { - Logging.Debug(remote, RecvSize, "TCP Relay"); + Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); } else @@ -337,6 +339,16 @@ namespace Shadowsocks.Controller } // inner class + private class ProxyTimer : Timer + { + public EndPoint DestEndPoint; + public Server Server; + + public ProxyTimer(int p) : base(p) + { + } + } + private class ServerTimer : Timer { public Server Server; @@ -357,32 +369,118 @@ namespace Shadowsocks.Controller IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server); ipAddress = ipHostInfo.AddressList[0]; } - IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port); + IPEndPoint destEP = new IPEndPoint(ipAddress, server.server_port); + + // Setting up proxy + IPEndPoint proxyEP; + if (_config.useProxy) + { + parsed = IPAddress.TryParse(_config.proxyServer, out ipAddress); + if (!parsed) + { + IPHostEntry ipHostInfo = Dns.GetHostEntry(_config.proxyServer); + ipAddress = ipHostInfo.AddressList[0]; + } + + remote = new Socks5Proxy(); + proxyEP = new IPEndPoint(ipAddress, _config.proxyPort); + } + else + { + remote = new DirectConnect(); + proxyEP = destEP; + } - remote = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + + ProxyTimer proxyTimer = new ProxyTimer(3000); + proxyTimer.AutoReset = false; + proxyTimer.Elapsed += proxyConnectTimer_Elapsed; + proxyTimer.Enabled = true; + proxyTimer.DestEndPoint = destEP; + proxyTimer.Server = server; + + _proxyConnected = false; + + // Connect to the proxy server. + remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), proxyTimer); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + Close(); + } + } + + private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) + { + if (_proxyConnected || _destConnected) + { + return; + } + var ep = ((ProxyTimer)sender).DestEndPoint; + + Logging.Info($"Proxy {ep} timed out"); + remote.Close(); + RetryConnect(); + } + + private void ProxyConnectCallback(IAsyncResult ar) + { + Server server = null; + if (_closed) + { + return; + } + try + { + ProxyTimer timer = (ProxyTimer)ar.AsyncState; + var destEP = timer.DestEndPoint; + server = timer.Server; + timer.Elapsed -= proxyConnectTimer_Elapsed; + timer.Enabled = false; + timer.Dispose(); + + // Complete the connection. + remote.EndConnectProxy(ar); + + _proxyConnected = true; + + if (_config.isVerboseLogging) + { + if (!(remote is DirectConnect)) + { + Logging.Info($"Socket connected to proxy {remote.ProxyEndPoint}"); + } + } _startConnectTime = DateTime.Now; ServerTimer connectTimer = new ServerTimer(3000); connectTimer.AutoReset = false; - connectTimer.Elapsed += connectTimer_Elapsed; + connectTimer.Elapsed += destConnectTimer_Elapsed; connectTimer.Enabled = true; connectTimer.Server = server; - - _connected = false; + + _destConnected = false; // Connect to the remote endpoint. - remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer); + remote.BeginConnectDest(destEP, new AsyncCallback(ConnectCallback), connectTimer); + } + catch (ArgumentException) + { } catch (Exception e) { Logging.LogUsefulException(e); - Close(); + RetryConnect(); } } - private void connectTimer_Elapsed(object sender, ElapsedEventArgs e) + private void destConnectTimer_Elapsed(object sender, ElapsedEventArgs e) { - if (_connected) return; + if (_destConnected) + { + return; + } + Server server = ((ServerTimer)sender).Server; IStrategy strategy = controller.GetCurrentStrategy(); strategy?.SetFailure(server); @@ -393,7 +491,7 @@ namespace Shadowsocks.Controller private void RetryConnect() { - if (_retryCount < _maxRetry) + if (_retryCount < MaxRetry) { Logging.Debug($"Connection failed, retry ({_retryCount})"); StartConnect(); @@ -409,15 +507,20 @@ namespace Shadowsocks.Controller try { ServerTimer timer = (ServerTimer)ar.AsyncState; - Server server = timer.Server; - timer.Elapsed -= connectTimer_Elapsed; + server = timer.Server; + timer.Elapsed -= destConnectTimer_Elapsed; timer.Enabled = false; timer.Dispose(); // Complete the connection. - remote.EndConnect(ar); + remote.EndConnectDest(ar); + + _destConnected = true; - _connected = true; + if (_config.isVerboseLogging) + { + Logging.Info($"Socket connected to ss server {remote.DestEndPoint}"); + } var latency = DateTime.Now - _startConnectTime; IStrategy strategy = controller.GetCurrentStrategy(); diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs old mode 100755 new mode 100644 index 9cd2a77a..5b6cd588 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -213,6 +213,20 @@ namespace Shadowsocks.Controller } } + public void DisableProxy() + { + _config.useProxy = false; + SaveConfig(_config); + } + + public void EnableProxy(string proxy, int port) + { + _config.useProxy = true; + _config.proxyServer = proxy; + _config.proxyPort = port; + SaveConfig(_config); + } + public void ToggleVerboseLogging(bool enabled) { _config.isVerboseLogging = enabled; diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index 3f371457..83b7f2d5 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -12,6 +12,7 @@ Servers=服务器 Edit Servers...=编辑服务器... Statistics Config...=统计配置... Start on Boot=开机启动 +Proxy...=代理设置... Allow Clients from LAN=允许来自局域网的连接 Local PAC=使用本地 PAC Online PAC=使用在线 PAC @@ -51,6 +52,13 @@ New server=未配置的服务器 Move &Up=上移(&U) Move D&own=下移(&O) +# Proxy Form + +Edit Proxy=代理设置 +Use Proxy=使用代理 +Proxy Addr=代理地址 +Proxy Port=代理端口 + # Log Form &File=文件(&F) diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index 91c95871..010189ed 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -26,6 +26,9 @@ namespace Shadowsocks.Model public bool autoCheckUpdate; public bool isVerboseLogging; public LogViewerConfig logViewer; + public bool useProxy; + public string proxyServer; + public int proxyPort; private static string CONFIG_FILE = "gui-config.json"; @@ -129,7 +132,7 @@ namespace Shadowsocks.Model throw new ArgumentException(I18N.GetString("Password can not be blank")); } - private static void CheckServer(string server) + public static void CheckServer(string server) { if (server.IsNullOrEmpty()) throw new ArgumentException(I18N.GetString("Server IP can not be blank")); diff --git a/shadowsocks-csharp/Proxy/DirectConnect.cs b/shadowsocks-csharp/Proxy/DirectConnect.cs new file mode 100644 index 00000000..af8d70ae --- /dev/null +++ b/shadowsocks-csharp/Proxy/DirectConnect.cs @@ -0,0 +1,96 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace Shadowsocks.Proxy +{ + public class DirectConnect : IProxy + { + private class FakeAsyncResult : IAsyncResult + { + public FakeAsyncResult(object state) + { + AsyncState = state; + } + + public bool IsCompleted { get; } = true; + public WaitHandle AsyncWaitHandle { get; } = null; + public object AsyncState { get; } + public bool CompletedSynchronously { get; } = true; + } + + private Socket _remote; + + public EndPoint LocalEndPoint => _remote.LocalEndPoint; + + public EndPoint ProxyEndPoint { get; private set; } + + public EndPoint DestEndPoint { get; private set; } + + + public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) + { + // do nothing + ProxyEndPoint = remoteEP; + + var r = new FakeAsyncResult(state); + callback?.Invoke(r); + } + + public void EndConnectProxy(IAsyncResult asyncResult) + { + // do nothing + } + + public void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state) + { + if (_remote == null) + { + _remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + } + + DestEndPoint = remoteEP; + + _remote.BeginConnect(remoteEP, callback, state); + } + + public void EndConnectDest(IAsyncResult asyncResult) + { + _remote.EndConnect(asyncResult); + } + + 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?.Close(); + } + } +} diff --git a/shadowsocks-csharp/Proxy/IProxy.cs b/shadowsocks-csharp/Proxy/IProxy.cs new file mode 100644 index 00000000..4796c92f --- /dev/null +++ b/shadowsocks-csharp/Proxy/IProxy.cs @@ -0,0 +1,38 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace Shadowsocks.Proxy +{ + + public interface IProxy + { + EndPoint LocalEndPoint { get; } + + EndPoint ProxyEndPoint { get; } + + EndPoint DestEndPoint { get; } + + void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state); + + void EndConnectProxy(IAsyncResult asyncResult); + + void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state); + + void EndConnectDest(IAsyncResult asyncResult); + + void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, + object state); + + int EndSend(IAsyncResult asyncResult); + + void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, + object state); + + int EndReceive(IAsyncResult asyncResult); + + void Shutdown(SocketShutdown how); + + void Close(); + } +} diff --git a/shadowsocks-csharp/Proxy/Socks5Proxy.cs b/shadowsocks-csharp/Proxy/Socks5Proxy.cs new file mode 100644 index 00000000..b66a18c4 --- /dev/null +++ b/shadowsocks-csharp/Proxy/Socks5Proxy.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using Shadowsocks.Controller; + +namespace Shadowsocks.Proxy +{ + public class Socks5Proxy : IProxy + { + private class FakeAsyncResult : IAsyncResult + { + public readonly Socks5State innerState; + + private readonly IAsyncResult r; + + public FakeAsyncResult(IAsyncResult orig, Socks5State 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 Socks5State + { + public AsyncCallback Callback { get; set; } + + public object AsyncState { get; set; } + + public int BytesToRead; + + public Exception ex { get; set; } + } + + private Socket _remote; + + private const int Socks5PktMaxSize = 4 + 16 + 2; + private readonly byte[] _receiveBuffer = new byte[Socks5PktMaxSize]; + + public EndPoint LocalEndPoint => _remote.LocalEndPoint; + public EndPoint ProxyEndPoint { get; private set; } + public EndPoint DestEndPoint { get; private set; } + + public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state) + { + _remote = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + + var st = new Socks5State(); + st.Callback = callback; + st.AsyncState = state; + + ProxyEndPoint = remoteEP; + + _remote.BeginConnect(remoteEP, ConnectCallback, st); + } + + public void EndConnectProxy(IAsyncResult asyncResult) + { + var state = ((FakeAsyncResult)asyncResult).innerState; + + if (state.ex != null) + { + throw state.ex; + } + } + + public void BeginConnectDest(EndPoint remoteEP, AsyncCallback callback, object state) + { + var ep = remoteEP as IPEndPoint; + if (ep == null) + { + throw new Exception(I18N.GetString("Proxy request faild")); + } + + byte[] request = null; + byte atyp = 0; + switch (ep.AddressFamily) + { + case AddressFamily.InterNetwork: + request = new byte[4 + 4 + 2]; + atyp = 1; + break; + case AddressFamily.InterNetworkV6: + request = new byte[4 + 16 + 2]; + atyp = 4; + break; + } + if (request == null) + { + throw new Exception(I18N.GetString("Proxy request faild")); + } + + // 构造request包 + var addr = ep.Address.GetAddressBytes(); + request[0] = 5; + request[1] = 1; + request[2] = 0; + request[3] = atyp; + Array.Copy(addr, 0, request, 4, request.Length - 4 - 2); + request[request.Length - 2] = (byte) ((ep.Port >> 8) & 0xff); + request[request.Length - 1] = (byte) (ep.Port & 0xff); + + var st = new Socks5State(); + st.Callback = callback; + st.AsyncState = state; + + DestEndPoint = remoteEP; + + _remote.BeginSend(request, 0, request.Length, 0, Socks5RequestSendCallback, 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?.Close(); + } + + + private void ConnectCallback(IAsyncResult ar) + { + var state = (Socks5State) ar.AsyncState; + try + { + _remote.EndConnect(ar); + + byte[] handshake = {5, 1, 0}; + _remote.BeginSend(handshake, 0, handshake.Length, 0, Socks5HandshakeSendCallback, state); + } + catch (Exception ex) + { + state.ex = ex; + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + } + } + + private void Socks5HandshakeSendCallback(IAsyncResult ar) + { + var state = (Socks5State)ar.AsyncState; + try + { + _remote.EndSend(ar); + + _remote.BeginReceive(_receiveBuffer, 0, 2, 0, Socks5HandshakeReceiveCallback, state); + } + catch (Exception ex) + { + state.ex = ex; + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + } + } + + private void Socks5HandshakeReceiveCallback(IAsyncResult ar) + { + Exception ex = null; + var state = (Socks5State)ar.AsyncState; + try + { + var bytesRead = _remote.EndReceive(ar); + if (bytesRead >= 2) + { + if (_receiveBuffer[0] != 5 || _receiveBuffer[1] != 0) + { + ex = new Exception(I18N.GetString("Proxy handshake faild")); + } + } + else + { + ex = new Exception(I18N.GetString("Proxy handshake faild")); + } + } + catch (Exception ex2) + { + ex = ex2; + } + state.ex = ex; + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + } + + + private void Socks5RequestSendCallback(IAsyncResult ar) + { + var state = (Socks5State)ar.AsyncState; + try + { + _remote.EndSend(ar); + + _remote.BeginReceive(_receiveBuffer, 0, 4, 0, Socks5ReplyReceiveCallback, state); + } + catch (Exception ex) + { + state.ex = ex; + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + } + } + + private void Socks5ReplyReceiveCallback(IAsyncResult ar) + { + var state = (Socks5State)ar.AsyncState; + try + { + var bytesRead = _remote.EndReceive(ar); + if (bytesRead >= 4) + { + if (_receiveBuffer[0] == 5 && _receiveBuffer[1] == 0) + { + // 跳过剩下的reply + switch (_receiveBuffer[3]) // atyp + { + case 1: + state.BytesToRead = 4 + 2; + _remote.BeginReceive(_receiveBuffer, 0, 4 + 2, 0, Socks5ReplyReceiveCallback2, state); + break; + case 4: + state.BytesToRead = 16 + 2; + _remote.BeginReceive(_receiveBuffer, 0, 16 + 2, 0, Socks5ReplyReceiveCallback2, state); + break; + default: + state.ex = new Exception(I18N.GetString("Proxy request faild")); + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + break; + } + } + else + { + state.ex = new Exception(I18N.GetString("Proxy request faild")); + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + } + } + else + { + state.ex = new Exception(I18N.GetString("Proxy request faild")); + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + } + } + catch (Exception ex) + { + state.ex = ex; + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + } + } + + + private void Socks5ReplyReceiveCallback2(IAsyncResult ar) + { + Exception ex = null; + var state = (Socks5State)ar.AsyncState; + try + { + var bytesRead = _remote.EndReceive(ar); + var bytesNeedSkip = state.BytesToRead; + + if (bytesRead < bytesNeedSkip) + { + ex = new Exception(I18N.GetString("Proxy request faild")); + } + } + catch (Exception ex2) + { + ex = ex2; + } + + state.ex = ex; + state.Callback?.Invoke(new FakeAsyncResult(ar, state)); + } + } +} diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs old mode 100755 new mode 100644 index 42f0fa48..2133b2c0 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -48,8 +48,10 @@ namespace Shadowsocks.View private MenuItem editGFWUserRuleItem; private MenuItem editOnlinePACItem; private MenuItem autoCheckUpdatesToggleItem; + private MenuItem proxyItem; private MenuItem VerboseLoggingToggleItem; private ConfigForm configForm; + private ProxyForm proxyForm; private List logForms = new List(); private bool logFormsVisible = false; private string _urlToOpen; @@ -262,6 +264,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("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)), @@ -434,6 +437,20 @@ namespace Shadowsocks.View } } + private void ShowProxyForm() + { + if (proxyForm != null) + { + proxyForm.Activate(); + } + else + { + proxyForm = new ProxyForm(controller); + proxyForm.Show(); + proxyForm.FormClosed += proxyForm_FormClosed; + } + } + private void ShowLogForms() { if (logForms.Count == 0) @@ -472,6 +489,12 @@ namespace Shadowsocks.View } } + void proxyForm_FormClosed(object sender, FormClosedEventArgs e) + { + proxyForm = null; + Utils.ReleaseMemory(true); + } + private void Config_Click(object sender, EventArgs e) { ShowConfigForm(); @@ -783,5 +806,10 @@ namespace Shadowsocks.View { updateChecker.CheckUpdate(controller.GetConfigurationCopy()); } + + private void proxyItem_Click(object sender, EventArgs e) + { + ShowProxyForm(); + } } } diff --git a/shadowsocks-csharp/View/ProxyForm.Designer.cs b/shadowsocks-csharp/View/ProxyForm.Designer.cs new file mode 100644 index 00000000..991da996 --- /dev/null +++ b/shadowsocks-csharp/View/ProxyForm.Designer.cs @@ -0,0 +1,218 @@ +namespace Shadowsocks.View +{ + partial class ProxyForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.MyCancelButton = new System.Windows.Forms.Button(); + this.OKButton = new System.Windows.Forms.Button(); + this.UseProxyCheckBox = new System.Windows.Forms.CheckBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.ProxyAddrLabel = new System.Windows.Forms.Label(); + this.ProxyServerTextBox = new System.Windows.Forms.TextBox(); + this.ProxyPortLable = new System.Windows.Forms.Label(); + this.ProxyPortTextBox = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.AutoSize = true; + 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.UseProxyCheckBox, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 1); + this.tableLayoutPanel1.Location = new System.Drawing.Point(15, 15); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 3; + 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.TabIndex = 0; + // + // tableLayoutPanel3 + // + this.tableLayoutPanel3.AutoSize = true; + this.tableLayoutPanel3.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.tableLayoutPanel3.ColumnCount = 2; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + 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.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + this.tableLayoutPanel3.RowCount = 1; + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel3.Size = new System.Drawing.Size(159, 26); + this.tableLayoutPanel3.TabIndex = 9; + // + // MyCancelButton + // + this.MyCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.MyCancelButton.Dock = System.Windows.Forms.DockStyle.Right; + this.MyCancelButton.Location = new System.Drawing.Point(84, 3); + this.MyCancelButton.Margin = new System.Windows.Forms.Padding(3, 3, 0, 0); + this.MyCancelButton.Name = "MyCancelButton"; + this.MyCancelButton.Size = new System.Drawing.Size(75, 23); + this.MyCancelButton.TabIndex = 13; + this.MyCancelButton.Text = "Cancel"; + this.MyCancelButton.UseVisualStyleBackColor = true; + this.MyCancelButton.Click += new System.EventHandler(this.CancelButton_Click); + // + // OKButton + // + this.OKButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.OKButton.Dock = System.Windows.Forms.DockStyle.Right; + this.OKButton.Location = new System.Drawing.Point(3, 3); + this.OKButton.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); + this.OKButton.Name = "OKButton"; + this.OKButton.Size = new System.Drawing.Size(75, 23); + this.OKButton.TabIndex = 12; + this.OKButton.Text = "OK"; + this.OKButton.UseVisualStyleBackColor = true; + this.OKButton.Click += new System.EventHandler(this.OKButton_Click); + // + // UseProxyCheckBox + // + this.UseProxyCheckBox.AutoSize = true; + this.UseProxyCheckBox.Location = new System.Drawing.Point(3, 3); + this.UseProxyCheckBox.Name = "UseProxyCheckBox"; + this.UseProxyCheckBox.Size = new System.Drawing.Size(78, 16); + this.UseProxyCheckBox.TabIndex = 0; + this.UseProxyCheckBox.Text = "Use Proxy"; + this.UseProxyCheckBox.UseVisualStyleBackColor = true; + this.UseProxyCheckBox.CheckedChanged += new System.EventHandler(this.UseProxyCheckBox_CheckedChanged); + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.AutoSize = true; + this.tableLayoutPanel2.ColumnCount = 4; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.Controls.Add(this.ProxyAddrLabel, 0, 0); + 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.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 1; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(389, 27); + this.tableLayoutPanel2.TabIndex = 1; + // + // ProxyAddrLabel + // + this.ProxyAddrLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.ProxyAddrLabel.AutoSize = true; + this.ProxyAddrLabel.Location = new System.Drawing.Point(3, 7); + this.ProxyAddrLabel.Name = "ProxyAddrLabel"; + this.ProxyAddrLabel.Size = new System.Drawing.Size(65, 12); + this.ProxyAddrLabel.TabIndex = 0; + this.ProxyAddrLabel.Text = "Proxy Addr"; + // + // ProxyServerTextBox + // + this.ProxyServerTextBox.Location = new System.Drawing.Point(74, 3); + this.ProxyServerTextBox.MaxLength = 512; + this.ProxyServerTextBox.Name = "ProxyServerTextBox"; + this.ProxyServerTextBox.Size = new System.Drawing.Size(135, 21); + this.ProxyServerTextBox.TabIndex = 1; + this.ProxyServerTextBox.WordWrap = false; + // + // ProxyPortLable + // + this.ProxyPortLable.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.ProxyPortLable.AutoSize = true; + this.ProxyPortLable.Location = new System.Drawing.Point(215, 7); + this.ProxyPortLable.Name = "ProxyPortLable"; + this.ProxyPortLable.Size = new System.Drawing.Size(65, 12); + this.ProxyPortLable.TabIndex = 2; + this.ProxyPortLable.Text = "Proxy Port"; + // + // ProxyPortTextBox + // + this.ProxyPortTextBox.Location = new System.Drawing.Point(286, 3); + this.ProxyPortTextBox.MaxLength = 10; + this.ProxyPortTextBox.Name = "ProxyPortTextBox"; + this.ProxyPortTextBox.Size = new System.Drawing.Size(100, 21); + this.ProxyPortTextBox.TabIndex = 3; + this.ProxyPortTextBox.WordWrap = false; + // + // ProxyForm + // + this.AcceptButton = this.OKButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.CancelButton = this.MyCancelButton; + this.ClientSize = new System.Drawing.Size(441, 149); + this.Controls.Add(this.tableLayoutPanel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ProxyForm"; + this.Padding = new System.Windows.Forms.Padding(12, 12, 12, 9); + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Edit Proxy"; + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ProxyForm_FormClosed); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.tableLayoutPanel3.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.CheckBox UseProxyCheckBox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Label ProxyAddrLabel; + private System.Windows.Forms.TextBox ProxyServerTextBox; + private System.Windows.Forms.Label ProxyPortLable; + private System.Windows.Forms.TextBox ProxyPortTextBox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Button MyCancelButton; + private System.Windows.Forms.Button OKButton; + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/View/ProxyForm.cs b/shadowsocks-csharp/View/ProxyForm.cs new file mode 100644 index 00000000..4f2a900d --- /dev/null +++ b/shadowsocks-csharp/View/ProxyForm.cs @@ -0,0 +1,116 @@ +using System; +using System.Drawing; +using System.Windows.Forms; +using Shadowsocks.Controller; +using Shadowsocks.Model; +using Shadowsocks.Properties; + +namespace Shadowsocks.View +{ + public partial class ProxyForm : Form + { + private ShadowsocksController controller; + + // this is a copy of configuration that we are working on + private Configuration _modifiedConfiguration; + + public ProxyForm(ShadowsocksController controller) + { + this.Font = System.Drawing.SystemFonts.MessageBoxFont; + InitializeComponent(); + + UpdateTexts(); + this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); + + this.controller = controller; + controller.ConfigChanged += controller_ConfigChanged; + + UpdateEnabled(); + LoadCurrentConfiguration(); + } + + private void UpdateTexts() + { + UseProxyCheckBox.Text = I18N.GetString("Use Proxy"); + ProxyAddrLabel.Text = I18N.GetString("Proxy Addr"); + ProxyPortLable.Text = I18N.GetString("Proxy Port"); + OKButton.Text = I18N.GetString("OK"); + MyCancelButton.Text = I18N.GetString("Cancel"); + this.Text = I18N.GetString("Edit Proxy"); + } + + private void controller_ConfigChanged(object sender, EventArgs e) + { + LoadCurrentConfiguration(); + } + + private void LoadCurrentConfiguration() + { + _modifiedConfiguration = controller.GetConfigurationCopy(); + + UseProxyCheckBox.Checked = _modifiedConfiguration.useProxy; + ProxyServerTextBox.Text = _modifiedConfiguration.proxyServer; + ProxyPortTextBox.Text = _modifiedConfiguration.proxyPort.ToString(); + } + + private void OKButton_Click(object sender, EventArgs e) + { + if (UseProxyCheckBox.Checked) + { + try + { + var proxy = ProxyServerTextBox.Text; + var port = int.Parse(ProxyPortTextBox.Text); + Configuration.CheckServer(proxy); + Configuration.CheckPort(port); + + controller.EnableProxy(proxy, port); + } + catch (FormatException) + { + MessageBox.Show(I18N.GetString("Illegal port number format")); + return; + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + return; + } + } + else + { + controller.DisableProxy(); + } + this.Close(); + } + + private void CancelButton_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void ProxyForm_FormClosed(object sender, FormClosedEventArgs e) + { + controller.ConfigChanged -= controller_ConfigChanged; + } + + private void UseProxyCheckBox_CheckedChanged(object sender, EventArgs e) + { + UpdateEnabled(); + } + + private void UpdateEnabled() + { + if (UseProxyCheckBox.Checked) + { + ProxyServerTextBox.Enabled = true; + ProxyPortTextBox.Enabled = true; + } + else + { + ProxyServerTextBox.Enabled = false; + ProxyPortTextBox.Enabled = false; + } + } + } +} diff --git a/shadowsocks-csharp/View/ProxyForm.resx b/shadowsocks-csharp/View/ProxyForm.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/shadowsocks-csharp/View/ProxyForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 2bd9fa88..9b0899b1 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -138,6 +138,8 @@ + + @@ -173,6 +175,7 @@ + @@ -200,6 +203,12 @@ LogForm.cs + + Form + + + ProxyForm.cs + Form @@ -230,6 +239,9 @@ LogForm.cs + + ProxyForm.cs + QRCodeForm.cs