Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efc486c733 | ||
|
|
b7b92a4af9 | ||
|
|
a447feece2 | ||
|
|
2a0dd88525 | ||
|
|
5650b1d847 | ||
|
|
b972cb250b | ||
|
|
f363b94fd6 | ||
|
|
a08993519f | ||
|
|
8a68a3515b | ||
|
|
76fbd808e3 | ||
|
|
557c797a9a | ||
|
|
ac45b44c09 | ||
|
|
c0299090c4 | ||
|
|
de4a2f862b | ||
|
|
430137495c | ||
|
|
566a0fcfa5 | ||
|
|
2c15520230 | ||
|
|
9cdb71875a | ||
|
|
b04c59d8e6 | ||
|
|
f2b8e91f81 | ||
|
|
d1e8f38767 | ||
|
|
f9e39fdd1d | ||
|
|
5ba0dffa5d | ||
|
|
0d4d01af3b | ||
|
|
a61b10bb67 | ||
|
|
be3cc3f3a6 | ||
|
|
5c5f753721 | ||
|
|
264b91d6b0 | ||
|
|
4b067346bf | ||
|
|
9e51b4a41b | ||
|
|
983c1adb28 | ||
|
|
22d8cdeff7 |
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
@@ -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.
@@ -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=請新增至少一個伺服器
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-2
@@ -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
|
||||
{
|
||||
+4
-4
@@ -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
-2
@@ -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; }
|
||||
|
||||
+5
-42
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 [字符串的其余部分被截断]"; 的本地化字符串。
|
||||
/// 查找类似 # 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 [字符串的其余部分被截断]"; 的本地化字符串。
|
||||
/// </summary>
|
||||
internal static string zh_tw {
|
||||
get {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user