diff --git a/shadowsocks-csharp/Controller/Listener.cs b/shadowsocks-csharp/Controller/Listener.cs new file mode 100755 index 00000000..923bc0e7 --- /dev/null +++ b/shadowsocks-csharp/Controller/Listener.cs @@ -0,0 +1,143 @@ +using Shadowsocks.Model; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Shadowsocks.Controller +{ + public class Listener + { + public interface Service + { + bool Handle(byte[] firstPacket, int length, Socket socket); + } + + Configuration _config; + bool _shareOverLAN; + Socket _socket; + IList _services; + + public Listener(IList services) + { + this._services = services; + } + + public void Start(Configuration config) + { + this._config = config; + this._shareOverLAN = config.shareOverLan; + try + { + // Create a TCP/IP socket. + _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + IPEndPoint localEndPoint = null; + if (_shareOverLAN) + { + localEndPoint = new IPEndPoint(IPAddress.Any, _config.localPort); + } + else + { + localEndPoint = new IPEndPoint(IPAddress.Loopback, _config.localPort); + } + + // Bind the socket to the local endpoint and listen for incoming connections. + _socket.Bind(localEndPoint); + _socket.Listen(1024); + + + // Start an asynchronous socket to listen for connections. + Console.WriteLine("Shadowsocks started"); + _socket.BeginAccept( + new AsyncCallback(AcceptCallback), + _socket); + } + catch (SocketException) + { + _socket.Close(); + throw; + } + } + + public void Stop() + { + if (_socket != null) + { + _socket.Close(); + _socket = null; + } + } + + public void AcceptCallback(IAsyncResult ar) + { + Socket listener = (Socket)ar.AsyncState; + try + { + Socket conn = listener.EndAccept(ar); + + byte[] buf = new byte[4096]; + object[] state = new object[] { + conn, + buf + }; + + conn.BeginReceive(buf, 0, buf.Length, 0, + new AsyncCallback(ReceiveCallback), state); + } + catch (ObjectDisposedException) + { + } + catch (Exception e) + { + Console.WriteLine(e); + } + finally + { + try + { + listener.BeginAccept( + new AsyncCallback(AcceptCallback), + listener); + } + catch (ObjectDisposedException) + { + // do nothing + } + catch (Exception e) + { + Logging.LogUsefulException(e); + } + } + } + + + private void ReceiveCallback(IAsyncResult ar) + { + object[] state = (object[])ar.AsyncState; + + Socket conn = (Socket)state[0]; + byte[] buf = (byte[])state[1]; + try + { + int bytesRead = conn.EndReceive(ar); + foreach (Service service in _services) + { + if (service.Handle(buf, bytesRead, conn)) + { + return; + } + } + // no service found for this + // shouldn't happen + conn.Close(); + } + catch (Exception e) + { + Console.WriteLine(e); + conn.Close(); + } + } + } +} diff --git a/shadowsocks-csharp/Controller/PortForwarder.cs b/shadowsocks-csharp/Controller/PortForwarder.cs new file mode 100755 index 00000000..ce7d1048 --- /dev/null +++ b/shadowsocks-csharp/Controller/PortForwarder.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Shadowsocks.Controller +{ + class PortForwarder : Listener.Service + { + int _targetPort; + + public PortForwarder(int targetPort) + { + this._targetPort = targetPort; + } + + public bool Handle(byte[] firstPacket, int length, Socket socket) + { + new Handler().Start(firstPacket, length, socket, this._targetPort); + return true; + } + + class Handler + { + private byte[] _firstPacket; + private int _firstPacketLength; + private Socket _local; + private Socket _remote; + private bool _closed = false; + private bool _localShutdown = false; + private bool _remoteShutdown = false; + public const int RecvSize = 16384; + // remote receive buffer + private byte[] remoteRecvBuffer = new byte[RecvSize]; + // connection receive buffer + private byte[] connetionRecvBuffer = new byte[RecvSize]; + + public void Start(byte[] firstPacket, int length, Socket socket, int targetPort) + { + this._firstPacket = firstPacket; + this._firstPacketLength = length; + this._local = socket; + try + { + // TODO async resolving + IPAddress ipAddress; + bool parsed = IPAddress.TryParse("127.0.0.1", out ipAddress); + IPEndPoint remoteEP = new IPEndPoint(ipAddress, targetPort); + + + _remote = new Socket(ipAddress.AddressFamily, + SocketType.Stream, ProtocolType.Tcp); + _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + + // Connect to the remote endpoint. + _remote.BeginConnect(remoteEP, + new AsyncCallback(ConnectCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void ConnectCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + _remote.EndConnect(ar); + HandshakeReceive(); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void HandshakeReceive() + { + if (_closed) + { + return; + } + try + { + _remote.BeginSend(_firstPacket, 0, _firstPacketLength, 0, new AsyncCallback(StartPipe), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + + private void StartPipe(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + _remote.EndSend(ar); + _remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeRemoteReceiveCallback), null); + _local.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeConnectionReceiveCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeRemoteReceiveCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + int bytesRead = _remote.EndReceive(ar); + + if (bytesRead > 0) + { + int bytesToSend; + _local.BeginSend(remoteRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeConnectionSendCallback), null); + } + else + { + //Console.WriteLine("bytesRead: " + bytesRead.ToString()); + _local.Shutdown(SocketShutdown.Send); + _localShutdown = true; + CheckClose(); + } + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeConnectionReceiveCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + int bytesRead = _local.EndReceive(ar); + + if (bytesRead > 0) + { + _remote.BeginSend(connetionRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeRemoteSendCallback), null); + } + else + { + _remote.Shutdown(SocketShutdown.Send); + _remoteShutdown = true; + CheckClose(); + } + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeRemoteSendCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + _remote.EndSend(ar); + _local.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeConnectionReceiveCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeConnectionSendCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + _local.EndSend(ar); + _remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeRemoteReceiveCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void CheckClose() + { + if (_localShutdown && _remoteShutdown) + { + this.Close(); + } + } + + public void Close() + { + lock (this) + { + if (_closed) + { + return; + } + _closed = true; + } + if (_local != null) + { + try + { + _local.Shutdown(SocketShutdown.Both); + _local.Close(); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + } + } + if (_remote != null) + { + try + { + _remote.Shutdown(SocketShutdown.Both); + _remote.Close(); + } + catch (SocketException e) + { + Logging.LogUsefulException(e); + } + } + } + } + } +} diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 06f04f61..fde74419 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -221,6 +221,7 @@ namespace Shadowsocks.Controller List services = new List(); services.Add(local); services.Add(_pacServer); + services.Add(new PortForwarder(8123)); _listener = new Listener(services); _listener.Start(_config); } diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index e22a9fbd..3d51fabb 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -129,6 +129,7 @@ +