forked from vpnhood/VpnHood
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUdpProxy.cs
102 lines (85 loc) · 3.39 KB
/
UdpProxy.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
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using VpnHood.Common.Collections;
using VpnHood.Common.Logging;
using VpnHood.Common.Utils;
namespace VpnHood.Tunneling;
internal class UdpProxy : ITimeoutItem
{
private readonly IPacketReceiver _packetReceiver;
private readonly UdpClient _udpClient;
private readonly SemaphoreSlim _sendSemaphore = new(1, 1);
public DateTime LastUsedTime { get; set; }
public IPEndPoint SourceEndPoint { get; }
public bool Disposed { get; private set; }
public IPEndPoint LocalEndPoint { get; }
public UdpProxy(IPacketReceiver packetReceiver, UdpClient udpClient, IPEndPoint sourceEndPoint)
{
_packetReceiver = packetReceiver;
_udpClient = udpClient;
SourceEndPoint = sourceEndPoint;
LastUsedTime = FastDateTime.Now;
LocalEndPoint = (IPEndPoint)udpClient.Client.LocalEndPoint;
// prevent raise exception when there is no listener
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
udpClient.Client.IOControl(-1744830452, new byte[] { 0 }, new byte[] { 0 });
_ = Listen();
}
private bool IsInvalidState(Exception ex)
{
return Disposed || ex is ObjectDisposedException
or SocketException { SocketErrorCode: SocketError.InvalidArgument };
}
public async Task SendPacket(IPEndPoint ipEndPoint, byte[] datagram, bool? noFragment)
{
LastUsedTime = FastDateTime.Now;
try
{
await _sendSemaphore.WaitAsync();
if (VhLogger.IsDiagnoseMode)
VhLogger.Instance.Log(LogLevel.Information, GeneralEventId.Udp,
$"Sending all udp bytes to host. Requested: {datagram.Length}, From: {VhLogger.Format(LocalEndPoint)}, To: {VhLogger.Format(ipEndPoint)}");
// IpV4 fragmentation
if (noFragment != null && ipEndPoint.AddressFamily == AddressFamily.InterNetwork)
_udpClient.DontFragment = noFragment.Value; // Never call this for IPv6, it will throw exception for any value
var sentBytes = await _udpClient.SendAsync(datagram, datagram.Length, ipEndPoint);
if (sentBytes != datagram.Length)
VhLogger.Instance.LogWarning(
$"Couldn't send all udp bytes. Requested: {datagram.Length}, Sent: {sentBytes}");
}
catch (Exception ex)
{
VhLogger.Instance.LogWarning(GeneralEventId.Udp,
"Couldn't send a udp packet. RemoteEp: {RemoteEp}, Exception: {Message}",
VhLogger.Format(ipEndPoint), ex.Message);
if (IsInvalidState(ex))
Dispose();
}
finally
{
_sendSemaphore.Release();
}
}
public async Task Listen()
{
while (!Disposed)
{
var udpResult = await _udpClient.ReceiveAsync();
LastUsedTime = FastDateTime.Now;
// create packet for audience
var ipPacket = PacketUtil.CreateUdpPacket(udpResult.RemoteEndPoint, SourceEndPoint, udpResult.Buffer);
// send packet to audience
await _packetReceiver.OnPacketReceived(ipPacket);
}
}
public void Dispose()
{
Disposed = true;
_udpClient.Dispose();
}
}