forked from vpnhood/VpnHood
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWinDivertPacketCapture.cs
196 lines (166 loc) · 7.57 KB
/
WinDivertPacketCapture.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
using Microsoft.Extensions.Logging;
using PacketDotNet;
using SharpPcap.WinDivert;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Threading;
using VpnHood.Logging;
namespace VpnHood.Client.Device.WinDivert
{
public class WinDivertPacketCapture : IPacketCapture
{
private IpNetwork[]? _includeNetworks;
private WinDivertHeader? _lastCaptureHeader;
protected readonly SharpPcap.WinDivert.WinDivertDevice _device;
public event EventHandler<PacketReceivedEventArgs>? OnPacketReceivedFromInbound;
public event EventHandler? OnStopped;
public bool Started => _device.Started;
public virtual bool CanSendPacketToOutbound => true;
public virtual bool IsDnsServersSupported => false;
public virtual IPAddress[]? DnsServers { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public virtual bool CanProtectSocket => false;
public virtual void ProtectSocket(System.Net.Sockets.Socket socket) => throw new NotSupportedException($"{nameof(ProcessPacketReceivedFromInbound)} is not supported by {nameof(WinDivertDevice)}");
private static void SetWinDivertDllFolder()
{
// I got sick trying to add it to nuget ad anative library in (x86/x64) folder, OOF!
var tempLibFolder = Path.Combine(Path.GetTempPath(), "VpnHood-WinDivertDevice");
var dllFolderPath = Environment.Is64BitOperatingSystem ? Path.Combine(tempLibFolder, "x64") : Path.Combine(tempLibFolder, "x86");
var requiredFiles = Environment.Is64BitOperatingSystem
? new string[] { "WinDivert.dll", "WinDivert64.sys" }
: new string[] { "WinDivert.dll", "WinDivert32.sys", "WinDivert64.sys" };
// extract WinDivert
var checkFiles = requiredFiles.Select(x => Path.Combine(dllFolderPath, x));
if (checkFiles.Any(x => !File.Exists(x)))
{
using var memStream = new MemoryStream(Resource.WinDivertLibZip);
using var zipArchive = new ZipArchive(memStream);
zipArchive.ExtractToDirectory(tempLibFolder, true);
}
// set dll folder
var path = Environment.GetEnvironmentVariable("PATH");
if (path.IndexOf(dllFolderPath + ";") == -1)
Environment.SetEnvironmentVariable("PATH", dllFolderPath + ";" + path);
}
private readonly EventWaitHandle _newPacketEvent = new(false, EventResetMode.AutoReset);
public WinDivertPacketCapture()
{
SetWinDivertDllFolder();
// initialize devices
_device = new SharpPcap.WinDivert.WinDivertDevice { Flags = 0 };
_device.OnPacketArrival += Device_OnPacketArrival;
}
private void Device_OnPacketArrival(object sender, SharpPcap.PacketCapture e)
{
var rawPacket = e.GetPacket();
var packet = Packet.ParsePacket(rawPacket.LinkLayerType, rawPacket.Data);
var ipPacket = packet.Extract<IPPacket>();
_lastCaptureHeader = (WinDivertHeader)e.Header;
ProcessPacketReceivedFromInbound(ipPacket);
}
private readonly IPPacket[] _receivedPackets = new IPPacket[1];
protected virtual void ProcessPacketReceivedFromInbound(IPPacket ipPacket)
{
try
{
_receivedPackets[0] = ipPacket;
var eventArgs = new PacketReceivedEventArgs(_receivedPackets, this);
OnPacketReceivedFromInbound?.Invoke(this, eventArgs);
}
catch (Exception ex)
{
VhLogger.Instance.Log(LogLevel.Error, $"Error in processing packet {ipPacket}! Error: {ex}");
}
}
public void SendPacketToInbound(IEnumerable<IPPacket> ipPackets)
{
foreach (var ipPacket in ipPackets)
SendPacket(ipPacket, false);
}
public void SendPacketToInbound(IPPacket ipPacket)
=> SendPacket(ipPacket, false);
public void SendPacketToOutbound(IPPacket ipPacket)
=> SendPacket(ipPacket, true);
public void SendPacketToOutbound(IEnumerable<IPPacket> ipPackets)
{
foreach (var ipPacket in ipPackets)
SendPacket(ipPacket, true);
}
private void SendPacket(IPPacket ipPacket, bool outbound)
{
if (_lastCaptureHeader == null)
throw new InvalidOperationException("Could not send any data without receiving a packet!");
// send by a device
_lastCaptureHeader.Flags = outbound ? WinDivertPacketFlags.Outbound : 0;
_device.SendPacket(ipPacket.Bytes, _lastCaptureHeader);
}
public IpNetwork[]? IncludeNetworks
{
get => _includeNetworks;
set
{
if (Started)
throw new InvalidOperationException($"Can't set {nameof(IncludeNetworks)} when {nameof(WinDivertPacketCapture)} is started!");
_includeNetworks = value;
}
}
#region Applications Filter
public bool CanExcludeApps => false;
public bool CanIncludeApps => false;
public string[]? ExcludeApps { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public string[]? IncludeApps { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public bool IsMtuSupported => false;
public int Mtu { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
#endregion
public void StartCapture()
{
if (Started)
throw new InvalidOperationException("Device has been already started!");
// create include and exclude phrases
var phraseX = "true";
if (IncludeNetworks != null)
{
var ipRanges = IpNetwork.ToIpRange(IncludeNetworks);
var phrases = ipRanges.Select(x => x.FirstIpAddress.Equals(x.LastIpAddress)
? $"ip.DstAddr=={x.FirstIpAddress}"
: $"(ip.DstAddr>={x.FirstIpAddress} and ip.DstAddr<={x.LastIpAddress})");
var phrase = string.Join(" or ", phrases);
phraseX += $" and ({phrase})";
}
// add outbound; filter loopback
var filter = $"ip and outbound and !loopback and (udp.DstPort==53 or ({phraseX}))";
try
{
_device.Filter = filter;
_device.Open(new SharpPcap.DeviceConfiguration());
_device.StartCapture();
}
catch (Exception ex)
{
if (ex.Message.IndexOf("access is denied", StringComparison.OrdinalIgnoreCase) >= 0)
throw new Exception("Access denied! Could not open WinDivert driver! Make sure the app is running with admin privilege.", ex);
throw;
}
}
public void StopCapture()
{
if (!Started)
return;
_device.StopCapture();
OnStopped?.Invoke(this, EventArgs.Empty);
}
private bool _disposed;
public void Dispose()
{
if (!_disposed)
{
StopCapture();
_device.Dispose();
_disposed = true;
}
}
}
}