Browse Source

Merge pull request #667 from Noisyfox/dev

Minor bug fix and improvements.
tags/3.3
Syrone Wong GitHub 8 years ago
parent
commit
f935514c05
9 changed files with 207 additions and 91 deletions
  1. +2
    -1
      shadowsocks-csharp/Controller/Service/Listener.cs
  2. +16
    -2
      shadowsocks-csharp/Controller/Service/PolipoRunner.cs
  3. +2
    -6
      shadowsocks-csharp/Controller/Service/PortForwarder.cs
  4. +104
    -47
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  5. +11
    -4
      shadowsocks-csharp/Controller/Service/UDPRelay.cs
  6. +2
    -7
      shadowsocks-csharp/Proxy/DirectConnect.cs
  7. +2
    -5
      shadowsocks-csharp/Proxy/Socks5Proxy.cs
  8. +55
    -19
      shadowsocks-csharp/Util/SocketUtil.cs
  9. +13
    -0
      shadowsocks-csharp/app.manifest

+ 2
- 1
shadowsocks-csharp/Controller/Service/Listener.cs View File

@@ -116,8 +116,9 @@ namespace Shadowsocks.Controller
catch (ObjectDisposedException)
{
}
catch (Exception)
catch (Exception ex)
{
Logging.Debug(ex);
}
finally
{


+ 16
- 2
shadowsocks-csharp/Controller/Service/PolipoRunner.cs View File

@@ -122,6 +122,7 @@ namespace Shadowsocks.Controller
* different instance will create their unique "privoxy_UID.conf" where
* UID is hash of ss's location.
*/
private static bool IsChildProcess(Process process)
{
if (Utils.IsPortableMode())
@@ -129,8 +130,21 @@ namespace Shadowsocks.Controller
/*
* Under PortableMode, we could identify it by the path of ss_privoxy.exe.
*/
string path = process.MainModule.FileName;
return Utils.GetTempPath("ss_privoxy.exe").Equals(path);
try
{
/*
* Sometimes Process.GetProcessesByName will return some processes that
* are already dead, and that will cause exceptions here.
* We could simply ignore those exceptions.
*/
string path = process.MainModule.FileName;
return Utils.GetTempPath("ss_privoxy.exe").Equals(path);
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
return false;
}
}
else
{


+ 2
- 6
shadowsocks-csharp/Controller/Service/PortForwarder.cs View File

@@ -48,12 +48,8 @@ namespace Shadowsocks.Controller
{
EndPoint remoteEP = SocketUtil.GetEndPoint("localhost", targetPort);
_remote = SocketUtil.CreateSocket(remoteEP);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
// Connect to the remote endpoint.
_remote.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), null);
SocketUtil.BeginConnectTcp(remoteEP, ConnectCallback, null);
}
catch (Exception e)
{
@@ -70,7 +66,7 @@ namespace Shadowsocks.Controller
}
try
{
_remote.EndConnect(ar);
_remote = SocketUtil.EndConnectTcp(ar);
HandshakeReceive();
}
catch (Exception e)


