- Use `Splat`'s logging interface - Cleanupspull/3006/head
@@ -1,5 +1,6 @@ | |||
using Shadowsocks.Net.Crypto.Exception; | |||
using Shadowsocks.Net.Crypto.Stream; | |||
using Splat; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Net; | |||
@@ -8,9 +9,8 @@ using System.Text; | |||
namespace Shadowsocks.Net.Crypto.AEAD | |||
{ | |||
public abstract class AEADCrypto : CryptoBase | |||
public abstract class AEADCrypto : CryptoBase, IEnableLogger | |||
{ | |||
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); | |||
// We are using the same saltLen and keyLen | |||
private const string Info = "ss-subkey"; | |||
private static readonly byte[] InfoBytes = Encoding.ASCII.GetBytes(Info); | |||
@@ -60,8 +60,8 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
// Initialize all-zero nonce for each connection | |||
nonce = new byte[nonceLen]; | |||
logger.Dump($"masterkey {instanceId}", masterKey, keyLen); | |||
logger.Dump($"nonce {instanceId}", nonce, keyLen); | |||
this.Log().Debug($"masterkey {instanceId} {masterKey} {keyLen}"); | |||
this.Log().Debug($"nonce {instanceId} {nonce} {keyLen}"); | |||
} | |||
protected abstract Dictionary<string, CipherInfo> GetCiphers(); | |||
@@ -92,8 +92,8 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
CryptoUtils.HKDF(keyLen, masterKey, salt, InfoBytes).CopyTo(sessionKey, 0); | |||
logger.Dump($"salt {instanceId}", salt, saltLen); | |||
logger.Dump($"sessionkey {instanceId}", sessionKey, keyLen); | |||
this.Log().Debug($"salt {instanceId}", salt, saltLen); | |||
this.Log().Debug($"sessionkey {instanceId}", sessionKey, keyLen); | |||
} | |||
public abstract int CipherEncrypt(ReadOnlySpan<byte> plain, Span<byte> cipher); | |||
@@ -138,7 +138,7 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
// if not, keep buf for next run, at this condition, buffer is not empty | |||
if (outlength + ChunkOverhead > cipher.Length) | |||
{ | |||
logger.Debug("enc outbuf almost full, giving up"); | |||
this.Log().Debug("enc outbuf almost full, giving up"); | |||
// write rest data to head of shared buffer | |||
tmp.CopyTo(buffer); | |||
@@ -150,7 +150,7 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
bufSize = tmp.Length; | |||
if (bufSize <= 0) | |||
{ | |||
logger.Debug("No more data to encrypt, leaving"); | |||
this.Log().Debug("No more data to encrypt, leaving"); | |||
return outlength; | |||
} | |||
} | |||
@@ -165,7 +165,7 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
cipher.CopyTo(tmp.Slice(bufPtr)); | |||
int bufSize = tmp.Length; | |||
logger.Debug($"{instanceId} decrypt tcp, read salt: {!saltReady}"); | |||
this.Log().Debug($"{instanceId} decrypt tcp, read salt: {!saltReady}"); | |||
if (!saltReady) | |||
{ | |||
// check if we get the leading salt | |||
@@ -191,7 +191,7 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
// check if we have any data | |||
if (bufSize <= 0) | |||
{ | |||
logger.Trace("No data in buffer"); | |||
this.Log().Debug("No data in buffer"); | |||
return outlength; | |||
} | |||
@@ -200,23 +200,23 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
{ | |||
// so we only have chunk length and its tag? | |||
// wait more | |||
logger.Trace($"{instanceId} not enough data to decrypt chunk. write {tmp.Length} byte back to buffer."); | |||
this.Log().Debug($"{instanceId} not enough data to decrypt chunk. write {tmp.Length} byte back to buffer."); | |||
tmp.CopyTo(buffer); | |||
bufPtr = tmp.Length; | |||
return outlength; | |||
} | |||
logger.Trace($"{instanceId} try decrypt to offset {outlength}"); | |||
this.Log().Debug($"{instanceId} try decrypt to offset {outlength}"); | |||
int len = ChunkDecrypt(plain.Slice(outlength), tmp); | |||
if (len <= 0) | |||
{ | |||
logger.Trace($"{instanceId} no chunk decrypted, write {tmp.Length} byte back to buffer."); | |||
this.Log().Debug($"{instanceId} no chunk decrypted, write {tmp.Length} byte back to buffer."); | |||
// no chunk decrypted | |||
tmp.CopyTo(buffer); | |||
bufPtr = tmp.Length; | |||
return outlength; | |||
} | |||
logger.Trace($"{instanceId} decrypted {len} to offset {outlength}"); | |||
this.Log().Debug($"{instanceId} decrypted {len} to offset {outlength}"); | |||
// drop decrypted data | |||
tmp = tmp.Slice(ChunkLengthBytes + tagLen + len + tagLen); | |||
@@ -225,7 +225,7 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
// logger.Debug("aead dec outlength " + outlength); | |||
if (outlength + ChunkOverhead > cipher.Length) | |||
{ | |||
logger.Trace($"{instanceId} output almost full, write {tmp.Length} byte back to buffer."); | |||
this.Log().Debug($"{instanceId} output almost full, write {tmp.Length} byte back to buffer."); | |||
tmp.CopyTo(buffer); | |||
bufPtr = tmp.Length; | |||
return outlength; | |||
@@ -235,7 +235,7 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
if (bufSize <= 0) | |||
{ | |||
bufPtr = 0; | |||
logger.Debug($"{instanceId} no data in buffer, already all done"); | |||
this.Log().Debug($"{instanceId} no data in buffer, already all done"); | |||
return outlength; | |||
} | |||
} | |||
@@ -266,7 +266,7 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
{ | |||
if (plain.Length > ChunkLengthMask) | |||
{ | |||
logger.Error("enc chunk too big"); | |||
this.Log().Error("enc chunk too big"); | |||
throw new CryptoErrorException(); | |||
} | |||
@@ -289,14 +289,14 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
if (chunkLength > ChunkLengthMask) | |||
{ | |||
// we get invalid chunk | |||
logger.Error($"{instanceId} Invalid chunk length: {chunkLength}"); | |||
this.Log().Error($"{instanceId} Invalid chunk length: {chunkLength}"); | |||
throw new CryptoErrorException(); | |||
} | |||
// logger.Debug("Get the real chunk len:" + chunkLength); | |||
int bufSize = cipher.Length; | |||
if (bufSize < ChunkLengthBytes + tagLen /* we haven't remove them */+ chunkLength + tagLen) | |||
{ | |||
logger.Debug($"{instanceId} need {ChunkLengthBytes + tagLen + chunkLength + tagLen}, but have {cipher.Length}"); | |||
this.Log().Debug($"{instanceId} need {ChunkLengthBytes + tagLen + chunkLength + tagLen}, but have {cipher.Length}"); | |||
return 0; | |||
} | |||
CryptoUtils.SodiumIncrement(nonce); | |||
@@ -304,7 +304,7 @@ namespace Shadowsocks.Net.Crypto.AEAD | |||
// drop chunk len and its tag from buffer | |||
int len = CipherDecrypt(plain, cipher.Slice(ChunkLengthBytes + tagLen, chunkLength + tagLen)); | |||
CryptoUtils.SodiumIncrement(nonce); | |||
logger.Trace($"{instanceId} decrypted {len} byte chunk used {ChunkLengthBytes + tagLen + chunkLength + tagLen} from {cipher.Length}"); | |||
this.Log().Debug($"{instanceId} decrypted {len} byte chunk used {ChunkLengthBytes + tagLen + chunkLength + tagLen} from {cipher.Length}"); | |||
return len; | |||
} | |||
} | |||
@@ -1,3 +1,4 @@ | |||
using Splat; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Runtime.CompilerServices; | |||
@@ -5,10 +6,8 @@ using System.Text; | |||
namespace Shadowsocks.Net.Crypto.Stream | |||
{ | |||
public abstract class StreamCrypto : CryptoBase | |||
public abstract class StreamCrypto : CryptoBase, IEnableLogger | |||
{ | |||
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); | |||
// shared by TCP decrypt UDP encrypt and decrypt | |||
protected static byte[] sharedBuffer = new byte[65536]; | |||
@@ -34,7 +33,7 @@ namespace Shadowsocks.Net.Crypto.Stream | |||
InitKey(password); | |||
logger.Dump($"key {instanceId}", key, keyLen); | |||
this.Log().Debug($"key {instanceId} {key} {keyLen}"); | |||
} | |||
protected abstract Dictionary<string, CipherInfo> GetCiphers(); | |||
@@ -93,7 +92,7 @@ namespace Shadowsocks.Net.Crypto.Stream | |||
public override int Encrypt(ReadOnlySpan<byte> plain, Span<byte> cipher) | |||
{ | |||
int cipherOffset = 0; | |||
logger.Trace($"{instanceId} encrypt TCP, generate iv: {!ivReady}"); | |||
this.Log().Debug($"{instanceId} encrypt TCP, generate iv: {!ivReady}"); | |||
if (!ivReady) | |||
{ | |||
// Generate IV | |||
@@ -106,9 +105,9 @@ namespace Shadowsocks.Net.Crypto.Stream | |||
} | |||
int clen = CipherEncrypt(plain, cipher); | |||
logger.DumpBase64($"plain {instanceId}", plain); | |||
logger.DumpBase64($"cipher {instanceId}", cipher.Slice(0, clen)); | |||
logger.Dump($"iv {instanceId}", iv, ivLen); | |||
this.Log().Debug($"plain {instanceId} {Convert.ToBase64String(plain)}"); | |||
this.Log().Debug($"cipher {instanceId} {Convert.ToBase64String(cipher.Slice(0, clen))}"); | |||
this.Log().Debug($"iv {instanceId} {iv} {ivLen}"); | |||
return clen + cipherOffset; | |||
} | |||
@@ -116,7 +115,7 @@ namespace Shadowsocks.Net.Crypto.Stream | |||
[MethodImpl(MethodImplOptions.Synchronized)] | |||
public override int Decrypt(Span<byte> plain, ReadOnlySpan<byte> cipher) | |||
{ | |||
logger.Trace($"{instanceId} decrypt TCP, read iv: {!ivReady}"); | |||
this.Log().Debug($"{instanceId} decrypt TCP, read iv: {!ivReady}"); | |||
int cipherOffset = 0; | |||
// is first packet, need read iv | |||
@@ -149,9 +148,9 @@ namespace Shadowsocks.Net.Crypto.Stream | |||
// read all data from buffer | |||
int len = CipherDecrypt(plain, cipher.Slice(cipherOffset)); | |||
logger.DumpBase64($"cipher {instanceId}", cipher.Slice(cipherOffset)); | |||
logger.DumpBase64($"plain {instanceId}", plain.Slice(0, len)); | |||
logger.Dump($"iv {instanceId}", iv, ivLen); | |||
this.Log().Debug($"cipher {instanceId} {Convert.ToBase64String(cipher.Slice(cipherOffset))}"); | |||
this.Log().Debug($"plain {instanceId} {Convert.ToBase64String(plain.Slice(0, len))}"); | |||
this.Log().Debug($"iv {instanceId} {iv} {ivLen}"); | |||
return len; | |||
} | |||
@@ -1,3 +1,4 @@ | |||
using Splat; | |||
using System; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
@@ -5,14 +6,11 @@ using System.Text; | |||
using System.Text.RegularExpressions; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using NLog; | |||
namespace Shadowsocks.Net.Proxy | |||
{ | |||
public class HttpProxy : IProxy | |||
public class HttpProxy : IProxy, IEnableLogger | |||
{ | |||
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); | |||
public EndPoint LocalEndPoint => _remote.LocalEndPoint; | |||
public EndPoint ProxyEndPoint { get; private set; } | |||
public EndPoint DestEndPoint { get; private set; } | |||
@@ -45,7 +43,7 @@ namespace Shadowsocks.Net.Proxy | |||
private bool OnLineRead(string line, object state) | |||
{ | |||
logger.Trace(line); | |||
this.Log().Debug(line); | |||
if (_respondLineCount == 0) | |||
{ | |||
@@ -7,6 +7,11 @@ | |||
<ItemGroup> | |||
<PackageReference Include="BouncyCastle.NetCore" Version="1.8.6" /> | |||
<PackageReference Include="NaCl.Core" Version="2.0.0" /> | |||
<PackageReference Include="Splat" Version="9.6.1" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\Shadowsocks\Shadowsocks.csproj" /> | |||
</ItemGroup> | |||
</Project> |
@@ -1,4 +1,4 @@ | |||
using NLog; | |||
using Splat; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
@@ -28,10 +28,8 @@ namespace Shadowsocks.Net | |||
public virtual void Stop() { } | |||
} | |||
public class TCPListener | |||
public class TCPListener : IEnableLogger | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
public class UDPState | |||
{ | |||
public UDPState(Socket s) | |||
@@ -44,15 +42,13 @@ namespace Shadowsocks.Net | |||
public EndPoint remoteEndPoint; | |||
} | |||
Configuration _config; | |||
bool _shareOverLAN; | |||
IPEndPoint _localEndPoint; | |||
Socket _tcpSocket; | |||
IEnumerable<IStreamService> _services; | |||
public TCPListener(Configuration config, IEnumerable<IStreamService> services) | |||
public TCPListener(IPEndPoint localEndPoint, IEnumerable<IStreamService> services) | |||
{ | |||
_config = config; | |||
_shareOverLAN = config.shareOverLan; | |||
_localEndPoint = localEndPoint; | |||
_services = services; | |||
} | |||
@@ -64,28 +60,22 @@ namespace Shadowsocks.Net | |||
public void Start() | |||
{ | |||
if (CheckIfPortInUse(_config.localPort)) | |||
{ | |||
throw new Exception(I18N.GetString("Port {0} already in use", this._config.localPort)); | |||
} | |||
if (CheckIfPortInUse(_localEndPoint.Port)) | |||
throw new Exception($"Port {_localEndPoint.Port} already in use"); | |||
try | |||
{ | |||
// Create a TCP/IP socket. | |||
_tcpSocket = new Socket(_config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | |||
_tcpSocket = new Socket(_localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); | |||
_tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); | |||
IPEndPoint localEndPoint = null; | |||
localEndPoint = _shareOverLAN | |||
? new IPEndPoint(_config.isIPv6Enabled ? IPAddress.IPv6Any : IPAddress.Any, this._config.localPort) | |||
: new IPEndPoint(_config.isIPv6Enabled ? IPAddress.IPv6Loopback : IPAddress.Loopback, this._config.localPort); | |||
// Bind the socket to the local endpoint and listen for incoming connections. | |||
_tcpSocket.Bind(localEndPoint); | |||
_tcpSocket.Bind(_localEndPoint); | |||
_tcpSocket.Listen(1024); | |||
// Start an asynchronous socket to listen for connections. | |||
logger.Info($"Shadowsocks started TCP ({UpdateChecker.Version})"); | |||
logger.Debug(Encryption.EncryptorFactory.DumpRegisteredEncryptor()); | |||
this.Log().Info($"Shadowsocks started TCP"); | |||
this.Log().Debug(Crypto.CryptoFactory.DumpRegisteredEncryptor()); | |||
_tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket); | |||
} | |||
catch (SocketException) | |||
@@ -126,7 +116,7 @@ namespace Shadowsocks.Net | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
this.Log().Error(e, ""); | |||
} | |||
finally | |||
{ | |||
@@ -142,7 +132,7 @@ namespace Shadowsocks.Net | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
this.Log().Error(e, ""); | |||
} | |||
} | |||
} | |||
@@ -177,7 +167,7 @@ namespace Shadowsocks.Net | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
this.Log().Error(e, ""); | |||
conn.Close(); | |||
} | |||
} | |||
@@ -1,45 +1,36 @@ | |||
using Splat; | |||
using Shadowsocks.Net.Crypto; | |||
using Shadowsocks.Net.Crypto.AEAD; | |||
using Shadowsocks.Net.Proxy; | |||
using System; | |||
using System.Buffers; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.Net; | |||
using System.Net.Sockets; | |||
using System.Runtime.CompilerServices; | |||
using System.Text; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using NLog; | |||
using Shadowsocks.Controller.Strategy; | |||
using Shadowsocks.Net.Crypto; | |||
using Shadowsocks.Net.Crypto.AEAD; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Net.Proxy; | |||
using Shadowsocks.Net.Sockets; | |||
using static Shadowsocks.Net.Crypto.CryptoBase; | |||
using Shadowsocks.Models; | |||
namespace Shadowsocks.Net | |||
{ | |||
class TCPRelay : StreamService | |||
class TCPRelay : StreamService, IEnableLogger | |||
{ | |||
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 Server _server; | |||
private DateTime _lastSweepTime; | |||
private readonly Configuration _config; | |||
public ISet<TCPHandler> Handlers { get; set; } | |||
public TCPRelay(ShadowsocksController controller, Configuration conf) | |||
public TCPRelay(Server server) | |||
{ | |||
_controller = controller; | |||
_config = conf; | |||
_server = server; | |||
Handlers = new HashSet<TCPHandler>(); | |||
_lastSweepTime = DateTime.Now; | |||
} | |||
@@ -58,7 +49,7 @@ namespace Shadowsocks.Net | |||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | |||
TCPHandler handler = new TCPHandler(_controller, _config, socket); | |||
TCPHandler handler = new TCPHandler(_server, socket); | |||
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | |||
lock (Handlers) | |||
@@ -75,7 +66,7 @@ namespace Shadowsocks.Net | |||
} | |||
foreach (TCPHandler handler1 in handlersToClose) | |||
{ | |||
logger.Debug("Closing timed out TCP connection."); | |||
this.Log().Debug("Closing timed out TCP connection."); | |||
handler1.Close(); | |||
} | |||
@@ -102,7 +93,7 @@ namespace Shadowsocks.Net | |||
} | |||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | |||
TCPHandler handler = new TCPHandler(_controller, _config, socket); | |||
TCPHandler handler = new TCPHandler(_server, socket); | |||
handler.OnConnected += OnConnected; | |||
handler.OnInbound += OnInbound; | |||
@@ -135,7 +126,7 @@ namespace Shadowsocks.Net | |||
} | |||
foreach (TCPHandler handler1 in handlersToClose) | |||
{ | |||
logger.Debug("Closing timed out TCP connection."); | |||
this.Log().Debug("Closing timed out TCP connection."); | |||
handler1.Close(); | |||
} | |||
@@ -191,7 +182,7 @@ namespace Shadowsocks.Net | |||
} | |||
} | |||
internal class TCPHandler | |||
internal class TCPHandler : IEnableLogger | |||
{ | |||
public event EventHandler<SSTCPConnectedEventArgs> OnConnected; | |||
public event EventHandler<SSTransmitEventArgs> OnInbound; | |||
@@ -199,8 +190,6 @@ namespace Shadowsocks.Net | |||
public event EventHandler<SSRelayEventArgs> OnClosed; | |||
public event EventHandler<SSRelayEventArgs> OnFailed; | |||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); | |||
private readonly int _serverTimeout; | |||
private readonly int _proxyTimeout; | |||
@@ -216,14 +205,14 @@ namespace Shadowsocks.Net | |||
public DateTime lastActivity; | |||
private readonly ShadowsocksController _controller; | |||
private readonly ForwardProxyConfig _config; | |||
// TODO: forward proxy | |||
//private readonly ForwardProxyConfig _config; | |||
private readonly Server _server; | |||
private readonly Socket _connection; | |||
private IProxy _remote; | |||
private IEncryptor encryptor; | |||
private ICrypto encryptor; | |||
// workaround | |||
private IEncryptor decryptor; | |||
private Server _server; | |||
private ICrypto decryptor; | |||
private byte[] _firstPacket; | |||
private int _firstPacketLength; | |||
@@ -240,28 +229,25 @@ namespace Shadowsocks.Net | |||
private readonly object _closeConnLock = new object(); | |||
// TODO: decouple controller | |||
public TCPHandler(ShadowsocksController controller, Configuration config, Socket socket) | |||
public TCPHandler(Server server, Socket socket) | |||
{ | |||
_controller = controller; | |||
_config = config.proxy; | |||
_server = server; | |||
_connection = socket; | |||
_proxyTimeout = config.proxy.proxyTimeout * 1000; | |||
_serverTimeout = config.GetCurrentServer().timeout * 1000; | |||
_proxyTimeout = 5000; | |||
_serverTimeout = 5000; | |||
lastActivity = DateTime.Now; | |||
} | |||
public void CreateRemote(EndPoint destination) | |||
{ | |||
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint, destination); | |||
if (server == null || server.server == "") | |||
if (_server == null || _server.Host == "") | |||
{ | |||
throw new ArgumentException("No server configured"); | |||
} | |||
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | |||
decryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | |||
_server = server; | |||
encryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password); | |||
decryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password); | |||
} | |||
public async Task StartAsync(byte[] firstPacket, int length) | |||
@@ -283,7 +269,7 @@ namespace Shadowsocks.Net | |||
private void ErrorClose(Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
this.Log().Error(e, ""); | |||
Close(); | |||
} | |||
@@ -308,7 +294,7 @@ namespace Shadowsocks.Net | |||
} | |||
catch (Exception e) | |||
{ | |||
Logger.LogUsefulException(e); | |||
this.Log().Error(e, ""); | |||
} | |||
} | |||
@@ -330,7 +316,7 @@ namespace Shadowsocks.Net | |||
{ | |||
// reject socks 4 | |||
response = new byte[] { 0, 91 }; | |||
Logger.Error("socks 5 protocol error"); | |||
this.Log().Error("socks5 protocol error"); | |||
} | |||
await _connection.SendAsync(response, SocketFlags.None); | |||
@@ -452,11 +438,12 @@ namespace Shadowsocks.Net | |||
CreateRemote(destination); | |||
IProxy remote; | |||
EndPoint proxyEP = null; | |||
EndPoint serverEP = new DnsEndPoint(_server.server, _server.server_port); | |||
EndPoint pluginEP = _controller.GetPluginLocalEndPointIfConfigured(_server); | |||
EndPoint serverEP = new DnsEndPoint(_server.Host, _server.Port); | |||
EndPoint pluginEP = null; // TODO: plugin local end point | |||
remote = new DirectConnect(); // TODO: forward proxy | |||
NetworkCredential auth = null; | |||
if (_config.useAuth) | |||
/*if (_config.useAuth) | |||
{ | |||
auth = new NetworkCredential(_config.authUser, _config.authPwd); | |||
} | |||
@@ -478,8 +465,7 @@ namespace Shadowsocks.Net | |||
else | |||
{ | |||
remote = new DirectConnect(); | |||
} | |||
}*/ | |||
CancellationTokenSource cancelProxy = new CancellationTokenSource(_proxyTimeout * 1000); | |||
@@ -488,13 +474,13 @@ namespace Shadowsocks.Net | |||
if (!(remote is DirectConnect)) | |||
{ | |||
Logger.Debug($"Socket connected to proxy {remote.ProxyEndPoint}"); | |||
this.Log().Debug($"Socket connected to proxy {remote.ProxyEndPoint}"); | |||
} | |||
var _startConnectTime = DateTime.Now; | |||
CancellationTokenSource cancelServer = new CancellationTokenSource(_serverTimeout * 1000); | |||
await remote.ConnectRemoteAsync(serverEP, cancelServer.Token); | |||
Logger.Debug($"Socket connected to ss server: {_server}"); | |||
this.Log().Debug($"Socket connected to ss server: {_server}"); | |||
TimeSpan latency = DateTime.Now - _startConnectTime; | |||
OnConnected?.Invoke(this, new SSTCPConnectedEventArgs(_server, latency)); | |||
@@ -1,4 +1,5 @@ | |||
using System; | |||
using Splat; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Net; | |||
@@ -6,10 +7,8 @@ using System.Net.NetworkInformation; | |||
using System.Net.Sockets; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using NLog; | |||
using Shadowsocks.Model; | |||
namespace Shadowsocks.Controller | |||
namespace Shadowsocks.Net | |||
{ | |||
public interface IDatagramService | |||
{ | |||
@@ -25,10 +24,8 @@ namespace Shadowsocks.Controller | |||
public virtual void Stop() { } | |||
} | |||
public class UDPListener | |||
public class UDPListener : IEnableLogger | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
public class UDPState | |||
{ | |||
public UDPState(Socket s) | |||
@@ -41,18 +38,15 @@ namespace Shadowsocks.Controller | |||
public EndPoint remoteEndPoint; | |||
} | |||
Configuration _config; | |||
bool _shareOverLAN; | |||
IPEndPoint _localEndPoint; | |||
Socket _udpSocket; | |||
IEnumerable<IDatagramService> _services; | |||
CancellationTokenSource tokenSource = new CancellationTokenSource(); | |||
public UDPListener(Configuration config, IEnumerable<IDatagramService> services) | |||
public UDPListener(IPEndPoint localEndPoint, IEnumerable<IDatagramService> services) | |||
{ | |||
this._config = config; | |||
this._shareOverLAN = _config.shareOverLan; | |||
this._services = services; | |||
_localEndPoint = localEndPoint; | |||
_services = services; | |||
} | |||
private bool CheckIfPortInUse(int port) | |||
@@ -63,23 +57,19 @@ namespace Shadowsocks.Controller | |||
public void Start() | |||
{ | |||
if (CheckIfPortInUse(this._config.localPort)) | |||
throw new Exception(I18N.GetString("Port {0} already in use", this._config.localPort)); | |||
if (CheckIfPortInUse(_localEndPoint.Port)) | |||
throw new Exception($"Port {_localEndPoint.Port} already in use"); | |||
// Create a TCP/IP socket. | |||
_udpSocket = new Socket(_config.isIPv6Enabled ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); | |||
_udpSocket = new Socket(_localEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); | |||
_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); | |||
IPEndPoint localEndPoint = null; | |||
localEndPoint = _shareOverLAN | |||
? new IPEndPoint(_config.isIPv6Enabled ? IPAddress.IPv6Any : IPAddress.Any, this._config.localPort) | |||
: new IPEndPoint(_config.isIPv6Enabled ? IPAddress.IPv6Loopback : IPAddress.Loopback, this._config.localPort); | |||
// Bind the socket to the local endpoint and listen for incoming connections. | |||
_udpSocket.Bind(localEndPoint); | |||
_udpSocket.Bind(_localEndPoint); | |||
// Start an asynchronous socket to listen for connections. | |||
logger.Info($"Shadowsocks started UDP ({UpdateChecker.Version})"); | |||
logger.Debug(Encryption.EncryptorFactory.DumpRegisteredEncryptor()); | |||
this.Log().Info($"Shadowsocks started UDP"); | |||
this.Log().Debug(Crypto.CryptoFactory.DumpRegisteredEncryptor()); | |||
UDPState udpState = new UDPState(_udpSocket); | |||
// _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); | |||
Task.Run(() => WorkLoop(tokenSource.Token)); | |||
@@ -1,3 +1,6 @@ | |||
using Shadowsocks.Models; | |||
using Shadowsocks.Net.Crypto; | |||
using Splat; | |||
using System; | |||
using System.Buffers; | |||
using System.Collections.Generic; | |||
@@ -6,24 +9,21 @@ using System.Net.Sockets; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
using System.Threading.Tasks; | |||
using NLog; | |||
using Shadowsocks.Net.Crypto; | |||
namespace Shadowsocks.Controller | |||
namespace Shadowsocks.Net | |||
{ | |||
class UDPRelay : DatagramService | |||
{ | |||
private ShadowsocksController _controller; | |||
Server _server; | |||
// TODO: choose a smart number | |||
private LRUCache<IPEndPoint, UDPHandler> _cache = new LRUCache<IPEndPoint, UDPHandler>(512); | |||
public long outbound = 0; | |||
public long inbound = 0; | |||
public UDPRelay(ShadowsocksController controller) | |||
public UDPRelay(Server server) | |||
{ | |||
this._controller = controller; | |||
_server = server; | |||
} | |||
public override async Task<bool> Handle(Memory<byte> packet, Socket socket, EndPoint client) | |||
@@ -40,7 +40,7 @@ namespace Shadowsocks.Controller | |||
UDPHandler handler = _cache.get(remoteEndPoint); | |||
if (handler == null) | |||
{ | |||
handler = new UDPHandler(socket, _controller.GetAServer(IStrategyCallerType.UDP, remoteEndPoint, null/*TODO: fix this*/), remoteEndPoint); | |||
handler = new UDPHandler(socket, _server, remoteEndPoint); | |||
handler.Receive(); | |||
_cache.add(remoteEndPoint, handler); | |||
} | |||
@@ -48,9 +48,8 @@ namespace Shadowsocks.Controller | |||
return true; | |||
} | |||
public class UDPHandler | |||
public class UDPHandler : IEnableLogger | |||
{ | |||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||
private static MemoryPool<byte> pool = MemoryPool<byte>.Shared; | |||
private Socket _local; | |||
private Socket _remote; | |||
@@ -81,25 +80,25 @@ namespace Shadowsocks.Controller | |||
_localEndPoint = localEndPoint; | |||
// TODO async resolving | |||
bool parsed = IPAddress.TryParse(server.server, out IPAddress ipAddress); | |||
bool parsed = IPAddress.TryParse(server.Host, out IPAddress ipAddress); | |||
if (!parsed) | |||
{ | |||
IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server); | |||
IPHostEntry ipHostInfo = Dns.GetHostEntry(server.Host); | |||
ipAddress = ipHostInfo.AddressList[0]; | |||
} | |||
_remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); | |||
_remoteEndPoint = new IPEndPoint(ipAddress, server.Port); | |||
_remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); | |||
_remote.Bind(new IPEndPoint(ListenAddress, 0)); | |||
} | |||
public async Task SendAsync(ReadOnlyMemory<byte> data) | |||
{ | |||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | |||
ICrypto encryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password); | |||
using IMemoryOwner<byte> mem = pool.Rent(data.Length + 1000); | |||
// byte[] dataOut = new byte[slicedData.Length + 1000]; | |||
int outlen = encryptor.EncryptUDP(data.Span[3..], mem.Memory.Span); | |||
logger.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay up"); | |||
this.Log().Debug($"{_localEndPoint} {_remoteEndPoint} {outlen} UDP Relay up"); | |||
if (!MemoryMarshal.TryGetArray(mem.Memory[..outlen], out ArraySegment<byte> outData)) | |||
{ | |||
throw new InvalidOperationException("Can't extract underly array segment"); | |||
@@ -110,7 +109,7 @@ namespace Shadowsocks.Controller | |||
public async Task ReceiveAsync() | |||
{ | |||
EndPoint remoteEndPoint = new IPEndPoint(ListenAddress, 0); | |||
logger.Debug($"++++++Receive Server Port, size:" + _buffer.Length); | |||
this.Log().Debug($"++++++Receive Server Port, size:" + _buffer.Length); | |||
try | |||
{ | |||
while (true) | |||
@@ -121,9 +120,9 @@ namespace Shadowsocks.Controller | |||
using IMemoryOwner<byte> owner = pool.Rent(bytesRead + 3); | |||
Memory<byte> o = owner.Memory; | |||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | |||
ICrypto encryptor = CryptoFactory.GetEncryptor(_server.Method, _server.Password); | |||
int outlen = encryptor.DecryptUDP(o.Span[3..], _buffer.AsSpan(0, bytesRead)); | |||
logger.Debug(_remoteEndPoint, _localEndPoint, outlen, "UDP Relay down"); | |||
this.Log().Debug($"{_remoteEndPoint} {_localEndPoint} {outlen} UDP Relay down"); | |||
if (!MemoryMarshal.TryGetArray(o[..(outlen + 3)], out ArraySegment<byte> data)) | |||
{ | |||
throw new InvalidOperationException("Can't extract underly array segment"); | |||
@@ -134,7 +133,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception e) | |||
{ | |||
logger.LogUsefulException(e); | |||
this.Log().Warn(e, ""); | |||
} | |||
} | |||