@@ -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<Service> _services; | |||
public Listener(IList<Service> 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) | |||
{ | |||
@@ -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); | |||
@@ -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; | |||
} | |||
@@ -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<Listener.Service> services = new List<Listener.Service>(); | |||
services.Add(local); | |||
services.Add(tcpRelay); | |||
services.Add(udpRelay); | |||
services.Add(_pacServer); | |||
services.Add(new PortForwarder(polipoRunner.RunningPort)); | |||
_listener = new Listener(services); | |||
@@ -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); | |||
} | |||
@@ -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 | |||
{ | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -131,6 +131,7 @@ | |||
<Compile Include="Controller\Listener.cs" /> | |||
<Compile Include="Controller\Logging.cs" /> | |||
<Compile Include="Controller\PortForwarder.cs" /> | |||
<Compile Include="Controller\UDPRelay.cs" /> | |||
<Compile Include="Controller\UpdateChecker.cs" /> | |||
<Compile Include="Encryption\EncryptorBase.cs" /> | |||
<Compile Include="Encryption\EncryptorFactory.cs" /> | |||
@@ -156,7 +157,7 @@ | |||
<Compile Include="View\ConfigForm.Designer.cs"> | |||
<DependentUpon>ConfigForm.cs</DependentUpon> | |||
</Compile> | |||
<Compile Include="Controller\Local.cs" /> | |||
<Compile Include="Controller\TCPRelay.cs" /> | |||
<Compile Include="Controller\PolipoRunner.cs" /> | |||
<Compile Include="Program.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||