@@ -0,0 +1,39 @@ | |||
--- | |||
name: Bug report (English) | |||
about: Create a report to help us improve | |||
title: '' | |||
labels: '' | |||
assignees: '' | |||
--- | |||
<!-- | |||
- Shadowsocks is a non-profit open source project. If you bought the service from a provider, please contact them. | |||
- If you have questions rather than Shadowsocks Windows client, please go to https://github.com/shadowsocks | |||
- Please read Wiki carefully, especially https://github.com/shadowsocks/shadowsocks-windows/wiki/Troubleshooting | |||
- And search from Issue Board https://github.com/shadowsocks/shadowsocks-windows/issues?utf8=%E2%9C%93&q=is%3Aissue | |||
- Please include the following information. Questions lacking details will be closed. | |||
--> | |||
### Describe the bug | |||
### Environment | |||
- Shadowsocks client version: | |||
- OS version: | |||
- .NET version: | |||
### Steps you have tried | |||
### What did you expect to see? | |||
### What did you see instead? | |||
### Config and error log in detail (with all sensitive info masked) | |||
``` | |||
PASTE LOG HERE | |||
``` |
@@ -0,0 +1,39 @@ | |||
--- | |||
name: Bug报告 (中文) | |||
about: 反馈Bug | |||
title: '' | |||
labels: bug report | |||
assignees: '' | |||
--- | |||
<!-- | |||
- 影梭(Shadowsocks)是一个开源非盈利项目,不提供任何托管服务。如果你是从服务提供商购买的服务,请联系他们。 | |||
- 如果你有非影梭Windows客户端相关的问题,请去 https://github.com/shadowsocks | |||
- 提问前请先阅读wiki https://github.com/shadowsocks/shadowsocks-windows/wiki/Troubleshooting. | |||
- 并在Issue Board中搜索 https://github.com/shadowsocks/shadowsocks-windows/issues?utf8=%E2%9C%93&q=is%3Aissue | |||
- 请按照以下格式描述你的问题,描述不清的问题将会被关闭。 | |||
--> | |||
### 简要描述问题 | |||
### 环境 | |||
- Shadowsocks客户端版本: | |||
- 操作系统版本: | |||
- .NET版本: | |||
### 操作步骤 | |||
### 期望的结果 | |||
### 实际结果 | |||
### 配置文件和日志文件(请隐去敏感信息) | |||
``` | |||
在此粘贴日志 | |||
``` |
@@ -0,0 +1,20 @@ | |||
--- | |||
name: Feature request | |||
about: Suggest an idea for this project | |||
title: '' | |||
labels: '' | |||
assignees: '' | |||
--- | |||
**Is your feature request related to a problem? Please describe.** | |||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] | |||
**Describe the solution you'd like** | |||
A clear and concise description of what you want to happen. | |||
**Describe alternatives you've considered** | |||
A clear and concise description of any alternative solutions or features you've considered. | |||
**Additional context** | |||
Add any other context or screenshots about the feature request here. |
@@ -1,29 +0,0 @@ | |||
<!-- | |||
- Shadowsocks is a non-profit open source project. If you bought the service from a provider, please contact them. | |||
影梭(Shadowsocks)是一个开源非盈利项目,不提供任何托管服务。如果你是从服务提供商购买的服务,请联系他们。 | |||
- If you have questions rather than Shadowsocks Windows client, please go to https://github.com/shadowsocks | |||
如果你有非影梭Windows客户端相关的问题,请去 https://github.com/shadowsocks | |||
- Please read Wiki carefully, especially https://github.com/shadowsocks/shadowsocks-windows/wiki/Troubleshooting | |||
提问前请先阅读wiki https://github.com/shadowsocks/shadowsocks-windows/wiki/Troubleshooting. | |||
- And search from Issue Board https://github.com/shadowsocks/shadowsocks-windows/issues?utf8=%E2%9C%93&q=is%3Aissue | |||
并在Issue Board中搜索 https://github.com/shadowsocks/shadowsocks-windows/issues?utf8=%E2%9C%93&q=is%3Aissue | |||
- Please include the following information. Questions lacking details will be closed. | |||
请按照以下格式描述你的问题,描述不清的问题将会被关闭。 | |||
--> | |||
### Shadowsocks version / 影梭版本 | |||
### Environment (Operating system, .NET Framework, etc) / 使用环境(操作系统,.NET Framework等) | |||
### Steps you have tried / 操作步骤 | |||
### What did you expect to see? / 期望的结果 | |||
### What did you see instead? / 实际结果 | |||
### Config and error log in detail (with all sensitive info masked) / 配置文件和日志文件(请隐去敏感信息) |
@@ -1,10 +1,12 @@ | |||
using NLog; | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
using System.Timers; | |||
using NLog; | |||
using Shadowsocks.Controller.Strategy; | |||
using Shadowsocks.Encryption; | |||
using Shadowsocks.Encryption.AEAD; | |||
@@ -12,16 +14,22 @@ using Shadowsocks.Encryption.Exception; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Proxy; | |||
using Shadowsocks.Util.Sockets; | |||
using static Shadowsocks.Encryption.EncryptorBase; | |||
namespace Shadowsocks.Controller | |||
{ | |||
class TCPRelay : Listener.Service | |||
internal class TCPRelay : Listener.Service | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
private ShadowsocksController _controller; | |||
public event EventHandler<SSTCPConnectedEventArgs> OnConnected; | |||
public event EventHandler<SSTransmitEventArgs> OnInbound; | |||
public event EventHandler<SSTransmitEventArgs> OnOutbound; | |||
public event EventHandler<SSRelayEventArgs> OnFailed; | |||
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); | |||
private readonly ShadowsocksController _controller; | |||
private DateTime _lastSweepTime; | |||
private Configuration _config; | |||
private readonly Configuration _config; | |||
public ISet<TCPHandler> Handlers { get; set; } | |||
@@ -37,9 +45,24 @@ namespace Shadowsocks.Controller | |||
{ | |||
if (socket.ProtocolType != ProtocolType.Tcp | |||
|| (length < 2 || firstPacket[0] != 5)) | |||
{ | |||
return false; | |||
} | |||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | |||
TCPHandler handler = new TCPHandler(_controller, _config, this, socket); | |||
TCPHandler handler = new TCPHandler(_controller, _config, socket); | |||
handler.OnConnected += OnConnected; | |||
handler.OnInbound += OnInbound; | |||
handler.OnOutbound += OnOutbound; | |||
handler.OnFailed += OnFailed; | |||
handler.OnClosed += (h, arg) => | |||
{ | |||
lock (Handlers) | |||
{ | |||
Handlers.Remove(handler); | |||
} | |||
}; | |||
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | |||
lock (Handlers) | |||
@@ -50,8 +73,12 @@ namespace Shadowsocks.Controller | |||
{ | |||
_lastSweepTime = now; | |||
foreach (TCPHandler handler1 in Handlers) | |||
{ | |||
if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) | |||
{ | |||
handlersToClose.Add(handler1); | |||
} | |||
} | |||
} | |||
} | |||
foreach (TCPHandler handler1 in handlersToClose) | |||
@@ -80,26 +107,46 @@ namespace Shadowsocks.Controller | |||
} | |||
handlersToClose.ForEach(h => h.Close()); | |||
} | |||
} | |||
public void UpdateInboundCounter(Server server, long n) | |||
public class SSRelayEventArgs : EventArgs | |||
{ | |||
public readonly Server server; | |||
public SSRelayEventArgs(Server server) | |||
{ | |||
_controller.UpdateInboundCounter(server, n); | |||
this.server = server; | |||
} | |||
} | |||
public void UpdateOutboundCounter(Server server, long n) | |||
public class SSTransmitEventArgs : SSRelayEventArgs | |||
{ | |||
public readonly long length; | |||
public SSTransmitEventArgs(Server server, long length) : base(server) | |||
{ | |||
_controller.UpdateOutboundCounter(server, n); | |||
this.length = length; | |||
} | |||
} | |||
public class SSTCPConnectedEventArgs : SSRelayEventArgs | |||
{ | |||
public readonly TimeSpan latency; | |||
public void UpdateLatency(Server server, TimeSpan latency) | |||
public SSTCPConnectedEventArgs(Server server, TimeSpan latency) : base(server) | |||
{ | |||
_controller.UpdateLatency(server, latency); | |||
this.latency = latency; | |||
} | |||
} | |||
internal class TCPHandler | |||
{ | |||
class AsyncSession | |||
public event EventHandler<SSTCPConnectedEventArgs> OnConnected; | |||
public event EventHandler<SSTransmitEventArgs> OnInbound; | |||
public event EventHandler<SSTransmitEventArgs> OnOutbound; | |||
public event EventHandler<SSRelayEventArgs> OnClosed; | |||
public event EventHandler<SSRelayEventArgs> OnFailed; | |||
private class AsyncSession | |||
{ | |||
public IProxy Remote { get; } | |||
@@ -109,7 +156,7 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
class AsyncSession<T> : AsyncSession | |||
private class AsyncSession<T> : AsyncSession | |||
{ | |||
public T State { get; set; } | |||
@@ -124,7 +171,7 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private static Logger Logger = LogManager.GetCurrentClassLogger(); | |||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); | |||
private readonly int _serverTimeout; | |||
private readonly int _proxyTimeout; | |||
@@ -143,10 +190,9 @@ namespace Shadowsocks.Controller | |||
public DateTime lastActivity; | |||
private ShadowsocksController _controller; | |||
private Configuration _config; | |||
private TCPRelay _tcprelay; | |||
private Socket _connection; | |||
private readonly ShadowsocksController _controller; | |||
private readonly ProxyConfig _config; | |||
private readonly Socket _connection; | |||
private IEncryptor _encryptor; | |||
private Server _server; | |||
@@ -170,16 +216,16 @@ namespace Shadowsocks.Controller | |||
private int _totalWrite = 0; | |||
// remote -> local proxy (ciphertext, before decrypt) | |||
private byte[] _remoteRecvBuffer = new byte[BufferSize]; | |||
private readonly byte[] _remoteRecvBuffer = new byte[BufferSize]; | |||
// client -> local proxy (plaintext, before encrypt) | |||
private byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||
private readonly byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||
// local proxy -> remote (plaintext, after decrypt) | |||
private byte[] _remoteSendBuffer = new byte[BufferSize]; | |||
private readonly byte[] _remoteSendBuffer = new byte[BufferSize]; | |||
// local proxy -> client (ciphertext, before decrypt) | |||
private byte[] _connetionSendBuffer = new byte[BufferSize]; | |||
private readonly byte[] _connetionSendBuffer = new byte[BufferSize]; | |||
private bool _connectionShutdown = false; | |||
private bool _remoteShutdown = false; | |||
@@ -197,11 +243,11 @@ namespace Shadowsocks.Controller | |||
private EndPoint _destEndPoint = null; | |||
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket) | |||
// TODO: decouple controller | |||
public TCPHandler(ShadowsocksController controller, Configuration config, Socket socket) | |||
{ | |||
_controller = controller; | |||
_config = config; | |||
_tcprelay = tcprelay; | |||
_config = config.proxy; | |||
_connection = socket; | |||
_proxyTimeout = config.proxy.proxyTimeout * 1000; | |||
_serverTimeout = config.GetCurrentServer().timeout * 1000; | |||
@@ -214,11 +260,13 @@ namespace Shadowsocks.Controller | |||
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, | |||
_destEndPoint); | |||
if (server == null || server.server == "") | |||
{ | |||
throw new ArgumentException("No server configured"); | |||
} | |||
_encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | |||
this._server = server; | |||
_server = server; | |||
/* prepare address buffer length for AEAD */ | |||
Logger.Trace($"_addrBufLength={_addrBufLength}"); | |||
@@ -235,20 +283,31 @@ namespace Shadowsocks.Controller | |||
private void CheckClose() | |||
{ | |||
if (_connectionShutdown && _remoteShutdown) | |||
{ | |||
Close(); | |||
} | |||
} | |||
private void ErrorClose(Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
} | |||
public void Close() | |||
{ | |||
lock (_closeConnLock) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
_closed = true; | |||
} | |||
lock (_tcprelay.Handlers) | |||
{ | |||
_tcprelay.Handlers.Remove(this); | |||
} | |||
OnClosed?.Invoke(this, new SSRelayEventArgs(_server)); | |||
try | |||
{ | |||
_connection.Shutdown(SocketShutdown.Both); | |||
@@ -263,7 +322,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
var remote = _currentRemoteSession.Remote; | |||
IProxy remote = _currentRemoteSession.Remote; | |||
remote.Shutdown(SocketShutdown.Both); | |||
remote.Close(); | |||
} | |||
@@ -284,7 +343,11 @@ namespace Shadowsocks.Controller | |||
private void HandshakeReceive() | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
int bytesRead = _firstPacketLength; | |||
@@ -301,18 +364,23 @@ namespace Shadowsocks.Controller | |||
HandshakeSendCallback, null); | |||
} | |||
else | |||
{ | |||
Close(); | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void HandshakeSendCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_connection.EndSend(ar); | |||
@@ -324,27 +392,30 @@ namespace Shadowsocks.Controller | |||
// +-----+-----+-------+------+----------+----------+ | |||
// Skip first 3 bytes, and read 2 more bytes to analysis the address. | |||
// 2 more bytes is designed if address is domain then we don't need to read once more to get the addr length. | |||
// TODO validate | |||
// validate is unnecessary, we did it in first packet, but we can do it in future version | |||
_connection.BeginReceive(_connetionRecvBuffer, 0, 3 + ADDR_ATYP_LEN + 1, SocketFlags.None, | |||
HandshakeReceive2Callback, null); | |||
AddressReceiveCallback, null); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void HandshakeReceive2Callback(IAsyncResult ar) | |||
private void AddressReceiveCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
int bytesRead = _connection.EndReceive(ar); | |||
if (bytesRead >= 5) | |||
{ | |||
_command = _connetionRecvBuffer[1]; | |||
switch(_command) | |||
switch (_command) | |||
{ | |||
case CMD_CONNECT: | |||
@@ -355,7 +426,7 @@ namespace Shadowsocks.Controller | |||
// +----+-----+-------+------+----------+----------+ | |||
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | |||
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, | |||
ResponseCallback, null); | |||
ConnectResponseCallback, null); | |||
break; | |||
case CMD_UDP_ASSOC: | |||
ReadAddress(HandleUDPAssociate); | |||
@@ -376,12 +447,11 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void ResponseCallback(IAsyncResult ar) | |||
private void ConnectResponseCallback(IAsyncResult ar) | |||
{ | |||
try | |||
{ | |||
@@ -391,8 +461,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -431,15 +500,19 @@ namespace Shadowsocks.Controller | |||
private void OnAddressFullyRead(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
int bytesRead = _connection.EndReceive(ar); | |||
var states = (object[])ar.AsyncState; | |||
object[] states = (object[])ar.AsyncState; | |||
int bytesRemain = (int)states[0]; | |||
var onSuccess = (Action)states[1]; | |||
Action onSuccess = (Action)states[1]; | |||
if (bytesRead >= bytesRemain) | |||
{ | |||
@@ -472,7 +545,7 @@ namespace Shadowsocks.Controller | |||
break; | |||
} | |||
Logger.Debug($"connect to {dstAddr}:{dstPort}"); | |||
Logger.Debug($"connect to {dstAddr}:{dstPort}"); | |||
_destEndPoint = SocketUtil.GetEndPoint(dstAddr, dstPort); | |||
@@ -486,8 +559,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -515,7 +587,11 @@ namespace Shadowsocks.Controller | |||
private void ReadAll(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
if (ar.AsyncState != null) | |||
@@ -533,13 +609,14 @@ namespace Shadowsocks.Controller | |||
ReadAll, null); | |||
} | |||
else | |||
{ | |||
Close(); | |||
} | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -584,9 +661,9 @@ namespace Shadowsocks.Controller | |||
serverEP = pluginEP; | |||
remote = new DirectConnect(); | |||
} | |||
else if (_config.proxy.useProxy) | |||
else if (_config.useProxy) | |||
{ | |||
switch (_config.proxy.proxyType) | |||
switch (_config.proxyType) | |||
{ | |||
case ProxyConfig.PROXY_SOCKS5: | |||
remote = new Socks5Proxy(); | |||
@@ -597,14 +674,14 @@ namespace Shadowsocks.Controller | |||
default: | |||
throw new NotSupportedException("Unknown forward proxy."); | |||
} | |||
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort); | |||
proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort); | |||
} | |||
else | |||
{ | |||
remote = new DirectConnect(); | |||
} | |||
var session = new AsyncSession(remote); | |||
AsyncSession session = new AsyncSession(remote); | |||
lock (_closeConnLock) | |||
{ | |||
if (_closed) | |||
@@ -632,14 +709,13 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void ProxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | |||
{ | |||
var timer = (ProxyTimer)sender; | |||
ProxyTimer timer = (ProxyTimer)sender; | |||
timer.Elapsed -= ProxyConnectTimer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
@@ -649,7 +725,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
return; | |||
} | |||
var proxy = timer.Session.Remote; | |||
IProxy proxy = timer.Session.Remote; | |||
Logger.Info($"Proxy {proxy.ProxyEndPoint} timed out"); | |||
proxy.Close(); | |||
@@ -664,15 +740,15 @@ namespace Shadowsocks.Controller | |||
} | |||
try | |||
{ | |||
var session = (AsyncSession<ProxyTimer>)ar.AsyncState; | |||
AsyncSession<ProxyTimer> session = (AsyncSession<ProxyTimer>)ar.AsyncState; | |||
ProxyTimer timer = session.State; | |||
var destEndPoint = timer.DestEndPoint; | |||
var server = timer.Server; | |||
EndPoint destEndPoint = timer.DestEndPoint; | |||
Server server = timer.Server; | |||
timer.Elapsed -= ProxyConnectTimer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
var remote = session.Remote; | |||
IProxy remote = session.Remote; | |||
// Complete the connection. | |||
remote.EndConnectProxy(ar); | |||
@@ -694,9 +770,9 @@ namespace Shadowsocks.Controller | |||
_destConnected = false; | |||
NetworkCredential auth = null; | |||
if (_config.proxy.useAuth) | |||
if (_config.useAuth) | |||
{ | |||
auth = new NetworkCredential(_config.proxy.authUser, _config.proxy.authPwd); | |||
auth = new NetworkCredential(_config.authUser, _config.authPwd); | |||
} | |||
// Connect to the remote endpoint. | |||
@@ -708,14 +784,13 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void DestConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | |||
{ | |||
var timer = (ServerTimer)sender; | |||
ServerTimer timer = (ServerTimer)sender; | |||
timer.Elapsed -= DestConnectTimer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
@@ -725,10 +800,9 @@ namespace Shadowsocks.Controller | |||
return; | |||
} | |||
var session = timer.Session; | |||
AsyncSession session = timer.Session; | |||
Server server = timer.Server; | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.SetFailure(server); | |||
OnFailed?.Invoke(this, new SSRelayEventArgs(_server)); | |||
Logger.Info($"{server.FriendlyName()} timed out"); | |||
session.Remote.Close(); | |||
Close(); | |||
@@ -736,17 +810,21 @@ namespace Shadowsocks.Controller | |||
private void ConnectCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
var session = (AsyncSession<ServerTimer>)ar.AsyncState; | |||
AsyncSession<ServerTimer> session = (AsyncSession<ServerTimer>)ar.AsyncState; | |||
ServerTimer timer = session.State; | |||
_server = timer.Server; | |||
timer.Elapsed -= DestConnectTimer_Elapsed; | |||
timer.Enabled = false; | |||
timer.Dispose(); | |||
var remote = session.Remote; | |||
IProxy remote = session.Remote; | |||
// Complete the connection. | |||
remote.EndConnectDest(ar); | |||
@@ -754,10 +832,9 @@ namespace Shadowsocks.Controller | |||
Logger.Debug($"Socket connected to ss server: {_server.FriendlyName()}"); | |||
var latency = DateTime.Now - _startConnectTime; | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLatency(_server, latency); | |||
_tcprelay.UpdateLatency(_server, latency); | |||
TimeSpan latency = DateTime.Now - _startConnectTime; | |||
OnConnected?.Invoke(this, new SSTCPConnectedEventArgs(_server, latency)); | |||
StartPipe(session); | |||
} | |||
@@ -768,11 +845,9 @@ namespace Shadowsocks.Controller | |||
{ | |||
if (_server != null) | |||
{ | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.SetFailure(_server); | |||
OnFailed?.Invoke(this, new SSRelayEventArgs(_server)); | |||
} | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -781,7 +856,7 @@ namespace Shadowsocks.Controller | |||
int available = Math.Min(_connection.Available, RecvSize - _firstPacketLength); | |||
if (available > 0) | |||
{ | |||
var size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available, | |||
int size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available, | |||
SocketFlags.None); | |||
_firstPacketLength += size; | |||
@@ -790,7 +865,11 @@ namespace Shadowsocks.Controller | |||
private void StartPipe(AsyncSession session) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
_startReceivingTime = DateTime.Now; | |||
@@ -803,20 +882,24 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void PipeRemoteReceiveCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
var session = (AsyncSession)ar.AsyncState; | |||
AsyncSession session = (AsyncSession)ar.AsyncState; | |||
int bytesRead = session.Remote.EndReceive(ar); | |||
_totalRead += bytesRead; | |||
_tcprelay.UpdateInboundCounter(_server, bytesRead); | |||
OnInbound?.Invoke(this, new SSTransmitEventArgs(_server, bytesRead)); | |||
if (bytesRead > 0) | |||
{ | |||
lastActivity = DateTime.Now; | |||
@@ -845,8 +928,6 @@ namespace Shadowsocks.Controller | |||
Logger.Trace($"start sending {bytesToSend}"); | |||
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, | |||
PipeConnectionSendCallback, new object[] { session, bytesToSend }); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastRead(_server); | |||
} | |||
else | |||
{ | |||
@@ -857,20 +938,23 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
private void PipeConnectionReceiveCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
int bytesRead = _connection.EndReceive(ar); | |||
var session = (AsyncSession)ar.AsyncState; | |||
var remote = session.Remote; | |||
AsyncSession session = (AsyncSession)ar.AsyncState; | |||
IProxy remote = session.Remote; | |||
if (bytesRead > 0) | |||
{ | |||
@@ -885,8 +969,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -907,22 +990,25 @@ namespace Shadowsocks.Controller | |||
return; | |||
} | |||
} | |||
_tcprelay.UpdateOutboundCounter(_server, bytesToSend); | |||
OnOutbound?.Invoke(this, new SSTransmitEventArgs(_server, bytesToSend)); | |||
_startSendingTime = DateTime.Now; | |||
session.Remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, | |||
PipeRemoteSendCallback, new object[] { session, bytesToSend }); | |||
IStrategy strategy = _controller.GetCurrentStrategy(); | |||
strategy?.UpdateLastWrite(_server); | |||
} | |||
private void PipeRemoteSendCallback(IAsyncResult ar) | |||
{ | |||
if (_closed) return; | |||
if (_closed) | |||
{ | |||
return; | |||
} | |||
try | |||
{ | |||
var container = (object[])ar.AsyncState; | |||
var session = (AsyncSession)container[0]; | |||
var bytesShouldSend = (int)container[1]; | |||
object[] container = (object[])ar.AsyncState; | |||
AsyncSession session = (AsyncSession)container[0]; | |||
int bytesShouldSend = (int)container[1]; | |||
int bytesSent = session.Remote.EndSend(ar); | |||
if (bytesSent > 0) | |||
@@ -944,8 +1030,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
@@ -954,11 +1039,11 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
var container = (object[])ar.AsyncState; | |||
var session = (AsyncSession)container[0]; | |||
var bytesShouldSend = (int)container[1]; | |||
var bytesSent = _connection.EndSend(ar); | |||
var bytesRemaining = bytesShouldSend - bytesSent; | |||
object[] container = (object[])ar.AsyncState; | |||
AsyncSession session = (AsyncSession)container[0]; | |||
int bytesShouldSend = (int)container[1]; | |||
int bytesSent = _connection.EndSend(ar); | |||
int bytesRemaining = bytesShouldSend - bytesSent; | |||
if (bytesRemaining > 0) | |||
{ | |||
Logger.Info("reconstruct _remoteSendBuffer to re-send"); | |||
@@ -972,8 +1057,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
Close(); | |||
ErrorClose(e); | |||
} | |||
} | |||
} |
@@ -217,25 +217,25 @@ namespace Shadowsocks.Controller | |||
StatisticsStrategyConfiguration.Save(configuration); | |||
} | |||
public bool AskAddServerBySSURL(string ssURL) | |||
{ | |||
var dr = MessageBox.Show(I18N.GetString("Import from URL: {0} ?", ssURL), I18N.GetString("Shadowsocks"), MessageBoxButtons.YesNo); | |||
if (dr == DialogResult.Yes) | |||
{ | |||
public bool AskAddServerBySSURL(string ssURL) | |||
{ | |||
var dr = MessageBox.Show(I18N.GetString("Import from URL: {0} ?", ssURL), I18N.GetString("Shadowsocks"), MessageBoxButtons.YesNo); | |||
if (dr == DialogResult.Yes) | |||
{ | |||
if (AddServerBySSURL(ssURL)) | |||
{ | |||
MessageBox.Show(I18N.GetString("Successfully imported from {0}", ssURL)); | |||
return true; | |||
} | |||
} | |||
else | |||
{ | |||
MessageBox.Show(I18N.GetString("Failed to import. Please check if the link is valid.")); | |||
} | |||
} | |||
return false; | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
public bool AddServerBySSURL(string ssURL) | |||
{ | |||
try | |||
@@ -486,29 +486,32 @@ namespace Shadowsocks.Controller | |||
ConfigChanged?.Invoke(this, new EventArgs()); | |||
} | |||
public void UpdateLatency(Server server, TimeSpan latency) | |||
public void UpdateLatency(object sender, SSTCPConnectedEventArgs args) | |||
{ | |||
GetCurrentStrategy()?.UpdateLatency(args.server, args.latency); | |||
if (_config.availabilityStatistics) | |||
{ | |||
availabilityStatistics.UpdateLatency(server, (int)latency.TotalMilliseconds); | |||
availabilityStatistics.UpdateLatency(args.server, (int)args.latency.TotalMilliseconds); | |||
} | |||
} | |||
public void UpdateInboundCounter(Server server, long n) | |||
public void UpdateInboundCounter(object sender, SSTransmitEventArgs args) | |||
{ | |||
Interlocked.Add(ref _inboundCounter, n); | |||
GetCurrentStrategy()?.UpdateLastRead(args.server); | |||
Interlocked.Add(ref _inboundCounter, args.length); | |||
if (_config.availabilityStatistics) | |||
{ | |||
availabilityStatistics.UpdateInboundCounter(server, n); | |||
availabilityStatistics.UpdateInboundCounter(args.server, args.length); | |||
} | |||
} | |||
public void UpdateOutboundCounter(Server server, long n) | |||
public void UpdateOutboundCounter(object sender, SSTransmitEventArgs args) | |||
{ | |||
Interlocked.Add(ref _outboundCounter, n); | |||
GetCurrentStrategy()?.UpdateLastWrite(args.server); | |||
Interlocked.Add(ref _outboundCounter, args.length); | |||
if (_config.availabilityStatistics) | |||
{ | |||
availabilityStatistics.UpdateOutboundCounter(server, n); | |||
availabilityStatistics.UpdateOutboundCounter(args.server, args.length); | |||
} | |||
} | |||
@@ -552,6 +555,11 @@ namespace Shadowsocks.Controller | |||
privoxyRunner.Start(_config); | |||
TCPRelay tcpRelay = new TCPRelay(this, _config); | |||
tcpRelay.OnConnected += UpdateLatency; | |||
tcpRelay.OnInbound += UpdateInboundCounter; | |||
tcpRelay.OnOutbound += UpdateOutboundCounter; | |||
tcpRelay.OnFailed += (o, e) => GetCurrentStrategy()?.SetFailure(e.server); | |||
UDPRelay udpRelay = new UDPRelay(this); | |||
List<Listener.IService> services = new List<Listener.IService> | |||
{ | |||
@@ -12,22 +12,22 @@ Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowso | |||
,,,,,, | |||
System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,Proxy système | |||
Disable,Отключен,禁用,禁用,無効,비활성화,Désactiver | |||
PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PAC,PAC,PAC | |||
PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PAC,프록시 자동 구성 (PAC),PAC | |||
Global,Для всей системы,全局模式,全局模式,全般,전역,Global | |||
Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs | |||
Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs… | |||
Statistics Config...,Настройки статистики…,统计配置...,統計設定檔...,統計情報の設定...,통계 설정,Configuration des statistiques… | |||
Start on Boot,Автозагрузка,开机启动,開機啟動,システムと同時に起動,시스템 시작 시에 시작하기,Démarrage automatique | |||
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,,, | |||
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,,ss:// 링크 연결, | |||
Forward Proxy...,Прямой прокси…,正向代理设置...,正向 Proxy 設定...,フォワードプロキシの設定...,포워드 프록시,Forward-proxy… | |||
Allow other Devices to connect,Общий доступ к подключению,允许其他设备连入,允許其他裝置連入,他のデバイスからの接続を許可する,다른 기기에서 연결 허용,Autoriser d'autres appareils à se connecter | |||
Local PAC,Локальный PAC,使用本地 PAC,使用本機 PAC,ローカル PAC,로컬 PAC,PAC local | |||
Online PAC,Удаленный PAC,使用在线 PAC,使用線上 PAC,オンライン PAC,온라인 PAC,PAC en ligne | |||
Edit Local PAC File...,Редактировать локальный PAC…,编辑本地 PAC 文件...,編輯本機 PAC 檔案...,ローカル PAC ファイルの編集...,로컬 PAC 파일 수정,Modifier le fichier PAC local ... | |||
Update Local PAC from Geosite,Обновить локальный PAC из Geosite,从 Geosite 更新本地 PAC,從 Geosite 更新本機 PAC,Geosite からローカル PAC を更新,Geosite에서 로컬 PAC 파일 업데이트,Mettre à jour le PAC local à partir de Geosite | |||
Local PAC,Локальный PAC,使用本地 PAC,使用本機 PAC,ローカル PAC,로컬 프록시 자동 구성,PAC local | |||
Online PAC,Удаленный PAC,使用在线 PAC,使用線上 PAC,オンライン PAC,온라인 프록시 자동 구성,PAC en ligne | |||
Edit Local PAC File...,Редактировать локальный PAC…,编辑本地 PAC 文件...,編輯本機 PAC 檔案...,ローカル PAC ファイルの編集...,로컬 프록시 자동 구성 파일 수정,Modifier le fichier PAC local ... | |||
Update Local PAC from Geosite,Обновить локальный PAC из Geosite,从 Geosite 更新本地 PAC,從 Geosite 更新本機 PAC,Geosite からローカル PAC を更新,Geosite에서 로컬 프록시 자동 구성 파일 업데이트,Mettre à jour le PAC local à partir de Geosite | |||
Edit User Rule for Geosite...,Редактировать свои правила для Geosite,编辑 Geosite 的用户规则...,編輯 Geosite 的使用者規則...,ユーザールールの編集...,Geosite 사용자 수정,Modifier la règle utilisateur pour Geosite ... | |||
Secure Local PAC,Безопасный URL локального PAC,保护本地 PAC,安全本機 PAC,ローカル PAC を保護,로컬 PAC 암호화,Sécuriser PAC local | |||
Copy Local PAC URL,Копировать URL локального PAC,复制本地 PAC 网址,複製本機 PAC 網址,ローカル PAC URL をコピー,로컬 PAC 파일 URL 복사,Copier l'URL du PAC local | |||
Secure Local PAC,Безопасный URL локального PAC,保护本地 PAC,安全本機 PAC,ローカル PAC を保護,로컬 프록시 자동 구성 파일 암호화,Sécuriser PAC local | |||
Copy Local PAC URL,Копировать URL локального PAC,复制本地 PAC 网址,複製本機 PAC 網址,ローカル PAC URL をコピー,로컬 프록시 자동 구성 파일 URL 복사,Copier l'URL du PAC local | |||
Share Server Config...,Поделиться конфигурацией сервера…,分享服务器配置...,分享伺服器設定檔...,サーバーの設定を共有...,서버 설정 공유,Partager la configuration du serveur ... | |||
Scan QRCode from Screen...,Сканировать QRCode с экрана…,扫描屏幕上的二维码...,掃描螢幕上的 QR 碼...,画面から QR コードをスキャン...,화면에서 QR코드 스캔,Scanner le QRCode à partir de l'écran ... | |||
Import URL from Clipboard...,Импорт адреса из буфера обмена…,从剪贴板导入URL...,從剪貼簿匯入 URL...,クリップボードから URL をインポート...,클립보드에서 URL 가져오기…,Importer l'URL du presse-papiers ... | |||
@@ -66,17 +66,17 @@ Plugin Options,Опции плагина,插件选项,外掛程式選項,プラ | |||
Need Plugin Argument,Требуются аргументы,需要命令行参数,,,플러그인 인자가 필요함,Besoin d'un argument de plugin | |||
Plugin Arguments,Аргументы,插件参数,外掛程式參數,プラグインの引数,플러그인 인자,Arguments du plugin | |||
Proxy Port,Порт прокси,代理端口,Proxy 連接埠,プロキシポート,프록시 포트,Port proxy | |||
Portable Mode,Переносимый режим,便携模式,便攜模式,ポータブルモード,포터블(Portable) 모드,Mode portable | |||
Restart required,Требуется перезапуск программы,需要重新启动SS,需要重新啟動SS,再起動SSが必要,재시작 필요함,Redémarrage nécessaire | |||
Portable Mode,Переносимый режим,便携模式,便攜模式,ポータブルモード,포터블 모드,Mode portable | |||
Restart required,Требуется перезапуск программы,需要重新启动SS,需要重新啟動SS,再起動SSが必要,재시작이 필요합니다,Redémarrage nécessaire | |||
Remarks,Примечания,备注,註解,付記,알림,Remarques | |||
Timeout(Sec),Таймаут(сек),超时(秒),逾時 (秒),タイムアウト (秒),시간초과(Timeout) (초),Délai d'attente(sec) | |||
Timeout(Sec),Таймаут(сек),超时(秒),逾時 (秒),タイムアウト (秒),시간 초과 (초),Délai d'attente(sec) | |||
OK,ОК,确定,確定,OK,확인,OK | |||
Cancel,Отмена,取消,取消,キャンセル,취소,Annuler | |||
Apply,Применить,应用,應用,適用,적용,Appliquer | |||
New server,Новый сервер,未配置的服务器,新伺服器,新規サーバー,새 서버,Nouveau serveur | |||
Move &Up,Выше,上移(&U),上移 (&U),上に移動 (&U),위로 (&U),Monter | |||
Move D&own,Ниже,下移(&O),下移 (&O),下に移動 (&O),아래로 (&O),Descendre | |||
deprecated,Устаревшее,不推荐,不推薦,非推奨,폐기됨(deprecated),Obsolète | |||
deprecated,Устаревшее,不推荐,不推薦,非推奨,더 이상 사용되지 않음,Obsolète | |||
"Encryption method {0} not exist, will replace with {1}",,加密方法{0}不存在,将使用{1}代替,,,{0} 암호화 방식이 존재하지 않으므로 {1}로 대체될 것입니다.,"Méthode de chiffrement {0} n'existe pas, sera remplacée par {1}" | |||
,,,,,, | |||
#Statistics Config,,,,,, | |||
@@ -116,8 +116,8 @@ Use Proxy,Использовать прокси,使用代理,使用 Proxy,プロ | |||
Proxy Type,Тип прокси,代理类型,Proxy 類型,プロキシの種類,프록시 종류,Type de proxy | |||
Proxy Addr,Адрес прокси,代理地址,Proxy 位址,プロキシアドレス,프록시 주소,Adresse de proxy | |||
Proxy Port,Порт прокси,代理端口,Proxy 連接埠,プロキシポート,프록시 포트,Port de proxy | |||
"If server has a plugin, proxy will not be used","Если сервер использует плагины, прокси НЕ будет использоваться",若服务器含有插件,代理将不被使用,若伺服器含有外掛程式,Proxy 將不被使用,サーバーにプラグインがある場合、プロキシは利用されません,"만약 서버가 플러그인이 있다면, 프록시는용되지 않을 것입니다.","Si le serveur a un plugin, le proxy ne sera pas utilisé" | |||
Use Auth,Требуется авторизация,使用认证,使用認證,認証を利用する,서버 증명(Auth) 사용,Utiliser l'authentification | |||
"If server has a plugin, proxy will not be used","Если сервер использует плагины, прокси НЕ будет использоваться",若服务器含有插件,代理将不被使用,若伺服器含有外掛程式,Proxy 將不被使用,サーバーにプラグインがある場合、プロキシは利用されません,서버에 플러그인이 설치 되어 있는 경우 프록시를 사용할 수 없습니다.,"Si le serveur a un plugin, le proxy ne sera pas utilisé" | |||
Use Auth,Требуется авторизация,使用认证,使用認證,認証を利用する,서버 인증 사용,Utiliser l'authentification | |||
User Name,Пользователь,用户名,認證用戶,認証ユーザ,사용자 이름,Nom d'utilisateur | |||
Auth Pwd,Пароль,认证密码,認證口令,認証パスワード,비밀번호,Mot de passe d'authentification | |||
,,,,,, | |||
@@ -142,9 +142,9 @@ QRCode and URL,QRCode и URL,二维码与 URL,QR 碼與 URL,QR コードと URL, | |||
,,,,,, | |||
# PAC Url Form,,,,,, | |||
,,,,,, | |||
Edit Online PAC URL,Изменение URL удаленного PAC,编辑在线 PAC 网址,編輯線上 PAC 網址,オンライン PAC URL の編集,온라인 PAC URL 수정,Modifier l'URL du PAC en ligne | |||
Edit Online PAC URL...,Редактировать URL удаленного PAC…,编辑在线 PAC 网址...,編輯線上 PAC 網址...,オンライン PAC URL の編集...,온라인 PAC URL 수정…,Modifier l'URL du PAC en ligne ... | |||
Please input PAC Url,Введите URL адрес для PAC-файла,请输入 PAC 网址,請輸入 PAC 網址,PAC URLを入力して下さい,PAC URL을 입력하세요,Veuillez saisir l'URL PAC | |||
Edit Online PAC URL,Изменение URL удаленного PAC,编辑在线 PAC 网址,編輯線上 PAC 網址,オンライン PAC URL の編集,온라인 프록시 자동 구성 URL 수정,Modifier l'URL du PAC en ligne | |||
Edit Online PAC URL...,Редактировать URL удаленного PAC…,编辑在线 PAC 网址...,編輯線上 PAC 網址...,オンライン PAC URL の編集...,온라인 프록시 자동 구성 URL 수정…,Modifier l'URL du PAC en ligne ... | |||
Please input PAC Url,Введите URL адрес для PAC-файла,请输入 PAC 网址,請輸入 PAC 網址,PAC URLを入力して下さい,프록시 자동 구성 URL을 입력하세요,Veuillez saisir l'URL PAC | |||
,,,,,, | |||
# HotkeySettings Form,,,,,, | |||
,,,,,, | |||
@@ -164,7 +164,7 @@ Port {0} already in use,Порт {0} уже используется,端口 {0} | |||
Port {0} is reserved by system,Порт {0} зарезервирован системой,端口 {0} 是系统保留端口,連接埠號碼 {0} 由系統保留, ポート番号 {0} はシステムによって予約されています,{0}번 포트는 시스템에서 사용 중입니다.,Port {0} réservé par le système | |||
Invalid server address,Неверный адрес сервера,非法服务器地址,無效伺服器位址,サーバーアドレスが無効です。,올바르지 않은 서버 주소입니다.,Adresse de serveur non valide | |||
Illegal port number format,Неверный числовой формат порта,非法端口格式,無效連接埠號碼格式,ポート番号のフォーマットが無効です。,올바르지 않은 포트 번호 형식입니다.,Format de numéro de port illégal | |||
Illegal timeout format,Неверный формат таймаута,非法超时格式,無效逾時格式,タイムアウト値のフォーマットが無効です。,올바르지 않은 시간초과(Timeout) 형식입니다.,Format de délai d'attente illégal | |||
Illegal timeout format,Неверный формат таймаута,非法超时格式,無效逾時格式,タイムアウト値のフォーマットが無効です。,올바르지 않은 시간 초과 형식입니다.,Format de délai d'attente illégal | |||
Server IP can not be blank,IP-адрес сервера не может быть пустым,服务器 IP 不能为空,伺服器 IP 不能為空,サーバー IP が指定されていません。,서버 IP는 비어있으면 안됩니다.,L'adresse IP du serveur ne peut pas être vide | |||
Password can not be blank,Пароль не может быть пустым,密码不能为空,密碼不能為空,パスワードが指定されていません。,비밀번호는 비어있으면 안됩니다.,Le mot de passe ne peut pas être vide | |||
Port out of range,Порт выходит за допустимый диапазон,端口超出范围,連接埠號碼超出範圍,ポート番号は範囲外です。,올바른 포트 범위가 아닙니다.,Port hors de portée | |||
@@ -176,8 +176,8 @@ Shadowsocks is here,Shadowsocks находится здесь,Shadowsocks 在这 | |||
You can turn on/off Shadowsocks in the context menu,Вы можете управлять Shadowsocks из контекстного меню,可以在右键菜单中开关 Shadowsocks,可以在右鍵選項單中開關 Shadowsocks,コンテキストメニューを使って、Shadowsocks を有効または無効にすることができます。,프로그램 메뉴에서 Shadowsocks를 끄고 켤 수 있습니다.,Vous pouvez activer / désactiver Shadowsocks dans le menu contextuel | |||
System Proxy Enabled,Системный прокси включен,系统代理已启用,系統 Proxy 已啟用,システム プロキシが有効です。,시스템 프록시가 활성화되었습니다.,Proxy système activé | |||
System Proxy Disabled,Системный прокси отключен,系统代理未启用,系統 Proxy 未啟用,システム プロキシが無効です。,시스템 프록시가 비활성화되었습니다.,Proxy système désactivé | |||
Failed to update PAC file ,Не удалось обновить PAC файл,更新 PAC 文件失败,更新 PAC 檔案失敗,PAC の更新に失敗しました。,PAC 파일을 업데이트하는데 실패했습니다.,Impossible de mettre à jour le fichier PAC | |||
PAC updated,PAC файл обновлен,更新 PAC 成功,更新 PAC 成功,PAC を更新しました。,PAC 파일이 업데이트되었습니다.,PAC mis à jour | |||
Failed to update PAC file ,Не удалось обновить PAC файл,更新 PAC 文件失败,更新 PAC 檔案失敗,PAC の更新に失敗しました。,프록시 자동 구성 파일을 업데이트하는데 실패했습니다.,Impossible de mettre à jour le fichier PAC | |||
PAC updated,PAC файл обновлен,更新 PAC 成功,更新 PAC 成功,PAC を更新しました。,프록시 자동 구성 파일이 업데이트되었습니다.,PAC mis à jour | |||
No updates found. Please report to Geosite if you have problems with it.,Обновлений не найдено. Сообщите авторам Geosite если у вас возникли проблемы.,未发现更新。如有问题请提交给 Geosite。,未發現更新。如有問題請報告至 Geosite。,お使いのバージョンは最新です。問題がある場合は、Geosite に報告して下さい。,사용 가능한 업데이트를 찾지 못했습니다. 문제가 있다면 Geosite로 전송해주세요.,Aucune mise à jour trouvée. Veuillez signaler à Geosite si vous avez des problèmes concernant. | |||
No QRCode found. Try to zoom in or move it to the center of the screen.,QRCode не обнаружен. Попробуйте увеличить изображение или переместить его в центр экрана.,未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置,未發現 QR 碼,嘗試把它放大或移動到靠近熒幕中間的位置,QR コードが見つかりませんでした。コードを大きくするか、画面の中央に移動して下さい。,QR코드를 찾을 수 없습니다. 가운데로 화면을 이동시키거나 확대해보세요.,Aucun QRCode trouvé. Essayez de zoomer ou de le déplacer vers le centre de l'écran. | |||
Shadowsocks is already running.,Shadowsocks уже запущен.,Shadowsocks 已经在运行。,Shadowsocks 已經在執行。,Shadowsocks 実行中,Shadowsocks가 이미 실행 중입니다.,Shadowsocks est déjà en cours d'exécution. | |||
@@ -190,25 +190,25 @@ Successfully imported from {0},Успешно импортировано из {0 | |||
Failed to import. Please check if the link is valid.,,导入失败,请检查链接是否有效。,導入失敗,請檢查鏈接是否有效。,,, | |||
System Proxy On: ,Системный прокси:,系统代理已启用:,系統 Proxy 已啟用:,システム プロキシが有効:,시스템 프록시 활성화됨: ,Proxy système activé: | |||
Running: Port {0},Запущен на порту {0},正在运行:端口 {0},正在執行:連接埠號碼 {0},実行中:ポート {0},실행 중: 포트 {0}번,En cours d'exécution: port {0} | |||
"Unexpected error, shadowsocks will exit. Please report to","Непредвиденная ошибка, пожалуйста сообщите на",非预期错误,Shadowsocks将退出。请提交此错误到,非預期錯誤,Shadowsocks 將結束。請報告此錯誤至,予想外のエラーが発生したため、Shadowsocks を終了します。詳しくは下記までお問い合わせ下さい:,알 수 없는 오류로 Shadowsocks가 종료될 것입니다. 오류를 제보해주세요:,Shadowsocks va quitter en présence d/érreur inattendue. Veuillez signaler à | |||
"Unsupported operating system, use Windows Vista at least.","Операционная система не поддерживается, минимальные системные требования: Windows Vista или выше.",不支持的操作系统版本,最低需求为Windows Vista。,不支援的作業系統版本,最低需求為 Windows Vista。,お使いの OS はサポートされていません。Windows Vista 以降の OS で実行して下さい。,"지원하지 않는 운영체제입니다, 최소한 Windows Vista가 필요합니다.","Système d'exploitation incompatible, veuillez utiliser Windows Vista ou ultérieure." | |||
"Unsupported .NET Framework, please update to {0} or later.","Версия .NET Framework не поддерживается, минимальные системные требования: {0} или выше.",当前 .NET Framework 版本过低,请升级至{0}或更新版本。,目前 .NET Framework 版本過低,最低需求為{0}。,お使いの .NET Framework はサポートされていません。{0} 以降のバンジョーをインストールして下さい。,"지원하지 않는 .NET 프레임워크입니다, {0} 또는 상위 버전으로 업데이트해주세요.",".NET Framework incompatible, veuillez mettre à jour vers {0} ou ultérieure." | |||
"Unexpected error, shadowsocks will exit. Please report to","Непредвиденная ошибка, пожалуйста сообщите на",非预期错误,Shadowsocks将退出。请提交此错误到,非預期錯誤,Shadowsocks 將結束。請報告此錯誤至,予想外のエラーが発生したため、Shadowsocks を終了します。詳しくは下記までお問い合わせ下さい:,알 수 없는 오류로 Shadowsocks가 종료될 것입니다. 오류를 여기로 제보해주세요:,Shadowsocks va quitter en présence d/érreur inattendue. Veuillez signaler à | |||
"Unsupported operating system, use Windows Vista at least.","Операционная система не поддерживается, минимальные системные требования: Windows Vista или выше.",不支持的操作系统版本,最低需求为Windows Vista。,不支援的作業系統版本,最低需求為 Windows Vista。,お使いの OS はサポートされていません。Windows Vista 以降の OS で実行して下さい。,지원하지 않는 운영체제입니다. 최소 Windows Vista가 필요합니다.,"Système d'exploitation incompatible, veuillez utiliser Windows Vista ou ultérieure." | |||
"Unsupported .NET Framework, please update to {0} or later.","Версия .NET Framework не поддерживается, минимальные системные требования: {0} или выше.",当前 .NET Framework 版本过低,请升级至{0}或更新版本。,目前 .NET Framework 版本過低,最低需求為{0}。,お使いの .NET Framework はサポートされていません。{0} 以降のバンジョーをインストールして下さい。,지원하지 않는 .NET 프레임워크입니다. {0} 또는 상위 버전으로 업데이트해주세요.,".NET Framework incompatible, veuillez mettre à jour vers {0} ou ultérieure." | |||
Proxy request failed,Не удалось выполнить запрос,代理请求失败,Proxy 要求失敗,プロキシ要求が失敗しました。,프록시 요청에 실패했습니다.,Échec de la demande de proxy | |||
Proxy handshake failed,Не удалось выполнить хэндшейк,代理握手失败,Proxy 交握失敗,プロキシ ハンドシェイクに失敗しました。,프록시 핸드쉐이크에 실패했습니다.,Échec de la prise de contact par proxy | |||
Register hotkey failed,Не удалось применить настройки горячих клавиш,注册快捷键失败,註冊快速鍵失敗,ホットキーの登錄に失敗しました。,단축키 등록에 실패했습니다.,Échec de l'enregistrement du raccourci clavier | |||
Cannot parse hotkey: {0},Не удалось распознать следующие горячие клавиши: {0},解析快捷键失败: {0},剖析快速鍵失敗: {0},ホットキーを解析できません: {0},단축키를 해석할 수 없습니다: {0},Impossible d'analyser le raccourci clavier: {0} | |||
"Timeout is invalid, it should not exceed {0}",Таймаут не может превышать значение {0},超时无效,不应超过 {0},逾時無效,不應超過 {0},タイムアウト値が無効です。{0} 以下の値を指定して下さい。,"올바르지 않은 시간초과(Timeout) 설정입니다, {0}을 초과하지 않아야 합니다.","Le délai d'attente invalide, il ne doit pas dépasser {0}" | |||
"Timeout is invalid, it should not exceed {0}",Таймаут не может превышать значение {0},超时无效,不应超过 {0},逾時無效,不應超過 {0},タイムアウト値が無効です。{0} 以下の値を指定して下さい。,올바르지 않은 시간 초과 설정입니다. {0}을 초과하지 않아야 합니다.,"Le délai d'attente invalide, il ne doit pas dépasser {0}" | |||
Cannot find the plugin program file,Файл плагина не найден,找不到插件程序文件,找不到外掛程式文件,,플러그인 프로그램 파일을 찾을 수 없습니다.,Impossible de trouver le fichier du programme du plugin | |||
,,,,,, | |||
Operation failure,Операция завершилась неудачей,操作失败,,,작업에 실패했습니다.,Operation failure | |||
Auto save failed,Автоматическое сохранение не удалось,自动保存失败,,,자동 저장에 실패했습니다.,Échec de l'enregistrement automatique | |||
Whether to discard unconfigured servers,Внесенные изменения будут утеряны,是否丢弃未配置的服务器,,,완전하지 않은 서버 설정 변경 사항을 폐기하시겠습니까?,Eliminer les serveurs non configurés ou non | |||
"Invalid server address, Cannot automatically save or discard changes",Неверный адрес сервера. Невозможно сохранить или отменить изменения,非法服务器地址,无法自动保存,是否丢弃修改,,,"서버 주소가 올바르지 않아 자동저장할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Adresse de serveur invalide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||
"Illegal port number format, Cannot automatically save or discard changes",Неверный числовой адрес порта. Невозможно сохранить или отменить изменения,非法端口格式,无法自动保存,是否丢弃修改,,,"포트 번호가 올바르지 않아 자동저장할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Format de numéro de port illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||
"Password can not be blank, Cannot automatically save or discard changes",Пароль не может быть пустым. Невозможно сохранить или отменить изменения,密码不能为空,无法自动保存,是否丢弃修改,,,"비밀번호가 비어있어 자동저장할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Le mot de passe ne peut pas être vide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||
"Illegal timeout format, Cannot automatically save or discard changes",Неверный формат таймаута. Невозможно сохранить или отменить изменения,非法超时格式,无法自动保存,是否丢弃修改,,,"시간초과(Timeout) 형식이 올바르지 않아 자동저장할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Format de délai d'attente illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||
Whether to discard unconfigured servers,Внесенные изменения будут утеряны,是否丢弃未配置的服务器,,,구성되지 않은 서버 설정 변경 사항을 폐기하시겠습니까?,Eliminer les serveurs non configurés ou non | |||
"Invalid server address, Cannot automatically save or discard changes",Неверный адрес сервера. Невозможно сохранить или отменить изменения,非法服务器地址,无法自动保存,是否丢弃修改,,,"서버 주소가 올바르지 않아 자동 저장 할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Adresse de serveur invalide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||
"Illegal port number format, Cannot automatically save or discard changes",Неверный числовой адрес порта. Невозможно сохранить или отменить изменения,非法端口格式,无法自动保存,是否丢弃修改,,,"포트 번호가 올바르지 않아 자동 저장 할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Format de numéro de port illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||
"Password can not be blank, Cannot automatically save or discard changes",Пароль не может быть пустым. Невозможно сохранить или отменить изменения,密码不能为空,无法自动保存,是否丢弃修改,,,"비밀번호를 입력하지 않아 자동 저장 할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Le mot de passe ne peut pas être vide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||
"Illegal timeout format, Cannot automatically save or discard changes",Неверный формат таймаута. Невозможно сохранить или отменить изменения,非法超时格式,无法自动保存,是否丢弃修改,,,시간 초과 형식이 올바르지 않아 자동 저장 할 수 없습니다. 변경 사항을 폐기하시겠습니까?,"Format de délai d'attente illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||
,,,,,, | |||
"Error occured when process proxy setting, do you want reset current setting and retry?",Произошла ошибка при обработке настроек. Хотите сбросить текущие настройки и попробовать снова?,处理代理设置时发生错误,是否重置当前代理设置并重试?,,,프록시 설정을 처리하는데에 오류가 발생했습니다. 현재 설정을 폐기하고 다시시도하시겠습니까?,Une erreur s'est produite lors du processus de configuration du proxy. Voulez-vous réinitialiser le paramètre actuel et réessayer? | |||
"Error occured when process proxy setting, do you want reset current setting and retry?",Произошла ошибка при обработке настроек. Хотите сбросить текущие настройки и попробовать снова?,处理代理设置时发生错误,是否重置当前代理设置并重试?,,,프록시 설정을 처리하는데에 오류가 발생했습니다. 현재 설정을 폐기하고 다시 시도하시겠습니까?,Une erreur s'est produite lors du processus de configuration du proxy. Voulez-vous réinitialiser le paramètre actuel et réessayer? | |||
"Unrecoverable proxy setting error occured, see log for detail","Произошла серьезная ошибка, подробности можно узнать в журналах",发生不可恢复的代理设置错误,查看日志以取得详情,,,복구 불가능한 프록시 설정 오류가 발생했습니다. 자세한 정보는 로그를 참조하세요.,"Une erreur de paramètre de proxy irrécupérable s'est produite, consultez le journal pour plus de détails" | |||
Auth user can not be blank,Пользователь не может быть пустым,认证用户不能为空,認證用戶不能為空,認証ユーザが指定されていません。,증명 정보의 사용자 이름은 비어있을 수 없습니다.,L'utilisateur d'authentification ne peut pas être vide | |||
Auth pwd can not be blank,Пароль не может быть пустым,认证密码不能为空,認證口令不能為空,認証パスワードが指定されていません。,증명 정보의 비밀번호는 비어있을 수 없습니다.,Le mot de passe d'authentification ne peut pas être vide | |||
Auth user can not be blank,Пользователь не может быть пустым,认证用户不能为空,認證用戶不能為空,認証ユーザが指定されていません。,인증 정보의 사용자 이름은 비어있을 수 없습니다.,L'utilisateur d'authentification ne peut pas être vide | |||
Auth pwd can not be blank,Пароль не может быть пустым,认证密码不能为空,認證口令不能為空,認証パスワードが指定されていません。,인증 정보의 비밀번호는 비어있을 수 없습니다.,Le mot de passe d'authentification ne peut pas être vide |