@@ -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 | |||
@@ -10,6 +10,7 @@ Shadowsocks for Windows | |||
3. PAC mode and global mode | |||
4. GFWList and user rules | |||
5. Supports HTTP proxy | |||
6. Supports UDP relay (see Usage) | |||
#### Download | |||
@@ -33,6 +34,8 @@ 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 | |||
7. For UDP, you need to use SocksCap or ProxyCap to force programs you want | |||
to proxy to tunnel over Shadowsocks | |||
### Develop | |||
@@ -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) | |||
{ | |||
_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) | |||
{ | |||
_socket.Close(); | |||
_socket = null; | |||
} | |||
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) | |||
{ | |||
@@ -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; | |||
} | |||
@@ -250,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); | |||
@@ -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 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(IAsyncResult ar) | |||
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, |
@@ -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; | |||
} | |||
} |
@@ -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"]) | |||
{ | |||
continue; | |||
} | |||
foreach (JsonObject asset in (JsonArray)release["assets"]) | |||
{ | |||
if (attr.Name == "url") | |||
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; | |||
@@ -58,6 +58,7 @@ 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 在这里 | |||
@@ -118,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) | |||
@@ -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=编辑服务器 | |||
///&Add=添加(&A) | |||
///&Delete=删除(&D) | |||
///Server=服务器 | |||
///Server IP=服务器 IP | |||
///Server Port=服务器端口 | |||
///Password=密码 | |||
///Encryption=加密 | |||
///Proxy Port=代理端口 | |||
///Remarks=备注 | |||
///OK= [rest of string was truncated]";. | |||
///Quit=退出 [rest of string was truncated]";. | |||
/// </summary> | |||
internal static string cn { | |||
get { | |||
@@ -131,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" /> | |||
@@ -156,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" /> | |||