|
@@ -5,15 +5,18 @@ using Shadowsocks.Encryption; |
|
|
using Shadowsocks.Model;
|
|
|
using Shadowsocks.Model;
|
|
|
using System.Net.Sockets;
|
|
|
using System.Net.Sockets;
|
|
|
using System.Net;
|
|
|
using System.Net;
|
|
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
|
|
|
|
namespace Shadowsocks.Controller
|
|
|
namespace Shadowsocks.Controller
|
|
|
{
|
|
|
{
|
|
|
class UDPRelay : Listener.Service
|
|
|
class UDPRelay : Listener.Service
|
|
|
{
|
|
|
{
|
|
|
private Configuration _config;
|
|
|
private Configuration _config;
|
|
|
|
|
|
private LRUCache<IPEndPoint, UDPHandler> _cache;
|
|
|
public UDPRelay(Configuration config)
|
|
|
public UDPRelay(Configuration config)
|
|
|
{
|
|
|
{
|
|
|
this._config = config;
|
|
|
this._config = config;
|
|
|
|
|
|
this._cache = new LRUCache<IPEndPoint, UDPHandler>(512); // todo: choose a smart number
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
|
|
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
|
|
@@ -27,14 +30,19 @@ namespace Shadowsocks.Controller |
|
|
return false;
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
Listener.UDPState udpState = (Listener.UDPState)state;
|
|
|
Listener.UDPState udpState = (Listener.UDPState)state;
|
|
|
// TODO add cache
|
|
|
|
|
|
UDPHandler handler = new UDPHandler(socket, _config.GetCurrentServer(), (IPEndPoint)udpState.remoteEndPoint);
|
|
|
|
|
|
|
|
|
IPEndPoint remoteEndPoint = (IPEndPoint)udpState.remoteEndPoint;
|
|
|
|
|
|
UDPHandler handler = _cache.get(remoteEndPoint);
|
|
|
|
|
|
if (handler == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
handler = new UDPHandler(socket, _config.GetCurrentServer(), remoteEndPoint);
|
|
|
|
|
|
_cache.add(remoteEndPoint, handler);
|
|
|
|
|
|
}
|
|
|
handler.Send(firstPacket, length);
|
|
|
handler.Send(firstPacket, length);
|
|
|
handler.Receive();
|
|
|
handler.Receive();
|
|
|
return true;
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
class UDPHandler
|
|
|
|
|
|
|
|
|
public class UDPHandler
|
|
|
{
|
|
|
{
|
|
|
private Socket _local;
|
|
|
private Socket _local;
|
|
|
private Socket _remote;
|
|
|
private Socket _remote;
|
|
@@ -47,7 +55,6 @@ namespace Shadowsocks.Controller |
|
|
|
|
|
|
|
|
public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint)
|
|
|
public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint)
|
|
|
{
|
|
|
{
|
|
|
// TODO add timeout
|
|
|
|
|
|
_local = local;
|
|
|
_local = local;
|
|
|
_server = server;
|
|
|
_server = server;
|
|
|
_localEndPoint = localEndPoint;
|
|
|
_localEndPoint = localEndPoint;
|
|
@@ -108,6 +115,85 @@ namespace Shadowsocks.Controller |
|
|
{
|
|
|
{
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void Close()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_remote.Close();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (ObjectDisposedException)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054
|
|
|
|
|
|
class LRUCache<K, V> where V : UDPRelay.UDPHandler
|
|
|
|
|
|
{
|
|
|
|
|
|
private int capacity;
|
|
|
|
|
|
private Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>> cacheMap = new Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>>();
|
|
|
|
|
|
private LinkedList<LRUCacheItem<K, V>> lruList = new LinkedList<LRUCacheItem<K, V>>();
|
|
|
|
|
|
|
|
|
|
|
|
public LRUCache(int capacity)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.capacity = capacity;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.Synchronized)]
|
|
|
|
|
|
public V get(K key)
|
|
|
|
|
|
{
|
|
|
|
|
|
LinkedListNode<LRUCacheItem<K, V>> node;
|
|
|
|
|
|
if (cacheMap.TryGetValue(key, out node))
|
|
|
|
|
|
{
|
|
|
|
|
|
V value = node.Value.value;
|
|
|
|
|
|
lruList.Remove(node);
|
|
|
|
|
|
lruList.AddLast(node);
|
|
|
|
|
|
return value;
|
|
|
|
|
|
}
|
|
|
|
|
|
return default(V);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.Synchronized)]
|
|
|
|
|
|
public void add(K key, V val)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cacheMap.Count >= capacity)
|
|
|
|
|
|
{
|
|
|
|
|
|
RemoveFirst();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LRUCacheItem<K, V> cacheItem = new LRUCacheItem<K, V>(key, val);
|
|
|
|
|
|
LinkedListNode<LRUCacheItem<K, V>> node = new LinkedListNode<LRUCacheItem<K, V>>(cacheItem);
|
|
|
|
|
|
lruList.AddLast(node);
|
|
|
|
|
|
cacheMap.Add(key, node);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void RemoveFirst()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Remove from LRUPriority
|
|
|
|
|
|
LinkedListNode<LRUCacheItem<K, V>> node = lruList.First;
|
|
|
|
|
|
lruList.RemoveFirst();
|
|
|
|
|
|
|
|
|
|
|
|
// Remove from cache
|
|
|
|
|
|
cacheMap.Remove(node.Value.key);
|
|
|
|
|
|
node.Value.value.Close();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class LRUCacheItem<K, V>
|
|
|
|
|
|
{
|
|
|
|
|
|
public LRUCacheItem(K k, V v)
|
|
|
|
|
|
{
|
|
|
|
|
|
key = k;
|
|
|
|
|
|
value = v;
|
|
|
|
|
|
}
|
|
|
|
|
|
public K key;
|
|
|
|
|
|
public V value;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
}
|
|
|
}
|