forked from shadowsocks/shadowsocks-windows
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUDPListener.cs
107 lines (93 loc) · 3.79 KB
/
UDPListener.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
using Splat;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace Shadowsocks.Net
{
public interface IDatagramService
{
public abstract Task< bool> Handle(Memory<byte> packet, Socket socket, EndPoint client);
void Stop();
}
public abstract class DatagramService : IDatagramService
{
public abstract Task<bool> Handle(Memory<byte> packet, Socket socket, EndPoint client);
public virtual void Stop() { }
}
public class UDPListener : IEnableLogger
{
public class UDPState
{
public UDPState(Socket s)
{
socket = s;
remoteEndPoint = new IPEndPoint(s.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0);
}
public Socket socket;
public byte[] buffer = new byte[4096];
public EndPoint remoteEndPoint;
}
IPEndPoint _localEndPoint;
Socket _udpSocket;
IEnumerable<IDatagramService> _services;
CancellationTokenSource tokenSource = new CancellationTokenSource();
public UDPListener(IPEndPoint localEndPoint, IEnumerable<IDatagramService> services)
{
_localEndPoint = localEndPoint;
_services = services;
}
private bool CheckIfPortInUse(int port)
{
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
return ipProperties.GetActiveUdpListeners().Any(endPoint => endPoint.Port == port);
}
public void Start()
{
if (CheckIfPortInUse(_localEndPoint.Port))
throw new Exception($"Port {_localEndPoint.Port} already in use");
// Create a TCP/IP socket.
_udpSocket = new Socket(_localEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_udpSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
// Bind the socket to the local endpoint and listen for incoming connections.
_udpSocket.Bind(_localEndPoint);
// Start an asynchronous socket to listen for connections.
this.Log().Info($"Shadowsocks started UDP");
this.Log().Debug(Crypto.CryptoFactory.DumpRegisteredEncryptor());
UDPState udpState = new UDPState(_udpSocket);
// _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState);
Task.Run(() => WorkLoop(tokenSource.Token));
}
private async Task WorkLoop(CancellationToken token)
{
byte[] buffer = new byte[4096];
EndPoint remote = new IPEndPoint(_udpSocket.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0);
while (!token.IsCancellationRequested)
{
var result = await _udpSocket.ReceiveFromAsync(buffer, SocketFlags.None, remote);
var len = result.ReceivedBytes;
foreach (IDatagramService service in _services)
{
if (await service.Handle(new Memory<byte>(buffer)[..len], _udpSocket, result.RemoteEndPoint))
{
break;
}
}
}
}
public void Stop()
{
tokenSource.Cancel();
_udpSocket?.Close();
foreach (var s in _services)
{
s.Stop();
}
}
}
}