add UDPRelay
This commit is contained in:
@@ -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)
|
||||
{
|
||||
_socket.Close();
|
||||
_socket = 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)
|
||||
{
|
||||
}
|
||||
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" />
|
||||
|
||||
Reference in New Issue
Block a user