+ 104
- 47
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -80,6 +80,33 @@ namespace Shadowsocks.Controller
class TCPHandler
{
class AsyncSession
{
public IProxy Remote { get; }
public AsyncSession(IProxy remote)
{
Remote = remote;
}
}
class AsyncSession<T> : AsyncSession
{
public T State { get; set; }
public AsyncSession(IProxy remote, T state) : base(remote)
{
State = state;
}
public AsyncSession(AsyncSession session, T state): base(session.Remote)
{
State = state;
}
}
// Size of receive buffer.
public static readonly int RecvSize = 8192;
public static readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth
@@ -89,7 +116,8 @@ namespace Shadowsocks.Controller
public IEncryptor encryptor;
public Server server;
// Client socket.
public IProxy remote;
private AsyncSession _currentRemoteSession;
public Socket connection;
public ShadowsocksController controller;
public TCPRelay tcprelay;
@@ -177,6 +205,7 @@ namespace Shadowsocks.Controller
}
try
{
var remote = _currentRemoteSession?.Remote;
remote?.Shutdown(SocketShutdown.Both);
remote?.Close();
}
@@ -340,7 +369,8 @@ namespace Shadowsocks.Controller
// inner class
private class ProxyTimer : Timer
{
public IProxy Proxy;
public AsyncSession Session;
public EndPoint DestEndPoint;
public Server Server;
@@ -351,6 +381,8 @@ namespace Shadowsocks.Controller
private class ServerTimer : Timer
{
public AsyncSession Session;
public Server Server;
public ServerTimer(int p) : base(p) { }
}
@@ -362,6 +394,7 @@ namespace Shadowsocks.Controller
CreateRemote();
// Setting up proxy
IProxy remote;
EndPoint proxyEP;
if (_config.useProxy)
{
@@ -374,20 +407,22 @@ namespace Shadowsocks.Controller
proxyEP = null;
}
var session = new AsyncSession(remote);
_currentRemoteSession = session;
ProxyTimer proxyTimer = new ProxyTimer(3000);
proxyTimer.AutoReset = false;
proxyTimer.Elapsed += proxyConnectTimer_Elapsed;
proxyTimer.Enabled = true;
proxyTimer.Proxy = remote;
proxyTimer.Session = session;
proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port);
proxyTimer.Server = server;
_proxyConnected = false;
// Connect to the proxy server.
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), proxyTimer);
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), new AsyncSession<ProxyTimer>(remote, proxyTimer));
}
catch (Exception e)
{
@@ -402,10 +437,10 @@ namespace Shadowsocks.Controller
{
return;
}
var proxy = ((ProxyTimer)sender).Proxy;
var proxy = ((ProxyTimer)sender).Session.Remote;
Logging.Info($"Proxy {proxy.ProxyEndPoint} timed out");
remote?.Close();
proxy.Close();
RetryConnect();
}
@@ -418,13 +453,16 @@ namespace Shadowsocks.Controller
}
try
{
ProxyTimer timer = (ProxyTimer)ar.AsyncState;
var session = (AsyncSession<ProxyTimer>) ar.AsyncState;
ProxyTimer timer = session.State;
var destEndPoint = timer.DestEndPoint;
server = timer.Server;
timer.Elapsed -= proxyConnectTimer_Elapsed;
timer.Enabled = false;
timer.Dispose();
var remote = session.Remote;
// Complete the connection.
remote.EndConnectProxy(ar);
@@ -443,11 +481,12 @@ namespace Shadowsocks.Controller
connectTimer.AutoReset = false;
connectTimer.Elapsed += destConnectTimer_Elapsed;
connectTimer.Enabled = true;
connectTimer.Session = session;
connectTimer.Server = server;
_destConnected = false;
// Connect to the remote endpoint.
remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), connectTimer);
remote.BeginConnectDest(destEndPoint, new AsyncCallback(ConnectCallback), new AsyncSession<ServerTimer>(session, connectTimer));
}
catch (ArgumentException)
{
@@ -466,11 +505,12 @@ namespace Shadowsocks.Controller
return;
}
var session = ((ServerTimer) sender).Session;
Server server = ((ServerTimer)sender).Server;
IStrategy strategy = controller.GetCurrentStrategy();
strategy?.SetFailure(server);
Logging.Info($"{server.FriendlyName()} timed out");
remote?.Close();
session.Remote.Close();
RetryConnect();
}
@@ -491,12 +531,14 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
ServerTimer timer = (ServerTimer)ar.AsyncState;
var 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;
// Complete the connection.
remote?.EndConnectDest(ar);
@@ -512,7 +554,7 @@ namespace Shadowsocks.Controller
strategy?.UpdateLatency(server, latency);
_tcprelay.UpdateLatency(server, latency);
StartPipe();
StartPipe(session);
}
catch (ArgumentException)
{
@@ -529,14 +571,15 @@ namespace Shadowsocks.Controller
}
}
private void StartPipe()
private void StartPipe(AsyncSession session)
{
if (_closed) return;
try
{
_startReceivingTime = DateTime.Now;
remote?.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), null);
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), null);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback),
new AsyncSession<bool>(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */);
}
catch (Exception e)
{
@@ -550,8 +593,8 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
if ( remote == null ) return;
int bytesRead = remote.EndReceive(ar);
var session = (AsyncSession) ar.AsyncState;
int bytesRead = session.Remote.EndReceive(ar);
_totalRead += bytesRead;
_tcprelay.UpdateInboundCounter(server, bytesRead);
if (bytesRead > 0)
@@ -563,7 +606,7 @@ namespace Shadowsocks.Controller
if (_closed) return;
encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
}
connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), null);
connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
IStrategy strategy = controller.GetCurrentStrategy();
strategy?.UpdateLastRead(server);
}
@@ -589,36 +632,48 @@ namespace Shadowsocks.Controller
if(connection == null) return;
int bytesRead = connection.EndReceive(ar);
_totalWrite += bytesRead;
var session = (AsyncSession<bool>) ar.AsyncState;
var remote = session.Remote;
if (bytesRead > 0)
{
int atyp = _connetionRecvBuffer[0];
string dst_addr;
int dst_port;
switch (atyp)
/*
* Only the first packet contains the socks5 header, it doesn't make sense to parse every packets.
* Also it's unnecessary to parse these data if we turn off the VerboseLogging.
*/
if (session.State && _config.isVerboseLogging)
{
case 1: // IPv4 address, 4 bytes
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6];
if ( _config.isVerboseLogging ) {
Logging.Info( $"connect to {dst_addr}:{dst_port}" );
}
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[1];
dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len);
dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3];
if ( _config.isVerboseLogging ) {
Logging.Info( $"connect to {dst_addr}:{dst_port}" );
}
break;
case 4: // IPv6 address, 16 bytes
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray()).ToString();
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18];
if ( _config.isVerboseLogging ) {
Logging.Info( $"connect to [{dst_addr}]:{dst_port}" );
}
break;
int atyp = _connetionRecvBuffer[0];
string dst_addr;
int dst_port;
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString();
dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6];
Logging.Info($"connect to {dst_addr}:{dst_port}");
session.State = false;
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[1];
dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len);
dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3];
Logging.Info($"connect to {dst_addr}:{dst_port}");
session.State = false;
break;
case 4: // IPv6 address, 16 bytes
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray()).ToString();
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18];
Logging.Info($"connect to [{dst_addr}]:{dst_port}");
session.State = false;
break;
}
}
int bytesToSend;
lock (_encryptionLock)
{
@@ -628,7 +683,7 @@ namespace Shadowsocks.Controller
_tcprelay.UpdateOutboundCounter(server, bytesToSend);
_startSendingTime = DateTime.Now;
_bytesToSend = bytesToSend;
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), null);
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
IStrategy strategy = controller.GetCurrentStrategy();
strategy?.UpdateLastWrite(server);
}
@@ -651,8 +706,9 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
remote?.EndSend(ar);
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), null);
var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar);
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
}
catch (Exception e)
{
@@ -666,8 +722,9 @@ namespace Shadowsocks.Controller
if (_closed) return;
try
{
var session = (AsyncSession)ar.AsyncState;
connection?.EndSend(ar);
remote?.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), null);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
}
catch (Exception e)
{


+ 11
- 4
shadowsocks-csharp/Controller/Service/UDPRelay.cs View File

@@ -7,7 +7,6 @@ using System.Runtime.CompilerServices;
using Shadowsocks.Controller.Strategy;
using Shadowsocks.Encryption;
using Shadowsocks.Model;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
@@ -57,7 +56,7 @@ namespace Shadowsocks.Controller
private byte[] _buffer = new byte[1500];
private IPEndPoint _localEndPoint;
private EndPoint _remoteEndPoint;
private IPEndPoint _remoteEndPoint;
public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint)
{
@@ -65,8 +64,16 @@ namespace Shadowsocks.Controller
_server = server;
_localEndPoint = localEndPoint;
_remoteEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port);
_remote = SocketUtil.CreateSocket(_remoteEndPoint, ProtocolType.Udp);
// TODO async resolving
IPAddress ipAddress;
bool parsed = IPAddress.TryParse(server.server, out ipAddress);
if (!parsed)
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server);
ipAddress = ipHostInfo.AddressList[0];
}
_remoteEndPoint = new IPEndPoint(ipAddress, server.server_port);
_remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
}
public void Send(byte[] data, int length)


+ 2
- 7
shadowsocks-csharp/Proxy/DirectConnect.cs View File

@@ -55,17 +55,12 @@ namespace Shadowsocks.Proxy
{
DestEndPoint = destEndPoint;

if (_remote == null)
{
_remote = SocketUtil.CreateSocket(destEndPoint);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
}
_remote.BeginConnect(destEndPoint, callback, state);
SocketUtil.BeginConnectTcp(destEndPoint, callback, state);
}

public void EndConnectDest(IAsyncResult asyncResult)
{
_remote?.EndConnect(asyncResult);
_remote = SocketUtil.EndConnectTcp(asyncResult);
}

public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,


+ 2
- 5
shadowsocks-csharp/Proxy/Socks5Proxy.cs View File

@@ -52,16 +52,13 @@ namespace Shadowsocks.Proxy

public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
{
_remote = SocketUtil.CreateSocket(remoteEP);
_remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);

var st = new Socks5State();
st.Callback = callback;
st.AsyncState = state;

ProxyEndPoint = remoteEP;

_remote.BeginConnect(remoteEP, ConnectCallback, st);
SocketUtil.BeginConnectTcp(remoteEP, ConnectCallback, st);
}

public void EndConnectProxy(IAsyncResult asyncResult)
@@ -180,7 +177,7 @@ namespace Shadowsocks.Proxy
var state = (Socks5State) ar.AsyncState;
try
{
_remote.EndConnect(ar);
_remote = SocketUtil.EndConnectTcp(ar);

byte[] handshake = {5, 1, 0};
_remote.BeginSend(handshake, 0, handshake.Length, 0, Socks5HandshakeSendCallback, state);


+ 55
- 19
shadowsocks-csharp/Util/SocketUtil.cs View File

@@ -1,6 +1,7 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Shadowsocks.Util
{
@@ -35,33 +36,68 @@ namespace Shadowsocks.Util
return new DnsEndPoint2(host, port);
}

public static Socket CreateSocket(EndPoint endPoint, ProtocolType protocolType = ProtocolType.Tcp)
private class TcpUserToken : IAsyncResult
{
SocketType socketType;
switch (protocolType)
public AsyncCallback Callback { get; }
public SocketAsyncEventArgs Args { get; }

public TcpUserToken(AsyncCallback callback, object state, SocketAsyncEventArgs args)
{
case ProtocolType.Tcp:
socketType = SocketType.Stream;
break;
case ProtocolType.Udp:
socketType = SocketType.Dgram;
break;
default:
throw new NotSupportedException("Protocol " + protocolType + " doesn't supported!");
Callback = callback;
AsyncState = state;
Args = args;
}

if (endPoint is DnsEndPoint)
{
// use dual-mode socket
var socket = new Socket(AddressFamily.InterNetworkV6, socketType, protocolType);
socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);
public bool IsCompleted { get; } = true;
public WaitHandle AsyncWaitHandle { get; } = null;
public object AsyncState { get; }
public bool CompletedSynchronously { get; } = true;
}

private static void OnTcpConnectCompleted(object sender, SocketAsyncEventArgs args)
{
TcpUserToken token = (TcpUserToken) args.UserToken;

token.Callback(token);
}

public static void BeginConnectTcp(EndPoint endPoint, AsyncCallback callback, object state)
{
var arg = new SocketAsyncEventArgs();
arg.RemoteEndPoint = endPoint;
arg.Completed += OnTcpConnectCompleted;
arg.UserToken = new TcpUserToken(callback, state, arg);


return socket;
Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, arg);
}

public static Socket EndConnectTcp(IAsyncResult asyncResult)
{
var tut = asyncResult as TcpUserToken;
if (tut == null)
{
throw new ArgumentException("Invalid asyncResult.", nameof(asyncResult));
}
else

var arg = tut.Args;

if (arg.SocketError != SocketError.Success)
{
return new Socket(endPoint.AddressFamily, socketType, protocolType);
if (arg.ConnectByNameError != null)
{
throw arg.ConnectByNameError;
}

var ex = new SocketException((int)arg.SocketError);
throw ex;
}

var so = tut.Args.ConnectSocket;

so.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);

return so;
}
}
}

+ 13
- 0
shadowsocks-csharp/app.manifest View File

@@ -13,4 +13,17 @@
<dpiAware>True/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<!-- We specify these, in addition to the UAC above, so we avoid Program Compatibility Assistant in Vista and Win7 -->
<!-- We try to avoid PCA so we can use Windows Job Objects -->
<!-- See http://stackoverflow.com/questions/3342941/kill-child-process-when-parent-process-is-killed -->
<application>
<!--The ID below indicates application support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!--The ID below indicates application support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>

Loading…
Cancel
Save