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