Compare commits

..
28 Commits
Author SHA1 Message Date
clowwindy c673f18f80 update CHANGES 2015-07-11 19:47:26 +08:00
clowwindy ccce252d62 bump 2015-07-11 19:36:09 +08:00
clowwindy 544c9dc766 update generated code 2015-07-11 19:35:02 +08:00
clowwindy 21fda9e81b use GitHub releases instead of SourceForge 2015-07-11 19:31:43 +08:00
clowwindy 5906ddbc4b fix warnings 2015-07-11 18:50:10 +08:00
clowwindy f9f49552ae add port check for 8123 2015-07-11 18:38:45 +08:00
clowwindy 09def2fc27 add LRUCache for UDPRelay 2015-07-11 18:32:32 +08:00
clowwindy 680d465393 add UDPRelay 2015-07-11 18:07:49 +08:00
clowwindy f5500c4080 support UDP assoc in UDP relay 2015-07-11 15:41:47 +08:00
clowwindy ef357bbbcf handshake before connecting to server 2015-07-11 13:19:16 +08:00
clowwindy 96f996c6de Update README.md 2015-06-28 14:15:49 +08:00
clowwindy 967c4e8c8b Update CONTRIBUTING.md 2015-04-29 11:42:57 +08:00
clowwindy 5182dcdc66 Update CONTRIBUTING.md 2015-04-29 11:40:05 +08:00
clowwindy 660857a348 Update CONTRIBUTING.md 2015-04-29 11:39:34 +08:00
clowwindy 5c172a3e49 Update README.md 2015-04-04 12:33:23 +08:00
clowwindy dd0ac006b5 Update README.md 2015-04-04 12:25:05 +08:00
clowwindy 95242208ec UX fix
1. only enable menu items relevant to pac status
2. rename edit pac file into edit local pac file
3. users are allowed to enable online pac then input online pac url, not to input then enable
2015-03-22 20:28:40 +08:00
clowwindy 0d4dc2ada6 Merge pull request #197 from GangZhuo/online-pac
Support Online PAC URL
2015-03-21 19:23:38 +08:00
Gang Zhuo 532a53c971 revert 2015-03-21 19:07:58 +08:00
Gang Zhuo c6e6d019f1 fix regular expression 2015-03-21 19:02:17 +08:00
Gang Zhuo 00f57ee329 validate user input 2015-03-21 18:47:05 +08:00
Gang Zhuo 80dc5c0e90 remove not use message 2015-03-20 21:51:07 +08:00
Gang Zhuo 5f2ec3ccff Use VB.NET InputBox instead of implement a message box 2015-03-20 21:26:31 +08:00
Gang Zhuo b762c8046a Direct set the system PAC to the online PAC URL 2015-03-20 21:14:27 +08:00
Gang Zhuo 1bb01bb68e rename CancelButton to MyCancelButton 2015-03-20 01:37:05 -04:00
Gang Zhuo 4d4d02604c layout by TableLayoutPanel 2015-03-20 01:08:01 -04:00
Gang Zhuo f946b0d99c fix I18N 2015-03-20 01:07:10 -04:00
Gang Zhuo e0612854d6 support online pac, see https://github.com/shadowsocks/shadowsocks-csharp/issues/163 2015-03-20 00:46:46 -04:00
16 changed files with 591 additions and 130 deletions
+9
View File
@@ -1,3 +1,12 @@
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 2.3 2015-01-25
- Use the same port for every profile - Use the same port for every profile
- Use the same port for HTTP/Socks5/PAC - Use the same port for HTTP/Socks5/PAC
+3 -12
View File
@@ -4,24 +4,15 @@ How to Contribute
Pull Requests Pull Requests
------------- -------------
1. Pull requests are welcome. If you would like to add a large feature 1. Pull requests are welcome.
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 2. Make sure to pass the unit tests. Write unit tests for new modules if
needed. needed.
Issues Issues
------ ------
1. Only bugs and feature requests are accepted here. 1. Nobody has reported any bugs but posted a lot of questions in the last
2. We'll only work on important features. If the feature you're asking only few months. So we're closing the issue tracker.
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].
4. Issues in languages other than English will be Google translated into English
later.
[Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting [Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting
+10 -7
View File
@@ -8,20 +8,20 @@ Shadowsocks for Windows
1. System proxy configuration 1. System proxy configuration
2. Fast profile switching 2. Fast profile switching
3. PAC mode and global mode 3. PAC mode and global mode
4. Compatible with IE 4. GFWList and user rules
5. Only a single exe file of 200KB size 5. Supports HTTP proxy
#### Download #### Download
Download [latest release]. Download a [latest release].
For <= Windows 7, download Shadowsocks-win-x.x.x.zip. For >= Windows 8 or with .Net 4.0, download Shadowsocks-win-dotnet4.0-x.x.x.zip.
For >= Windows 8, download Shadowsocks-win-dotnet4.0-x.x.x.zip, unless you have .Net 2.0 installed. For <= Windows 7 or with .Net 2.0, download Shadowsocks-win-x.x.x.zip.
#### Usage #### Usage
1. Find Shadowsocks icon in notification tray 1. Find Shadowsocks icon in the notification tray
2. You can add multiple servers in servers menu 2. You can add multiple servers in servers menu
3. Select Enable System Proxy menu to enable system proxy. Please disable other 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 proxy addons in your browser, or set them to use system proxy
@@ -30,6 +30,9 @@ system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this
port in Server -> Edit Servers port in Server -> Edit Servers
5. You can change PAC rules by editing the PAC file. When you save the PAC file 5. 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 with any editor, Shadowsocks will notify browsers about the change automatically
6. You can also update the PAC file from GFWList. Note your modifications to the PAC
file will be lost. However you can put your rules in the user rule file for GFWList.
Don't forget to update from GFWList again after you've edited the user rule
### Develop ### Develop
@@ -42,4 +45,4 @@ GPLv3
[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp [Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp
[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master [Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master
[latest release]: https://sourceforge.net/projects/shadowsocksgui/files/dist/ [latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
+70 -15
View File
@@ -12,12 +12,19 @@ namespace Shadowsocks.Controller
{ {
public interface Service public interface Service
{ {
bool Handle(byte[] firstPacket, int length, Socket socket); 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, 1);
} }
Configuration _config; Configuration _config;
bool _shareOverLAN; bool _shareOverLAN;
Socket _socket; Socket _tcpSocket;
Socket _udpSocket;
IList<Service> _services; IList<Service> _services;
public Listener(IList<Service> services) public Listener(IList<Service> services)
@@ -51,8 +58,10 @@ namespace Shadowsocks.Controller
try try
{ {
// Create a TCP/IP socket. // Create a TCP/IP socket.
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); _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; IPEndPoint localEndPoint = null;
if (_shareOverLAN) if (_shareOverLAN)
{ {
@@ -64,29 +73,73 @@ namespace Shadowsocks.Controller
} }
// Bind the socket to the local endpoint and listen for incoming connections. // Bind the socket to the local endpoint and listen for incoming connections.
_socket.Bind(localEndPoint); _tcpSocket.Bind(localEndPoint);
_socket.Listen(1024); _udpSocket.Bind(localEndPoint);
_tcpSocket.Listen(1024);
// Start an asynchronous socket to listen for connections. // Start an asynchronous socket to listen for connections.
Console.WriteLine("Shadowsocks started"); Console.WriteLine("Shadowsocks started");
_socket.BeginAccept( _tcpSocket.BeginAccept(
new AsyncCallback(AcceptCallback), new AsyncCallback(AcceptCallback),
_socket); _tcpSocket);
UDPState udpState = new UDPState();
_udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState);
} }
catch (SocketException) catch (SocketException)
{ {
_socket.Close(); _tcpSocket.Close();
throw; throw;
} }
} }
public void Stop() public void Stop()
{ {
if (_socket != null) if (_tcpSocket != null)
{ {
_socket.Close(); _tcpSocket.Close();
_socket = null; _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)
{
}
} }
} }
@@ -144,14 +197,16 @@ namespace Shadowsocks.Controller
int bytesRead = conn.EndReceive(ar); int bytesRead = conn.EndReceive(ar);
foreach (Service service in _services) foreach (Service service in _services)
{ {
if (service.Handle(buf, bytesRead, conn)) if (service.Handle(buf, bytesRead, conn, null))
{ {
return; return;
} }
} }
// no service found for this // no service found for this
// shouldn't happen if (conn.ProtocolType == ProtocolType.Tcp)
conn.Close(); {
conn.Close();
}
} }
catch (Exception e) catch (Exception e)
{ {
+5 -1
View File
@@ -33,8 +33,12 @@ namespace Shadowsocks.Controller
this._config = config; this._config = config;
} }
public bool Handle(byte[] firstPacket, int length, Socket socket) public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
{ {
if (socket.ProtocolType != ProtocolType.Tcp)
{
return false;
}
try try
{ {
string request = Encoding.UTF8.GetString(firstPacket, 0, length); string request = Encoding.UTF8.GetString(firstPacket, 0, length);
@@ -15,8 +15,12 @@ namespace Shadowsocks.Controller
this._targetPort = targetPort; this._targetPort = targetPort;
} }
public bool Handle(byte[] firstPacket, int length, Socket socket) 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); new Handler().Start(firstPacket, length, socket, this._targetPort);
return true; return true;
} }
@@ -192,6 +192,28 @@ namespace Shadowsocks.Controller
} }
} }
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());
}
}
protected void Reload() protected void Reload()
{ {
// some logic in configuration updated the config when saving, we need to read it again // some logic in configuration updated the config when saving, we need to read it again
@@ -228,9 +250,11 @@ namespace Shadowsocks.Controller
{ {
polipoRunner.Start(_config); polipoRunner.Start(_config);
Local local = new Local(_config); TCPRelay tcpRelay = new TCPRelay(_config);
UDPRelay udpRelay = new UDPRelay(_config);
List<Listener.Service> services = new List<Listener.Service>(); List<Listener.Service> services = new List<Listener.Service>();
services.Add(local); services.Add(tcpRelay);
services.Add(udpRelay);
services.Add(_pacServer); services.Add(_pacServer);
services.Add(new PortForwarder(polipoRunner.RunningPort)); services.Add(new PortForwarder(polipoRunner.RunningPort));
_listener = new Listener(services); _listener = new Listener(services);
+6 -1
View File
@@ -49,9 +49,14 @@ namespace Shadowsocks.Controller
} }
else 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("ProxyEnable", 0);
registry.SetValue("ProxyServer", ""); registry.SetValue("ProxyServer", "");
registry.SetValue("AutoConfigURL", "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now)); registry.SetValue("AutoConfigURL", pacUrl);
} }
} }
else else
@@ -9,16 +9,20 @@ using Shadowsocks.Model;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
class Local : Listener.Service class TCPRelay : Listener.Service
{ {
private Configuration _config; private Configuration _config;
public Local(Configuration config) public TCPRelay(Configuration config)
{ {
this._config = config; this._config = config;
} }
public bool Handle(byte[] firstPacket, int length, Socket socket) public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
{ {
if (socket.ProtocolType != ProtocolType.Tcp)
{
return false;
}
if (length < 2 || firstPacket[0] != 5) if (length < 2 || firstPacket[0] != 5)
{ {
return false; return false;
@@ -44,6 +48,7 @@ namespace Shadowsocks.Controller
public Socket remote; public Socket remote;
public Socket connection; public Socket connection;
private byte command;
private byte[] _firstPacket; private byte[] _firstPacket;
private int _firstPacketLength; private int _firstPacketLength;
// Size of receive buffer. // Size of receive buffer.
@@ -70,32 +75,7 @@ namespace Shadowsocks.Controller
{ {
this._firstPacket = firstPacket; this._firstPacket = firstPacket;
this._firstPacketLength = length; this._firstPacketLength = length;
try this.HandshakeReceive();
{
// 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);
// Connect to the remote endpoint.
remote.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), null);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
} }
private void CheckClose() private void CheckClose()
@@ -149,28 +129,6 @@ namespace Shadowsocks.Controller
} }
} }
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() private void HandshakeReceive()
{ {
@@ -241,11 +199,19 @@ namespace Shadowsocks.Controller
try try
{ {
int bytesRead = connection.EndReceive(ar); int bytesRead = connection.EndReceive(ar);
if (bytesRead > 0) if (bytesRead >= 3)
{ {
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; command = connetionRecvBuffer[1];
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(StartPipe), null); if (command == 1)
{
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(StartConnect), null);
}
else if (command == 3)
{
HandleUDPAssociate();
}
} }
else else
{ {
@@ -260,8 +226,119 @@ 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 StartPipe(IAsyncResult ar) 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 StartConnect(IAsyncResult ar)
{
try
{
connection.EndSend(ar);
// 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);
// 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
{
// Complete the connection.
remote.EndConnect(ar);
//Console.WriteLine("Socket connected to {0}",
// remote.RemoteEndPoint.ToString());
StartPipe();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
this.Close();
}
}
private void StartPipe()
{ {
if (closed) if (closed)
{ {
@@ -269,7 +346,6 @@ namespace Shadowsocks.Controller
} }
try try
{ {
connection.EndReceive(ar);
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null); new AsyncCallback(PipeRemoteReceiveCallback), null);
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0,
+199
View File
@@ -0,0 +1,199 @@
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;
namespace Shadowsocks.Controller
{
class UDPRelay : Listener.Service
{
private Configuration _config;
private LRUCache<IPEndPoint, UDPHandler> _cache;
public UDPRelay(Configuration config)
{
this._config = config;
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, _config.GetCurrentServer(), 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;
}
}
+16 -13
View File
@@ -6,24 +6,25 @@ using System.Net;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml; using SimpleJson;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
public class UpdateChecker public class UpdateChecker
{ {
private const string UpdateURL = "https://sourceforge.net/api/file/index/project-id/1817190/path/dist/mtime/desc/limit/10/rss"; private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-csharp/releases";
public string LatestVersionNumber; public string LatestVersionNumber;
public string LatestVersionURL; public string LatestVersionURL;
public event EventHandler NewVersionFound; public event EventHandler NewVersionFound;
public const string Version = "2.3.1"; public const string Version = "2.4";
public void CheckUpdate(Configuration config) public void CheckUpdate(Configuration config)
{ {
// TODO test failures // TODO test failures
WebClient http = new WebClient(); 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(), config.localPort);
http.DownloadStringCompleted += http_DownloadStringCompleted; http.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(UpdateURL)); http.DownloadStringAsync(new Uri(UpdateURL));
@@ -119,23 +120,25 @@ namespace Shadowsocks.Controller
{ {
string response = e.Result; string response = e.Result;
XmlDocument xmlDoc = new XmlDocument(); JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
xmlDoc.LoadXml(response);
XmlNodeList elements = xmlDoc.GetElementsByTagName("media:content");
List<string> versions = new List<string>(); List<string> versions = new List<string>();
foreach (XmlNode el in elements) foreach (JsonObject release in result)
{ {
foreach (XmlAttribute attr in el.Attributes) if ((bool)release["prerelease"])
{ {
if (attr.Name == "url") continue;
}
foreach (JsonObject asset in (JsonArray)release["assets"])
{
string url = (string)asset["browser_download_url"];
if (IsNewVersion(url))
{ {
if (IsNewVersion(attr.Value)) versions.Add(url);
{
versions.Add(attr.Value);
}
} }
} }
} }
if (versions.Count == 0) if (versions.Count == 0)
{ {
return; return;
+11 -2
View File
@@ -12,7 +12,10 @@ Servers=服务器
Edit Servers...=编辑服务器... Edit Servers...=编辑服务器...
Start on Boot=开机启动 Start on Boot=开机启动
Allow Clients from LAN=允许来自局域网的连接 Allow Clients from LAN=允许来自局域网的连接
Edit PAC File...=编辑 PAC 文件... Local PAC=使用本地 PAC
Online PAC=使用在线 PAC
Edit Local PAC File...=编辑本地 PAC 文件...
Update Local PAC from GFWList=从 GFWList 更新本地 PAC
Edit User Rule for GFWList...=编辑 GFWList 的用户规则... Edit User Rule for GFWList...=编辑 GFWList 的用户规则...
Show QRCode...=显示二维码... Show QRCode...=显示二维码...
Scan QRCode from Screen...=扫描屏幕上的二维码... Scan QRCode from Screen...=扫描屏幕上的二维码...
@@ -40,6 +43,12 @@ New server=未配置的服务器
QRCode=二维码 QRCode=二维码
# PAC Url Form
Edit Online PAC URL=编辑在线 PAC 网址
Edit Online PAC URL...=编辑在线 PAC 网址...
Please input PAC Url=请输入 PAC 网址
# Messages # Messages
Shadowsocks Error: {0}=Shadowsocks 错误: {0} Shadowsocks Error: {0}=Shadowsocks 错误: {0}
@@ -49,13 +58,13 @@ Please add at least one server=请添加至少一个服务器
Server IP can not be blank=服务器 IP 不能为空 Server IP can not be blank=服务器 IP 不能为空
Password can not be blank=密码不能为空 Password can not be blank=密码不能为空
Port out of range=端口超出范围 Port out of range=端口超出范围
Port can't be 8123=端口不能为 8123
Shadowsocks {0} Update Found=Shadowsocks {0} 更新 Shadowsocks {0} Update Found=Shadowsocks {0} 更新
Click here to download=点击这里下载 Click here to download=点击这里下载
Shadowsocks is here=Shadowsocks 在这里 Shadowsocks is here=Shadowsocks 在这里
You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks
System Proxy Enabled=系统代理已启用 System Proxy Enabled=系统代理已启用
System Proxy Disabled=系统代理未启用 System Proxy Disabled=系统代理未启用
Update PAC from GFWList=从 GFWList 更新 PAC
Failed to update PAC file =更新 PAC 文件失败 Failed to update PAC file =更新 PAC 文件失败
PAC updated=更新 PAC 成功 PAC updated=更新 PAC 成功
No updates found. Please report to GFWList if you have problems with it.=未发现更新。如有问题请提交给 GFWList。 No updates found. Please report to GFWList if you have problems with it.=未发现更新。如有问题请提交给 GFWList。
@@ -17,6 +17,8 @@ namespace Shadowsocks.Model
public bool shareOverLan; public bool shareOverLan;
public bool isDefault; public bool isDefault;
public int localPort; public int localPort;
public string pacUrl;
public bool useOnlinePac;
private static string CONFIG_FILE = "gui-config.json"; private static string CONFIG_FILE = "gui-config.json";
@@ -116,6 +118,10 @@ namespace Shadowsocks.Model
{ {
throw new ArgumentException(I18N.GetString("Port out of range")); 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) private static void CheckPassword(string password)
+11 -14
View File
@@ -71,7 +71,12 @@ namespace Shadowsocks.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Shadowsocks=Shadowsocks /// Looks up a localized string similar to # translation for Simplified Chinese
///
///Shadowsocks=Shadowsocks
///
///# Menu items
///
///Enable System Proxy=启用系统代理 ///Enable System Proxy=启用系统代理
///Mode=系统代理模式 ///Mode=系统代理模式
///PAC=PAC 模式 ///PAC=PAC 模式
@@ -80,24 +85,16 @@ namespace Shadowsocks.Properties {
///Edit Servers...=编辑服务器... ///Edit Servers...=编辑服务器...
///Start on Boot=开机启动 ///Start on Boot=开机启动
///Allow Clients from LAN=允许来自局域网的连接 ///Allow Clients from LAN=允许来自局域网的连接
///Edit PAC File...=编辑 PAC 文件... ///Local PAC=使用本地 PAC
///Online PAC=使用在线 PAC
///Edit Local PAC File...=编辑本地 PAC 文件...
///Update Local PAC from GFWList=从 GFWList 更新本地 PAC
///Edit User Rule for GFWList...=编辑 GFWList 的用户规则... ///Edit User Rule for GFWList...=编辑 GFWList 的用户规则...
///Show QRCode...=显示二维码... ///Show QRCode...=显示二维码...
///Scan QRCode from Screen...=扫描屏幕上的二维码... ///Scan QRCode from Screen...=扫描屏幕上的二维码...
///Show Logs...=显示日志... ///Show Logs...=显示日志...
///About...=关于... ///About...=关于...
///Quit=退出 ///Quit=退出 [rest of string was truncated]&quot;;.
///Edit Servers=编辑服务器
///&amp;Add=添加(&amp;A)
///&amp;Delete=删除(&amp;D)
///Server=服务器
///Server IP=服务器 IP
///Server Port=服务器端口
///Password=密码
///Encryption=加密
///Proxy Port=代理端口
///Remarks=备注
///OK= [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string cn { internal static string cn {
get { get {
+78 -4
View File
@@ -35,6 +35,12 @@ namespace Shadowsocks.View
private MenuItem ServersItem; private MenuItem ServersItem;
private MenuItem globalModeItem; private MenuItem globalModeItem;
private MenuItem PACModeItem; 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 ConfigForm configForm;
private string _urlToOpen; private string _urlToOpen;
@@ -151,10 +157,15 @@ namespace Shadowsocks.View
CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)),
CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)) CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click))
}), }),
new MenuItem("-"), CreateMenuGroup("PAC ", new MenuItem[] {
CreateMenuItem("Edit PAC File...", new EventHandler(this.EditPACFileItem_Click)), this.localPACItem = CreateMenuItem("Local PAC", new EventHandler(this.LocalPACItem_Click)),
CreateMenuItem("Update PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)), this.onlinePACItem = CreateMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)),
CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_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("-"), new MenuItem("-"),
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), 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("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
@@ -240,6 +251,9 @@ namespace Shadowsocks.View
PACModeItem.Checked = !config.global; PACModeItem.Checked = !config.global;
ShareOverLANItem.Checked = config.shareOverLan; ShareOverLANItem.Checked = config.shareOverLan;
AutoStartupItem.Checked = AutoStartup.Check(); AutoStartupItem.Checked = AutoStartup.Check();
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
localPACItem.Checked = !onlinePACItem.Checked;
UpdatePACItemsEnabledStatus();
} }
private void UpdateServersMenu() private void UpdateServersMenu()
@@ -481,5 +495,65 @@ namespace Shadowsocks.View
MessageBox.Show(I18N.GetString("Failed to update registry")); 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.GetConfiguration().pacUrl))
{
UpdateOnlinePACURLItem_Click(sender, e);
}
if (!String.IsNullOrEmpty(controller.GetConfiguration().pacUrl))
{
localPACItem.Checked = false;
onlinePACItem.Checked = true;
controller.UseOnlinePAC(true);
}
UpdatePACItemsEnabledStatus();
}
}
private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e)
{
string origPacUrl = controller.GetConfiguration().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;
}
}
} }
} }
+3 -1
View File
@@ -63,6 +63,7 @@
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
@@ -130,6 +131,7 @@
<Compile Include="Controller\Listener.cs" /> <Compile Include="Controller\Listener.cs" />
<Compile Include="Controller\Logging.cs" /> <Compile Include="Controller\Logging.cs" />
<Compile Include="Controller\PortForwarder.cs" /> <Compile Include="Controller\PortForwarder.cs" />
<Compile Include="Controller\UDPRelay.cs" />
<Compile Include="Controller\UpdateChecker.cs" /> <Compile Include="Controller\UpdateChecker.cs" />
<Compile Include="Encryption\EncryptorBase.cs" /> <Compile Include="Encryption\EncryptorBase.cs" />
<Compile Include="Encryption\EncryptorFactory.cs" /> <Compile Include="Encryption\EncryptorFactory.cs" />
@@ -155,7 +157,7 @@
<Compile Include="View\ConfigForm.Designer.cs"> <Compile Include="View\ConfigForm.Designer.cs">
<DependentUpon>ConfigForm.cs</DependentUpon> <DependentUpon>ConfigForm.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Controller\Local.cs" /> <Compile Include="Controller\TCPRelay.cs" />
<Compile Include="Controller\PolipoRunner.cs" /> <Compile Include="Controller\PolipoRunner.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />