diff --git a/shadowsocks-csharp/Controller/Listener.cs b/shadowsocks-csharp/Controller/Listener.cs index 9943e536..1cf7cb97 100755 --- a/shadowsocks-csharp/Controller/Listener.cs +++ b/shadowsocks-csharp/Controller/Listener.cs @@ -12,12 +12,19 @@ namespace Shadowsocks.Controller { public interface Service { - bool Handle(byte[] firstPacket, int length, Socket socket); + bool Handle(byte[] firstPacket, int length, Socket socket, object state); + } + + public class UDPState + { + public byte[] buffer = new byte[4096]; + public EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); } Configuration _config; bool _shareOverLAN; - Socket _socket; + Socket _tcpSocket; + Socket _udpSocket; IList _services; public Listener(IList services) @@ -51,8 +58,10 @@ namespace Shadowsocks.Controller try { // Create a TCP/IP socket. - _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + _tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + _tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); IPEndPoint localEndPoint = null; if (_shareOverLAN) { @@ -64,29 +73,73 @@ namespace Shadowsocks.Controller } // Bind the socket to the local endpoint and listen for incoming connections. - _socket.Bind(localEndPoint); - _socket.Listen(1024); + _tcpSocket.Bind(localEndPoint); + _udpSocket.Bind(localEndPoint); + _tcpSocket.Listen(1024); // Start an asynchronous socket to listen for connections. Console.WriteLine("Shadowsocks started"); - _socket.BeginAccept( + _tcpSocket.BeginAccept( new AsyncCallback(AcceptCallback), - _socket); + _tcpSocket); + UDPState udpState = new UDPState(); + _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); } catch (SocketException) { - _socket.Close(); + _tcpSocket.Close(); throw; } } public void Stop() { - if (_socket != null) + if (_tcpSocket != null) + { + _tcpSocket.Close(); + _tcpSocket = null; + } + if (_udpSocket != null) + { + _udpSocket.Close(); + _udpSocket = null; + } + } + + public void RecvFromCallback(IAsyncResult ar) + { + UDPState state = (UDPState)ar.AsyncState; + try + { + int bytesRead = _udpSocket.EndReceiveFrom(ar, ref state.remoteEndPoint); + foreach (Service service in _services) + { + if (service.Handle(state.buffer, bytesRead, _udpSocket, state)) + { + break; + } + } + } + catch (ObjectDisposedException) { - _socket.Close(); - _socket = null; + } + catch (Exception e) + { + } + finally + { + try + { + _udpSocket.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, 0, ref state.remoteEndPoint, new AsyncCallback(RecvFromCallback), state); + } + catch (ObjectDisposedException) + { + // do nothing + } + catch (Exception e) + { + } } } @@ -144,14 +197,16 @@ namespace Shadowsocks.Controller int bytesRead = conn.EndReceive(ar); foreach (Service service in _services) { - if (service.Handle(buf, bytesRead, conn)) + if (service.Handle(buf, bytesRead, conn, null)) { return; } } // no service found for this - // shouldn't happen - conn.Close(); + if (conn.ProtocolType == ProtocolType.Tcp) + { + conn.Close(); + } } catch (Exception e) { diff --git a/shadowsocks-csharp/Controller/PACServer.cs b/shadowsocks-csharp/Controller/PACServer.cs index e193d9b4..038ead8c 100755 --- a/shadowsocks-csharp/Controller/PACServer.cs +++ b/shadowsocks-csharp/Controller/PACServer.cs @@ -33,8 +33,12 @@ namespace Shadowsocks.Controller this._config = config; } - public bool Handle(byte[] firstPacket, int length, Socket socket) + public bool Handle(byte[] firstPacket, int length, Socket socket, object state) { + if (socket.ProtocolType != ProtocolType.Tcp) + { + return false; + } try { string request = Encoding.UTF8.GetString(firstPacket, 0, length); diff --git a/shadowsocks-csharp/Controller/PortForwarder.cs b/shadowsocks-csharp/Controller/PortForwarder.cs index 1057143f..e0b05aea 100755 --- a/shadowsocks-csharp/Controller/PortForwarder.cs +++ b/shadowsocks-csharp/Controller/PortForwarder.cs @@ -15,8 +15,12 @@ namespace Shadowsocks.Controller this._targetPort = targetPort; } - public bool Handle(byte[] firstPacket, int length, Socket socket) + public bool Handle(byte[] firstPacket, int length, Socket socket, object state) { + if (socket.ProtocolType != ProtocolType.Tcp) + { + return false; + } new Handler().Start(firstPacket, length, socket, this._targetPort); return true; } diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 734f27d1..0eb57c94 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -250,9 +250,11 @@ namespace Shadowsocks.Controller { polipoRunner.Start(_config); - Local local = new Local(_config); + TCPRelay tcpRelay = new TCPRelay(_config); + UDPRelay udpRelay = new UDPRelay(_config); List services = new List(); - services.Add(local); + services.Add(tcpRelay); + services.Add(udpRelay); services.Add(_pacServer); services.Add(new PortForwarder(polipoRunner.RunningPort)); _listener = new Listener(services); diff --git a/shadowsocks-csharp/Controller/Local.cs b/shadowsocks-csharp/Controller/TCPRelay.cs similarity index 94% rename from shadowsocks-csharp/Controller/Local.cs rename to shadowsocks-csharp/Controller/TCPRelay.cs index 0c62b5f8..610d28b7 100755 --- a/shadowsocks-csharp/Controller/Local.cs +++ b/shadowsocks-csharp/Controller/TCPRelay.cs @@ -9,16 +9,20 @@ using Shadowsocks.Model; namespace Shadowsocks.Controller { - class Local : Listener.Service + class TCPRelay : Listener.Service { private Configuration _config; - public Local(Configuration config) + public TCPRelay(Configuration config) { this._config = config; } - public bool Handle(byte[] firstPacket, int length, Socket socket) + public bool Handle(byte[] firstPacket, int length, Socket socket, object state) { + if (socket.ProtocolType != ProtocolType.Tcp) + { + return false; + } if (length < 2 || firstPacket[0] != 5) { return false; @@ -238,8 +242,8 @@ namespace Shadowsocks.Controller response[3] = 4; } address.CopyTo(response, 4); - response[response.Length - 2] = (byte)(port & 0xFF); - response[response.Length - 1] = (byte)((port >> 8) & 0xFF); + response[response.Length - 1] = (byte)(port & 0xFF); + response[response.Length - 2] = (byte)((port >> 8) & 0xFF); connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ReadAll), true); } diff --git a/shadowsocks-csharp/Controller/UDPRelay.cs b/shadowsocks-csharp/Controller/UDPRelay.cs new file mode 100644 index 00000000..afa25aa2 --- /dev/null +++ b/shadowsocks-csharp/Controller/UDPRelay.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Shadowsocks.Encryption; +using Shadowsocks.Model; +using System.Net.Sockets; +using System.Net; + +namespace Shadowsocks.Controller +{ + class UDPRelay : Listener.Service + { + private Configuration _config; + public UDPRelay(Configuration config) + { + this._config = config; + } + + public bool Handle(byte[] firstPacket, int length, Socket socket, object state) + { + if (socket.ProtocolType != ProtocolType.Udp) + { + return false; + } + if (length < 4) + { + return false; + } + Listener.UDPState udpState = (Listener.UDPState)state; + // TODO add cache + UDPHandler handler = new UDPHandler(socket, _config.GetCurrentServer(), (IPEndPoint)udpState.remoteEndPoint); + handler.Send(firstPacket, length); + handler.Receive(); + return true; + } + + class UDPHandler + { + private Socket _local; + private Socket _remote; + + private Server _server; + private byte[] _buffer = new byte[1500]; + + private IPEndPoint _localEndPoint; + private IPEndPoint _remoteEndPoint; + + public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint) + { + // TODO add timeout + _local = local; + _server = server; + _localEndPoint = localEndPoint; + + // 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) + { + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); + byte[] dataIn = new byte[length - 3]; + Array.Copy(data, 3, dataIn, 0, length - 3); + byte[] dataOut = new byte[length - 3 + 16]; + int outlen; + encryptor.Encrypt(dataIn, dataIn.Length, dataOut, out outlen); + _remote.SendTo(dataOut, _remoteEndPoint); + } + public void Receive() + { + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + _remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); + } + public void RecvFromCallback(IAsyncResult ar) + { + try + { + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + int bytesRead = _remote.EndReceiveFrom(ar, ref remoteEndPoint); + + byte[] dataOut = new byte[bytesRead]; + int outlen; + + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); + encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen); + + byte[] sendBuf = new byte[outlen + 3]; + Array.Copy(dataOut, 0, sendBuf, 3, outlen); + + _local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint); + Receive(); + } + catch (ObjectDisposedException) + { + } + catch (Exception e) + { + } + finally + { + } + } + } + } +} diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index aa2c9000..fc87e606 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -131,6 +131,7 @@ + @@ -156,7 +157,7 @@ ConfigForm.cs - +