Compare commits

..
1 Commits
Author SHA1 Message Date
clowwindy f33da19f06 debug system proxy 2015-01-15 01:53:42 +08:00
33 changed files with 677 additions and 2254 deletions
-23
View File
@@ -1,26 +1,3 @@
2.5 2015-07-11
- Support load balance
- Support high availability
2.4 2015-07-11
- Support UDP relay
- Support online PAC
- Migrate update checker to GitHub releases
- Other fixes
2.3.1 2015-03-06
- Support user rule
2.3 2015-01-25
- Use the same port for every profile
- Use the same port for HTTP/Socks5/PAC
- Fix GFWList PAC compatibility issue with IE11
- Encourage users to report to GFWList when no update found
- Minor UI improvements
2.2.1 2015-01-18
- Fix QR Code compatibility
2.2 2015-01-14
- Support updating PAC from GFWList
- Support adding server by scanning QR Code
+10 -3
View File
@@ -4,15 +4,22 @@ How to Contribute
Pull Requests
-------------
1. Pull requests are welcome.
1. Pull requests are welcome. If you would like to add a large feature
or make a significant change, make sure to open an issue to discuss with
people first.
2. Make sure to pass the unit tests. Write unit tests for new modules if
needed.
Issues
------
1. Nobody has reported any bugs but posted a lot of questions in the last
few months. So we're closing the issue tracker.
1. Only bugs and feature requests are accepted here.
2. We'll only work on important features. If the feature you're asking only
benefits a few people, you'd better implement the feature yourself and send us
a pull request, or ask some of your friends to do so.
3. We don't answer questions of any other types here. Since very few people
are watching the issue tracker here, you'll probably get no help from here.
Read [Troubleshooting] and get help from forums or [mailing lists].
[Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting
+18 -32
View File
@@ -6,47 +6,33 @@ Shadowsocks for Windows
#### Features
1. System proxy configuration
2. PAC mode and global mode
3. GFWList and user rules
4. Supports HTTP proxy
5. Supports server auto switching
6. Supports UDP relay (see Usage)
2. Fast profile switching
3. PAC mode and global mode
4. Compatible with IE
5. Only a single exe file of 200KB size
#### Download
Download a [latest release].
Download [latest release].
For >= Windows 8 or with .Net 4.0, download Shadowsocks-win-dotnet4.0-x.x.x.zip.
For <= Windows 7, download Shadowsocks-win-x.x.x.zip.
For <= Windows 7 or with .Net 2.0, download Shadowsocks-win-x.x.x.zip.
For >= Windows 8, download Shadowsocks-win-dotnet4.0-x.x.x.zip.
#### Basic
#### Usage
1. Find Shadowsocks icon in the notification tray
1. Find Shadowsocks icon in notification tray
2. You can add multiple servers in servers menu
3. Select Enable System Proxy menu to enable system proxy. Please disable other
proxy addons in your browser, or set them to use system proxy
4. You can also configure your browser proxy manually if you don't want to enable
system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this
port in Server -> Edit Servers
3. Select Enable menu to enable system proxy
4. Leave Enable menu unchecked, Shadowsocks will still provide an HTTP proxy at 127.0.0.1:8123
5. After you saved PAC file with any editor, Shadowsocks will notify browsers
about the change automatically
6. Please disable other proxy addons in your browser, or set them to use
system proxy
#### PAC
### Develop
1. You can change PAC rules by editing the PAC file. When you save the PAC file
with any editor, Shadowsocks will notify browsers about the change automatically
2. You can also update PAC file from GFWList (maintained by 3rd party)
3. You can also use online PAC URL
#### Server Auto Switching
1. Load balance: choosing server randomly
2. High availability: choosing the best server (low latency and packet loss)
3. Write your own strategy by implement IStrategy interface and send us a pull request!
#### UDP
For UDP, you need to use SocksCap or ProxyCap to force programs you want
to proxy to tunnel over Shadowsocks
Visual Studio Express 2012 is recommended.
#### License
@@ -55,4 +41,4 @@ GPLv3
[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp
[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[latest release]: https://sourceforge.net/projects/shadowsocksgui/files/dist/
@@ -6,7 +6,6 @@ using System.IO;
using Shadowsocks.Properties;
using SimpleJson;
using Shadowsocks.Util;
using Shadowsocks.Model;
namespace Shadowsocks.Controller
{
@@ -16,53 +15,22 @@ namespace Shadowsocks.Controller
private static string PAC_FILE = PACServer.PAC_FILE;
private static string USER_RULE_FILE = PACServer.USER_RULE_FILE;
public event EventHandler<ResultEventArgs> UpdateCompleted;
public event EventHandler UpdateCompleted;
public event ErrorEventHandler Error;
public class ResultEventArgs : EventArgs
{
public bool Success;
public ResultEventArgs(bool success)
{
this.Success = success;
}
}
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
try
{
List<string> lines = ParseResult(e.Result);
if (File.Exists(USER_RULE_FILE))
{
string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8);
string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach(string rule in rules)
{
if (rule.StartsWith("!") || rule.StartsWith("["))
continue;
lines.Add(rule);
}
}
string abpContent = Utils.UnGzip(Resources.abp_js);
abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines));
if (File.Exists(PAC_FILE))
{
string original = File.ReadAllText(PAC_FILE, Encoding.UTF8);
if (original == abpContent)
{
UpdateCompleted(this, new ResultEventArgs(false));
return;
}
}
File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8);
if (UpdateCompleted != null)
{
UpdateCompleted(this, new ResultEventArgs(true));
UpdateCompleted(this, new EventArgs());
}
}
catch (Exception ex)
@@ -74,10 +42,10 @@ namespace Shadowsocks.Controller
}
}
public void UpdatePACFromGFWList(Configuration config)
public void UpdatePACFromGFWList()
{
WebClient http = new WebClient();
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort);
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), 8123);
http.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(GFWLIST_URL));
}
-4
View File
@@ -18,10 +18,6 @@ namespace Shadowsocks.Controller
string[] lines = Regex.Split(Resources.cn, "\r\n|\r|\n");
foreach (string line in lines)
{
if (line.StartsWith("#"))
{
continue;
}
string[] kv = Regex.Split(line, "=");
if (kv.Length == 2)
{
@@ -5,62 +5,112 @@ using System.Net.Sockets;
using System.Net;
using Shadowsocks.Encryption;
using Shadowsocks.Model;
using Shadowsocks.Controller.Strategy;
using System.Timers;
namespace Shadowsocks.Controller
{
class TCPRelay : Listener.Service
class Local
{
private ShadowsocksController _controller;
public TCPRelay(ShadowsocksController controller)
private Server _server;
private bool _shareOverLAN;
//private Encryptor encryptor;
Socket _listener;
public Local(Configuration config)
{
this._controller = controller;
this._server = config.GetCurrentServer();
_shareOverLAN = config.shareOverLan;
//this.encryptor = new Encryptor(config.method, config.password);
}
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
public void Start()
{
if (socket.ProtocolType != ProtocolType.Tcp)
try
{
return false;
// Create a TCP/IP socket.
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
IPEndPoint localEndPoint = null;
if (_shareOverLAN)
{
localEndPoint = new IPEndPoint(IPAddress.Any, _server.local_port);
}
else
{
localEndPoint = new IPEndPoint(IPAddress.Loopback, _server.local_port);
}
// Bind the socket to the local endpoint and listen for incoming connections.
_listener.Bind(localEndPoint);
_listener.Listen(100);
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Shadowsocks started");
_listener.BeginAccept(
new AsyncCallback(AcceptCallback),
_listener);
}
if (length < 2 || firstPacket[0] != 5)
catch(SocketException)
{
return false;
_listener.Close();
throw;
}
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
Handler handler = new Handler();
handler.connection = socket;
handler.controller = _controller;
handler.Start(firstPacket, length);
return true;
}
public void Stop()
{
_listener.Close();
}
public void AcceptCallback(IAsyncResult ar)
{
Socket listener = (Socket)ar.AsyncState;
try
{
Socket conn = listener.EndAccept(ar);
conn.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
Handler handler = new Handler();
handler.connection = conn;
handler.encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password);
handler.config = _server;
handler.Start();
}
catch
{
//Console.WriteLine(e.Message);
}
finally
{
try
{
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
}
catch
{
//Console.WriteLine(e.Message);
}
}
}
}
class Handler
{
//public Encryptor encryptor;
public IEncryptor encryptor;
public Server server;
public Server config;
// Client socket.
public Socket remote;
public Socket connection;
public ShadowsocksController controller;
private int retryCount = 0;
private bool connected;
private byte command;
private byte[] _firstPacket;
private int _firstPacketLength;
// Size of receive buffer.
public const int RecvSize = 16384;
public const int BufferSize = RecvSize + 32;
private int totalRead = 0;
private int totalWrite = 0;
// remote receive buffer
private byte[] remoteRecvBuffer = new byte[RecvSize];
// remote send buffer
@@ -78,20 +128,34 @@ namespace Shadowsocks.Controller
private object encryptionLock = new object();
private object decryptionLock = new object();
private DateTime _startConnectTime;
public void CreateRemote()
public void Start()
{
Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint);
this.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password);
this.server = server;
}
try
{
// TODO async resolving
IPAddress ipAddress;
bool parsed = IPAddress.TryParse(config.server, out ipAddress);
if (!parsed)
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(config.server);
ipAddress = ipHostInfo.AddressList[0];
}
IPEndPoint remoteEP = new IPEndPoint(ipAddress, config.server_port);
public void Start(byte[] firstPacket, int length)
{
this._firstPacket = firstPacket;
this._firstPacketLength = length;
this.HandshakeReceive();
remote = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
// Connect to the remote endpoint.
remote.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void CheckClose()
@@ -131,7 +195,7 @@ namespace Shadowsocks.Controller
remote.Shutdown(SocketShutdown.Both);
remote.Close();
}
catch (Exception e)
catch (SocketException e)
{
Logging.LogUsefulException(e);
}
@@ -140,14 +204,33 @@ namespace Shadowsocks.Controller
{
lock (decryptionLock)
{
if (encryptor != null)
{
((IDisposable)encryptor).Dispose();
}
((IDisposable)encryptor).Dispose();
}
}
}
private void ConnectCallback(IAsyncResult ar)
{
if (closed)
{
return;
}
try
{
// Complete the connection.
remote.EndConnect(ar);
//Console.WriteLine("Socket connected to {0}",
// remote.RemoteEndPoint.ToString());
HandshakeReceive();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void HandshakeReceive()
{
@@ -157,15 +240,33 @@ namespace Shadowsocks.Controller
}
try
{
int bytesRead = _firstPacketLength;
connection.BeginReceive(connetionRecvBuffer, 0, 256, 0,
new AsyncCallback(HandshakeReceiveCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void HandshakeReceiveCallback(IAsyncResult ar)
{
if (closed)
{
return;
}
try
{
int bytesRead = connection.EndReceive(ar);
if (bytesRead > 1)
{
byte[] response = { 5, 0 };
if (_firstPacket[0] != 5)
if (connetionRecvBuffer[0] != 5)
{
// reject socks 4
response = new byte[] { 0, 91 };
response = new byte[]{ 0, 91 };
Console.WriteLine("socks 5 protocol error");
}
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null);
@@ -218,19 +319,11 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = connection.EndReceive(ar);
if (bytesRead >= 3)
if (bytesRead > 0)
{
command = connetionRecvBuffer[1];
if (command == 1)
{
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null);
}
else if (command == 3)
{
HandleUDPAssociate();
}
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(StartPipe), null);
}
else
{
@@ -245,209 +338,8 @@ namespace Shadowsocks.Controller
}
}
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;
if (endPoint.AddressFamily == AddressFamily.InterNetwork)
{
response[3] = 1;
}
else if (endPoint.AddressFamily == AddressFamily.InterNetworkV6)
{
response[3] = 4;
}
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, 0, 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, 0,
new AsyncCallback(ReadAll), null);
}
else
{
int bytesRead = connection.EndReceive(ar);
if (bytesRead > 0)
{
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(ReadAll), null);
}
else
{
this.Close();
}
}
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void ResponseCallback(IAsyncResult ar)
{
try
{
connection.EndSend(ar);
StartConnect();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private class ServerTimer : Timer
{
public Server Server;
public ServerTimer(int p) :base(p)
{
}
}
private void StartConnect()
{
try
{
CreateRemote();
// TODO async resolving
IPAddress ipAddress;
bool parsed = IPAddress.TryParse(server.server, out ipAddress);
if (!parsed)
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server);
ipAddress = ipHostInfo.AddressList[0];
}
IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port);
remote = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
_startConnectTime = DateTime.Now;
ServerTimer connectTimer = new ServerTimer(3000);
connectTimer.AutoReset = false;
connectTimer.Elapsed += connectTimer_Elapsed;
connectTimer.Enabled = true;
connectTimer.Server = server;
connected = false;
// Connect to the remote endpoint.
remote.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), connectTimer);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void connectTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (connected)
{
return;
}
Server server = ((ServerTimer)sender).Server;
IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null)
{
strategy.SetFailure(server);
}
Console.WriteLine(String.Format("{0} timed out", server.FriendlyName()));
remote.Close();
RetryConnect();
}
private void RetryConnect()
{
if (retryCount < 4)
{
Logging.Debug("Connection failed, retrying");
StartConnect();
retryCount++;
}
else
{
this.Close();
}
}
private void ConnectCallback(IAsyncResult ar)
{
Server server = null;
if (closed)
{
return;
}
try
{
ServerTimer timer = (ServerTimer)ar.AsyncState;
server = timer.Server;
timer.Elapsed -= connectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
// Complete the connection.
remote.EndConnect(ar);
connected = true;
//Console.WriteLine("Socket connected to {0}",
// remote.RemoteEndPoint.ToString());
var latency = DateTime.Now - _startConnectTime;
IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null)
{
strategy.UpdateLatency(server, latency);
}
StartPipe();
}
catch (ArgumentException e)
{
}
catch (Exception e)
{
if (server != null)
{
IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null)
{
strategy.SetFailure(server);
}
}
Logging.LogUsefulException(e);
RetryConnect();
}
}
private void StartPipe()
private void StartPipe(IAsyncResult ar)
{
if (closed)
{
@@ -455,6 +347,7 @@ namespace Shadowsocks.Controller
}
try
{
connection.EndReceive(ar);
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null);
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
@@ -476,7 +369,6 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = remote.EndReceive(ar);
totalRead += bytesRead;
if (bytesRead > 0)
{
@@ -490,12 +382,6 @@ namespace Shadowsocks.Controller
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend);
}
connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null);
IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null)
{
strategy.UpdateLastRead(this.server);
}
}
else
{
@@ -503,13 +389,6 @@ namespace Shadowsocks.Controller
connection.Shutdown(SocketShutdown.Send);
connectionShutdown = true;
CheckClose();
if (totalRead == 0)
{
// closed before anything received, reports as failure
// disable this feature
// controller.GetCurrentStrategy().SetFailure(this.server);
}
}
}
catch (Exception e)
@@ -528,7 +407,6 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = connection.EndReceive(ar);
totalWrite += bytesRead;
if (bytesRead > 0)
{
@@ -542,13 +420,6 @@ namespace Shadowsocks.Controller
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend);
}
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null);
IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null)
{
strategy.UpdateLastWrite(this.server);
}
}
else
{
-11
View File
@@ -31,14 +31,6 @@ namespace Shadowsocks.Controller
}
}
public static void Debug(object o)
{
#if DEBUG
Console.WriteLine(o);
#endif
}
public static void LogUsefulException(Exception e)
{
// just log useful exceptions, not all of them
@@ -63,9 +55,6 @@ namespace Shadowsocks.Controller
Console.WriteLine(e);
}
}
else if (e is ObjectDisposedException)
{
}
else
{
Console.WriteLine(e);
+235
View File
@@ -0,0 +1,235 @@
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Shadowsocks.Controller
{
class PACServer
{
private static int PORT = 8093;
public static string PAC_FILE = "pac.txt";
private static Configuration config;
Socket _listener;
FileSystemWatcher watcher;
public event EventHandler PACFileChanged;
public void Start(Configuration configuration)
{
try
{
config = configuration;
// Create a TCP/IP socket.
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
IPEndPoint localEndPoint = null;
if (configuration.shareOverLan)
{
localEndPoint = new IPEndPoint(IPAddress.Any, PORT);
}
else
{
localEndPoint = new IPEndPoint(IPAddress.Loopback, PORT);
}
// Bind the socket to the local endpoint and listen for incoming connections.
_listener.Bind(localEndPoint);
_listener.Listen(100);
_listener.BeginAccept(
new AsyncCallback(AcceptCallback),
_listener);
WatchPacFile();
}
catch (SocketException)
{
_listener.Close();
throw;
}
}
public void Stop()
{
if (_listener != null)
{
_listener.Close();
_listener = null;
}
}
public string TouchPACFile()
{
if (File.Exists(PAC_FILE))
{
return PAC_FILE;
}
else
{
FileManager.UncompressFile(PAC_FILE, Resources.proxy_pac_txt);
return PAC_FILE;
}
}
// we don't even use it
static byte[] requestBuf = new byte[2048];
public void AcceptCallback(IAsyncResult ar)
{
Socket listener = (Socket)ar.AsyncState;
try
{
Socket conn = listener.EndAccept(ar);
object[] state = new object[] {
conn,
requestBuf
};
conn.BeginReceive(requestBuf, 0, requestBuf.Length, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (ObjectDisposedException)
{
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
try
{
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
}
catch (ObjectDisposedException)
{
// do nothing
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}
}
private string GetPACContent()
{
if (File.Exists(PAC_FILE))
{
return File.ReadAllText(PAC_FILE, Encoding.UTF8);
}
else
{
return Utils.UnGzip(Resources.proxy_pac_txt);
}
}
private void ReceiveCallback(IAsyncResult ar)
{
object[] state = (object[])ar.AsyncState;
Socket conn = (Socket)state[0];
byte[] requestBuf = (byte[])state[1];
try
{
int bytesRead = conn.EndReceive(ar);
string pac = GetPACContent();
IPEndPoint localEndPoint = (IPEndPoint)conn.LocalEndPoint;
string proxy = GetPACAddress(requestBuf, localEndPoint);
pac = pac.Replace("__PROXY__", proxy);
if (bytesRead > 0)
{
string text = String.Format(@"HTTP/1.1 200 OK
Server: Shadowsocks
Content-Type: application/x-ns-proxy-autoconfig
Content-Length: {0}
Connection: Close
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac;
byte[] response = System.Text.Encoding.UTF8.GetBytes(text);
conn.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), conn);
Util.Utils.ReleaseMemory();
}
else
{
conn.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e);
conn.Close();
}
}
private void SendCallback(IAsyncResult ar)
{
Socket conn = (Socket)ar.AsyncState;
try
{
conn.Shutdown(SocketShutdown.Send);
}
catch
{ }
}
private void WatchPacFile()
{
if (watcher != null)
{
watcher.Dispose();
}
watcher = new FileSystemWatcher(Directory.GetCurrentDirectory());
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = PAC_FILE;
watcher.Changed += Watcher_Changed;
watcher.Created += Watcher_Changed;
watcher.Deleted += Watcher_Changed;
watcher.Renamed += Watcher_Changed;
watcher.EnableRaisingEvents = true;
}
private void Watcher_Changed(object sender, FileSystemEventArgs e)
{
if (PACFileChanged != null)
{
PACFileChanged(this, new EventArgs());
}
}
private string GetPACAddress(byte[] requestBuf, IPEndPoint localEndPoint)
{
string proxy = "PROXY " + localEndPoint.Address + ":8123;";
//try
//{
// string requestString = Encoding.UTF8.GetString(requestBuf);
// if (requestString.IndexOf("AppleWebKit") >= 0)
// {
// string address = "" + localEndPoint.Address + ":" + config.GetCurrentServer().local_port;
// proxy = "SOCKS5 " + address + "; SOCKS " + address + ";";
// }
//}
//catch (Exception e)
//{
// Console.WriteLine(e);
//}
return proxy;
}
}
}
@@ -6,8 +6,6 @@ using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Net.NetworkInformation;
using System.Net;
namespace Shadowsocks.Controller
{
@@ -15,7 +13,6 @@ namespace Shadowsocks.Controller
{
private Process _process;
private static string temppath;
private int _runningPort;
static PolipoRunner()
{
@@ -30,14 +27,6 @@ namespace Shadowsocks.Controller
}
}
public int RunningPort
{
get
{
return _runningPort;
}
}
public void Start(Configuration configuration)
{
Server server = configuration.GetCurrentServer();
@@ -56,10 +45,8 @@ namespace Shadowsocks.Controller
Console.WriteLine(e.ToString());
}
}
string polipoConfig = Resources.polipo_config;
_runningPort = this.GetFreePort();
polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString());
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_PORT__", _runningPort.ToString());
string polipoConfig = Resources.polipo_config;
polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", server.local_port.ToString());
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
FileManager.ByteArrayToFile(temppath + "/polipo.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig));
@@ -92,35 +79,5 @@ namespace Shadowsocks.Controller
_process = null;
}
}
private int GetFreePort()
{
int defaultPort = 8123;
try
{
IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] tcpEndPoints = properties.GetActiveTcpListeners();
List<int> usedPorts = new List<int>();
foreach (IPEndPoint endPoint in IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners())
{
usedPorts.Add(endPoint.Port);
}
for (int port = defaultPort; port <= 65535; port++)
{
if (!usedPorts.Contains(port))
{
return port;
}
}
}
catch (Exception e)
{
// in case access denied
Logging.LogUsefulException(e);
return defaultPort;
}
throw new Exception("No free port found.");
}
}
}
@@ -1,218 +0,0 @@
using Shadowsocks.Model;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
namespace Shadowsocks.Controller
{
public class Listener
{
public interface Service
{
bool Handle(byte[] firstPacket, int length, Socket socket, object state);
}
public class UDPState
{
public byte[] buffer = new byte[4096];
public EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
}
Configuration _config;
bool _shareOverLAN;
Socket _tcpSocket;
Socket _udpSocket;
IList<Service> _services;
public Listener(IList<Service> services)
{
this._services = services;
}
private bool CheckIfPortInUse(int port)
{
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();
foreach (IPEndPoint endPoint in ipEndPoints)
{
if (endPoint.Port == port)
{
return true;
}
}
return false;
}
public void Start(Configuration config)
{
this._config = config;
this._shareOverLAN = config.shareOverLan;
if (CheckIfPortInUse(_config.localPort))
throw new Exception(I18N.GetString("Port already in use"));
try
{
// Create a TCP/IP socket.
_tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
IPEndPoint localEndPoint = null;
if (_shareOverLAN)
{
localEndPoint = new IPEndPoint(IPAddress.Any, _config.localPort);
}
else
{
localEndPoint = new IPEndPoint(IPAddress.Loopback, _config.localPort);
}
// Bind the socket to the local endpoint and listen for incoming connections.
_tcpSocket.Bind(localEndPoint);
_udpSocket.Bind(localEndPoint);
_tcpSocket.Listen(1024);
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Shadowsocks started");
_tcpSocket.BeginAccept(
new AsyncCallback(AcceptCallback),
_tcpSocket);
UDPState udpState = new UDPState();
_udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState);
}
catch (SocketException)
{
_tcpSocket.Close();
throw;
}
}
public void Stop()
{
if (_tcpSocket != null)
{
_tcpSocket.Close();
_tcpSocket = null;
}
if (_udpSocket != null)
{
_udpSocket.Close();
_udpSocket = null;
}
}
public void RecvFromCallback(IAsyncResult ar)
{
UDPState state = (UDPState)ar.AsyncState;
try
{
int bytesRead = _udpSocket.EndReceiveFrom(ar, ref state.remoteEndPoint);
foreach (Service service in _services)
{
if (service.Handle(state.buffer, bytesRead, _udpSocket, state))
{
break;
}
}
}
catch (ObjectDisposedException)
{
}
catch (Exception)
{
}
finally
{
try
{
_udpSocket.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, 0, ref state.remoteEndPoint, new AsyncCallback(RecvFromCallback), state);
}
catch (ObjectDisposedException)
{
// do nothing
}
catch (Exception)
{
}
}
}
public void AcceptCallback(IAsyncResult ar)
{
Socket listener = (Socket)ar.AsyncState;
try
{
Socket conn = listener.EndAccept(ar);
byte[] buf = new byte[4096];
object[] state = new object[] {
conn,
buf
};
conn.BeginReceive(buf, 0, buf.Length, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (ObjectDisposedException)
{
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
try
{
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
}
catch (ObjectDisposedException)
{
// do nothing
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}
}
private void ReceiveCallback(IAsyncResult ar)
{
object[] state = (object[])ar.AsyncState;
Socket conn = (Socket)state[0];
byte[] buf = (byte[])state[1];
try
{
int bytesRead = conn.EndReceive(ar);
foreach (Service service in _services)
{
if (service.Handle(buf, bytesRead, conn, null))
{
return;
}
}
// no service found for this
if (conn.ProtocolType == ProtocolType.Tcp)
{
conn.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e);
conn.Close();
}
}
}
}
@@ -1,211 +0,0 @@
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Shadowsocks.Controller
{
class PACServer : Listener.Service
{
public static string PAC_FILE = "pac.txt";
public static string USER_RULE_FILE = "user-rule.txt";
FileSystemWatcher watcher;
private Configuration _config;
public event EventHandler PACFileChanged;
public PACServer()
{
this.WatchPacFile();
}
public void UpdateConfiguration(Configuration config)
{
this._config = config;
}
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
{
if (socket.ProtocolType != ProtocolType.Tcp)
{
return false;
}
try
{
string request = Encoding.UTF8.GetString(firstPacket, 0, length);
string[] lines = request.Split('\r', '\n');
bool hostMatch = false, pathMatch = false, useSocks = false;
foreach (string line in lines)
{
string[] kv = line.Split(new char[]{':'}, 2);
if (kv.Length == 2)
{
if (kv[0] == "Host")
{
if (kv[1].Trim() == ((IPEndPoint)socket.LocalEndPoint).ToString())
{
hostMatch = true;
}
}
else if (kv[0] == "User-Agent")
{
// we need to drop connections when changing servers
/* if (kv[1].IndexOf("Chrome") >= 0)
{
useSocks = true;
} */
}
}
else if (kv.Length == 1)
{
if (line.IndexOf("pac") >= 0)
{
pathMatch = true;
}
}
}
if (hostMatch && pathMatch)
{
SendResponse(firstPacket, length, socket, useSocks);
return true;
}
return false;
}
catch (ArgumentException)
{
return false;
}
}
public string TouchPACFile()
{
if (File.Exists(PAC_FILE))
{
return PAC_FILE;
}
else
{
FileManager.UncompressFile(PAC_FILE, Resources.proxy_pac_txt);
return PAC_FILE;
}
}
internal string TouchUserRuleFile()
{
if (File.Exists(USER_RULE_FILE))
{
return USER_RULE_FILE;
}
else
{
File.WriteAllText(USER_RULE_FILE, Resources.user_rule);
return USER_RULE_FILE;
}
}
private string GetPACContent()
{
if (File.Exists(PAC_FILE))
{
return File.ReadAllText(PAC_FILE, Encoding.UTF8);
}
else
{
return Utils.UnGzip(Resources.proxy_pac_txt);
}
}
public void SendResponse(byte[] firstPacket, int length, Socket socket, bool useSocks)
{
try
{
string pac = GetPACContent();
IPEndPoint localEndPoint = (IPEndPoint)socket.LocalEndPoint;
string proxy = GetPACAddress(firstPacket, length, localEndPoint, useSocks);
pac = pac.Replace("__PROXY__", proxy);
string text = String.Format(@"HTTP/1.1 200 OK
Server: Shadowsocks
Content-Type: application/x-ns-proxy-autoconfig
Content-Length: {0}
Connection: Close
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac;
byte[] response = System.Text.Encoding.UTF8.GetBytes(text);
socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket);
Util.Utils.ReleaseMemory();
}
catch (Exception e)
{
Console.WriteLine(e);
socket.Close();
}
}
private void SendCallback(IAsyncResult ar)
{
Socket conn = (Socket)ar.AsyncState;
try
{
conn.Shutdown(SocketShutdown.Send);
}
catch
{ }
}
private void WatchPacFile()
{
if (watcher != null)
{
watcher.Dispose();
}
watcher = new FileSystemWatcher(Directory.GetCurrentDirectory());
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = PAC_FILE;
watcher.Changed += Watcher_Changed;
watcher.Created += Watcher_Changed;
watcher.Deleted += Watcher_Changed;
watcher.Renamed += Watcher_Changed;
watcher.EnableRaisingEvents = true;
}
private void Watcher_Changed(object sender, FileSystemEventArgs e)
{
if (PACFileChanged != null)
{
PACFileChanged(this, new EventArgs());
}
}
private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks)
{
//try
//{
// string requestString = Encoding.UTF8.GetString(requestBuf);
// if (requestString.IndexOf("AppleWebKit") >= 0)
// {
// string address = "" + localEndPoint.Address + ":" + config.GetCurrentServer().local_port;
// proxy = "SOCKS5 " + address + "; SOCKS " + address + ";";
// }
//}
//catch (Exception e)
//{
// Console.WriteLine(e);
//}
return (useSocks ? "SOCKS5 " : "PROXY ") + localEndPoint.Address + ":" + this._config.localPort + ";";
}
}
}
@@ -1,268 +0,0 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Shadowsocks.Controller
{
class PortForwarder : Listener.Service
{
int _targetPort;
public PortForwarder(int targetPort)
{
this._targetPort = targetPort;
}
public 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 Socket _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];
public void Start(byte[] firstPacket, int length, Socket socket, int targetPort)
{
this._firstPacket = firstPacket;
this._firstPacketLength = length;
this._local = socket;
try
{
// TODO async resolving
IPAddress ipAddress;
bool parsed = IPAddress.TryParse("127.0.0.1", out ipAddress);
IPEndPoint remoteEP = new IPEndPoint(ipAddress, targetPort);
_remote = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
// Connect to the remote endpoint.
_remote.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void ConnectCallback(IAsyncResult ar)
{
if (_closed)
{
return;
}
try
{
_remote.EndConnect(ar);
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
{
//Console.WriteLine("bytesRead: " + bytesRead.ToString());
_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 (this)
{
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.Close();
}
catch (SocketException e)
{
Logging.LogUsefulException(e);
}
}
}
}
}
}
@@ -1,200 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using Shadowsocks.Encryption;
using Shadowsocks.Model;
using System.Net.Sockets;
using System.Net;
using System.Runtime.CompilerServices;
using Shadowsocks.Controller.Strategy;
namespace Shadowsocks.Controller
{
class UDPRelay : Listener.Service
{
private ShadowsocksController _controller;
private LRUCache<IPEndPoint, UDPHandler> _cache;
public UDPRelay(ShadowsocksController controller)
{
this._controller = controller;
this._cache = new LRUCache<IPEndPoint, UDPHandler>(512); // todo: choose a smart number
}
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
{
if (socket.ProtocolType != ProtocolType.Udp)
{
return false;
}
if (length < 4)
{
return false;
}
Listener.UDPState udpState = (Listener.UDPState)state;
IPEndPoint remoteEndPoint = (IPEndPoint)udpState.remoteEndPoint;
UDPHandler handler = _cache.get(remoteEndPoint);
if (handler == null)
{
handler = new UDPHandler(socket, _controller.GetAServer(IStrategyCallerType.UDP, remoteEndPoint), remoteEndPoint);
_cache.add(remoteEndPoint, handler);
}
handler.Send(firstPacket, length);
handler.Receive();
return true;
}
public class UDPHandler
{
private Socket _local;
private Socket _remote;
private Server _server;
private byte[] _buffer = new byte[1500];
private IPEndPoint _localEndPoint;
private IPEndPoint _remoteEndPoint;
public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint)
{
_local = local;
_server = server;
_localEndPoint = localEndPoint;
// TODO async resolving
IPAddress ipAddress;
bool parsed = IPAddress.TryParse(server.server, out ipAddress);
if (!parsed)
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server);
ipAddress = ipHostInfo.AddressList[0];
}
_remoteEndPoint = new IPEndPoint(ipAddress, server.server_port);
_remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
}
public void Send(byte[] data, int length)
{
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password);
byte[] dataIn = new byte[length - 3];
Array.Copy(data, 3, dataIn, 0, length - 3);
byte[] dataOut = new byte[length - 3 + 16];
int outlen;
encryptor.Encrypt(dataIn, dataIn.Length, dataOut, out outlen);
_remote.SendTo(dataOut, _remoteEndPoint);
}
public void Receive()
{
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
_remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null);
}
public void RecvFromCallback(IAsyncResult ar)
{
try
{
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
int bytesRead = _remote.EndReceiveFrom(ar, ref remoteEndPoint);
byte[] dataOut = new byte[bytesRead];
int outlen;
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password);
encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen);
byte[] sendBuf = new byte[outlen + 3];
Array.Copy(dataOut, 0, sendBuf, 3, outlen);
_local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint);
Receive();
}
catch (ObjectDisposedException)
{
}
catch (Exception)
{
}
finally
{
}
}
public void Close()
{
try
{
_remote.Close();
}
catch (ObjectDisposedException)
{
}
catch (Exception)
{
}
finally
{
}
}
}
}
// cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054
class LRUCache<K, V> where V : UDPRelay.UDPHandler
{
private int capacity;
private Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>> cacheMap = new Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>>();
private LinkedList<LRUCacheItem<K, V>> lruList = new LinkedList<LRUCacheItem<K, V>>();
public LRUCache(int capacity)
{
this.capacity = capacity;
}
[MethodImpl(MethodImplOptions.Synchronized)]
public V get(K key)
{
LinkedListNode<LRUCacheItem<K, V>> node;
if (cacheMap.TryGetValue(key, out node))
{
V value = node.Value.value;
lruList.Remove(node);
lruList.AddLast(node);
return value;
}
return default(V);
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void add(K key, V val)
{
if (cacheMap.Count >= capacity)
{
RemoveFirst();
}
LRUCacheItem<K, V> cacheItem = new LRUCacheItem<K, V>(key, val);
LinkedListNode<LRUCacheItem<K, V>> node = new LinkedListNode<LRUCacheItem<K, V>>(cacheItem);
lruList.AddLast(node);
cacheMap.Add(key, node);
}
private void RemoveFirst()
{
// Remove from LRUPriority
LinkedListNode<LRUCacheItem<K, V>> node = lruList.First;
lruList.RemoveFirst();
// Remove from cache
cacheMap.Remove(node.Value.key);
node.Value.value.Close();
}
}
class LRUCacheItem<K, V>
{
public LRUCacheItem(K k, V v)
{
key = k;
value = v;
}
public K key;
public V value;
}
}
@@ -5,8 +5,6 @@ using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using Shadowsocks.Controller.Strategy;
using System.Net;
namespace Shadowsocks.Controller
{
@@ -19,10 +17,9 @@ namespace Shadowsocks.Controller
private Thread _ramThread;
private Listener _listener;
private PACServer _pacServer;
private Local local;
private PACServer pacServer;
private Configuration _config;
private StrategyManager _strategyManager;
private PolipoRunner polipoRunner;
private GFWListUpdater gfwListUpdater;
private bool stopped = false;
@@ -41,9 +38,8 @@ namespace Shadowsocks.Controller
// when user clicked Edit PAC, and PAC file has already created
public event EventHandler<PathEventArgs> PACFileReadyToOpen;
public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen;
public event EventHandler<GFWListUpdater.ResultEventArgs> UpdatePACFromGFWListCompleted;
public event EventHandler UpdatePACFromGFWListCompleted;
public event ErrorEventHandler UpdatePACFromGFWListError;
@@ -52,7 +48,6 @@ namespace Shadowsocks.Controller
public ShadowsocksController()
{
_config = Configuration.Load();
_strategyManager = new StrategyManager(this);
}
public void Start()
@@ -74,48 +69,14 @@ namespace Shadowsocks.Controller
}
// always return copy
public Configuration GetConfigurationCopy()
public Configuration GetConfiguration()
{
return Configuration.Load();
}
// always return current instance
public Configuration GetCurrentConfiguration()
{
return _config;
}
public IList<IStrategy> GetStrategies()
{
return _strategyManager.GetStrategies();
}
public IStrategy GetCurrentStrategy()
{
foreach (var strategy in _strategyManager.GetStrategies())
{
if (strategy.ID == this._config.strategy)
{
return strategy;
}
}
return null;
}
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint)
{
IStrategy strategy = GetCurrentStrategy();
if (strategy != null)
{
return strategy.GetAServer(type, localIPEndPoint);
}
return GetCurrentServer();
}
public void SaveServers(List<Server> servers, int localPort)
public void SaveServers(List<Server> servers)
{
_config.configs = servers;
_config.localPort = localPort;
SaveConfig(_config);
}
@@ -171,27 +132,20 @@ namespace Shadowsocks.Controller
public void SelectServerIndex(int index)
{
_config.index = index;
_config.strategy = null;
SaveConfig(_config);
}
public void SelectStrategy(string strategyID)
{
_config.index = -1;
_config.strategy = strategyID;
SaveConfig(_config);
}
public void Stop()
{
Console.WriteLine("Stop");
if (stopped)
{
return;
}
stopped = true;
if (_listener != null)
if (local != null)
{
_listener.Stop();
local.Stop();
}
if (polipoRunner != null)
{
@@ -199,28 +153,19 @@ namespace Shadowsocks.Controller
}
if (_config.enabled)
{
SystemProxy.Update(_config, true);
SystemProxy.Disable();
}
}
public void TouchPACFile()
{
string pacFilename = _pacServer.TouchPACFile();
string pacFilename = pacServer.TouchPACFile();
if (PACFileReadyToOpen != null)
{
PACFileReadyToOpen(this, new PathEventArgs() { Path = pacFilename });
}
}
public void TouchUserRuleFile()
{
string userRuleFilename = _pacServer.TouchUserRuleFile();
if (UserRuleFileReadyToOpen != null)
{
UserRuleFileReadyToOpen(this, new PathEventArgs() { Path = userRuleFilename });
}
}
public string GetQRCodeForCurrentServer()
{
Server server = GetCurrentServer();
@@ -233,34 +178,13 @@ namespace Shadowsocks.Controller
{
if (gfwListUpdater != null)
{
gfwListUpdater.UpdatePACFromGFWList(_config);
}
}
public void SavePACUrl(string pacUrl)
{
_config.pacUrl = pacUrl;
UpdateSystemProxy();
SaveConfig(_config);
if (ConfigChanged != null)
{
ConfigChanged(this, new EventArgs());
}
}
public void UseOnlinePAC(bool useOnlinePac)
{
_config.useOnlinePac = useOnlinePac;
UpdateSystemProxy();
SaveConfig(_config);
if (ConfigChanged != null)
{
ConfigChanged(this, new EventArgs());
gfwListUpdater.UpdatePACFromGFWList();
}
}
protected void Reload()
{
Console.WriteLine("Reload");
// some logic in configuration updated the config when saving, we need to read it again
_config = Configuration.Load();
@@ -268,12 +192,11 @@ namespace Shadowsocks.Controller
{
polipoRunner = new PolipoRunner();
}
if (_pacServer == null)
if (pacServer == null)
{
_pacServer = new PACServer();
_pacServer.PACFileChanged += pacServer_PACFileChanged;
pacServer = new PACServer();
pacServer.PACFileChanged += pacServer_PACFileChanged;
}
_pacServer.UpdateConfiguration(_config);
if (gfwListUpdater == null)
{
gfwListUpdater = new GFWListUpdater();
@@ -281,9 +204,11 @@ namespace Shadowsocks.Controller
gfwListUpdater.Error += pacServer_PACUpdateError;
}
if (_listener != null)
pacServer.Stop();
if (local != null)
{
_listener.Stop();
local.Stop();
}
// don't put polipoRunner.Start() before pacServer.Stop()
@@ -293,22 +218,11 @@ namespace Shadowsocks.Controller
polipoRunner.Stop();
try
{
foreach (var strategy in GetStrategies())
{
strategy.ReloadServers();
}
polipoRunner.Start(_config);
TCPRelay tcpRelay = new TCPRelay(this);
UDPRelay udpRelay = new UDPRelay(this);
List<Listener.Service> services = new List<Listener.Service>();
services.Add(tcpRelay);
services.Add(udpRelay);
services.Add(_pacServer);
services.Add(new PortForwarder(polipoRunner.RunningPort));
_listener = new Listener(services);
_listener.Start(_config);
local = new Local(_config);
local.Start();
pacServer.Start(_config);
}
catch (Exception e)
{
@@ -345,9 +259,10 @@ namespace Shadowsocks.Controller
private void UpdateSystemProxy()
{
Console.WriteLine("UpdateSystemProxy");
if (_config.enabled)
{
SystemProxy.Update(_config, false);
SystemProxy.Enable(_config.global);
_systemProxyIsDirty = true;
}
else
@@ -355,7 +270,7 @@ namespace Shadowsocks.Controller
// only switch it off if we have switched it on
if (_systemProxyIsDirty)
{
SystemProxy.Update(_config, false);
SystemProxy.Disable();
_systemProxyIsDirty = false;
}
}
@@ -366,7 +281,7 @@ namespace Shadowsocks.Controller
UpdateSystemProxy();
}
private void pacServer_PACUpdateCompleted(object sender, GFWListUpdater.ResultEventArgs e)
private void pacServer_PACUpdateCompleted(object sender, EventArgs e)
{
if (UpdatePACFromGFWListCompleted != null)
UpdatePACFromGFWListCompleted(this, e);
@@ -1,71 +0,0 @@
using Shadowsocks.Controller;
using Shadowsocks.Model;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
namespace Shadowsocks.Controller.Strategy
{
class BalancingStrategy : IStrategy
{
ShadowsocksController _controller;
Random _random;
public BalancingStrategy(ShadowsocksController controller)
{
_controller = controller;
_random = new Random();
}
public string Name
{
get { return I18N.GetString("Load Balance"); }
}
public string ID
{
get { return "com.shadowsocks.strategy.balancing"; }
}
public void ReloadServers()
{
// do nothing
}
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint)
{
var configs = _controller.GetCurrentConfiguration().configs;
int index;
if (type == IStrategyCallerType.TCP)
{
index = _random.Next();
}
else
{
index = localIPEndPoint.GetHashCode();
}
return configs[index % configs.Count];
}
public void UpdateLatency(Model.Server server, TimeSpan latency)
{
// do nothing
}
public void UpdateLastRead(Model.Server server)
{
// do nothing
}
public void UpdateLastWrite(Model.Server server)
{
// do nothing
}
public void SetFailure(Model.Server server)
{
// do nothing
}
}
}
@@ -1,177 +0,0 @@
using Shadowsocks.Model;
using System;
using System.Collections.Generic;
using System.Text;
namespace Shadowsocks.Controller.Strategy
{
class HighAvailabilityStrategy : IStrategy
{
protected Server _currentServer;
protected Dictionary<Server, ServerStatus> _serverStatus;
ShadowsocksController _controller;
Random _random;
public class ServerStatus
{
// time interval between SYN and SYN+ACK
public TimeSpan latency;
public DateTime lastTimeDetectLatency;
// last time anything received
public DateTime lastRead;
// last time anything sent
public DateTime lastWrite;
// connection refused or closed before anything received
public DateTime lastFailure;
public Server server;
public double score;
}
public HighAvailabilityStrategy(ShadowsocksController controller)
{
_controller = controller;
_random = new Random();
_serverStatus = new Dictionary<Server, ServerStatus>();
}
public string Name
{
get { return I18N.GetString("High Availability"); }
}
public string ID
{
get { return "com.shadowsocks.strategy.ha"; }
}
public void ReloadServers()
{
// make a copy to avoid locking
var newServerStatus = new Dictionary<Server, ServerStatus>(_serverStatus);
foreach (var server in _controller.GetCurrentConfiguration().configs)
{
if (!newServerStatus.ContainsKey(server))
{
var status = new ServerStatus();
status.server = server;
status.lastFailure = DateTime.MinValue;
status.lastRead = DateTime.Now;
status.lastWrite = DateTime.Now;
status.latency = new TimeSpan(0, 0, 0, 0, 10);
status.lastTimeDetectLatency = DateTime.Now;
newServerStatus[server] = status;
}
}
_serverStatus = newServerStatus;
ChooseNewServer();
}
public Server GetAServer(IStrategyCallerType type, System.Net.IPEndPoint localIPEndPoint)
{
if (type == IStrategyCallerType.TCP)
{
ChooseNewServer();
}
return _currentServer;
}
/**
* once failed, try after 5 min
* and (last write - last read) < 5s
* and (now - last read) < 5s // means not stuck
* and latency < 200ms, try after 30s
*/
public void ChooseNewServer()
{
Server oldServer = _currentServer;
List<ServerStatus> servers = new List<ServerStatus>(_serverStatus.Values);
DateTime now = DateTime.Now;
foreach (var status in servers)
{
// all of failure, latency, (lastread - lastwrite) normalized to 1000, then
// 100 * failure - 2 * latency - 0.5 * (lastread - lastwrite)
status.score =
100 * 1000 * Math.Min(5 * 60, (now - status.lastFailure).TotalSeconds)
-2 * 5 * (Math.Min(2000, status.latency.TotalMilliseconds) / (1 + (now - status.lastTimeDetectLatency).TotalSeconds / 30 / 10) +
-0.5 * 200 * Math.Min(5, (status.lastRead - status.lastWrite).TotalSeconds));
Logging.Debug(String.Format("server: {0} latency:{1} score: {2}", status.server.FriendlyName(), status.latency, status.score));
}
ServerStatus max = null;
foreach (var status in servers)
{
if (max == null)
{
max = status;
}
else
{
if (status.score >= max.score)
{
max = status;
}
}
}
if (max != null)
{
_currentServer = max.server;
if (_currentServer != oldServer)
{
Console.WriteLine("HA switching to server: {0}", _currentServer.FriendlyName());
}
Logging.Debug(String.Format("choosing server: {0}", _currentServer.FriendlyName()));
}
}
public void UpdateLatency(Model.Server server, TimeSpan latency)
{
Logging.Debug(String.Format("latency: {0} {1}", server.FriendlyName(), latency));
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
{
status.latency = latency;
status.lastTimeDetectLatency = DateTime.Now;
}
}
public void UpdateLastRead(Model.Server server)
{
Logging.Debug(String.Format("last read: {0}", server.FriendlyName()));
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
{
status.lastRead = DateTime.Now;
}
}
public void UpdateLastWrite(Model.Server server)
{
Logging.Debug(String.Format("last write: {0}", server.FriendlyName()));
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
{
status.lastWrite = DateTime.Now;
}
}
public void SetFailure(Model.Server server)
{
Logging.Debug(String.Format("failure: {0}", server.FriendlyName()));
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
{
status.lastFailure = DateTime.Now;
}
}
}
}
@@ -1,56 +0,0 @@
using Shadowsocks.Model;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
namespace Shadowsocks.Controller.Strategy
{
public enum IStrategyCallerType
{
TCP,
UDP
}
/*
* IStrategy
*
* Subclasses must be thread-safe
*/
public interface IStrategy
{
string Name { get; }
string ID { get; }
/*
* Called when servers need to be reloaded, i.e. new configuration saved
*/
void ReloadServers();
/*
* Get a new server to use in TCPRelay or UDPRelay
*/
Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint);
/*
* TCPRelay will call this when latency of a server detected
*/
void UpdateLatency(Server server, TimeSpan latency);
/*
* TCPRelay will call this when reading from a server
*/
void UpdateLastRead(Server server);
/*
* TCPRelay will call this when writing to a server
*/
void UpdateLastWrite(Server server);
/*
* TCPRelay will call this when fatal failure detected
*/
void SetFailure(Server server);
}
}
@@ -1,23 +0,0 @@
using Shadowsocks.Controller;
using System;
using System.Collections.Generic;
using System.Text;
namespace Shadowsocks.Controller.Strategy
{
class StrategyManager
{
List<IStrategy> _strategies;
public StrategyManager(ShadowsocksController controller)
{
_strategies = new List<IStrategy>();
_strategies.Add(new BalancingStrategy(controller));
_strategies.Add(new HighAvailabilityStrategy(controller));
// TODO: load DLL plugins
}
public IList<IStrategy> GetStrategies()
{
return _strategies;
}
}
}
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using Shadowsocks.Model;
namespace Shadowsocks.Controller
{
@@ -26,44 +25,24 @@ namespace Shadowsocks.Controller
_refreshReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
}
public static void Update(Configuration config, bool forceDisable)
public static void Enable(bool global)
{
bool global = config.global;
bool enabled = config.enabled;
if (forceDisable)
{
enabled = false;
}
try
{
RegistryKey registry =
Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
true);
if (enabled)
if (global)
{
if (global)
{
registry.SetValue("ProxyEnable", 1);
registry.SetValue("ProxyServer", "127.0.0.1:" + config.localPort.ToString());
registry.SetValue("AutoConfigURL", "");
}
else
{
string pacUrl;
if (config.useOnlinePac && !string.IsNullOrEmpty(config.pacUrl))
pacUrl = config.pacUrl;
else
pacUrl = "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now);
registry.SetValue("ProxyEnable", 0);
registry.SetValue("ProxyServer", "");
registry.SetValue("AutoConfigURL", pacUrl);
}
registry.SetValue("ProxyEnable", 1);
registry.SetValue("ProxyServer", "127.0.0.1:8123");
registry.SetValue("AutoConfigURL", "");
}
else
{
registry.SetValue("ProxyEnable", 0);
registry.SetValue("ProxyServer", "");
registry.SetValue("AutoConfigURL", "");
registry.SetValue("AutoConfigURL", "http://127.0.0.1:8093/pac?t=" + GetTimestamp(DateTime.Now));
}
SystemProxy.NotifyIE();
//Must Notify IE first, or the connections do not chanage
@@ -77,6 +56,32 @@ namespace Shadowsocks.Controller
}
}
public static void Disable()
{
try
{
Console.WriteLine("Disable");
RegistryKey registry =
Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
true);
Console.WriteLine("Disable1");
registry.SetValue("ProxyEnable", 0);
registry.SetValue("ProxyServer", "");
registry.SetValue("AutoConfigURL", "");
Console.WriteLine("Disable2");
SystemProxy.NotifyIE();
Console.WriteLine("Disable3");
CopyProxySettingFromLan();
Console.WriteLine("Disable4");
}
catch (Exception e)
{
Logging.LogUsefulException(e);
// TODO this should be moved into views
MessageBox.Show(I18N.GetString("Failed to update registry"));
}
}
private static void CopyProxySettingFromLan()
{
RegistryKey registry =
@@ -1,31 +1,29 @@
using Shadowsocks.Model;
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using SimpleJson;
using System.Xml;
namespace Shadowsocks.Controller
{
public class UpdateChecker
{
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-csharp/releases";
private const string UpdateURL = "https://sourceforge.net/api/file/index/project-id/1817190/path/dist/mtime/desc/limit/10/rss";
public string LatestVersionNumber;
public string LatestVersionURL;
public event EventHandler NewVersionFound;
public const string Version = "2.5";
public const string Version = "2.2";
public void CheckUpdate(Configuration config)
public void CheckUpdate()
{
// TODO test failures
WebClient http = new WebClient();
http.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36");
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort);
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), 8123);
http.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(UpdateURL));
}
@@ -76,10 +74,6 @@ namespace Shadowsocks.Controller
private bool IsNewVersion(string url)
{
if (url.IndexOf("prerelease") >= 0)
{
return false;
}
// check dotnet 4.0
AssemblyName[] references = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
Version dotNetVersion = Environment.Version;
@@ -120,25 +114,23 @@ namespace Shadowsocks.Controller
{
string response = e.Result;
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(response);
XmlNodeList elements = xmlDoc.GetElementsByTagName("media:content");
List<string> versions = new List<string>();
foreach (JsonObject release in result)
foreach (XmlNode el in elements)
{
if ((bool)release["prerelease"])
foreach (XmlAttribute attr in el.Attributes)
{
continue;
}
foreach (JsonObject asset in (JsonArray)release["assets"])
{
string url = (string)asset["browser_download_url"];
if (IsNewVersion(url))
if (attr.Name == "url")
{
versions.Add(url);
if (IsNewVersion(attr.Value))
{
versions.Add(attr.Value);
}
}
}
}
if (versions.Count == 0)
{
return;
Binary file not shown.
+8 -37
View File
@@ -1,33 +1,19 @@
# translation for Simplified Chinese
Shadowsocks=Shadowsocks
# Menu items
Enable System Proxy=启用系统代理
Mode=系统代理模式
Enable=启用代理
Mode=代理模式
PAC=PAC 模式
Global=全局模式
Servers=服务器
Edit Servers...=编辑服务器...
Start on Boot=开机启动
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 的用户规则...
Share over LAN=在局域网共享代理
Edit PAC File...=编辑 PAC 文件...
Show QRCode...=显示二维码...
Scan QRCode from Screen...=扫描屏幕上的二维码...
Show Logs...=显示日志...
About...=关于...
Quit=退出
Edit Servers=编辑服务器
Load Balance=负载均衡
High Availability=高可用
# Config Form
&Add=添加(&A)
&Delete=删除(&D)
Server=服务器
@@ -40,19 +26,7 @@ Remarks=备注
OK=确定
Cancel=取消
New server=未配置的服务器
# QRCode Form
QRCode=二维码
# PAC Url Form
Edit Online PAC URL=编辑在线 PAC 网址
Edit Online PAC URL...=编辑在线 PAC 网址...
Please input PAC Url=请输入 PAC 网址
# Messages
Shadowsocks Error: {0}=Shadowsocks 错误: {0}
Port already in use=端口已被占用
Illegal port number format=非法端口格式
@@ -60,18 +34,15 @@ Please add at least one server=请添加至少一个服务器
Server IP can not be blank=服务器 IP 不能为空
Password can not be blank=密码不能为空
Port out of range=端口超出范围
Port can't be 8123=端口不能为 8123
Shadowsocks {0} Update Found=Shadowsocks {0} 更新
Click here to download=点击这里下载
Shadowsocks is here=Shadowsocks 在这里
You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks
System Proxy Enabled=系统代理已启用
System Proxy Disabled=系统代理未启用
Enabled=已启用代理
Disabled=已禁用代理
Update PAC from GFWList=从 GFWList 更新 PAC
Failed to update PAC file =更新 PAC 文件失败
PAC updated=更新 PAC 成功
No updates found. Please report to GFWList if you have problems with it.=未发现更新。如有问题请提交给 GFWList。
No QRCode found. Try to zoom in or move it to the center of the screen.=未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置
No QRCode found. Try to zoom in or move it to the center of the screen.=找不到二维码,尝试把它放大或者移动到靠近屏幕中间的位置
Failed to decode QRCode=无法解析二维码
Failed to update registry=无法修改注册表
System Proxy On: =系统代理已启用:
Running: Port {0}=正在运行:端口 {0}
+1 -2
View File
@@ -1,5 +1,4 @@
proxyAddress = "__POLIPO_BIND_IP__"
proxyPort = 8123
proxyAddress = "__POLIPO_BIND_IP__"
socksParentProxy = "127.0.0.1:__SOCKS_PORT__"
socksProxyType = socks5
-2
View File
@@ -1,2 +0,0 @@
! Put user rules line by line in this file.
! See https://adblockplus.org/en/filter-cheatsheet
+4 -18
View File
@@ -11,17 +11,11 @@ namespace Shadowsocks.Model
public class Configuration
{
public List<Server> configs;
// when strategy is set, index is ignored
public string strategy;
public int index;
public bool global;
public bool enabled;
public bool shareOverLan;
public bool isDefault;
public int localPort;
public string pacUrl;
public bool useOnlinePac;
private static string CONFIG_FILE = "gui-config.json";
@@ -39,6 +33,7 @@ namespace Shadowsocks.Model
public static void CheckServer(Server server)
{
CheckPort(server.local_port);
CheckPort(server.server_port);
CheckPassword(server.password);
CheckServer(server.server);
@@ -51,10 +46,6 @@ namespace Shadowsocks.Model
string configContent = File.ReadAllText(CONFIG_FILE);
Configuration config = SimpleJson.SimpleJson.DeserializeObject<Configuration>(configContent, new JsonSerializerStrategy());
config.isDefault = false;
if (config.localPort == 0)
{
config.localPort = 1080;
}
return config;
}
catch (Exception e)
@@ -67,7 +58,6 @@ namespace Shadowsocks.Model
{
index = 0,
isDefault = true,
localPort = 1080,
configs = new List<Server>()
{
GetDefaultServer()
@@ -82,9 +72,9 @@ namespace Shadowsocks.Model
{
config.index = config.configs.Count - 1;
}
if (config.index < -1)
if (config.index < 0)
{
config.index = -1;
config.index = 0;
}
config.isDefault = false;
try
@@ -115,16 +105,12 @@ namespace Shadowsocks.Model
}
}
public static void CheckPort(int port)
private static void CheckPort(int port)
{
if (port <= 0 || port > 65535)
{
throw new ArgumentException(I18N.GetString("Port out of range"));
}
if (port == 8123)
{
throw new ArgumentException(I18N.GetString("Port can't be 8123"));
}
}
private static void CheckPassword(string password)
+9 -33
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
@@ -14,21 +14,11 @@ namespace Shadowsocks.Model
{
public string server;
public int server_port;
public int local_port;
public string password;
public string method;
public string remarks;
public override int GetHashCode()
{
return server.GetHashCode() ^ server_port;
}
public override bool Equals(object obj)
{
Server o2 = (Server)obj;
return this.server == o2.server && this.server_port == o2.server_port;
}
public string FriendlyName()
{
if (string.IsNullOrEmpty(server))
@@ -49,6 +39,7 @@ namespace Shadowsocks.Model
{
this.server = "";
this.server_port = 8388;
this.local_port = 1080;
this.method = "aes-256-cfb";
this.password = "";
this.remarks = "";
@@ -59,8 +50,7 @@ namespace Shadowsocks.Model
string[] r1 = Regex.Split(ssURL, "ss://", RegexOptions.IgnoreCase);
string base64 = r1[1].ToString();
byte[] bytes = null;
for (var i = 0; i < 3; i++)
{
for (var i = 0; i < 3; i++) {
try
{
bytes = System.Convert.FromBase64String(base64);
@@ -74,25 +64,11 @@ namespace Shadowsocks.Model
{
throw new FormatException();
}
try
{
string data = Encoding.UTF8.GetString(bytes);
int indexLastAt = data.LastIndexOf('@');
string afterAt = data.Substring(indexLastAt + 1);
int indexLastColon = afterAt.LastIndexOf(':');
this.server_port = int.Parse(afterAt.Substring(indexLastColon + 1));
this.server = afterAt.Substring(0, indexLastColon);
string beforeAt = data.Substring(0, indexLastAt);
string[] parts = beforeAt.Split(new[] { ':' });
this.method = parts[0];
this.password = parts[1];
}
catch (IndexOutOfRangeException)
{
throw new FormatException();
}
string[] parts = Encoding.UTF8.GetString(bytes).Split(new char[2] { ':', '@' });
this.method = parts[0].ToString();
this.password = parts[1].ToString();
this.server = parts[2].ToString();
this.server_port = int.Parse(parts[3].ToString());
}
}
}
+23 -29
View File
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34209
// Runtime Version:4.0.30319.18444
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -71,30 +71,36 @@ namespace Shadowsocks.Properties {
}
/// <summary>
/// Looks up a localized string similar to # translation for Simplified Chinese
///
///Shadowsocks=Shadowsocks
///
///# Menu items
///
///Enable System Proxy=启用系统代理
///Mode=系统代理模式
/// Looks up a localized string similar to Shadowsocks=Shadowsocks
///Enable=启用代理
///Mode=代理模式
///PAC=PAC 模式
///Global=全局模式
///Servers=服务器
///Edit Servers...=编辑服务器...
///Start on Boot=开机启动
///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 的用户规则...
///Share over LAN=在局域网共享代理
///Edit PAC File...=编辑 PAC 文件...
///Show QRCode...=显示二维码...
///Scan QRCode from Screen...=扫描屏幕上的二维码...
///Show Logs...=显示日志...
///About...=关于...
///Quit=退出 [rest of string was truncated]&quot;;.
///Quit=退出
///Edit Servers=编辑服务器
///&amp;Add=添加(&amp;A)
///&amp;Delete=删除(&amp;D)
///Server=服务器
///Server IP=服务器 IP
///Server Port=服务器端口
///Password=密码
///Encryption=加密
///Proxy Port=代理端口
///Remarks=备注
///OK=确定
///Cancel=取消
///New server=未配置的服务器
///QRCode=二维码
///Shadowsocks Error: {0}=Shadowsocks [rest of string was truncated]&quot;;.
/// </summary>
internal static string cn {
get {
@@ -113,8 +119,7 @@ namespace Shadowsocks.Properties {
}
/// <summary>
/// Looks up a localized string similar to proxyAddress = &quot;__POLIPO_BIND_IP__&quot;
///proxyPort = 8123
/// Looks up a localized string similar to proxyAddress = &quot;__POLIPO_BIND_IP__&quot;
///
///socksParentProxy = &quot;127.0.0.1:__SOCKS_PORT__&quot;
///socksProxyType = socks5
@@ -189,16 +194,5 @@ namespace Shadowsocks.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized string similar to ! Put user rules line by line in this file.
///! See https://adblockplus.org/en/filter-cheatsheet
///.
/// </summary>
internal static string user_rule {
get {
return ResourceManager.GetString("user_rule", resourceCulture);
}
}
}
}
@@ -148,7 +148,4 @@
<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="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>
</root>
+33 -61
View File
@@ -33,6 +33,8 @@
this.RemarksLabel = new System.Windows.Forms.Label();
this.IPLabel = new System.Windows.Forms.Label();
this.ServerPortLabel = new System.Windows.Forms.Label();
this.ProxyPortTextBox = new System.Windows.Forms.TextBox();
this.ProxyPortLabel = new System.Windows.Forms.Label();
this.PasswordLabel = new System.Windows.Forms.Label();
this.IPTextBox = new System.Windows.Forms.TextBox();
this.ServerPortTextBox = new System.Windows.Forms.TextBox();
@@ -47,15 +49,11 @@
this.ServerGroupBox = new System.Windows.Forms.GroupBox();
this.ServersListBox = new System.Windows.Forms.ListBox();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel();
this.ProxyPortTextBox = new System.Windows.Forms.TextBox();
this.ProxyPortLabel = new System.Windows.Forms.Label();
this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel();
this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel();
this.tableLayoutPanel1.SuspendLayout();
this.ServerGroupBox.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.tableLayoutPanel5.SuspendLayout();
this.tableLayoutPanel3.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
this.SuspendLayout();
@@ -71,6 +69,8 @@
this.tableLayoutPanel1.Controls.Add(this.RemarksLabel, 0, 5);
this.tableLayoutPanel1.Controls.Add(this.IPLabel, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.ServerPortLabel, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.ProxyPortTextBox, 1, 4);
this.tableLayoutPanel1.Controls.Add(this.ProxyPortLabel, 0, 4);
this.tableLayoutPanel1.Controls.Add(this.PasswordLabel, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.IPTextBox, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.ServerPortTextBox, 1, 1);
@@ -88,13 +88,13 @@
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(238, 137);
this.tableLayoutPanel1.Size = new System.Drawing.Size(238, 163);
this.tableLayoutPanel1.TabIndex = 0;
//
// RemarksTextBox
//
this.RemarksTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.RemarksTextBox.Location = new System.Drawing.Point(72, 111);
this.RemarksTextBox.Location = new System.Drawing.Point(72, 137);
this.RemarksTextBox.MaxLength = 32;
this.RemarksTextBox.Name = "RemarksTextBox";
this.RemarksTextBox.Size = new System.Drawing.Size(160, 20);
@@ -105,7 +105,7 @@
//
this.RemarksLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.RemarksLabel.AutoSize = true;
this.RemarksLabel.Location = new System.Drawing.Point(17, 114);
this.RemarksLabel.Location = new System.Drawing.Point(17, 140);
this.RemarksLabel.Name = "RemarksLabel";
this.RemarksLabel.Size = new System.Drawing.Size(49, 13);
this.RemarksLabel.TabIndex = 9;
@@ -131,6 +131,26 @@
this.ServerPortLabel.TabIndex = 1;
this.ServerPortLabel.Text = "Server Port";
//
// ProxyPortTextBox
//
this.ProxyPortTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.ProxyPortTextBox.Location = new System.Drawing.Point(72, 111);
this.ProxyPortTextBox.MaxLength = 10;
this.ProxyPortTextBox.Name = "ProxyPortTextBox";
this.ProxyPortTextBox.Size = new System.Drawing.Size(160, 20);
this.ProxyPortTextBox.TabIndex = 4;
this.ProxyPortTextBox.WordWrap = false;
//
// ProxyPortLabel
//
this.ProxyPortLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.ProxyPortLabel.AutoSize = true;
this.ProxyPortLabel.Location = new System.Drawing.Point(11, 114);
this.ProxyPortLabel.Name = "ProxyPortLabel";
this.ProxyPortLabel.Size = new System.Drawing.Size(55, 13);
this.ProxyPortLabel.TabIndex = 3;
this.ProxyPortLabel.Text = "Proxy Port";
//
// PasswordLabel
//
this.PasswordLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
@@ -272,7 +292,7 @@
this.ServerGroupBox.Location = new System.Drawing.Point(178, 0);
this.ServerGroupBox.Margin = new System.Windows.Forms.Padding(12, 0, 0, 0);
this.ServerGroupBox.Name = "ServerGroupBox";
this.ServerGroupBox.Size = new System.Drawing.Size(249, 174);
this.ServerGroupBox.Size = new System.Drawing.Size(249, 200);
this.ServerGroupBox.TabIndex = 6;
this.ServerGroupBox.TabStop = false;
this.ServerGroupBox.Text = "Server";
@@ -295,7 +315,6 @@
this.tableLayoutPanel2.ColumnCount = 2;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel5, 1, 1);
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 2);
this.tableLayoutPanel2.Controls.Add(this.ServersListBox, 0, 0);
this.tableLayoutPanel2.Controls.Add(this.ServerGroupBox, 1, 0);
@@ -307,53 +326,9 @@
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238);
this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 264);
this.tableLayoutPanel2.TabIndex = 7;
//
// tableLayoutPanel5
//
this.tableLayoutPanel5.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Right)));
this.tableLayoutPanel5.AutoSize = true;
this.tableLayoutPanel5.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.tableLayoutPanel5.ColumnCount = 2;
this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel5.Controls.Add(this.ProxyPortTextBox, 1, 0);
this.tableLayoutPanel5.Controls.Add(this.ProxyPortLabel, 0, 0);
this.tableLayoutPanel5.Location = new System.Drawing.Point(241, 174);
this.tableLayoutPanel5.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel5.Name = "tableLayoutPanel5";
this.tableLayoutPanel5.Padding = new System.Windows.Forms.Padding(3);
this.tableLayoutPanel5.RowCount = 1;
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F));
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F));
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F));
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F));
this.tableLayoutPanel5.Size = new System.Drawing.Size(186, 32);
this.tableLayoutPanel5.TabIndex = 9;
//
// ProxyPortTextBox
//
this.ProxyPortTextBox.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.ProxyPortTextBox.Location = new System.Drawing.Point(67, 6);
this.ProxyPortTextBox.MaxLength = 10;
this.ProxyPortTextBox.Name = "ProxyPortTextBox";
this.ProxyPortTextBox.Size = new System.Drawing.Size(113, 20);
this.ProxyPortTextBox.TabIndex = 4;
this.ProxyPortTextBox.WordWrap = false;
//
// ProxyPortLabel
//
this.ProxyPortLabel.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.ProxyPortLabel.AutoSize = true;
this.ProxyPortLabel.Location = new System.Drawing.Point(6, 9);
this.ProxyPortLabel.Name = "ProxyPortLabel";
this.ProxyPortLabel.Size = new System.Drawing.Size(55, 13);
this.ProxyPortLabel.TabIndex = 3;
this.ProxyPortLabel.Text = "Proxy Port";
//
// tableLayoutPanel3
//
this.tableLayoutPanel3.AutoSize = true;
@@ -365,7 +340,7 @@
this.tableLayoutPanel3.Controls.Add(this.MyCancelButton, 1, 0);
this.tableLayoutPanel3.Controls.Add(this.OKButton, 0, 0);
this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Right;
this.tableLayoutPanel3.Location = new System.Drawing.Point(268, 209);
this.tableLayoutPanel3.Location = new System.Drawing.Point(268, 235);
this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3);
this.tableLayoutPanel3.Name = "tableLayoutPanel3";
this.tableLayoutPanel3.RowCount = 1;
@@ -383,7 +358,7 @@
this.tableLayoutPanel4.Controls.Add(this.DeleteButton, 1, 0);
this.tableLayoutPanel4.Controls.Add(this.AddButton, 0, 0);
this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Top;
this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 174);
this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 200);
this.tableLayoutPanel4.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel4.Name = "tableLayoutPanel4";
this.tableLayoutPanel4.RowCount = 1;
@@ -418,8 +393,6 @@
this.ServerGroupBox.PerformLayout();
this.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout();
this.tableLayoutPanel5.ResumeLayout(false);
this.tableLayoutPanel5.PerformLayout();
this.tableLayoutPanel3.ResumeLayout(false);
this.tableLayoutPanel4.ResumeLayout(false);
this.ResumeLayout(false);
@@ -433,9 +406,11 @@
private System.Windows.Forms.Label IPLabel;
private System.Windows.Forms.Label ServerPortLabel;
private System.Windows.Forms.Label PasswordLabel;
private System.Windows.Forms.Label ProxyPortLabel;
private System.Windows.Forms.TextBox IPTextBox;
private System.Windows.Forms.TextBox ServerPortTextBox;
private System.Windows.Forms.TextBox PasswordTextBox;
private System.Windows.Forms.TextBox ProxyPortTextBox;
private System.Windows.Forms.Label EncryptionLabel;
private System.Windows.Forms.ComboBox EncryptionSelect;
private System.Windows.Forms.Panel panel2;
@@ -450,9 +425,6 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5;
private System.Windows.Forms.TextBox ProxyPortTextBox;
private System.Windows.Forms.Label ProxyPortLabel;
}
}
+5 -11
View File
@@ -79,14 +79,12 @@ namespace Shadowsocks.View
server = IPTextBox.Text,
server_port = int.Parse(ServerPortTextBox.Text),
password = PasswordTextBox.Text,
local_port = int.Parse(ProxyPortTextBox.Text),
method = EncryptionSelect.Text,
remarks = RemarksTextBox.Text
};
int localPort = int.Parse(ProxyPortTextBox.Text);
Configuration.CheckServer(server);
Configuration.CheckPort(localPort);
_modifiedConfiguration.configs[_oldSelectedIndex] = server;
_modifiedConfiguration.localPort = localPort;
return true;
}
@@ -110,7 +108,7 @@ namespace Shadowsocks.View
IPTextBox.Text = server.server;
ServerPortTextBox.Text = server.server_port.ToString();
PasswordTextBox.Text = server.password;
ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString();
ProxyPortTextBox.Text = server.local_port.ToString();
EncryptionSelect.Text = server.method ?? "aes-256-cfb";
RemarksTextBox.Text = server.remarks;
ServerGroupBox.Visible = true;
@@ -133,14 +131,10 @@ namespace Shadowsocks.View
private void LoadCurrentConfiguration()
{
_modifiedConfiguration = controller.GetConfigurationCopy();
_modifiedConfiguration = controller.GetConfiguration();
LoadConfiguration(_modifiedConfiguration);
_oldSelectedIndex = _modifiedConfiguration.index;
if (_oldSelectedIndex < 0)
{
_oldSelectedIndex = 0;
}
ServersListBox.SelectedIndex = _oldSelectedIndex;
ServersListBox.SelectedIndex = _modifiedConfiguration.index;
LoadSelectedServer();
}
@@ -208,7 +202,7 @@ namespace Shadowsocks.View
MessageBox.Show(I18N.GetString("Please add at least one server"));
return;
}
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort);
controller.SaveServers(_modifiedConfiguration.configs);
this.Close();
}
+87 -216
View File
@@ -27,7 +27,6 @@ namespace Shadowsocks.View
private bool _isFirstRun;
private MenuItem enableItem;
private MenuItem modeItem;
private MenuItem AutoStartupItem;
private MenuItem ShareOverLANItem;
private MenuItem SeperatorItem;
@@ -35,12 +34,6 @@ namespace Shadowsocks.View
private MenuItem ServersItem;
private MenuItem globalModeItem;
private MenuItem PACModeItem;
private MenuItem localPACItem;
private MenuItem onlinePACItem;
private MenuItem editLocalPACItem;
private MenuItem updateFromGFWListItem;
private MenuItem editGFWUserRuleItem;
private MenuItem editOnlinePACItem;
private ConfigForm configForm;
private string _urlToOpen;
@@ -52,8 +45,7 @@ namespace Shadowsocks.View
controller.EnableStatusChanged += controller_EnableStatusChanged;
controller.ConfigChanged += controller_ConfigChanged;
controller.PACFileReadyToOpen += controller_FileReadyToOpen;
controller.UserRuleFileReadyToOpen += controller_FileReadyToOpen;
controller.PACFileReadyToOpen += controller_PACFileReadyToOpen;
controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged;
controller.EnableGlobalChanged += controller_EnableGlobalChanged;
controller.Errored += controller_Errored;
@@ -71,9 +63,9 @@ namespace Shadowsocks.View
LoadCurrentConfiguration();
updateChecker.CheckUpdate(controller.GetConfigurationCopy());
updateChecker.CheckUpdate();
if (controller.GetConfigurationCopy().isDefault)
if (controller.GetConfiguration().isDefault)
{
_isFirstRun = true;
ShowConfigForm();
@@ -106,9 +98,7 @@ namespace Shadowsocks.View
{
icon = Resources.ss24;
}
Configuration config = controller.GetConfigurationCopy();
bool enabled = config.enabled;
bool global = config.global;
bool enabled = controller.GetConfiguration().enabled;
if (!enabled)
{
Bitmap iconCopy = new Bitmap(icon);
@@ -117,28 +107,14 @@ namespace Shadowsocks.View
for (int y = 0; y < iconCopy.Height; y++)
{
Color color = icon.GetPixel(x, y);
iconCopy.SetPixel(x, y, Color.FromArgb((byte)(color.A / 1.25), color.R, color.G, color.B));
iconCopy.SetPixel(x, y , Color.FromArgb((byte)(color.A / 1.25), color.R, color.G, color.B));
}
}
icon = iconCopy;
}
_notifyIcon.Icon = Icon.FromHandle(icon.GetHicon());
string serverInfo = null;
if (controller.GetCurrentStrategy() != null)
{
serverInfo = controller.GetCurrentStrategy().Name;
}
else
{
serverInfo = config.GetCurrentServer().FriendlyName();
}
// we want to show more details but notify icon title is limited to 63 characters
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
(enabled ?
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
+ "\n" + serverInfo;
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" + (enabled ? I18N.GetString("Enabled") : I18N.GetString("Disabled")) + "\n" + controller.GetCurrentServer().FriendlyName();
_notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length));
}
@@ -155,8 +131,8 @@ namespace Shadowsocks.View
private void LoadMenu()
{
this.contextMenu1 = new ContextMenu(new MenuItem[] {
this.enableItem = CreateMenuItem("Enable System Proxy", new EventHandler(this.EnableItem_Click)),
this.modeItem = CreateMenuGroup("Mode", new MenuItem[] {
this.enableItem = CreateMenuItem("Enable", new EventHandler(this.EnableItem_Click)),
CreateMenuGroup("Mode", new MenuItem[] {
this.PACModeItem = CreateMenuItem("PAC", new EventHandler(this.PACModeItem_Click)),
this.globalModeItem = CreateMenuItem("Global", new EventHandler(this.GlobalModeItem_Click))
}),
@@ -166,18 +142,11 @@ namespace Shadowsocks.View
CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)),
CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click))
}),
CreateMenuGroup("PAC ", new MenuItem[] {
this.localPACItem = CreateMenuItem("Local PAC", new EventHandler(this.LocalPACItem_Click)),
this.onlinePACItem = CreateMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)),
new MenuItem("-"),
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.editOnlinePACItem = CreateMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)),
}),
new MenuItem("-"),
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Share over LAN", new EventHandler(this.ShareOverLANItem_Click)),
CreateMenuItem("Edit PAC File...", new EventHandler(this.EditPACFileItem_Click)),
CreateMenuItem("Update PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)),
new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
@@ -194,22 +163,21 @@ namespace Shadowsocks.View
private void controller_EnableStatusChanged(object sender, EventArgs e)
{
enableItem.Checked = controller.GetConfigurationCopy().enabled;
modeItem.Enabled = enableItem.Checked;
enableItem.Checked = controller.GetConfiguration().enabled;
}
void controller_ShareOverLANStatusChanged(object sender, EventArgs e)
{
ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan;
ShareOverLANItem.Checked = controller.GetConfiguration().shareOverLan;
}
void controller_EnableGlobalChanged(object sender, EventArgs e)
{
globalModeItem.Checked = controller.GetConfigurationCopy().global;
globalModeItem.Checked = controller.GetConfiguration().global;
PACModeItem.Checked = !globalModeItem.Checked;
}
void controller_FileReadyToOpen(object sender, ShadowsocksController.PathEventArgs e)
void controller_PACFileReadyToOpen(object sender, ShadowsocksController.PathEventArgs e)
{
string argument = @"/select, " + e.Path;
@@ -230,10 +198,9 @@ namespace Shadowsocks.View
Logging.LogUsefulException(e.GetException());
}
void controller_UpdatePACFromGFWListCompleted(object sender, GFWListUpdater.ResultEventArgs e)
void controller_UpdatePACFromGFWListCompleted(object sender, EventArgs e)
{
string result = e.Success ? I18N.GetString("PAC updated") : I18N.GetString("No updates found. Please report to GFWList if you have problems with it.");
ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000);
ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("PAC updated"), ToolTipIcon.Info, 1000);
}
void updateChecker_NewVersionFound(object sender, EventArgs e)
@@ -252,17 +219,13 @@ namespace Shadowsocks.View
private void LoadCurrentConfiguration()
{
Configuration config = controller.GetConfigurationCopy();
Configuration config = controller.GetConfiguration();
UpdateServersMenu();
enableItem.Checked = config.enabled;
modeItem.Enabled = config.enabled;
globalModeItem.Checked = config.global;
PACModeItem.Checked = !config.global;
ShareOverLANItem.Checked = config.shareOverLan;
AutoStartupItem.Checked = AutoStartup.Check();
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
localPACItem.Checked = !onlinePACItem.Checked;
UpdatePACItemsEnabledStatus();
}
private void UpdateServersMenu()
@@ -272,33 +235,20 @@ namespace Shadowsocks.View
{
items.RemoveAt(0);
}
int i = 0;
foreach (var strategy in controller.GetStrategies())
{
MenuItem item = new MenuItem(strategy.Name);
item.Tag = strategy.ID;
item.Click += AStrategyItem_Click;
items.Add(i, item);
i++;
}
int strategyCount = i;
Configuration configuration = controller.GetConfigurationCopy();
foreach (var server in configuration.configs)
Configuration configuration = controller.GetConfiguration();
for (int i = 0; i < configuration.configs.Count; i++)
{
Server server = configuration.configs[i];
MenuItem item = new MenuItem(server.FriendlyName());
item.Tag = i - strategyCount;
item.Tag = i;
item.Click += AServerItem_Click;
items.Add(i, item);
i++;
}
foreach (MenuItem item in items)
if (configuration.index >= 0 && configuration.index < configuration.configs.Count)
{
if (item.Tag != null && (item.Tag.ToString() == configuration.index.ToString() || item.Tag.ToString() == configuration.strategy))
{
item.Checked = true;
}
items[configuration.index].Checked = true;
}
}
@@ -391,23 +341,12 @@ namespace Shadowsocks.View
controller.UpdatePACFromGFWList();
}
private void EditUserRuleFileForGFWListItem_Click(object sender, EventArgs e)
{
controller.TouchUserRuleFile();
}
private void AServerItem_Click(object sender, EventArgs e)
{
MenuItem item = (MenuItem)sender;
controller.SelectServerIndex((int)item.Tag);
}
private void AStrategyItem_Click(object sender, EventArgs e)
{
MenuItem item = (MenuItem)sender;
controller.SelectStrategy((string)item.Tag);
}
private void ShowLogItem_Click(object sender, EventArgs e)
{
string argument = Logging.LogFile;
@@ -425,82 +364,74 @@ namespace Shadowsocks.View
private void ScanQRCodeItem_Click(object sender, EventArgs e)
{
foreach (Screen screen in Screen.AllScreens)
{
using (Bitmap fullImage = new Bitmap(screen.Bounds.Width,
screen.Bounds.Height))
{
using (Graphics g = Graphics.FromImage(fullImage))
{
g.CopyFromScreen(screen.Bounds.X,
screen.Bounds.Y,
0, 0,
fullImage.Size,
CopyPixelOperation.SourceCopy);
}
int maxTry = 10;
for (int i = 0; i < maxTry; i++)
{
int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry);
int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry);
Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height);
double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width;
using (Graphics g = Graphics.FromImage(target))
using (Bitmap fullImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height))
{
using (Graphics g = Graphics.FromImage(fullImage))
{
g.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
Screen.PrimaryScreen.Bounds.Y,
0, 0,
fullImage.Size,
CopyPixelOperation.SourceCopy);
}
for (int i = 0; i < 5; i++)
{
int marginLeft = fullImage.Width * i / 3 / 5;
int marginTop = fullImage.Height * i / 3 / 5;
Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2);
Bitmap target = new Bitmap(cropRect.Width, cropRect.Height);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
var source = new BitmapLuminanceSource(target);
var bitmap = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
var result = reader.decode(bitmap);
if (result != null)
{
var success = controller.AddServerBySSURL(result.Text);
QRCodeSplashForm splash = new QRCodeSplashForm();
if (success)
{
g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
splash.FormClosed += splash_FormClosed;
}
var source = new BitmapLuminanceSource(target);
var bitmap = new BinaryBitmap(new HybridBinarizer(source));
QRCodeReader reader = new QRCodeReader();
var result = reader.decode(bitmap);
if (result != null)
else if (result.Text.StartsWith("http://") || result.Text.StartsWith("https://"))
{
var success = controller.AddServerBySSURL(result.Text);
QRCodeSplashForm splash = new QRCodeSplashForm();
if (success)
{
splash.FormClosed += splash_FormClosed;
}
else if (result.Text.StartsWith("http://") || result.Text.StartsWith("https://"))
{
_urlToOpen = result.Text;
splash.FormClosed += openURLFromQRCode;
}
else
{
MessageBox.Show(I18N.GetString("Failed to decode QRCode"));
return;
}
double minX = Int32.MaxValue, minY = Int32.MaxValue, maxX = 0, maxY = 0;
foreach (ResultPoint point in result.ResultPoints)
{
minX = Math.Min(minX, point.X);
minY = Math.Min(minY, point.Y);
maxX = Math.Max(maxX, point.X);
maxY = Math.Max(maxY, point.Y);
}
minX /= imageScale;
minY /= imageScale;
maxX /= imageScale;
maxY /= imageScale;
// make it 20% larger
double margin = (maxX - minX) * 0.20f;
minX += -margin + marginLeft;
maxX += margin + marginLeft;
minY += -margin + marginTop;
maxY += margin + marginTop;
splash.Location = new Point(screen.Bounds.X, screen.Bounds.Y);
// we need a panel because a window has a minimal size
// TODO: test on high DPI
splash.TargetRect = new Rectangle((int)minX + screen.Bounds.X, (int)minY + screen.Bounds.Y, (int)maxX - (int)minX, (int)maxY - (int)minY);
splash.Size = new Size(fullImage.Width, fullImage.Height);
splash.Show();
_urlToOpen = result.Text;
splash.FormClosed += openURLFromQRCode;
}
else
{
MessageBox.Show(I18N.GetString("Failed to decode QRCode"));
return;
}
float minX = Int32.MaxValue, minY = Int32.MaxValue, maxX = 0, maxY = 0;
foreach (ResultPoint point in result.ResultPoints)
{
minX = Math.Min(minX, point.X);
minY = Math.Min(minY, point.Y);
maxX = Math.Max(maxX, point.X);
maxY = Math.Max(maxY, point.Y);
}
// make it 20% larger
float margin = (maxX - minX) * 0.20f;
minX += -margin + marginLeft;
maxX += margin + marginLeft;
minY += -margin + marginTop;
maxY += margin + marginTop;
splash.Location = new Point(0, 0);
// we need a panel because a window has a minimal size
// TODO: test on high DPI
splash.TargetRect = new Rectangle((int)minX, (int)minY, (int)maxX - (int)minX, (int)maxY - (int)minY);
splash.Size = new Size(fullImage.Width, fullImage.Height);
splash.Show();
return;
}
}
}
@@ -523,65 +454,5 @@ namespace Shadowsocks.View
MessageBox.Show(I18N.GetString("Failed to update registry"));
}
}
private void LocalPACItem_Click(object sender, EventArgs e)
{
if (!localPACItem.Checked)
{
localPACItem.Checked = true;
onlinePACItem.Checked = false;
controller.UseOnlinePAC(false);
UpdatePACItemsEnabledStatus();
}
}
private void OnlinePACItem_Click(object sender, EventArgs e)
{
if (!onlinePACItem.Checked)
{
if (String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl))
{
UpdateOnlinePACURLItem_Click(sender, e);
}
if (!String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl))
{
localPACItem.Checked = false;
onlinePACItem.Checked = true;
controller.UseOnlinePAC(true);
}
UpdatePACItemsEnabledStatus();
}
}
private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e)
{
string origPacUrl = controller.GetConfigurationCopy().pacUrl;
string pacUrl = Microsoft.VisualBasic.Interaction.InputBox(
I18N.GetString("Please input PAC Url"),
I18N.GetString("Edit Online PAC URL"),
origPacUrl, -1, -1);
if (!string.IsNullOrEmpty(pacUrl) && pacUrl != origPacUrl)
{
controller.SavePACUrl(pacUrl);
}
}
private void UpdatePACItemsEnabledStatus()
{
if (this.localPACItem.Checked)
{
this.editLocalPACItem.Enabled = true;
this.updateFromGFWListItem.Enabled = true;
this.editGFWUserRuleItem.Enabled = true;
this.editOnlinePACItem.Enabled = false;
}
else
{
this.editLocalPACItem.Enabled = false;
this.updateFromGFWListItem.Enabled = false;
this.editGFWUserRuleItem.Enabled = false;
this.editOnlinePACItem.Enabled = true;
}
}
}
}
+7 -16
View File
@@ -63,7 +63,6 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
@@ -124,16 +123,12 @@
<Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" />
<Compile Include="Controller\System\AutoStartup.cs" />
<Compile Include="Controller\AutoStartup.cs" />
<Compile Include="Controller\FileManager.cs" />
<Compile Include="Controller\Service\GFWListUpdater.cs" />
<Compile Include="Controller\GFWListUpdater.cs" />
<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="Controller\UpdateChecker.cs" />
<Compile Include="Encryption\EncryptorBase.cs" />
<Compile Include="Encryption\EncryptorFactory.cs" />
<Compile Include="Encryption\IVEncryptor.cs" />
@@ -143,7 +138,7 @@
<Compile Include="Encryption\SodiumEncryptor.cs" />
<Compile Include="Encryption\TableEncryptor.cs" />
<Compile Include="Encryption\IEncryptor.cs" />
<Compile Include="Controller\Service\PACServer.cs" />
<Compile Include="Controller\PACServer.cs" />
<Compile Include="Model\Server.cs" />
<Compile Include="Model\Configuration.cs" />
<Compile Include="Properties\Resources.Designer.cs">
@@ -151,9 +146,6 @@
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Controller\Strategy\BalancingStrategy.cs" />
<Compile Include="Controller\Strategy\StrategyManager.cs" />
<Compile Include="Controller\Strategy\IStrategy.cs" />
<Compile Include="Util\Util.cs" />
<Compile Include="View\ConfigForm.cs">
<SubType>Form</SubType>
@@ -161,12 +153,12 @@
<Compile Include="View\ConfigForm.Designer.cs">
<DependentUpon>ConfigForm.cs</DependentUpon>
</Compile>
<Compile Include="Controller\Service\TCPRelay.cs" />
<Compile Include="Controller\Service\PolipoRunner.cs" />
<Compile Include="Controller\Local.cs" />
<Compile Include="Controller\PolipoRunner.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Controller\ShadowsocksController.cs" />
<Compile Include="Controller\System\SystemProxy.cs" />
<Compile Include="Controller\SystemProxy.cs" />
<Compile Include="View\MenuViewController.cs" />
<Compile Include="View\QRCodeForm.cs">
<SubType>Form</SubType>
@@ -204,7 +196,6 @@
<None Include="Resources\ss24.png" />
<None Include="Resources\ssw128.png" />
<Content Include="Data\cn.txt" />
<Content Include="Data\user-rule.txt" />
<Content Include="shadowsocks.ico" />
<None Include="Data\polipo_config.txt" />
</ItemGroup>