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
- Use the same port for every profile
- Use the same port for HTTP/Socks5/PAC
+3 -12
View File
@@ -4,24 +4,15 @@ How to Contribute
Pull Requests
-------------
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.
1. Pull requests are welcome.
2. Make sure to pass the unit tests. Write unit tests for new modules if
needed.
Issues
------
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].
4. Issues in languages other than English will be Google translated into English
later.
1. Nobody has reported any bugs but posted a lot of questions in the last
few months. So we're closing the issue tracker.
[Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting
+10 -7
View File
@@ -8,20 +8,20 @@ Shadowsocks for Windows
1. System proxy configuration
2. Fast profile switching
3. PAC mode and global mode
4. Compatible with IE
5. Only a single exe file of 200KB size
4. GFWList and user rules
5. Supports HTTP proxy
#### 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
1. Find Shadowsocks icon in notification tray
1. Find Shadowsocks icon in the 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
@@ -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
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
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
@@ -42,4 +45,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://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
{
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;
bool _shareOverLAN;
Socket _socket;
Socket _tcpSocket;
Socket _udpSocket;
IList<Service> _services;
public Listener(IList<Service> services)
@@ -51,8 +58,10 @@ namespace Shadowsocks.Controller
try
{
// Create a TCP/IP socket.
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_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)
{
@@ -64,29 +73,73 @@ namespace Shadowsocks.Controller
}
// Bind the socket to the local endpoint and listen for incoming connections.
_socket.Bind(localEndPoint);
_socket.Listen(1024);
_tcpSocket.Bind(localEndPoint);
_udpSocket.Bind(localEndPoint);
_tcpSocket.Listen(1024);
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Shadowsocks started");
_socket.BeginAccept(
_tcpSocket.BeginAccept(
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)
{
_socket.Close();
_tcpSocket.Close();
throw;
}
}
public void Stop()
{
if (_socket != null)
if (_tcpSocket != null)
{
_socket.Close();
_socket = 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)
{
}
}
}
@@ -144,14 +197,16 @@ namespace Shadowsocks.Controller
int bytesRead = conn.EndReceive(ar);
foreach (Service service in _services)
{
if (service.Handle(buf, bytesRead, conn))
if (service.Handle(buf, bytesRead, conn, null))
{
return;
}
}
// no service found for this
// shouldn't happen
conn.Close();
if (conn.ProtocolType == ProtocolType.Tcp)
{
conn.Close();
}
}
catch (Exception e)
{
+5 -1
View File
@@ -33,8 +33,12 @@ namespace Shadowsocks.Controller
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
{
string request = Encoding.UTF8.GetString(firstPacket, 0, length);
@@ -15,8 +15,12 @@ namespace Shadowsocks.Controller
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);
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()
{
// 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);
Local local = new Local(_config);
TCPRelay tcpRelay = new TCPRelay(_config);
UDPRelay udpRelay = new UDPRelay(_config);
List<Listener.Service> services = new List<Listener.Service>();
services.Add(local);
services.Add(tcpRelay);
services.Add(udpRelay);
services.Add(_pacServer);
services.Add(new PortForwarder(polipoRunner.RunningPort));
_listener = new Listener(services);
+6 -1
View File
@@ -49,9 +49,14 @@ namespace Shadowsocks.Controller
}
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", "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now));
registry.SetValue("AutoConfigURL", pacUrl);
}
}
else
@@ -9,16 +9,20 @@ using Shadowsocks.Model;
namespace Shadowsocks.Controller
{
class Local : Listener.Service
class TCPRelay : Listener.Service
{
private Configuration _config;
public Local(Configuration config)
public TCPRelay(Configuration 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)
{
return false;
@@ -44,6 +48,7 @@ namespace Shadowsocks.Controller
public Socket remote;
public Socket connection;
private byte command;
private byte[] _firstPacket;
private int _firstPacketLength;
// Size of receive buffer.
@@ -70,32 +75,7 @@ namespace Shadowsocks.Controller
{
this._firstPacket = firstPacket;
this._firstPacketLength = length;
try
{
// 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();
}
this.HandshakeReceive();
}
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()
{
@@ -241,11 +199,19 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = connection.EndReceive(ar);
if (bytesRead > 0)
if (bytesRead >= 3)
{
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(StartPipe), null);
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(StartConnect), null);
}
else if (command == 3)
{
HandleUDPAssociate();
}
}
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)
{
@@ -269,7 +346,6 @@ namespace Shadowsocks.Controller
}
try
{
connection.EndReceive(ar);
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0,
new AsyncCallback(PipeRemoteReceiveCallback), null);
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.Text;
using System.Text.RegularExpressions;
using System.Xml;
using SimpleJson;
namespace Shadowsocks.Controller
{
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 LatestVersionURL;
public event EventHandler NewVersionFound;
public const string Version = "2.3.1";
public const string Version = "2.4";
public void CheckUpdate(Configuration config)
{
// 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.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(UpdateURL));
@@ -119,23 +120,25 @@ namespace Shadowsocks.Controller
{
string response = e.Result;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(response);
XmlNodeList elements = xmlDoc.GetElementsByTagName("media:content");
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
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(attr.Value);
}
versions.Add(url);
}
}
}
if (versions.Count == 0)
{
return;
+11 -2
View File
@@ -12,7 +12,10 @@ Servers=服务器
Edit Servers...=编辑服务器...
Start on Boot=开机启动
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 的用户规则...
Show QRCode...=显示二维码...
Scan QRCode from Screen...=扫描屏幕上的二维码...
@@ -40,6 +43,12 @@ New server=未配置的服务器
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}
@@ -49,13 +58,13 @@ 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=系统代理未启用
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。
@@ -17,6 +17,8 @@ namespace Shadowsocks.Model
public bool shareOverLan;
public bool isDefault;
public int localPort;
public string pacUrl;
public bool useOnlinePac;
private static string CONFIG_FILE = "gui-config.json";
@@ -116,6 +118,10 @@ namespace Shadowsocks.Model
{
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)
+11 -14
View File
@@ -71,7 +71,12 @@ namespace Shadowsocks.Properties {
}
/// <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=启用系统代理
///Mode=系统代理模式
///PAC=PAC 模式
@@ -80,24 +85,16 @@ namespace Shadowsocks.Properties {
///Edit Servers...=编辑服务器...
///Start on Boot=开机启动
///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 的用户规则...
///Show QRCode...=显示二维码...
///Scan QRCode from Screen...=扫描屏幕上的二维码...
///Show Logs...=显示日志...
///About...=关于...
///Quit=退出
///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;;.
///Quit=退出 [rest of string was truncated]&quot;;.
/// </summary>
internal static string cn {
get {
+78 -4
View File
@@ -35,6 +35,12 @@ 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;
@@ -151,10 +157,15 @@ namespace Shadowsocks.View
CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)),
CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click))
}),
new MenuItem("-"),
CreateMenuItem("Edit PAC File...", new EventHandler(this.EditPACFileItem_Click)),
CreateMenuItem("Update PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)),
CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_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)),
@@ -240,6 +251,9 @@ namespace Shadowsocks.View
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()
@@ -481,5 +495,65 @@ 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.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>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
@@ -130,6 +131,7 @@
<Compile Include="Controller\Listener.cs" />
<Compile Include="Controller\Logging.cs" />
<Compile Include="Controller\PortForwarder.cs" />
<Compile Include="Controller\UDPRelay.cs" />
<Compile Include="Controller\UpdateChecker.cs" />
<Compile Include="Encryption\EncryptorBase.cs" />
<Compile Include="Encryption\EncryptorFactory.cs" />
@@ -155,7 +157,7 @@
<Compile Include="View\ConfigForm.Designer.cs">
<DependentUpon>ConfigForm.cs</DependentUpon>
</Compile>
<Compile Include="Controller\Local.cs" />
<Compile Include="Controller\TCPRelay.cs" />
<Compile Include="Controller\PolipoRunner.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />