Compare commits

...
32 Commits
Author SHA1 Message Date
noisyfox efc486c733 Update CHANGES and bump version 2016-12-13 21:08:32 +11:00
noisyfox b7b92a4af9 Very rarely used regex, do not compile 2016-12-13 20:47:39 +11:00
noisyfox a447feece2 Adjust qrcode display 2016-12-13 20:43:51 +11:00
noisyfox 2a0dd88525 Add import URL from clipboard 2016-12-13 02:52:58 +11:00
noisyfox 5650b1d847 Use compiled regex 2016-12-13 02:42:08 +11:00
noisyfox b972cb250b Don't zoom QRCode 2016-12-13 02:31:01 +11:00
noisyfox f363b94fd6 Show ss URL in QRCode form 2016-12-13 02:22:37 +11:00
noisyfox a08993519f Handle request line correctlly
This coudl fix #937
2016-12-12 23:53:15 +11:00
noisyfox 8a68a3515b Check server url before saving it
Don't clear the textbox, focus it instead. We want the user to see the wrong input instead of a blank textbox.
2016-12-10 01:50:28 +11:00
noisyfox 76fbd808e3 Add menu item to copy local pac url 2016-12-10 01:26:40 +11:00
noisyfox 557c797a9a Refine code 2016-12-10 00:59:57 +11:00
noisyfox ac45b44c09 Update CHANGES and bump version 2016-12-09 17:59:05 +11:00
noisyfox c0299090c4 Merge branch 'proxy_dev' 2016-12-09 02:48:22 +11:00
noisyfox de4a2f862b Update global proxy bypass list 2016-12-09 02:47:12 +11:00
noisyfox 430137495c Always use {} 2016-12-09 02:36:33 +11:00
noisyfox 566a0fcfa5 Logging exception 2016-12-09 02:17:46 +11:00
noisyfox 2c15520230 Remove unnecessary volatile 2016-12-09 02:17:46 +11:00
noisyfox 9cdb71875a Typo and refine code 2016-12-09 02:17:46 +11:00
noisyfox b04c59d8e6 http -> shadowsocks protocol directly
No more http->socks5 transform.
2016-12-09 02:17:46 +11:00
noisyfox f2b8e91f81 Reuse buffer from first pakcage
Remove some debug output
2016-12-09 02:17:46 +11:00
noisyfox d1e8f38767 Better host parse 2016-12-09 02:17:46 +11:00
noisyfox f9e39fdd1d Refine header parse 2016-12-09 02:17:46 +11:00
noisyfox 5ba0dffa5d First working built-in http proxy 2016-12-09 02:17:46 +11:00
noisyfox 0d4d01af3b Remove privoxy 2016-12-09 02:17:46 +11:00
noisyfox a61b10bb67 Allow LineReader to start with a first package 2016-12-09 02:17:46 +11:00
noisyfox be3cc3f3a6 Rename Shadowsocks.Proxy -> Shadowsocks.ForwardProxy 2016-12-09 02:17:46 +11:00
noisyfox 5c5f753721 Use external agents to set system proxy.
This fix the proxy issue on win10 insider preview.
2016-12-09 02:15:29 +11:00
noisyfox 264b91d6b0 Fix a problem that forward proxy settings are saved twice
Fix #926
2016-12-09 02:05:42 +11:00
noisyfox 4b067346bf Refine random string generate. 2016-12-09 01:39:07 +11:00
noisyfox 9e51b4a41b Add translation 2016-12-08 19:01:07 +11:00
noisyfox 983c1adb28 Check secret in PACServer 2016-12-08 18:54:57 +11:00
noisyfox 22d8cdeff7 Add secret in local pac url. 2016-12-08 18:13:22 +11:00
45 changed files with 1422 additions and 2024 deletions
+14
View File
@@ -1,3 +1,17 @@
3.4.1 (pre-release) 2016-12-13
- Fix crash if user input an invaild server address. (#933)
- Fix ERR_TOO_MANY_REDIRECTS with http proxy. (#937)
- Show SS URL in QRCode form.
- Add import URL from clipboard.
- Bug fixes and improvements.
3.4.0 (pre-release) 2016-12-9
- Replace Privoxy with built-in http proxy.
- Try fix system proxy settings on windows 10 insider preview.
- Secure local pac.
- Update bypass list.
- Bug fixes and improvements.
3.3.6 2016-12-6
- Refine system proxy mode switching logic,
merge 'Switch to PAC' and 'Switch to Global' into
@@ -0,0 +1,291 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Shadowsocks.Model;
using Shadowsocks.Util.Sockets;
using System.Text.RegularExpressions;
namespace Shadowsocks.Controller.Service
{
class HttpHandlerHandlerFactory : ITCPHandlerFactory
{
private static readonly ByteSearch.SearchTarget HttpSearch =
new ByteSearch.SearchTarget(Encoding.UTF8.GetBytes("HTTP"));
public bool CanHandle(byte[] firstPacket, int length)
{
return HttpSearch.SearchIn(firstPacket, 0, length) != -1;
}
public TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
{
return new HttpHandler(controller, config, tcprelay, socket);
}
}
class HttpHandler : TCPHandler
{
private const string HTTP_CRLF = "\r\n";
private const string HTTP_CONNECT_200 =
"HTTP/1.1 200 Connection established" + HTTP_CRLF +
"Proxy-Connection: close" + HTTP_CRLF +
"Proxy-Agent: Shadowsocks" + HTTP_CRLF +
"" + HTTP_CRLF; // End with an empty line
private readonly WrappedSocket _localSocket;
private byte[] _lastBytes;
private int _lastBytesIndex;
private int _lastBytesLength;
public HttpHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) : base(controller, config, tcprelay, socket)
{
_localSocket = new WrappedSocket(socket);
}
public override void StartHandshake(byte[] firstPacket, int length)
{
new LineReader(firstPacket, _localSocket, firstPacket, 0, length, OnLineRead, OnException, OnFinish,
Encoding.UTF8, HTTP_CRLF, null);
}
#region Header Parse
private void OnException(Exception ex, object state)
{
Logging.LogUsefulException(ex);
Close();
}
private static readonly Regex HttpRequestHeaderRegex = new Regex(@"^(?<method>[A-Z]+?) (?<path>[^\s]+) (?<tail>HTTP/1\.\d)$", RegexOptions.Compiled);
private int _requestLineCount = 0;
private bool _isConnect = false;
private string _targetHost;
private int _targetPort;
private readonly Queue<string> _headers = new Queue<string>();
private bool ParseHost(string host)
{
var locs = host.Split(':');
_targetHost = locs[0];
if (locs.Length > 1)
{
if (!int.TryParse(locs[1], out _targetPort))
{
return false;
}
}
else
{
_targetPort = 80;
}
return true;
}
private bool OnLineRead(string line, object state)
{
if (Closed)
{
return true;
}
Logging.Debug(line);
if (_requestLineCount == 0)
{
var m = HttpRequestHeaderRegex.Match(line);
if (m.Success)
{
var method = m.Groups["method"].Value;
var path = m.Groups["path"].Value;
if (method == "CONNECT")
{
_isConnect = true;
if (!ParseHost(path))
{
throw new Exception("Bad http header: " + line);
}
}
else
{
var targetUrl = new Uri(path);
if (!ParseHost(targetUrl.Authority))
{
throw new Exception("Bad http header: " + line);
}
var newRequestLine = $"{method} {targetUrl.PathAndQuery} {m.Groups["tail"].Value}";
_headers.Enqueue(newRequestLine);
}
}
else
{
throw new FormatException("Not a vaild request line");
}
}
else
{
// Handle Proxy-x Headers
if (!line.StartsWith("Proxy-"))
{
_headers.Enqueue(line);
}
else
{
if (line.StartsWith("Proxy-Connection: "))
{
_headers.Enqueue(line.Substring(6));
}
}
if (line.IsNullOrEmpty())
{
return true;
}
if (!_isConnect)
{
if (line.StartsWith("Host: "))
{
if (!ParseHost(line.Substring(6).Trim()))
{
throw new Exception("Bad http header: " + line);
}
}
}
}
_requestLineCount++;
return false;
}
private void OnFinish(byte[] lastBytes, int index, int length, object state)
{
if (Closed)
{
return;
}
if (_targetHost == null)
{
Logging.Error("Unknown host");
Close();
}
else
{
if (length > 0)
{
_lastBytes = lastBytes;
_lastBytesIndex = index;
_lastBytesLength = length;
}
StartConnect(SocketUtil.GetEndPoint(_targetHost, _targetPort));
}
}
#endregion
protected override void OnServerConnected(AsyncSession session)
{
if (_isConnect)
{
// http connect response
SendConnectResponse(session);
}
else
{
// send header
SendHeader(session);
}
}
#region CONNECT
private void SendConnectResponse(AsyncSession session)
{
var len = Encoding.UTF8.GetBytes(HTTP_CONNECT_200, 0, HTTP_CONNECT_200.Length, RemoteRecvBuffer, 0);
_localSocket.BeginSend(RemoteRecvBuffer, 0, len, SocketFlags.None, Http200SendCallback, session);
}
private void Http200SendCallback(IAsyncResult ar)
{
if (Closed)
{
return;
}
try
{
_localSocket.EndSend(ar);
StartPipe((AsyncSession) ar.AsyncState);
}
catch (ArgumentException)
{
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
#endregion
#region Other http method except CONNECT
private void SendHeader(AsyncSession session)
{
var h = _headers.Dequeue() + HTTP_CRLF;
var len = Encoding.UTF8.GetBytes(h, 0, h.Length, ConnetionRecvBuffer, 0);
BeginSendToServer(len, session, HeaderSendCallback);
}
private void HeaderSendCallback(IAsyncResult ar)
{
if (Closed)
{
return;
}
try
{
var session = EndSendToServer(ar);
if (_headers.Count > 0)
{
SendHeader(session);
}
else
{
StartPipe(session);
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
#endregion
}
}
@@ -5,19 +5,23 @@ using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Shadowsocks.Encryption;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
class PACServer : Listener.Service
public class PACServer : Listener.Service
{
public const string PAC_FILE = "pac.txt";
public const string USER_RULE_FILE = "user-rule.txt";
public const string USER_ABP_FILE = "abp.txt";
private string PacSecret { get; set; } = "";
public string PacUrl { get; private set; } = "";
FileSystemWatcher PACFileWatcher;
FileSystemWatcher UserRuleFileWatcher;
private Configuration _config;
@@ -34,6 +38,25 @@ namespace Shadowsocks.Controller
public void UpdateConfiguration(Configuration config)
{
this._config = config;
if (config.secureLocalPac)
{
var rd = new byte[32];
RNG.GetBytes(rd);
PacSecret = $"&secret={Convert.ToBase64String(rd)}";
}
else
{
PacSecret = "";
}
PacUrl = $"http://127.0.0.1:{config.localPort}/pac?t={GetTimestamp(DateTime.Now)}{PacSecret}";
}
private static string GetTimestamp(DateTime value)
{
return value.ToString("yyyyMMddHHmmssfff");
}
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
@@ -47,6 +70,7 @@ namespace Shadowsocks.Controller
string request = Encoding.UTF8.GetString(firstPacket, 0, length);
string[] lines = request.Split('\r', '\n');
bool hostMatch = false, pathMatch = false, useSocks = false;
bool secretMatch = PacSecret.IsNullOrEmpty();
foreach (string line in lines)
{
string[] kv = line.Split(new char[] { ':' }, 2);
@@ -74,11 +98,25 @@ namespace Shadowsocks.Controller
{
pathMatch = true;
}
if (!secretMatch)
{
if(line.IndexOf(PacSecret, StringComparison.Ordinal) >= 0)
{
secretMatch = true;
}
}
}
}
if (hostMatch && pathMatch)
{
SendResponse(firstPacket, length, socket, useSocks);
if (!secretMatch)
{
socket.Close(); // Close immediately
}
else
{
SendResponse(firstPacket, length, socket, useSocks);
}
return true;
}
return false;
@@ -1,261 +0,0 @@
using System;
using System.Net;
using System.Net.Sockets;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Controller
{
class PortForwarder : Listener.Service
{
int _targetPort;
public PortForwarder(int targetPort)
{
this._targetPort = targetPort;
}
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
{
if (socket.ProtocolType != ProtocolType.Tcp)
{
return false;
}
new Handler().Start(firstPacket, length, socket, this._targetPort);
return true;
}
class Handler
{
private byte[] _firstPacket;
private int _firstPacketLength;
private Socket _local;
private WrappedSocket _remote;
private bool _closed = false;
private bool _localShutdown = false;
private bool _remoteShutdown = false;
public const int RecvSize = 16384;
// remote receive buffer
private byte[] remoteRecvBuffer = new byte[RecvSize];
// connection receive buffer
private byte[] connetionRecvBuffer = new byte[RecvSize];
// instance-based lock
private readonly object _Lock = new object();
public void Start(byte[] firstPacket, int length, Socket socket, int targetPort)
{
this._firstPacket = firstPacket;
this._firstPacketLength = length;
this._local = socket;
try
{
EndPoint remoteEP = SocketUtil.GetEndPoint("127.0.0.1", targetPort);
// Connect to the remote endpoint.
_remote = new WrappedSocket();
_remote.BeginConnect(remoteEP, ConnectCallback, null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void ConnectCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
_remote.EndConnect(ar);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
HandshakeReceive();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void HandshakeReceive()
{
if (_closed)
{
return;
}
try
{
_remote.BeginSend(_firstPacket, 0, _firstPacketLength, 0, new AsyncCallback(StartPipe), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void StartPipe(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
_remote.EndSend(ar);
_remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null);
_local.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeConnectionReceiveCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void PipeRemoteReceiveCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
int bytesRead = _remote.EndReceive(ar);
if (bytesRead > 0)
{
_local.BeginSend(remoteRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeConnectionSendCallback), null);
}
else
{
_local.Shutdown(SocketShutdown.Send);
_localShutdown = true;
CheckClose();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void PipeConnectionReceiveCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
int bytesRead = _local.EndReceive(ar);
if (bytesRead > 0)
{
_remote.BeginSend(connetionRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeRemoteSendCallback), null);
}
else
{
_remote.Shutdown(SocketShutdown.Send);
_remoteShutdown = true;
CheckClose();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void PipeRemoteSendCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
_remote.EndSend(ar);
_local.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeConnectionReceiveCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void PipeConnectionSendCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
_local.EndSend(ar);
_remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void CheckClose()
{
if (_localShutdown && _remoteShutdown)
{
this.Close();
}
}
public void Close()
{
lock (_Lock)
{
if (_closed)
{
return;
}
_closed = true;
}
if (_local != null)
{
try
{
_local.Shutdown(SocketShutdown.Both);
_local.Close();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}
if (_remote != null)
{
try
{
_remote.Shutdown(SocketShutdown.Both);
_remote.Dispose();
}
catch (SocketException e)
{
Logging.LogUsefulException(e);
}
}
}
}
}
}
@@ -1,216 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using Shadowsocks.Util.ProcessManagement;
namespace Shadowsocks.Controller
{
class PrivoxyRunner
{
private static int Uid;
private static string UniqueConfigFile;
private static Job PrivoxyJob;
private Process _process;
private int _runningPort;
static PrivoxyRunner()
{
try
{
Uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different Privoxy instance.
UniqueConfigFile = $"privoxy_{Uid}.conf";
PrivoxyJob = new Job();
FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe);
FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll);
}
catch (IOException e)
{
Logging.LogUsefulException(e);
}
}
public int RunningPort => _runningPort;
public void Start(Configuration configuration)
{
if (_process == null)
{
Process[] existingPrivoxy = Process.GetProcessesByName("ss_privoxy");
foreach (Process p in existingPrivoxy.Where(IsChildProcess))
{
KillProcess(p);
}
string privoxyConfig = Resources.privoxy_conf;
_runningPort = this.GetFreePort();
privoxyConfig = privoxyConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString());
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_PORT__", _runningPort.ToString());
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
FileManager.ByteArrayToFile(Utils.GetTempPath(UniqueConfigFile), Encoding.UTF8.GetBytes(privoxyConfig));
_process = new Process();
// Configure the process using the StartInfo properties.
_process.StartInfo.FileName = "ss_privoxy.exe";
_process.StartInfo.Arguments = UniqueConfigFile;
_process.StartInfo.WorkingDirectory = Utils.GetTempPath();
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_process.StartInfo.UseShellExecute = true;
_process.StartInfo.CreateNoWindow = true;
_process.Start();
/*
* Add this process to job obj associated with this ss process, so that
* when ss exit unexpectedly, this process will be forced killed by system.
*/
PrivoxyJob.AddProcess(_process.Handle);
}
RefreshTrayArea();
}
public void Stop()
{
if (_process != null)
{
KillProcess(_process);
_process.Dispose();
_process = null;
}
RefreshTrayArea();
}
private static void KillProcess(Process p)
{
try
{
p.CloseMainWindow();
p.WaitForExit(100);
if (!p.HasExited)
{
p.Kill();
p.WaitForExit();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}
/*
* We won't like to kill other ss instances' ss_privoxy.exe.
* This function will check whether the given process is created
* by this process by checking the module path or command line.
*
* Since it's required to put ss in different dirs to run muti instances,
* different instance will create their unique "privoxy_UID.conf" where
* UID is hash of ss's location.
*/
private static bool IsChildProcess(Process process)
{
try
{
if (Utils.IsPortableMode())
{
/*
* Under PortableMode, we could identify it by the path of ss_privoxy.exe.
*/
var path = process.MainModule.FileName;
return Utils.GetTempPath("ss_privoxy.exe").Equals(path);
}
else
{
var cmd = process.GetCommandLine();
return cmd.Contains(UniqueConfigFile);
}
}
catch (Exception ex)
{
/*
* Sometimes Process.GetProcessesByName will return some processes that
* are already dead, and that will cause exceptions here.
* We could simply ignore those exceptions.
*/
Logging.LogUsefulException(ex);
return false;
}
}
private int GetFreePort()
{
int defaultPort = 8123;
try
{
// TCP stack please do me a favor
TcpListener l = new TcpListener(IPAddress.Loopback, 0);
l.Start();
var port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}
catch (Exception e)
{
// in case access denied
Logging.LogUsefulException(e);
return defaultPort;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
public void RefreshTrayArea()
{
IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null);
IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null);
IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area");
if (notificationAreaHandle == IntPtr.Zero)
{
notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area");
IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null);
IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area");
RefreshTrayArea(overflowNotificationAreaHandle);
}
RefreshTrayArea(notificationAreaHandle);
}
private static void RefreshTrayArea(IntPtr windowHandle)
{
const uint wmMousemove = 0x0200;
RECT rect;
GetClientRect(windowHandle, out rect);
for (var x = 0; x < rect.right; x += 5)
for (var y = 0; y < rect.bottom; y += 5)
SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x);
}
}
}
@@ -0,0 +1,293 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using Shadowsocks.Model;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Controller.Service
{
class Socks5HandlerFactory : ITCPHandlerFactory
{
public bool CanHandle(byte[] firstPacket, int length)
{
return length >= 2 && firstPacket[0] == 5;
}
public TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
{
return new Socks5Handler(controller, config, tcprelay, socket);
}
}
class Socks5Handler : TCPHandler
{
private byte _command;
private int _firstPacketLength;
public Socks5Handler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) : base(controller, config, tcprelay, socket, false)
{
}
public override void StartHandshake(byte[] firstPacket, int length)
{
if (Closed) return;
try
{
int bytesRead = length;
if (bytesRead > 1)
{
byte[] response = { 5, 0 };
if (firstPacket[0] != 5)
{
// reject socks 4
response = new byte[] { 0, 91 };
Logging.Error("socks 5 protocol error");
}
Connection.BeginSend(response, 0, response.Length, SocketFlags.None, HandshakeSendCallback, null);
}
else
Close();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void HandshakeSendCallback(IAsyncResult ar)
{
if (Closed) return;
try
{
Connection.EndSend(ar);
// +-----+-----+-------+------+----------+----------+
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +-----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +-----+-----+-------+------+----------+----------+
// Skip first 3 bytes, and read 2 more bytes to analysis the address.
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length.
// TODO validate
Connection.BeginReceive(ConnetionRecvBuffer, 0, 3 + 2, SocketFlags.None,
handshakeReceive2Callback, null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void handshakeReceive2Callback(IAsyncResult ar)
{
if (Closed) return;
try
{
int bytesRead = Connection.EndReceive(ar);
if (bytesRead >= 5)
{
_command = ConnetionRecvBuffer[1];
if (_command != 1 && _command != 3)
{
Logging.Debug("Unsupported CMD=" + _command);
Close();
}
else
{
int atyp = ConnetionRecvBuffer[3];
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
ReadAddress(4 + 2 - 1);
break;
case 3: // domain name, length + str
int len = ConnetionRecvBuffer[4];
ReadAddress(len + 2);
break;
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
Close();
break;
}
}
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()");
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ReadAddress(int bytesRemain)
{
Array.Copy(ConnetionRecvBuffer, 3, ConnetionRecvBuffer, 0, 2);
// Read the remain address bytes
Connection.BeginReceive(ConnetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, bytesRemain);
}
private void OnAddressFullyRead(IAsyncResult ar)
{
if (Closed) return;
try
{
int bytesRead = Connection.EndReceive(ar);
int bytesRemain = (int)ar.AsyncState;
if (bytesRead >= bytesRemain)
{
_firstPacketLength = bytesRead + 2;
int atyp = ConnetionRecvBuffer[0];
string dst_addr = "Unknown";
int dst_port = -1;
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
dst_addr = new IPAddress(ConnetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
dst_port = (ConnetionRecvBuffer[5] << 8) + ConnetionRecvBuffer[6];
break;
case 3: // domain name, length + str
int len = ConnetionRecvBuffer[1];
dst_addr = System.Text.Encoding.UTF8.GetString(ConnetionRecvBuffer, 2, len);
dst_port = (ConnetionRecvBuffer[len + 2] << 8) + ConnetionRecvBuffer[len + 3];
break;
case 4: // IPv6 address, 16 bytes
dst_addr = $"[{new IPAddress(ConnetionRecvBuffer.Skip(1).Take(16).ToArray())}]";
dst_port = (ConnetionRecvBuffer[17] << 8) + ConnetionRecvBuffer[18];
break;
}
if (Config.isVerboseLogging)
{
Logging.Info($"connect to {dst_addr}:{dst_port}");
}
var destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port);
if (_command == 1)
{
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
Connection.BeginSend(response, 0, response.Length, SocketFlags.None,
ResponseCallback, destEndPoint);
}
else if (_command == 3)
{
HandleUDPAssociate();
}
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.OnAddressFullyRead()");
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void HandleUDPAssociate()
{
IPEndPoint endPoint = (IPEndPoint)Connection.LocalEndPoint;
byte[] address = endPoint.Address.GetAddressBytes();
int port = endPoint.Port;
byte[] response = new byte[4 + address.Length + 2];
response[0] = 5;
switch (endPoint.AddressFamily)
{
case AddressFamily.InterNetwork:
response[3] = 1;
break;
case AddressFamily.InterNetworkV6:
response[3] = 4;
break;
}
address.CopyTo(response, 4);
response[response.Length - 1] = (byte)(port & 0xFF);
response[response.Length - 2] = (byte)((port >> 8) & 0xFF);
Connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true);
}
private void ReadAll(IAsyncResult ar)
{
if (Closed) return;
try
{
if (ar.AsyncState != null)
{
Connection.EndSend(ar);
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
{
int bytesRead = Connection.EndReceive(ar);
if (bytesRead > 0)
{
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ResponseCallback(IAsyncResult ar)
{
try
{
Connection.EndSend(ar);
StartConnect((EndPoint) ar.AsyncState);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
protected override void OnServerConnected(AsyncSession session)
{
BeginSendToServer(_firstPacketLength, session, FirstPackageSendCallback);
}
private void FirstPackageSendCallback(IAsyncResult ar)
{
if (Closed) return;
try
{
var session = EndSendToServer(ar);
StartPipe(session);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
}
}
+145 -339
View File
@@ -1,17 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Timers;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption;
using Shadowsocks.ForwardProxy;
using Shadowsocks.Model;
using Shadowsocks.Proxy;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Controller
namespace Shadowsocks.Controller.Service
{
class TCPRelay : Listener.Service
{
@@ -19,23 +17,39 @@ namespace Shadowsocks.Controller
private DateTime _lastSweepTime;
private Configuration _config;
public ISet<TCPHandler> Handlers { get; set; }
private readonly List<ITCPHandlerFactory> _factories = new List<ITCPHandlerFactory>();
public ISet<TCPHandler> Handlers { get; } = new HashSet<TCPHandler>();
public TCPRelay(ShadowsocksController controller, Configuration conf)
{
_controller = controller;
_config = conf;
Handlers = new HashSet<TCPHandler>();
_lastSweepTime = DateTime.Now;
_factories.Add(new Socks5HandlerFactory());
_factories.Add(new HttpHandlerHandlerFactory());
}
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
{
if (socket.ProtocolType != ProtocolType.Tcp
|| (length < 2 || firstPacket[0] != 5))
TCPHandler handler = null;
foreach (var factory in _factories)
{
if (factory.CanHandle(firstPacket, length))
{
handler = factory.NewHandler(_controller, _config, this, socket);
break;
}
}
if (handler == null)
{
return false;
}
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
TCPHandler handler = new TCPHandler(_controller, _config, this, socket);
IList<TCPHandler> handlersToClose = new List<TCPHandler>();
lock (Handlers)
@@ -62,7 +76,7 @@ namespace Shadowsocks.Controller
* Then the handler will never release until the next Handle call. Sometimes it will
* cause odd problems (especially during memory profiling).
*/
handler.Start(firstPacket, length);
handler.StartHandshake(firstPacket, length);
return true;
}
@@ -74,7 +88,7 @@ namespace Shadowsocks.Controller
{
handlersToClose.AddRange(Handlers);
}
handlersToClose.ForEach(h=>h.Close());
handlersToClose.ForEach(h => h.Close());
}
public void UpdateInboundCounter(Server server, long n)
@@ -93,28 +107,40 @@ namespace Shadowsocks.Controller
}
}
class TCPHandler
interface ITCPHandlerFactory
{
class AsyncSession
{
public IProxy Remote { get; }
bool CanHandle(byte[] firstPacket, int length);
public AsyncSession(IProxy remote)
TCPHandler NewHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket);
}
abstract class TCPHandler
{
public abstract void StartHandshake(byte[] firstPacket, int length);
protected abstract void OnServerConnected(AsyncSession session);
protected class AsyncSession
{
public IForwardProxy Remote { get; }
public AsyncSession(IForwardProxy remote)
{
Remote = remote;
}
}
class AsyncSession<T> : AsyncSession
protected class AsyncSession<T> : AsyncSession
{
public T State { get; set; }
public AsyncSession(IProxy remote, T state) : base(remote)
public AsyncSession(IForwardProxy remote, T state) : base(remote)
{
State = state;
}
public AsyncSession(AsyncSession session, T state): base(session.Remote)
public AsyncSession(AsyncSession session, T state) : base(session.Remote)
{
State = state;
}
@@ -130,39 +156,33 @@ namespace Shadowsocks.Controller
public DateTime lastActivity;
private ShadowsocksController _controller;
private Configuration _config;
private TCPRelay _tcprelay;
private Socket _connection;
private IEncryptor _encryptor;
private Server _server;
private ShadowsocksController _controller;
protected Configuration Config { get; }
private TCPRelay _tcprelay;
protected Socket Connection { get; }
private Server _server;
private AsyncSession _currentRemoteSession;
private bool _proxyConnected;
private bool _destConnected;
private bool _proxyConnected;
private bool _destConnected;
private byte _command;
private byte[] _firstPacket;
private int _firstPacketLength;
private int _totalRead = 0;
private int _totalWrite = 0;
private int _totalRead = 0;
private int _totalWrite = 0;
protected byte[] RemoteRecvBuffer { get; } = new byte[BufferSize];
private readonly byte[] _remoteSendBuffer = new byte[BufferSize];
protected byte[] ConnetionRecvBuffer { get; } = new byte[BufferSize];
private readonly byte[] _connetionSendBuffer = new byte[BufferSize];
private byte[] _remoteRecvBuffer = new byte[BufferSize];
private byte[] _remoteSendBuffer = new byte[BufferSize];
private byte[] _connetionRecvBuffer = new byte[BufferSize];
private byte[] _connetionSendBuffer = new byte[BufferSize];
private IEncryptor _encryptor;
private readonly object _encryptionLock = new object();
private readonly object _decryptionLock = new object();
private bool _connectionShutdown = false;
private bool _remoteShutdown = false;
private bool _closed = false;
// instance-based lock without static
private readonly object _encryptionLock = new object();
private readonly object _decryptionLock = new object();
private readonly object _closeConnLock = new object();
private bool _connectionShutdown = false;
private bool _remoteShutdown = false;
protected bool Closed { get; private set; }= false;
private readonly object _closeConnLock = new object();
private DateTime _startConnectTime;
private DateTime _startReceivingTime;
@@ -170,21 +190,24 @@ namespace Shadowsocks.Controller
private EndPoint _destEndPoint = null;
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket, bool autoAppendHeader = true)
{
_controller = controller;
_config = config;
Config = config;
_tcprelay = tcprelay;
_connection = socket;
Connection = socket;
_proxyTimeout = config.proxy.proxyTimeout * 1000;
_serverTimeout = config.GetCurrentServer().timeout * 1000;
lastActivity = DateTime.Now;
_serverHeaderSent = !autoAppendHeader;
}
public void CreateRemote()
private void CreateRemote()
{
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, _destEndPoint);
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)Connection.RemoteEndPoint, _destEndPoint);
if (server == null || server.server == "")
throw new ArgumentException("No server configured");
lock (_encryptionLock)
@@ -197,13 +220,6 @@ namespace Shadowsocks.Controller
this._server = server;
}
public void Start(byte[] firstPacket, int length)
{
_firstPacket = firstPacket;
_firstPacketLength = length;
HandshakeReceive();
}
private void CheckClose()
{
if (_connectionShutdown && _remoteShutdown)
@@ -214,8 +230,8 @@ namespace Shadowsocks.Controller
{
lock (_closeConnLock)
{
if (_closed) return;
_closed = true;
if (Closed) return;
Closed = true;
}
lock (_tcprelay.Handlers)
{
@@ -223,8 +239,8 @@ namespace Shadowsocks.Controller
}
try
{
_connection.Shutdown(SocketShutdown.Both);
_connection.Close();
Connection.Shutdown(SocketShutdown.Both);
Connection.Close();
}
catch (Exception e)
{
@@ -249,245 +265,6 @@ namespace Shadowsocks.Controller
}
}
private void HandshakeReceive()
{
if (_closed) return;
try
{
int bytesRead = _firstPacketLength;
if (bytesRead > 1)
{
byte[] response = { 5, 0 };
if (_firstPacket[0] != 5)
{
// reject socks 4
response = new byte[] { 0, 91 };
Logging.Error("socks 5 protocol error");
}
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null);
}
else
Close();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void HandshakeSendCallback(IAsyncResult ar)
{
if (_closed) return;
try
{
_connection.EndSend(ar);
// +-----+-----+-------+------+----------+----------+
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
// +-----+-----+-------+------+----------+----------+
// | 1 | 1 | X'00' | 1 | Variable | 2 |
// +-----+-----+-------+------+----------+----------+
// Skip first 3 bytes, and read 2 more bytes to analysis the address.
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length.
// TODO validate
_connection.BeginReceive(_connetionRecvBuffer, 0, 3 + 2, SocketFlags.None,
new AsyncCallback(handshakeReceive2Callback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void handshakeReceive2Callback(IAsyncResult ar)
{
if (_closed) return;
try
{
int bytesRead = _connection.EndReceive(ar);
if (bytesRead >= 5)
{
_command = _connetionRecvBuffer[1];
if (_command != 1 && _command != 3)
{
Logging.Debug("Unsupported CMD=" + _command);
Close();
}
else
{
int atyp = _connetionRecvBuffer[3];
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
ReadAddress(4 + 2 - 1);
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[4];
ReadAddress(len + 2);
break;
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
Close();
break;
}
}
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()");
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ReadAddress(int bytesRemain)
{
Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, 2);
// Read the remain address bytes
_connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, bytesRemain);
}
private void OnAddressFullyRead(IAsyncResult ar)
{
if (_closed) return;
try
{
int bytesRead = _connection.EndReceive(ar);
int bytesRemain = (int) ar.AsyncState;
if (bytesRead >= bytesRemain)
{
_firstPacketLength = bytesRead + 2;
int atyp = _connetionRecvBuffer[0];
string dst_addr = "Unknown";
int dst_port = -1;
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6];
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[1];
dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len);
dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3];
break;
case 4: // IPv6 address, 16 bytes
dst_addr = $"[{new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray())}]";
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18];
break;
}
if (_config.isVerboseLogging)
{
Logging.Info($"connect to {dst_addr}:{dst_port}");
}
_destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port);
if (_command == 1)
{
byte[] response = {5, 0, 0, 1, 0, 0, 0, 0, 0, 0};
_connection.BeginSend(response, 0, response.Length, SocketFlags.None,
new AsyncCallback(ResponseCallback), null);
}
else if (_command == 3)
{
HandleUDPAssociate();
}
}
else
{
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.OnAddressFullyRead()");
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void HandleUDPAssociate()
{
IPEndPoint endPoint = (IPEndPoint)_connection.LocalEndPoint;
byte[] address = endPoint.Address.GetAddressBytes();
int port = endPoint.Port;
byte[] response = new byte[4 + address.Length + 2];
response[0] = 5;
switch (endPoint.AddressFamily)
{
case AddressFamily.InterNetwork:
response[3] = 1;
break;
case AddressFamily.InterNetworkV6:
response[3] = 4;
break;
}
address.CopyTo(response, 4);
response[response.Length - 1] = (byte)(port & 0xFF);
response[response.Length - 2] = (byte)((port >> 8) & 0xFF);
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true);
}
private void ReadAll(IAsyncResult ar)
{
if (_closed) return;
try
{
if (ar.AsyncState != null)
{
_connection.EndSend(ar);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
{
int bytesRead = _connection.EndReceive(ar);
if (bytesRead > 0)
{
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
}
else
Close();
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ResponseCallback(IAsyncResult ar)
{
try
{
_connection.EndSend(ar);
StartConnect();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
// inner class
private class ProxyTimer : Timer
{
@@ -509,18 +286,20 @@ namespace Shadowsocks.Controller
public ServerTimer(int p) : base(p) { }
}
private void StartConnect()
protected void StartConnect(EndPoint target)
{
try
{
_destEndPoint = target;
CreateRemote();
// Setting up proxy
IProxy remote;
IForwardProxy remote;
EndPoint proxyEP;
if (_config.proxy.useProxy)
if (Config.proxy.useProxy)
{
switch (_config.proxy.proxyType)
switch (Config.proxy.proxyType)
{
case ProxyConfig.PROXY_SOCKS5:
remote = new Socks5Proxy();
@@ -531,7 +310,7 @@ namespace Shadowsocks.Controller
default:
throw new NotSupportedException("Unknown forward proxy.");
}
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort);
proxyEP = SocketUtil.GetEndPoint(Config.proxy.proxyServer, Config.proxy.proxyPort);
}
else
{
@@ -554,7 +333,7 @@ namespace Shadowsocks.Controller
_proxyConnected = false;
// Connect to the proxy server.
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), new AsyncSession<ProxyTimer>(remote, proxyTimer));
remote.BeginConnectProxy(proxyEP, ProxyConnectCallback, new AsyncSession<ProxyTimer>(remote, proxyTimer));
}
catch (Exception e)
{
@@ -563,15 +342,16 @@ namespace Shadowsocks.Controller
}
}
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e)
{
var timer = (ProxyTimer) sender;
var timer = (ProxyTimer)sender;
timer.Elapsed -= proxyConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
if (_proxyConnected || _destConnected || _closed)
if (_proxyConnected || _destConnected || Closed)
{
return;
}
@@ -585,13 +365,13 @@ namespace Shadowsocks.Controller
private void ProxyConnectCallback(IAsyncResult ar)
{
Server server = null;
if (_closed)
if (Closed)
{
return;
}
try
{
var session = (AsyncSession<ProxyTimer>) ar.AsyncState;
var session = (AsyncSession<ProxyTimer>)ar.AsyncState;
ProxyTimer timer = session.State;
var destEndPoint = timer.DestEndPoint;
server = timer.Server;
@@ -606,7 +386,7 @@ namespace Shadowsocks.Controller
_proxyConnected = true;
if (_config.isVerboseLogging)
if (Config.isVerboseLogging)
{
if (!(remote is DirectConnect))
{
@@ -621,10 +401,10 @@ namespace Shadowsocks.Controller
connectTimer.Enabled = true;
connectTimer.Session = session;
connectTimer.Server = server;
_destConnected = false;
// Connect to the remote endpoint.
remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), new AsyncSession<ServerTimer>(session, connectTimer));
remote.BeginConnectDest(destEndPoint, ConnectCallback, new AsyncSession<ServerTimer>(session, connectTimer));
}
catch (ArgumentException)
{
@@ -643,7 +423,7 @@ namespace Shadowsocks.Controller
timer.Enabled = false;
timer.Dispose();
if (_destConnected || _closed)
if (_destConnected || Closed)
{
return;
}
@@ -659,10 +439,10 @@ namespace Shadowsocks.Controller
private void ConnectCallback(IAsyncResult ar)
{
if (_closed) return;
if (Closed) return;
try
{
var session = (AsyncSession<ServerTimer>) ar.AsyncState;
var session = (AsyncSession<ServerTimer>)ar.AsyncState;
ServerTimer timer = session.State;
_server = timer.Server;
timer.Elapsed -= destConnectTimer_Elapsed;
@@ -672,10 +452,10 @@ namespace Shadowsocks.Controller
var remote = session.Remote;
// Complete the connection.
remote.EndConnectDest(ar);
_destConnected = true;
if (_config.isVerboseLogging)
if (Config.isVerboseLogging)
{
Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}");
}
@@ -685,7 +465,7 @@ namespace Shadowsocks.Controller
strategy?.UpdateLatency(_server, latency);
_tcprelay.UpdateLatency(_server, latency);
StartPipe(session);
OnServerConnected(session);
}
catch (ArgumentException)
{
@@ -702,14 +482,14 @@ namespace Shadowsocks.Controller
}
}
private void StartPipe(AsyncSession session)
protected void StartPipe(AsyncSession session)
{
if (_closed) return;
if (Closed) return;
try
{
_startReceivingTime = DateTime.Now;
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
SendToServer(_firstPacketLength, session);
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session);
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session);
}
catch (Exception e)
{
@@ -720,10 +500,10 @@ namespace Shadowsocks.Controller
private void PipeRemoteReceiveCallback(IAsyncResult ar)
{
if (_closed) return;
if (Closed) return;
try
{
var session = (AsyncSession) ar.AsyncState;
var session = (AsyncSession)ar.AsyncState;
int bytesRead = session.Remote.EndReceive(ar);
_totalRead += bytesRead;
_tcprelay.UpdateInboundCounter(_server, bytesRead);
@@ -733,15 +513,15 @@ namespace Shadowsocks.Controller
int bytesToSend;
lock (_decryptionLock)
{
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
_encryptor.Decrypt(RemoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
}
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
Connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, PipeConnectionSendCallback, session);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastRead(_server);
}
else
{
_connection.Shutdown(SocketShutdown.Send);
Connection.Shutdown(SocketShutdown.Send);
_connectionShutdown = true;
CheckClose();
}
@@ -755,12 +535,12 @@ namespace Shadowsocks.Controller
private void PipeConnectionReceiveCallback(IAsyncResult ar)
{
if (_closed) return;
if (Closed) return;
try
{
int bytesRead = _connection.EndReceive(ar);
int bytesRead = Connection.EndReceive(ar);
var session = (AsyncSession) ar.AsyncState;
var session = (AsyncSession)ar.AsyncState;
var remote = session.Remote;
if (bytesRead > 0)
@@ -783,27 +563,53 @@ namespace Shadowsocks.Controller
private void SendToServer(int length, AsyncSession session)
{
BeginSendToServer(length, session, PipeRemoteSendCallback);
}
private bool _serverHeaderSent;
protected void BeginSendToServer(int length, AsyncSession session, AsyncCallback callback)
{
if (!_serverHeaderSent)
{
_serverHeaderSent = true;
// Append socks5 header
int len = Socks5Util.HeaderAddrLength(_destEndPoint);
Array.Copy(ConnetionRecvBuffer, 0, ConnetionRecvBuffer, len, length);
Socks5Util.FillHeaderAddr(ConnetionRecvBuffer, 0, _destEndPoint);
length += len;
}
_totalWrite += length;
int bytesToSend;
lock (_encryptionLock)
{
_encryptor.Encrypt(_connetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend);
_encryptor.Encrypt(ConnetionRecvBuffer, length, _connetionSendBuffer, out bytesToSend);
}
_tcprelay.UpdateOutboundCounter(_server, bytesToSend);
_startSendingTime = DateTime.Now;
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, callback, session);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastWrite(_server);
}
protected AsyncSession EndSendToServer(IAsyncResult ar)
{
var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar);
return session;
}
private void PipeRemoteSendCallback(IAsyncResult ar)
{
if (_closed) return;
if (Closed) return;
try
{
var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
var session = EndSendToServer(ar);
Connection.BeginReceive(ConnetionRecvBuffer, 0, RecvSize, SocketFlags.None, PipeConnectionReceiveCallback, session);
}
catch (Exception e)
{
@@ -817,8 +623,8 @@ namespace Shadowsocks.Controller
try
{
var session = (AsyncSession)ar.AsyncState;
_connection.EndSend(ar);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
Connection.EndSend(ar);
session.Remote.BeginReceive(RemoteRecvBuffer, 0, RecvSize, SocketFlags.None, PipeRemoteReceiveCallback, session);
}
catch (Exception e)
{
@@ -24,7 +24,7 @@ namespace Shadowsocks.Controller
public string LatestVersionLocalName;
public event EventHandler CheckUpdateCompleted;
public const string Version = "3.3.6";
public const string Version = "3.4.1";
private class CheckUpdateTimer : System.Timers.Timer
{
@@ -176,10 +176,10 @@ namespace Shadowsocks.Controller
private void SortByVersions(List<Asset> asserts)
{
asserts.Sort(new VersionComparer());
asserts.Sort();
}
public class Asset
public class Asset : IComparable<Asset>
{
public bool prerelease;
public string name;
@@ -243,14 +243,10 @@ namespace Shadowsocks.Controller
}
return 0;
}
}
class VersionComparer : IComparer<Asset>
{
// Calls CaseInsensitiveComparer.Compare with the parameters reversed.
public int Compare(Asset x, Asset y)
public int CompareTo(Asset other)
{
return Asset.CompareVersion(x.version, y.version);
return CompareVersion(version, other.version);
}
}
}
@@ -6,8 +6,9 @@ using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Web;
using System.Windows.Forms;
using Newtonsoft.Json;
using Shadowsocks.Controller.Service;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Model;
using Shadowsocks.Properties;
@@ -29,7 +30,6 @@ namespace Shadowsocks.Controller
private PACServer _pacServer;
private Configuration _config;
private StrategyManager _strategyManager;
private PrivoxyRunner privoxyRunner;
private GFWListUpdater gfwListUpdater;
public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance;
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; }
@@ -220,12 +220,13 @@ namespace Shadowsocks.Controller
SaveConfig(_config);
}
public void EnableProxy(int type, string proxy, int port)
public void EnableProxy(int type, string proxy, int port, int timeout)
{
_config.proxy.useProxy = true;
_config.proxy.proxyType = type;
_config.proxy.proxyServer = proxy;
_config.proxy.proxyPort = port;
_config.proxy.proxyTimeout = timeout;
SaveConfig(_config);
}
@@ -263,13 +264,9 @@ namespace Shadowsocks.Controller
{
_listener.Stop();
}
if (privoxyRunner != null)
{
privoxyRunner.Stop();
}
if (_config.enabled)
{
SystemProxy.Update(_config, true);
SystemProxy.Update(_config, true, null);
}
Encryption.RNG.Close();
}
@@ -347,6 +344,16 @@ namespace Shadowsocks.Controller
}
}
public void ToggleSecureLocalPac(bool enabled)
{
_config.secureLocalPac = enabled;
SaveConfig(_config);
if (ConfigChanged != null)
{
ConfigChanged(this, new EventArgs());
}
}
public void ToggleCheckingUpdate(bool enabled)
{
_config.autoCheckUpdate = enabled;
@@ -378,16 +385,6 @@ namespace Shadowsocks.Controller
}
}
public void SaveProxyConfig(ProxyConfig newConfig)
{
_config.proxy = newConfig;
Configuration.Save(_config);
if (ConfigChanged != null)
{
ConfigChanged(this, new EventArgs());
}
}
public void SaveHotkeyConfig(HotkeyConfig newConfig)
{
_config.hotkey = newConfig;
@@ -430,11 +427,7 @@ namespace Shadowsocks.Controller
// some logic in configuration updated the config when saving, we need to read it again
_config = Configuration.Load();
StatisticsConfiguration = StatisticsStrategyConfiguration.Load();
if (privoxyRunner == null)
{
privoxyRunner = new PrivoxyRunner();
}
if (_pacServer == null)
{
_pacServer = new PACServer();
@@ -455,11 +448,7 @@ namespace Shadowsocks.Controller
{
_listener.Stop();
}
// don't put PrivoxyRunner.Start() before pacServer.Stop()
// or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1
// though UseShellExecute is set to true now
// http://stackoverflow.com/questions/10235093/socket-doesnt-close-after-application-exits-if-a-launched-process-is-open
privoxyRunner.Stop();
try
{
var strategy = GetCurrentStrategy();
@@ -468,15 +457,12 @@ namespace Shadowsocks.Controller
strategy.ReloadServers();
}
privoxyRunner.Start(_config);
TCPRelay tcpRelay = new TCPRelay(this, _config);
UDPRelay udpRelay = new UDPRelay(this);
List<Listener.IService> services = new List<Listener.IService>();
services.Add(_pacServer);
services.Add(tcpRelay);
services.Add(udpRelay);
services.Add(_pacServer);
services.Add(new PortForwarder(privoxyRunner.RunningPort));
_listener = new Listener(services);
_listener.Start(_config);
}
@@ -515,7 +501,7 @@ namespace Shadowsocks.Controller
{
if (_config.enabled)
{
SystemProxy.Update(_config, false);
SystemProxy.Update(_config, false, _pacServer);
_systemProxyIsDirty = true;
}
else
@@ -523,7 +509,7 @@ namespace Shadowsocks.Controller
// only switch it off if we have switched it on
if (_systemProxyIsDirty)
{
SystemProxy.Update(_config, false);
SystemProxy.Update(_config, false, _pacServer);
_systemProxyIsDirty = false;
}
}
@@ -590,6 +576,11 @@ namespace Shadowsocks.Controller
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8);
}
public void CopyPacUrl()
{
Clipboard.SetDataObject(_pacServer.PacUrl);
}
#region Memory Management
private void StartReleasingMemory()
@@ -11,7 +11,7 @@ namespace Shadowsocks.Controller
return value.ToString("yyyyMMddHHmmssfff");
}
public static void Update(Configuration config, bool forceDisable)
public static void Update(Configuration config, bool forceDisable, PACServer pacSrv)
{
bool global = config.global;
bool enabled = config.enabled;
@@ -27,21 +27,25 @@ namespace Shadowsocks.Controller
{
if (global)
{
WinINet.SetIEProxy(true, true, "127.0.0.1:" + config.localPort.ToString(), "");
Sysproxy.SetIEProxy(true, true, "127.0.0.1:" + config.localPort.ToString(), "");
}
else
{
string pacUrl;
if (config.useOnlinePac && !config.pacUrl.IsNullOrEmpty())
{
pacUrl = config.pacUrl;
}
else
pacUrl = $"http://127.0.0.1:{config.localPort}/pac?t={GetTimestamp(DateTime.Now)}";
WinINet.SetIEProxy(true, false, "", pacUrl);
{
pacUrl = pacSrv.PacUrl;
}
Sysproxy.SetIEProxy(true, false, "", pacUrl);
}
}
else
{
WinINet.SetIEProxy(false, false, "", "");
Sysproxy.SetIEProxy(false, false, "", "");
}
}
catch (ProxyException ex)
+6 -2
View File
@@ -19,8 +19,11 @@ Online PAC=使用在线 PAC
Edit Local PAC File...=编辑本地 PAC 文件...
Update Local PAC from GFWList=从 GFWList 更新本地 PAC
Edit User Rule for GFWList...=编辑 GFWList 的用户规则...
Show QRCode...=显示二维码...
Secure Local PAC=保护本地 PAC
Copy Local PAC URL=复制本地 PAC 网址
Share Server Config...=分享服务器配置...
Scan QRCode from Screen...=扫描屏幕上的二维码...
Import URL from Clipboard...=从剪贴板导入URL...
Availability Statistics=统计可用性
Show Logs...=显示日志...
Verbose Logging=详细记录日志
@@ -81,7 +84,7 @@ Outbound=出站
# QRCode Form
QRCode=二维码
QRCode and URL=二维码与 URL
# PAC Url Form
@@ -103,6 +106,7 @@ Reg All=注册全部热键
Shadowsocks Error: {0}=Shadowsocks 错误: {0}
Port already in use=端口已被占用
Invalid server address=非法服务器地址
Illegal port number format=非法端口格式
Illegal timeout format=非法超时格式
Please add at least one server=请添加至少一个服务器
Binary file not shown.
Binary file not shown.
-5
View File
@@ -1,5 +0,0 @@
listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__
show-on-task-bar 0
activity-animation 0
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
hide-console
Binary file not shown.
Binary file not shown.
+6 -2
View File
@@ -19,8 +19,11 @@ Online PAC=使用在線 PAC
Edit Local PAC File...=編輯本地 PAC 文件...
Update Local PAC from GFWList=從 GFWList 更新本地 PAC
Edit User Rule for GFWList...=編輯 GFWList 的用戶規則...
Show QRCode...=顯示 QR 碼...
Secure Local PAC=保護本地 PAC
Copy Local PAC URL=複製本地 PAC 網址
Share Server Config...=分享伺服器配置...
Scan QRCode from Screen...=掃描螢幕上的 QR 碼...
Import URL from Clipboard...=從剪貼板導入URL...
Availability Statistics=統計可用性
Show Logs...=顯示日誌...
Verbose Logging=詳細記錄日誌
@@ -81,7 +84,7 @@ Outbound=出站
# QRCode Form
QRCode=QR 碼
QRCode and URL=QR 碼与 URL
# PAC Url Form
@@ -103,6 +106,7 @@ Reg All=註冊全部捷徑鍵
Shadowsocks Error: {0}=Shadowsocks 錯誤: {0}
Port already in use=連接埠號碼已被占用
Invalid server address=非法伺服器位址
Illegal port number format=非法連接埠號碼格式
Illegal timeout format=非法超時格式
Please add at least one server=請新增至少一個伺服器
+5
View File
@@ -26,6 +26,11 @@ namespace Shadowsocks.Encryption
Init();
}
public static void GetBytes(byte[] buf)
{
_rng.GetBytes(buf);
}
public static void GetBytes(byte[] buf, int len)
{
try
@@ -4,9 +4,9 @@ using System.Net.Sockets;
using System.Threading;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Proxy
namespace Shadowsocks.ForwardProxy
{
public class DirectConnect : IProxy
public class DirectConnect : IForwardProxy
{
private class FakeAsyncResult : IAsyncResult
{
@@ -7,9 +7,9 @@ using System.Threading;
using Shadowsocks.Controller;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Proxy
namespace Shadowsocks.ForwardProxy
{
public class HttpProxy : IProxy
public class HttpProxy : IForwardProxy
{
private class FakeAsyncResult : IAsyncResult
{
@@ -134,7 +134,7 @@ namespace Shadowsocks.Proxy
_remote.EndSend(ar);
// start line read
new LineReader(_remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, 1024, new FakeAsyncResult(ar, state));
new LineReader(1024, _remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, new FakeAsyncResult(ar, state));
}
catch (Exception ex)
{
@@ -165,7 +165,7 @@ namespace Shadowsocks.Proxy
st.innerState.ex = ex;
}
private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$");
private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$", RegexOptions.Compiled);
private int _respondLineCount = 0;
private bool _established = false;
@@ -2,10 +2,10 @@
using System.Net;
using System.Net.Sockets;
namespace Shadowsocks.Proxy
namespace Shadowsocks.ForwardProxy
{
public interface IProxy
public interface IForwardProxy
{
EndPoint LocalEndPoint { get; }
@@ -6,9 +6,9 @@ using System.Threading;
using Shadowsocks.Controller;
using Shadowsocks.Util.Sockets;
namespace Shadowsocks.Proxy
namespace Shadowsocks.ForwardProxy
{
public class Socks5Proxy : IProxy
public class Socks5Proxy : IForwardProxy
{
private class FakeAsyncResult : IAsyncResult
{
@@ -73,52 +73,15 @@ namespace Shadowsocks.Proxy
{
DestEndPoint = destEndPoint;
byte[] request = null;
byte atyp = 0;
int port;
int len = Socks5Util.HeaderAddrLength(destEndPoint);
var dep = destEndPoint as DnsEndPoint;
if (dep != null)
{
// is a domain name, we will leave it to server
atyp = 3; // DOMAINNAME
var enc = Encoding.UTF8;
var hostByteCount = enc.GetByteCount(dep.Host);
request = new byte[4 + 1/*length byte*/ + hostByteCount + 2];
request[4] = (byte)hostByteCount;
enc.GetBytes(dep.Host, 0, dep.Host.Length, request, 5);
port = dep.Port;
}
else
{
switch (DestEndPoint.AddressFamily)
{
case AddressFamily.InterNetwork:
request = new byte[4 + 4 + 2];
atyp = 1; // IP V4 address
break;
case AddressFamily.InterNetworkV6:
request = new byte[4 + 16 + 2];
atyp = 4; // IP V6 address
break;
default:
throw new Exception(I18N.GetString("Proxy request failed"));
}
port = ((IPEndPoint) DestEndPoint).Port;
var addr = ((IPEndPoint)DestEndPoint).Address.GetAddressBytes();
Array.Copy(addr, 0, request, 4, request.Length - 4 - 2);
}
byte[] request = new byte[len + 3];
Socks5Util.FillHeaderAddr(request, 3, destEndPoint);
// 构造request包剩余部分
request[0] = 5;
request[1] = 1;
request[2] = 0;
request[3] = atyp;
request[request.Length - 2] = (byte) ((port >> 8) & 0xff);
request[request.Length - 1] = (byte) (port & 0xff);
var st = new Socks5State();
st.Callback = callback;
@@ -22,6 +22,7 @@ namespace Shadowsocks.Model
public int localPort;
public string pacUrl;
public bool useOnlinePac;
public bool secureLocalPac = true;
public bool availabilityStatistics;
public bool autoCheckUpdate;
public bool checkPreRelease;
+2 -3
View File
@@ -10,10 +10,9 @@ namespace Shadowsocks.Model
public class Server
{
public static readonly Regex
UrlFinder = new Regex("^(?i)ss://([A-Za-z0-9+-/=_]+)(#(.+))?$",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
UrlFinder = new Regex("^(?i)ss://([A-Za-z0-9+-/=_]+)(#(.+))?$", RegexOptions.IgnoreCase),
DetailsParser = new Regex("^((?<method>.+?)(?<auth>-auth)??:(?<password>.*)@(?<hostname>.+?)" +
":(?<port>\\d+?))$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
":(?<port>\\d+?))$", RegexOptions.IgnoreCase);
private const int DefaultServerTimeoutSec = 5;
public const int MaxServerTimeoutSec = 20;
+43 -57
View File
@@ -111,40 +111,6 @@ namespace Shadowsocks.Properties {
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] mgwz_dll {
get {
object obj = ResourceManager.GetObject("mgwz_dll", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// 查找类似 listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__
///show-on-task-bar 0
///activity-animation 0
///forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
///hide-console
/// 的本地化字符串。
/// </summary>
internal static string privoxy_conf {
get {
return ResourceManager.GetString("privoxy_conf", resourceCulture);
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] privoxy_exe {
get {
object obj = ResourceManager.GetObject("privoxy_exe", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
@@ -215,6 +181,26 @@ namespace Shadowsocks.Properties {
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] sysproxy_exe {
get {
object obj = ResourceManager.GetObject("sysproxy_exe", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// 查找 System.Byte[] 类型的本地化资源。
/// </summary>
internal static byte[] sysproxy64_exe {
get {
object obj = ResourceManager.GetObject("sysproxy64_exe", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// 查找类似 ! Put user rules line by line in this file.
///! See https://adblockplus.org/en/filter-cheatsheet
@@ -227,29 +213,29 @@ namespace Shadowsocks.Properties {
}
/// <summary>
/// 查找类似 # translation for Traditional Chinese
///
///Shadowsocks=Shadowsocks
///
///# Menu items
///
///Enable System Proxy=啟用系統代理
///Mode=系統代理模式
///PAC=PAC 模式
///Global=全局模式
///Servers=伺服器
///Edit Servers...=編輯伺服器...
///Statistics Config...=統計配置...
///Start on Boot=開機啟動
///Forward Proxy...=正向代理設置...
///Allow Clients from LAN=允許來自區域網路的連接
///Local PAC=使用本地 PAC
///Online PAC=使用在線 PAC
///Edit Local PAC File...=編輯本地 PAC 文件...
///Update Local PAC from GFWList=從 GFWList 更新本地 PAC
///Edit User Rule for GFWList...=編輯 GFWList 的用戶規則...
///Show QRCode...=顯示 QR 碼...
///Scan QRCode from Screen [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// 查找类似 # translation for Traditional Chinese
///
///Shadowsocks=Shadowsocks
///
///# Menu items
///
///Enable System Proxy=啟用系統代理
///Mode=系統代理模式
///PAC=PAC 模式
///Global=全局模式
///Servers=伺服器
///Edit Servers...=編輯伺服器...
///Statistics Config...=統計配置...
///Start on Boot=開機啟動
///Forward Proxy...=正向代理設置...
///Allow Clients from LAN=允許來自區域網路的連接
///Local PAC=使用本地 PAC
///Online PAC=使用在線 PAC
///Edit Local PAC File...=編輯本地 PAC 文件...
///Update Local PAC from GFWList=從 GFWList 更新本地 PAC
///Edit User Rule for GFWList...=編輯 GFWList 的用戶規則...
///Show QRCode...=顯示 QR 碼...
///S [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// </summary>
internal static string zh_tw {
get {
+6 -9
View File
@@ -127,15 +127,6 @@
<data name="libsscrypto_dll" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\libsscrypto.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="mgwz_dll" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\mgwz.dll.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="privoxy_conf" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\privoxy_conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="privoxy_exe" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\privoxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="proxy_pac_txt" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Data\proxy.pac.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
@@ -157,6 +148,12 @@
<data name="ssw128" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="sysproxy64_exe" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Data\sysproxy64.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="sysproxy_exe" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Data\sysproxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="user_rule" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\data\user-rule.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
@@ -1,179 +0,0 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Shadowsocks.Controller;
namespace Shadowsocks.Util.ProcessManagement
{
/*
* See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
*/
public class Job : IDisposable
{
private IntPtr handle = IntPtr.Zero;
public Job()
{
handle = CreateJobObject(IntPtr.Zero, null);
var extendedInfoPtr = IntPtr.Zero;
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
{
LimitFlags = 0x2000
};
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
BasicLimitInformation = info
};
try
{
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint) length))
throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error()));
}
finally
{
if (extendedInfoPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(extendedInfoPtr);
extendedInfoPtr = IntPtr.Zero;
}
}
}
public bool AddProcess(IntPtr processHandle)
{
var succ = AssignProcessToJobObject(handle, processHandle);
if (!succ)
{
Logging.Error("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
}
return succ;
}
public bool AddProcess(int processId)
{
return AddProcess(Process.GetProcessById(processId).Handle);
}
#region IDisposable
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed) return;
disposed = true;
if (disposing)
{
// no managed objects to free
}
if (handle != IntPtr.Zero)
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
}
~Job()
{
Dispose(false);
}
#endregion
#region Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr CreateJobObject(IntPtr a, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion
}
#region Helper classes
[StructLayout(LayoutKind.Sequential)]
struct IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
public UInt64 OtherOperationCount;
public UInt64 ReadTransferCount;
public UInt64 WriteTransferCount;
public UInt64 OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
public UInt32 LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public UInt32 ActiveProcessLimit;
public UIntPtr Affinity;
public UInt32 PriorityClass;
public UInt32 SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public UIntPtr ProcessMemoryLimit;
public UIntPtr JobMemoryLimit;
public UIntPtr PeakProcessMemoryUsed;
public UIntPtr PeakJobMemoryUsed;
}
public enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}
#endregion
}
@@ -1,31 +0,0 @@
using System.Diagnostics;
using System.Management;
using System.Text;
namespace Shadowsocks.Util.ProcessManagement
{
static class ThreadUtil
{
/*
* See:
* http://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c
*/
public static string GetCommandLine(this Process process)
{
var commandLine = new StringBuilder(process.MainModule.FileName);
commandLine.Append(" ");
using (var searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id))
{
foreach (var @object in searcher.Get())
{
commandLine.Append(@object["CommandLine"]);
commandLine.Append(" ");
}
}
return commandLine.ToString();
}
}
}
@@ -0,0 +1,117 @@
using System;
namespace Shadowsocks.Util.Sockets
{
// Boyer-Moore string search
public static class ByteSearch
{
public class SearchTarget
{
private readonly byte[] _needle;
private readonly int[] _needleCharTable;
private readonly int[] _needleOffsetTable;
public SearchTarget(byte[] needle)
{
_needle = needle;
_needleCharTable = MakeCharTable(needle);
_needleOffsetTable = MakeOffsetTable(needle);
}
// Thread-safe
public int SearchIn(byte[] haystack, int index, int length)
{
return IndexOf(haystack, index, length, _needle, _needleOffsetTable, _needleCharTable);
}
}
private 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;
}
}
}
+108 -143
View File
@@ -12,17 +12,24 @@ namespace Shadowsocks.Util.Sockets
private readonly Encoding _encoding;
// private readonly string _delimiter;
private readonly byte[] _delimiterBytes;
private readonly int[] _delimiterSearchCharTable;
private readonly int[] _delimiterSearchOffsetTable;
private readonly ByteSearch.SearchTarget _delimiterSearch;
private readonly object _state;
private readonly byte[] _lineBuffer;
private int _bufferIndex;
private int _bufferDataIndex;
private int _bufferDataLength;
public LineReader(WrappedSocket socket, Func<string, object, bool> onLineRead, Action<Exception, object> onException,
Action<byte[], int, int, object> onFinish, Encoding encoding, string delimiter, int maxLineBytes, object state)
public LineReader(byte[] buffer, WrappedSocket socket, byte[] firstPackge, int index, int length,
Func<string, object, bool> onLineRead, Action<Exception, object> onException,
Action<byte[], int, int, object> onFinish,
Encoding encoding, string delimiter,
object state)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (socket == null)
{
throw new ArgumentNullException(nameof(socket));
@@ -40,6 +47,19 @@ namespace Shadowsocks.Util.Sockets
throw new ArgumentNullException(nameof(delimiter));
}
if (buffer.Length < length)
{
throw new ArgumentException("Line buffer length can't less than first package length!", nameof(buffer));
}
if (length > 0)
{
if (firstPackge == null)
{
throw new ArgumentNullException(nameof(firstPackge));
}
}
_socket = socket;
_onLineRead = onLineRead;
_onException = onException;
@@ -56,176 +76,121 @@ namespace Shadowsocks.Util.Sockets
throw new ArgumentException("Too short!", nameof(delimiter));
}
if (maxLineBytes < _delimiterBytes.Length)
if (buffer.Length < _delimiterBytes.Length)
{
throw new ArgumentException("Too small!", nameof(maxLineBytes));
throw new ArgumentException("Too small!", nameof(buffer));
}
_delimiterSearchCharTable = MakeCharTable(_delimiterBytes);
_delimiterSearchOffsetTable = MakeOffsetTable(_delimiterBytes);
_delimiterSearch = new ByteSearch.SearchTarget(_delimiterBytes);
_lineBuffer = new byte[maxLineBytes];
_lineBuffer = buffer;
// start reading
socket.BeginReceive(_lineBuffer, 0, maxLineBytes, 0, ReceiveCallback, 0);
if (length > 0)
{
// process first package
if (buffer == firstPackge)
{
_bufferDataIndex = index;
}
else
{
Array.Copy(firstPackge, index, _lineBuffer, 0, length);
}
_bufferDataLength = length;
try
{
NewPackageRecv();
}
catch (Exception ex)
{
OnException(ex);
OnFinish();
}
}
else
{
// start reading
socket.BeginReceive(_lineBuffer, 0, _lineBuffer.Length, 0, ReceiveCallback, 0);
}
}
public LineReader(int maxLineBytes, WrappedSocket socket, Func<string, object, bool> onLineRead,
Action<Exception, object> onException, Action<byte[], int, int, object> onFinish, Encoding encoding,
string delimiter, object state)
: this(
new byte[maxLineBytes], socket, null, 0, 0, onLineRead, onException, onFinish, encoding, delimiter,
state)
{
}
private void ReceiveCallback(IAsyncResult ar)
{
int length = (int)ar.AsyncState;
try
{
var bytesRead = _socket.EndReceive(ar);
if (bytesRead == 0)
{
OnFinish(length);
OnFinish();
return;
}
length += bytesRead;
_bufferDataLength += 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);
NewPackageRecv();
}
catch (Exception ex)
{
OnException(ex);
OnFinish(length);
OnFinish();
}
}
private void NewPackageRecv()
{
int i;
while ((i = _delimiterSearch.SearchIn(_lineBuffer, _bufferDataIndex, _bufferDataLength)) != -1)
{
var decodeLen = i - _bufferDataIndex;
string line = _encoding.GetString(_lineBuffer, _bufferDataIndex, decodeLen);
_bufferDataIndex = i + _delimiterBytes.Length;
_bufferDataLength -= decodeLen;
_bufferDataLength -= _delimiterBytes.Length;
var stop = _onLineRead(line, _state);
if (stop)
{
OnFinish();
return;
}
}
if (_bufferDataLength == _lineBuffer.Length)
{
OnException(new IndexOutOfRangeException("LineBuffer full! Try increace maxLineBytes!"));
OnFinish();
return;
}
if (_bufferDataIndex > 0)
{
Buffer.BlockCopy(_lineBuffer, _bufferDataIndex, _lineBuffer, 0, _bufferDataLength);
_bufferDataIndex = 0;
}
_socket.BeginReceive(_lineBuffer, _bufferDataLength, _lineBuffer.Length - _bufferDataLength, 0, ReceiveCallback, _bufferDataLength);
}
private void OnException(Exception ex)
{
_onException?.Invoke(ex, _state);
}
private void OnFinish(int length)
private void OnFinish()
{
_onFinish?.Invoke(_lineBuffer, _bufferIndex, length, _state);
_onFinish?.Invoke(_lineBuffer, _bufferDataIndex, _bufferDataLength, _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
}
}
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Shadowsocks.Controller;
namespace Shadowsocks.Util.Sockets
{
public static class Socks5Util
{
public static int HeaderAddrLength(EndPoint addrEp)
{
var dep = addrEp as DnsEndPoint;
if (dep != null)
{
var enc = Encoding.UTF8;
var hostByteCount = enc.GetByteCount(dep.Host);
return 1 + 1 /*length byte*/+ hostByteCount + 2;
}
switch (addrEp.AddressFamily)
{
case AddressFamily.InterNetwork:
return 1 + 4 + 2;
case AddressFamily.InterNetworkV6:
return 1 + 16 + 2;
default:
throw new Exception(I18N.GetString("Proxy request failed"));
}
}
public static int FillHeaderAddr(byte[] buffer, int offset, EndPoint addrEp)
{
byte atyp;
int port;
int len;
var dep = addrEp as DnsEndPoint;
if (dep != null)
{
// is a domain name, we will leave it to server
atyp = 3; // DOMAINNAME
var enc = Encoding.UTF8;
var hostByteCount = enc.GetByteCount(dep.Host);
len = 1 + 1 /*length byte*/+ hostByteCount + 2;
buffer[offset + 1] = (byte)hostByteCount;
enc.GetBytes(dep.Host, 0, dep.Host.Length, buffer, offset + 2);
port = dep.Port;
}
else
{
switch (addrEp.AddressFamily)
{
case AddressFamily.InterNetwork:
len = 1 + 4 + 2;
atyp = 1; // IP V4 address
break;
case AddressFamily.InterNetworkV6:
len = 1 + 16 + 2;
atyp = 4; // IP V6 address
break;
default:
throw new Exception(I18N.GetString("Proxy request failed"));
}
port = ((IPEndPoint)addrEp).Port;
var addr = ((IPEndPoint)addrEp).Address.GetAddressBytes();
Array.Copy(addr, 0, buffer, offset + 1, len - 1 - 2);
}
buffer[offset] = atyp;
buffer[offset + len - 2] = (byte)((port >> 8) & 0xff);
buffer[offset + len - 1] = (byte)(port & 0xff);
return len;
}
}
}
@@ -21,11 +21,19 @@ namespace Shadowsocks.Util.Sockets
// Only used during connection and close, so it won't cost too much.
private SpinLock _socketSyncLock = new SpinLock();
private volatile bool _disposed;
private bool _disposed;
private bool Connected => _activeSocket != null;
private Socket _activeSocket;
public WrappedSocket() { }
public WrappedSocket(Socket socket)
{
_activeSocket = socket;
}
public void BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state)
{
if (_disposed)
@@ -1,42 +0,0 @@
/****************************** Module Header ******************************\
Module Name: INTERNET_OPTION.cs
Project: CSWebBrowserWithProxy
Copyright (c) Microsoft Corporation.
This enum contains 4 WinINet constants used in method InternetQueryOption and
InternetSetOption functions.
Visit http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx to get the
whole constants list.
This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
namespace Shadowsocks.Util.SystemProxy
{
public enum INTERNET_OPTION
{
// Sets or retrieves an INTERNET_PER_CONN_OPTION_LIST structure that specifies
// a list of options for a particular connection.
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
// Notify the system that the registry settings have been changed so that
// it verifies the settings on the next call to InternetConnect.
INTERNET_OPTION_SETTINGS_CHANGED = 39,
// Causes the proxy data to be reread from the registry for a handle.
INTERNET_OPTION_REFRESH = 37,
// Alerts the current WinInet instance that proxy settings have changed
// and that they must update with the new settings.
// To alert all available WinInet instances, set the Buffer parameter of
// InternetSetOption to NULL and BufferLength to 0 when passing this option.
INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95
}
}
@@ -1,115 +0,0 @@
/****************************** Module Header ******************************\
Module Name: INTERNET_PER_CONN_OPTION.cs
Project: CSWebBrowserWithProxy
Copyright (c) Microsoft Corporation.
This file defines the struct INTERNET_PER_CONN_OPTION and constants used by it.
The struct INTERNET_PER_CONN_OPTION contains the value of an option that to be
set to internet settings.
Visit http://msdn.microsoft.com/en-us/library/aa385145(VS.85).aspx to get the
detailed description.
This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Runtime.InteropServices;
namespace Shadowsocks.Util.SystemProxy
{
/// <summary>
/// Constants used in INTERNET_PER_CONN_OPTION_OptionUnion struct.
/// </summary>
public enum INTERNET_PER_CONN_OptionEnum
{
INTERNET_PER_CONN_FLAGS = 1,
INTERNET_PER_CONN_PROXY_SERVER = 2,
INTERNET_PER_CONN_PROXY_BYPASS = 3,
INTERNET_PER_CONN_AUTOCONFIG_URL = 4,
INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5,
INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL = 6,
INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS = 7,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME = 8,
INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL = 9,
INTERNET_PER_CONN_FLAGS_UI = 10
}
/// <summary>
/// Constants used in INTERNET_PER_CONN_OPTON struct.
/// </summary>
[Flags]
public enum INTERNET_OPTION_PER_CONN_FLAGS
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
/// <summary>
/// Constants used in INTERNET_PER_CONN_OPTON struct.
/// Windows 7 and later:
/// Clients that support Internet Explorer 8 should query the connection type using INTERNET_PER_CONN_FLAGS_UI.
/// If this query fails, then the system is running a previous version of Internet Explorer and the client should
/// query again with INTERNET_PER_CONN_FLAGS.
/// Restore the connection type using INTERNET_PER_CONN_FLAGS regardless of the version of Internet Explorer.
/// XXX: If fails, notify user to upgrade Internet Explorer
/// </summary>
[Flags]
public enum INTERNET_OPTION_PER_CONN_FLAGS_UI
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
/// <summary>
/// Used in INTERNET_PER_CONN_OPTION.
/// When create a instance of OptionUnion, only one filed will be used.
/// The StructLayout and FieldOffset attributes could help to decrease the struct size.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct INTERNET_PER_CONN_OPTION_OptionUnion : IDisposable
{
// A value in INTERNET_OPTION_PER_CONN_FLAGS.
[FieldOffset(0)]
public int dwValue;
[FieldOffset(0)]
public System.IntPtr pszValue;
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
if (pszValue != IntPtr.Zero)
{
Marshal.FreeHGlobal(pszValue);
pszValue = IntPtr.Zero;
}
}
}
}
[StructLayout(LayoutKind.Sequential)]
public struct INTERNET_PER_CONN_OPTION
{
// A value in INTERNET_PER_CONN_OptionEnum.
public int dwOption;
public INTERNET_PER_CONN_OPTION_OptionUnion Value;
}
}
@@ -1,63 +0,0 @@
/****************************** Module Header ******************************\
Module Name: INTERNET_PER_CONN_OPTION_LIST.cs
Project: CSWebBrowserWithProxy
Copyright (c) Microsoft Corporation.
The struct INTERNET_PER_CONN_OPTION contains a list of options that to be
set to internet connection.
Visit http://msdn.microsoft.com/en-us/library/aa385146(VS.85).aspx to get the
detailed description.
This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Runtime.InteropServices;
namespace Shadowsocks.Util.SystemProxy
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct INTERNET_PER_CONN_OPTION_LIST : IDisposable
{
public int Size;
// The connection to be set. NULL means LAN.
public System.IntPtr Connection;
public int OptionCount;
public int OptionError;
// List of INTERNET_PER_CONN_OPTIONs.
public System.IntPtr pOptions;
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
private void Dispose( bool disposing )
{
if ( disposing )
{
if ( Connection != IntPtr.Zero )
{
Marshal.FreeHGlobal( Connection );
Connection = IntPtr.Zero;
}
if ( pOptions != IntPtr.Zero )
{
Marshal.FreeHGlobal( pOptions );
pOptions = IntPtr.Zero;
}
}
}
}
}
@@ -1,36 +0,0 @@
/****************************** Module Header ******************************\
Module Name: NativeMethods.cs
Project: CSWebBrowserWithProxy
Copyright (c) Microsoft Corporation.
This class is a simple .NET wrapper of wininet.dll. It contains 4 extern
methods in wininet.dll. They are InternetOpen, InternetCloseHandle,
InternetSetOption and InternetQueryOption.
This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Runtime.InteropServices;
namespace Shadowsocks.Util.SystemProxy
{
internal static class NativeMethods
{
/// <summary>
/// Sets an Internet option.
/// </summary>
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool InternetSetOption(
IntPtr hInternet,
INTERNET_OPTION dwOption,
IntPtr lpBuffer,
int lpdwBufferLength);
}
}
-148
View File
@@ -1,148 +0,0 @@
using System.Runtime.InteropServices;
namespace Shadowsocks.Util.SystemProxy
{
internal static class RemoteAccessService
{
private enum RasFieldSizeConstants
{
#region original header
//#if (WINVER >= 0x400)
//#define RAS_MaxEntryName 256
//#define RAS_MaxDeviceName 128
//#define RAS_MaxCallbackNumber RAS_MaxPhoneNumber
//#else
//#define RAS_MaxEntryName 20
//#define RAS_MaxDeviceName 32
//#define RAS_MaxCallbackNumber 48
//#endif
#endregion
RAS_MaxEntryName = 256,
RAS_MaxPath = 260
}
private const int ERROR_SUCCESS = 0;
private const int RASBASE = 600;
private const int ERROR_BUFFER_TOO_SMALL = RASBASE + 3;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct RasEntryName
{
#region original header
//#define RASENTRYNAMEW struct tagRASENTRYNAMEW
//RASENTRYNAMEW
//{
// DWORD dwSize;
// WCHAR szEntryName[RAS_MaxEntryName + 1];
//
//#if (WINVER >= 0x500)
// //
// // If this flag is REN_AllUsers then its a
// // system phonebook.
// //
// DWORD dwFlags;
// WCHAR szPhonebookPath[MAX_PATH + 1];
//#endif
//};
//
//#define RASENTRYNAMEA struct tagRASENTRYNAMEA
//RASENTRYNAMEA
//{
// DWORD dwSize;
// CHAR szEntryName[RAS_MaxEntryName + 1];
//
//#if (WINVER >= 0x500)
// DWORD dwFlags;
// CHAR szPhonebookPath[MAX_PATH + 1];
//#endif
//};
#endregion
public int dwSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=(int)RasFieldSizeConstants.RAS_MaxEntryName + 1)]
public string szEntryName;
public int dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=(int)RasFieldSizeConstants.RAS_MaxPath + 1)]
public string szPhonebookPath;
}
[DllImport("rasapi32.dll", CharSet = CharSet.Auto)]
private static extern uint RasEnumEntries(
// reserved, must be NULL
string reserved,
// pointer to full path and file name of phone-book file
string lpszPhonebook,
// buffer to receive phone-book entries
[In, Out] RasEntryName[] lprasentryname,
// size in bytes of buffer
ref int lpcb,
// number of entries written to buffer
out int lpcEntries
);
/// <summary>
/// Get all entries from RAS
/// </summary>
/// <param name="allConns"></param>
/// <returns>
/// 0: success with entries
/// 1: success but no entries found
/// 2: failed
/// </returns>
public static uint GetAllConns(ref string[] allConns)
{
int lpNames = 0;
int entryNameSize = 0;
int lpSize = 0;
uint retval = ERROR_SUCCESS;
RasEntryName[] names = null;
entryNameSize = Marshal.SizeOf(typeof(RasEntryName));
// Windows Vista or later: To determine the required buffer size, call RasEnumEntries
// with lprasentryname set to NULL. The variable pointed to by lpcb should be set to zero.
// The function will return the required buffer size in lpcb and an error code of ERROR_BUFFER_TOO_SMALL.
retval = RasEnumEntries(null, null, null, ref lpSize, out lpNames);
if (retval == ERROR_BUFFER_TOO_SMALL)
{
names = new RasEntryName[lpNames];
for (int i = 0; i < names.Length; i++)
{
names[i].dwSize = entryNameSize;
}
retval = RasEnumEntries(null, null, names, ref lpSize, out lpNames);
}
if (retval == ERROR_SUCCESS)
{
if (lpNames == 0)
{
// no entries found.
return 1;
}
allConns = new string[names.Length];
for (int i = 0; i < names.Length; i++)
{
allConns[i] = names[i].szEntryName;
}
return 0;
}
else
{
return 2;
}
}
}
}
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Shadowsocks.Controller;
using Shadowsocks.Properties;
namespace Shadowsocks.Util.SystemProxy
{
public static class Sysproxy
{
enum RET_ERRORS : int
{
RET_NO_ERROR = 0,
INVALID_FORMAT = 1,
NO_PERMISSION = 2,
SYSCALL_FAILED = 3,
NO_MEMORY = 4,
INVAILD_OPTION_COUNT = 5,
};
static Sysproxy()
{
try
{
FileManager.UncompressFile(Utils.GetTempPath("sysproxy.exe"),
Environment.Is64BitOperatingSystem ? Resources.sysproxy64_exe : Resources.sysproxy_exe);
}
catch (IOException e)
{
Logging.LogUsefulException(e);
}
}
public static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL)
{
string arguments;
if (enable)
{
if (global)
{
arguments = $"global {proxyServer} <local>localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;172.32.*;192.168.*";
}
else
{
arguments = $"pac {pacURL}";
}
}
else
{
arguments = "off";
}
using (var process = new Process())
{
// Configure the process using the StartInfo properties.
process.StartInfo.FileName = Utils.GetTempPath("sysproxy.exe");
process.StartInfo.Arguments = arguments;
process.StartInfo.WorkingDirectory = Utils.GetTempPath();
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
var error = process.StandardError.ReadToEnd();
process.WaitForExit();
var exitCode = process.ExitCode;
if (exitCode != (int) RET_ERRORS.RET_NO_ERROR)
{
throw new ProxyException(error);
}
}
}
}
}
@@ -1,183 +0,0 @@
/****************************** Module Header ******************************\
Module Name: WinINet.cs
Project: CSWebBrowserWithProxy
Copyright (c) Microsoft Corporation.
This class is used to set the proxy. or restore to the system proxy for the
current application
This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using Shadowsocks.Controller;
namespace Shadowsocks.Util.SystemProxy
{
public static class WinINet
{
/// <summary>
/// Set IE settings.
/// </summary>
private static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL, string connName)
{
List<INTERNET_PER_CONN_OPTION> _optionlist = new List<INTERNET_PER_CONN_OPTION>();
if (enable)
{
if (global)
{
// global proxy
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS_UI,
Value = { dwValue = (int)(INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_PROXY
| INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_DIRECT) }
});
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER,
Value = { pszValue = Marshal.StringToHGlobalAuto(proxyServer) }
});
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_BYPASS,
Value = { pszValue = Marshal.StringToHGlobalAuto("<local>") }
});
}
else
{
// pac
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS_UI,
Value = { dwValue = (int)INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_AUTO_PROXY_URL }
});
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_AUTOCONFIG_URL,
Value = { pszValue = Marshal.StringToHGlobalAuto(pacURL) }
});
}
}
else
{
// direct
_optionlist.Add(new INTERNET_PER_CONN_OPTION
{
dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS_UI,
Value = { dwValue = (int)(INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_AUTO_DETECT
| INTERNET_OPTION_PER_CONN_FLAGS_UI.PROXY_TYPE_DIRECT) }
});
}
// Get total length of INTERNET_PER_CONN_OPTIONs
var len = _optionlist.Sum(each => Marshal.SizeOf(each));
// Allocate a block of memory of the options.
IntPtr buffer = Marshal.AllocCoTaskMem(len);
IntPtr current = buffer;
// Marshal data from a managed object to an unmanaged block of memory.
foreach (INTERNET_PER_CONN_OPTION eachOption in _optionlist)
{
Marshal.StructureToPtr(eachOption, current, false);
current = (IntPtr)((int)current + Marshal.SizeOf(eachOption));
}
// Initialize a INTERNET_PER_CONN_OPTION_LIST instance.
INTERNET_PER_CONN_OPTION_LIST optionList = new INTERNET_PER_CONN_OPTION_LIST();
// Point to the allocated memory.
optionList.pOptions = buffer;
// Return the unmanaged size of an object in bytes.
optionList.Size = Marshal.SizeOf(optionList);
optionList.Connection = connName.IsNullOrEmpty()
? IntPtr.Zero // NULL means LAN
: Marshal.StringToHGlobalAuto(connName); // TODO: not working if contains Chinese
optionList.OptionCount = _optionlist.Count;
optionList.OptionError = 0;
int optionListSize = Marshal.SizeOf(optionList);
// Allocate memory for the INTERNET_PER_CONN_OPTION_LIST instance.
IntPtr intptrStruct = Marshal.AllocCoTaskMem(optionListSize);
// Marshal data from a managed object to an unmanaged block of memory.
Marshal.StructureToPtr(optionList, intptrStruct, true);
// Set internet settings.
bool bReturn = NativeMethods.InternetSetOption(
IntPtr.Zero,
INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
intptrStruct, optionListSize);
// Free the allocated memory.
Marshal.FreeCoTaskMem(buffer);
Marshal.FreeCoTaskMem(intptrStruct);
// Throw an exception if this operation failed.
if (!bReturn)
{
throw new ProxyException("InternetSetOption failed.", new Win32Exception());
}
// Notify the system that the registry settings have been changed and cause
// the proxy data to be reread from the registry for a handle.
bReturn = NativeMethods.InternetSetOption(
IntPtr.Zero,
INTERNET_OPTION.INTERNET_OPTION_PROXY_SETTINGS_CHANGED,
IntPtr.Zero, 0);
if (!bReturn)
{
Logging.Error("InternetSetOption:INTERNET_OPTION_PROXY_SETTINGS_CHANGED");
}
bReturn = NativeMethods.InternetSetOption(
IntPtr.Zero,
INTERNET_OPTION.INTERNET_OPTION_REFRESH,
IntPtr.Zero, 0);
if (!bReturn)
{
Logging.Error("InternetSetOption:INTERNET_OPTION_REFRESH");
}
}
public static void SetIEProxy(bool enable, bool global, string proxyServer, string pacURL)
{
string[] allConnections = null;
var ret = RemoteAccessService.GetAllConns(ref allConnections);
if (ret == 2)
throw new ProxyException("Cannot get all connections");
if (ret == 1)
{
// no entries, only set LAN
SetIEProxy(enable, global, proxyServer, pacURL, null);
}
else if (ret == 0)
{
// found entries, set LAN and each connection
SetIEProxy(enable, global, proxyServer, pacURL, null);
foreach (string connName in allConnections)
{
SetIEProxy(enable, global, proxyServer, pacURL, connName);
}
}
}
}
}
+9 -11
View File
@@ -80,28 +80,26 @@ namespace Shadowsocks.View
return true;
}
Server server = new Server();
server.server = IPTextBox.Text.Trim();
try
if (Uri.CheckHostName(server.server = IPTextBox.Text.Trim()) == UriHostNameType.Unknown)
{
server.server_port = int.Parse(ServerPortTextBox.Text);
MessageBox.Show(I18N.GetString("Invalid server address"));
IPTextBox.Focus();
return false;
}
catch (FormatException)
if (!int.TryParse(ServerPortTextBox.Text, out server.server_port))
{
MessageBox.Show(I18N.GetString("Illegal port number format"));
ServerPortTextBox.Clear();
ServerPortTextBox.Focus();
return false;
}
server.password = PasswordTextBox.Text;
server.method = EncryptionSelect.Text;
server.remarks = RemarksTextBox.Text;
try
{
server.timeout = int.Parse(TimeoutTextBox.Text);
}
catch (FormatException)
if (!int.TryParse(TimeoutTextBox.Text, out server.timeout))
{
MessageBox.Show(I18N.GetString("Illegal timeout format"));
TimeoutTextBox.Clear();
TimeoutTextBox.Focus();
return false;
}
server.auth = OneTimeAuth.Checked;
+29 -2
View File
@@ -47,6 +47,7 @@ namespace Shadowsocks.View
private MenuItem updateFromGFWListItem;
private MenuItem editGFWUserRuleItem;
private MenuItem editOnlinePACItem;
private MenuItem secureLocalPacUrlToggleItem;
private MenuItem autoCheckUpdatesToggleItem;
private MenuItem checkPreReleaseToggleItem;
private MenuItem proxyItem;
@@ -260,8 +261,10 @@ namespace Shadowsocks.View
this.SeperatorItem = new MenuItem("-"),
this.ConfigItem = CreateMenuItem("Edit Servers...", new EventHandler(this.Config_Click)),
CreateMenuItem("Statistics Config...", StatisticsConfigItem_Click),
CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)),
CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click))
new MenuItem("-"),
CreateMenuItem("Share Server Config...", new EventHandler(this.QRCodeItem_Click)),
CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)),
CreateMenuItem("Import URL from Clipboard...", new EventHandler(this.ImportURLItem_Click))
}),
CreateMenuGroup("PAC ", new MenuItem[] {
this.localPACItem = CreateMenuItem("Local PAC", new EventHandler(this.LocalPACItem_Click)),
@@ -270,6 +273,8 @@ namespace Shadowsocks.View
this.editLocalPACItem = CreateMenuItem("Edit Local PAC File...", new EventHandler(this.EditPACFileItem_Click)),
this.updateFromGFWListItem = CreateMenuItem("Update Local PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)),
this.editGFWUserRuleItem = CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)),
this.secureLocalPacUrlToggleItem = CreateMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_Click)),
CreateMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)),
this.editOnlinePACItem = CreateMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)),
}),
this.proxyItem = CreateMenuItem("Forward Proxy...", new EventHandler(this.proxyItem_Click)),
@@ -397,6 +402,7 @@ namespace Shadowsocks.View
AutoStartupItem.Checked = AutoStartup.Check();
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
localPACItem.Checked = !onlinePACItem.Checked;
secureLocalPacUrlToggleItem.Checked = config.secureLocalPac;
UpdatePACItemsEnabledStatus();
UpdateUpdateMenu();
}
@@ -735,6 +741,15 @@ namespace Shadowsocks.View
MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen."));
}
private void ImportURLItem_Click(object sender, EventArgs e)
{
var success = controller.AddServerBySSURL(Clipboard.GetText(TextDataFormat.Text));
if (success)
{
ShowConfigForm();
}
}
void splash_FormClosed(object sender, FormClosedEventArgs e)
{
ShowConfigForm();
@@ -796,6 +811,17 @@ namespace Shadowsocks.View
}
}
private void SecureLocalPacUrlToggleItem_Click(object sender, EventArgs e)
{
Configuration configuration = controller.GetConfigurationCopy();
controller.ToggleSecureLocalPac(!configuration.secureLocalPac);
}
private void CopyLocalPacUrlItem_Click(object sender, EventArgs e)
{
controller.CopyPacUrl();
}
private void UpdatePACItemsEnabledStatus()
{
if (this.localPACItem.Checked)
@@ -814,6 +840,7 @@ namespace Shadowsocks.View
}
}
private void UpdateUpdateMenu()
{
Configuration configuration = controller.GetConfigurationCopy();
+7 -27
View File
@@ -58,35 +58,25 @@ namespace Shadowsocks.View
private void OKButton_Click(object sender, EventArgs e)
{
var type = ProxyTypeComboBox.SelectedIndex;
var proxy = ProxyServerTextBox.Text;
var port = 0;
var timeout = 3;
if (UseProxyCheckBox.Checked)
{
try
{
port = int.Parse(ProxyPortTextBox.Text);
}
catch (FormatException)
int port;
int timeout;
if (!int.TryParse(ProxyPortTextBox.Text, out port))
{
MessageBox.Show(I18N.GetString("Illegal port number format"));
ProxyPortTextBox.Clear();
return;
}
try
{
timeout = int.Parse(ProxyTimeoutTextBox.Text);
}
catch (FormatException)
if (!int.TryParse(ProxyTimeoutTextBox.Text, out timeout))
{
MessageBox.Show(I18N.GetString("Illegal timeout format"));
ProxyTimeoutTextBox.Clear();
return;
}
var type = ProxyTypeComboBox.SelectedIndex;
var proxy = ProxyServerTextBox.Text;
try
{
Configuration.CheckServer(proxy);
@@ -99,20 +89,13 @@ namespace Shadowsocks.View
return;
}
controller.EnableProxy(type, proxy, port);
controller.EnableProxy(type, proxy, port, timeout);
}
else
{
controller.DisableProxy();
}
_modifiedConfiguration.useProxy = UseProxyCheckBox.Checked;
_modifiedConfiguration.proxyType = type;
_modifiedConfiguration.proxyServer = proxy;
_modifiedConfiguration.proxyPort = port;
_modifiedConfiguration.proxyTimeout = timeout;
controller.SaveProxyConfig(_modifiedConfiguration);
this.Close();
}
@@ -142,9 +125,6 @@ namespace Shadowsocks.View
}
else
{
ProxyServerTextBox.Clear();
ProxyPortTextBox.Clear();
ProxyTimeoutTextBox.Clear();
ProxyServerTextBox.Enabled = false;
ProxyPortTextBox.Enabled = false;
ProxyTimeoutTextBox.Enabled = false;
+16 -3
View File
@@ -30,6 +30,7 @@
{
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.listBox1 = new System.Windows.Forms.ListBox();
this.textBoxURL = new System.Windows.Forms.TextBox();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
@@ -39,7 +40,7 @@
this.pictureBox1.Margin = new System.Windows.Forms.Padding(0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(210, 210);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
this.pictureBox1.TabIndex = 1;
this.pictureBox1.TabStop = false;
//
@@ -56,6 +57,15 @@
this.listBox1.ValueMember = "Key";
this.listBox1.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged);
//
// textBoxURL
//
this.textBoxURL.Location = new System.Drawing.Point(10, 227);
this.textBoxURL.Name = "textBoxURL";
this.textBoxURL.ReadOnly = true;
this.textBoxURL.Size = new System.Drawing.Size(441, 21);
this.textBoxURL.TabIndex = 3;
this.textBoxURL.Click += new System.EventHandler(this.textBoxURL_Click);
//
// QRCodeForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
@@ -63,7 +73,8 @@
this.AutoSize = true;
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(457, 228);
this.ClientSize = new System.Drawing.Size(457, 257);
this.Controls.Add(this.textBoxURL);
this.Controls.Add(this.listBox1);
this.Controls.Add(this.pictureBox1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
@@ -72,10 +83,11 @@
this.Name = "QRCodeForm";
this.Padding = new System.Windows.Forms.Padding(10);
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "QRCode";
this.Text = "QRCode and URL";
this.Load += new System.EventHandler(this.QRCodeForm_Load);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
@@ -83,5 +95,6 @@
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.ListBox listBox1;
private System.Windows.Forms.TextBox textBoxURL;
}
}
+17 -2
View File
@@ -24,7 +24,7 @@ namespace Shadowsocks.View
this.code = code;
InitializeComponent();
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
this.Text = I18N.GetString("QRCode");
this.Text = I18N.GetString("QRCode and URL");
}
private void GenQR(string ssconfig)
@@ -33,6 +33,14 @@ namespace Shadowsocks.View
QRCode code = ZXing.QrCode.Internal.Encoder.encode(qrText, ErrorCorrectionLevel.M);
ByteMatrix m = code.Matrix;
int blockSize = Math.Max(pictureBox1.Height/m.Height, 1);
var qrWidth = m.Width*blockSize;
var qrHeight = m.Height*blockSize;
var dWidth = pictureBox1.Width - qrWidth;
var dHeight = pictureBox1.Height - qrHeight;
var maxD = Math.Max(dWidth, dHeight);
pictureBox1.SizeMode = maxD >= 7*blockSize ? PictureBoxSizeMode.Zoom : PictureBoxSizeMode.CenterImage;
Bitmap drawArea = new Bitmap((m.Width*blockSize), (m.Height*blockSize));
using (Graphics g = Graphics.FromImage(drawArea))
{
@@ -69,7 +77,14 @@ namespace Shadowsocks.View
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
GenQR((sender as ListBox)?.SelectedValue.ToString());
var url = (sender as ListBox)?.SelectedValue.ToString();
GenQR(url);
textBoxURL.Text = url;
}
private void textBoxURL_Click(object sender, EventArgs e)
{
textBoxURL.SelectAll();
}
}
}
+37 -43
View File
@@ -140,6 +140,9 @@
<Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Controller\Service\HttpHandler.cs" />
<Compile Include="Controller\Service\Socks5Handler.cs" />
<Compile Include="Controller\Service\TCPRelay.cs" />
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" />
<Compile Include="Model\HotKeyConfig.cs" />
<Compile Include="Model\ProxyConfig.cs" />
@@ -148,9 +151,9 @@
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Include="Proxy\DirectConnect.cs" />
<Compile Include="Proxy\HttpProxy.cs" />
<Compile Include="Proxy\IProxy.cs" />
<Compile Include="ForwardProxy\DirectConnect.cs" />
<Compile Include="ForwardProxy\HttpProxy.cs" />
<Compile Include="ForwardProxy\IForwardProxy.cs" />
<Compile Include="Controller\Service\AvailabilityStatistics.cs" />
<Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" />
<Compile Include="Controller\Strategy\StatisticsStrategy.cs" />
@@ -160,7 +163,6 @@
<Compile Include="Controller\I18N.cs" />
<Compile Include="Controller\Service\Listener.cs" />
<Compile Include="Controller\Logging.cs" />
<Compile Include="Controller\Service\PortForwarder.cs" />
<Compile Include="Controller\Service\UDPRelay.cs" />
<Compile Include="Controller\Service\UpdateChecker.cs" />
<Compile Include="Encryption\EncryptorBase.cs" />
@@ -185,23 +187,18 @@
<Compile Include="Controller\Strategy\BalancingStrategy.cs" />
<Compile Include="Controller\Strategy\StrategyManager.cs" />
<Compile Include="Controller\Strategy\IStrategy.cs" />
<Compile Include="Proxy\Socks5Proxy.cs" />
<Compile Include="ForwardProxy\Socks5Proxy.cs" />
<Compile Include="Settings.cs" />
<Compile Include="StringEx.cs" />
<Compile Include="Controller\System\Hotkeys\Hotkeys.cs" />
<Compile Include="Util\ProcessManagement\Job.cs" />
<Compile Include="Util\ProcessManagement\ThreadUtil.cs" />
<Compile Include="Encryption\RNG.cs" />
<Compile Include="Util\Sockets\ByteSearch.cs" />
<Compile Include="Util\Sockets\LineReader.cs" />
<Compile Include="Util\Sockets\SocketUtil.cs" />
<Compile Include="Util\Sockets\Socks5Util.cs" />
<Compile Include="Util\Sockets\WrappedSocket.cs" />
<Compile Include="Util\SystemProxy\INTERNET_OPTION.cs" />
<Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION.cs" />
<Compile Include="Util\SystemProxy\INTERNET_PER_CONN_OPTION_LIST.cs" />
<Compile Include="Util\SystemProxy\NativeMethods.cs" />
<Compile Include="Util\SystemProxy\ProxyException.cs" />
<Compile Include="Util\SystemProxy\RAS.cs" />
<Compile Include="Util\SystemProxy\WinINet.cs" />
<Compile Include="Util\SystemProxy\Sysproxy.cs" />
<Compile Include="Util\Util.cs" />
<Compile Include="Util\ViewUtils.cs" />
<Compile Include="View\ConfigForm.cs">
@@ -210,8 +207,6 @@
<Compile Include="View\ConfigForm.Designer.cs">
<DependentUpon>ConfigForm.cs</DependentUpon>
</Compile>
<Compile Include="Controller\Service\TCPRelay.cs" />
<Compile Include="Controller\Service\PrivoxyRunner.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Controller\ShadowsocksController.cs" />
@@ -289,11 +284,11 @@
</None>
<None Include="Data\abp.js.gz" />
<None Include="Data\libsscrypto.dll.gz" />
<None Include="Data\mgwz.dll.gz" />
<None Include="Data\privoxy.exe.gz" />
<None Include="Data\proxy.pac.txt.gz" />
</ItemGroup>
<ItemGroup>
<None Include="Data\sysproxy.exe.gz" />
<None Include="Data\sysproxy64.exe.gz" />
<None Include="packages.config" />
<None Include="Properties\DataSources\Shadowsocks.Model.StatisticsStrategyConfiguration.datasource" />
<None Include="Properties\Settings.settings">
@@ -305,7 +300,6 @@
<None Include="Resources\ss24.png" />
<None Include="Resources\ssw128.png" />
<Content Include="Data\cn.txt" />
<Content Include="Data\privoxy_conf.txt" />
<Content Include="Data\user-rule.txt" />
<Content Include="Data\zh_tw.txt" />
<Content Include="FodyWeavers.xml">
@@ -359,25 +353,25 @@
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System" />
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System.IO" />
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System.Xml.Linq" />
<Code xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Type="Fragment" Language="cs"><![CDATA[
var config = XElement.Load(Config.ItemSpec).Elements("Costura").FirstOrDefault();
if (config == null) return true;
var excludedAssemblies = new List<string>();
var attribute = config.Attribute("ExcludeAssemblies");
if (attribute != null)
foreach (var item in attribute.Value.Split('|').Select(x => x.Trim()).Where(x => x != string.Empty))
excludedAssemblies.Add(item);
var element = config.Element("ExcludeAssemblies");
if (element != null)
foreach (var item in element.Value.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).Where(x => x != string.Empty))
excludedAssemblies.Add(item);
var filesToCleanup = Files.Select(f => f.ItemSpec).Where(f => !excludedAssemblies.Contains(Path.GetFileNameWithoutExtension(f), StringComparer.InvariantCultureIgnoreCase));
foreach (var item in filesToCleanup)
File.Delete(item);
<Code xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Type="Fragment" Language="cs"><![CDATA[
var config = XElement.Load(Config.ItemSpec).Elements("Costura").FirstOrDefault();
if (config == null) return true;
var excludedAssemblies = new List<string>();
var attribute = config.Attribute("ExcludeAssemblies");
if (attribute != null)
foreach (var item in attribute.Value.Split('|').Select(x => x.Trim()).Where(x => x != string.Empty))
excludedAssemblies.Add(item);
var element = config.Element("ExcludeAssemblies");
if (element != null)
foreach (var item in element.Value.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).Where(x => x != string.Empty))
excludedAssemblies.Add(item);
var filesToCleanup = Files.Select(f => f.ItemSpec).Where(f => !excludedAssemblies.Contains(Path.GetFileNameWithoutExtension(f), StringComparer.InvariantCultureIgnoreCase));
foreach (var item in filesToCleanup)
File.Delete(item);
]]></Code>
</Task>
</UsingTask>
@@ -391,11 +385,11 @@ foreach (var item in filesToCleanup)
</PropertyGroup>
<Error Condition="!Exists('3rd\Fody.1.29.4\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Fody.1.29.4\build\dotnet\Fody.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>