├── .gitattributes
├── .gitignore
├── App
├── App.csproj
├── Icmp
│ ├── FastPinger.cs
│ ├── PingPacket.cs
│ └── RouteTracer.cs
├── Program.cs
└── app.manifest
├── README.md
├── WindivertDotnet.Test
├── FilterTest.cs
├── HeaderSizeTest.cs
├── IPHeaderTest.cs
├── IcmpV4HeaderTest.cs
├── IcmpV6HeaderTest.cs
├── TcpHeaderTest.cs
├── UdpHeaderTest.cs
├── WinDivertAddressTest.cs
├── WinDivertBufferWriterTest.cs
├── WinDivertPacketExtensionsTest.cs
├── WinDivertPacketTest.cs
├── WindivertDotnet.Test.csproj
└── WindivertRouterTest.cs
├── WindivertDotnet.sln
├── WindivertDotnet
├── ChecksumsFlag.cs
├── Event.cs
├── Filter.cs
├── FragmentFlag.cs
├── IFilter.cs
├── IPV4Header.cs
├── IPV6Header.cs
├── IPVersion.cs
├── IcmpV4Header.cs
├── IcmpV4MessageType.cs
├── IcmpV4UnreachableCode.cs
├── IcmpV6Header.cs
├── IcmpV6MessageType.cs
├── IcmpV6UnreachableCode.cs
├── IdSeqNum.cs
├── Runtime
│ ├── InteropServices
│ │ ├── IPHelpApiNative.cs
│ │ ├── Kernel32Native.cs
│ │ ├── MemoryNative.cs
│ │ ├── WinDivertNative.Loader.cs
│ │ └── WinDivertNative.cs
│ └── Versioning
│ │ └── SupportedOSPlatformAttribute.cs
├── SockAddress.cs
├── TcpFlag.cs
├── TcpHeader.cs
├── UdpHeader.cs
├── WinDivert.cs
├── WinDivertAddress.cs
├── WinDivertAddressFlag.cs
├── WinDivertDataFlow.cs
├── WinDivertDataNetwork.cs
├── WinDivertDataReflect.cs
├── WinDivertDataSocket.cs
├── WinDivertEvent.cs
├── WinDivertFlag.cs
├── WinDivertLayer.cs
├── WinDivertOperation.cs
├── WinDivertPacket.cs
├── WinDivertParam.cs
├── WinDivertParseResult.cs
├── WinDivertRecvOperation.cs
├── WinDivertRouter.cs
├── WinDivertSendOperation.cs
├── WinDivertShutdown.cs
├── WindivertBufferWriter.cs
├── WindivertDotnet.csproj
└── v222
│ ├── x64
│ ├── WinDivert.dll
│ └── WinDivert64.sys
│ └── x86
│ ├── WinDivert.dll
│ ├── WinDivert32.sys
│ └── WinDivert64.sys
├── icon.png
└── license
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/App/App.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0-windows
6 | enable
7 | True
8 | win-x64
9 | app.manifest
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/App/Icmp/FastPinger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Collections.Generic;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using WindivertDotnet;
10 |
11 | namespace App.Icmp
12 | {
13 |
14 | ///
15 | /// 快速ping工具
16 | ///
17 | static class FastPinger
18 | {
19 | private record SrcAddrSeqNum(IPAddress SrcAddress, ushort SeqNum);
20 |
21 | ///
22 | /// Ping所有地址
23 | ///
24 | /// 开始地址
25 | /// IP数量
26 | /// 最后一个IP发出ping之后的等待回复时长
27 | ///
28 | ///
29 | public static Task PingAllAsync(IPAddress startAddr, int count, TimeSpan waitTime, CancellationToken cancellationToken = default)
30 | {
31 | var dstAddrs = CreateAddrs(startAddr, count);
32 | return PingAllAsync(dstAddrs, waitTime, cancellationToken);
33 | }
34 |
35 | ///
36 | /// 创建IP列表
37 | ///
38 | ///
39 | ///
40 | ///
41 | private static IEnumerable CreateAddrs(IPAddress startAddr, int count)
42 | {
43 | var bytes = startAddr.GetAddressBytes();
44 | var start = BinaryPrimitives.ReadUInt32BigEndian(bytes.AsSpan(bytes.Length - 4));
45 |
46 | for (var i = 0; i < count; i++)
47 | {
48 | var value = (uint)(start + i);
49 | BinaryPrimitives.WriteUInt32BigEndian(bytes.AsSpan(bytes.Length - 4), value);
50 | yield return new IPAddress(bytes);
51 | }
52 | }
53 |
54 | ///
55 | /// Ping所有地址
56 | ///
57 | /// 目标地址
58 | /// 最后一个IP发出ping之后的等待回复时长
59 | ///
60 | ///
61 | public static async Task PingAllAsync(IEnumerable dstAddrs, TimeSpan waitTime, CancellationToken cancellationToken = default)
62 | {
63 | var filter = Filter.True
64 | .And(f => f.Network.Inbound)
65 | .And(f => f.ICmp.Type == IcmpV4MessageType.EchoReply || f.IcmpV6.Type == IcmpV6MessageType.EchoReply);
66 |
67 | using var divert = new WinDivert(filter, WinDivertLayer.Network);
68 |
69 | // 开始监听ping的回复
70 | using var waitTokenSource = new CancellationTokenSource();
71 | using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(waitTokenSource.Token, cancellationToken);
72 | var recvTask = RecvRepliesAsync(divert, linkedTokenSource.Token);
73 | var seqNums = await SendPingsAsync(divert, dstAddrs, cancellationToken);
74 |
75 | // 延时取消监听
76 | waitTokenSource.CancelAfter(waitTime);
77 | var results = await recvTask;
78 |
79 | return results
80 | .Where(item => seqNums.Contains(item.SeqNum))
81 | .Select(item => item.SrcAddress)
82 | .Distinct()
83 | .ToArray();
84 | }
85 |
86 |
87 | ///
88 | /// 接收回复信息
89 | ///
90 | ///
91 | ///
92 | ///
93 | private async static Task> RecvRepliesAsync(WinDivert divert, CancellationToken cancellationToken)
94 | {
95 | var results = new List();
96 | using var packet = new WinDivertPacket();
97 | using var addr = new WinDivertAddress();
98 |
99 | while (cancellationToken.IsCancellationRequested == false)
100 | {
101 | try
102 | {
103 | await divert.RecvAsync(packet, addr, cancellationToken);
104 |
105 | if (TryParseReply(packet, out var srcAddrSeqNum))
106 | {
107 | results.Add(srcAddrSeqNum);
108 | }
109 |
110 | await divert.SendAsync(packet, addr, cancellationToken);
111 | }
112 | catch (OperationCanceledException)
113 | {
114 | break;
115 | }
116 | }
117 |
118 | return results;
119 | }
120 |
121 |
122 | ///
123 | /// 解析出icmp回复信息
124 | ///
125 | ///
126 | ///
127 | ///
128 | private unsafe static bool TryParseReply(WinDivertPacket packet, [MaybeNullWhen(false)] out SrcAddrSeqNum srcAddrSeqNum)
129 | {
130 | var result = packet.GetParseResult();
131 | var srcAddr = result.IPV4Header != null
132 | ? result.IPV4Header->SrcAddr
133 | : result.IPV6Header->SrcAddr;
134 |
135 | if (result.IcmpV4Header != null)
136 | {
137 | srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, result.IcmpV4Header->SequenceNumber);
138 | return true;
139 | }
140 |
141 | if (result.IcmpV6Header != null)
142 | {
143 | srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, result.IcmpV6Header->SequenceNumber);
144 | return true;
145 | }
146 |
147 | srcAddrSeqNum = null;
148 | return false;
149 | }
150 |
151 | ///
152 | /// 发送ping请求
153 | ///
154 | ///
155 | ///
156 | ///
157 | ///
158 | private static async Task> SendPingsAsync(WinDivert divert, IEnumerable dstAddrs, CancellationToken cancellationToken)
159 | {
160 | var seqNums = new HashSet();
161 | foreach (var address in dstAddrs)
162 | {
163 | // 使用router计算将进行通讯的本机地址
164 | var router = new WinDivertRouter(address);
165 | using var addr = router.CreateAddress();
166 |
167 | using var packet = new PingPacket(router);
168 | packet.CalcChecksums(addr);
169 | await divert.SendAsync(packet, addr, cancellationToken);
170 | seqNums.Add(packet.SeqNum);
171 | }
172 | return seqNums;
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/App/Icmp/PingPacket.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Sockets;
2 | using WindivertDotnet;
3 |
4 | namespace App.Icmp
5 | {
6 | ///
7 | /// ping包
8 | ///
9 | unsafe class PingPacket : WinDivertPacket
10 | {
11 | private static readonly IdSeqNum id = new();
12 | private static readonly IdSeqNum seqNum = new();
13 | private static readonly ushort v4PacketLength = (ushort)(sizeof(IPV4Header) + sizeof(IcmpV4Header) + 32);
14 | private static readonly ushort v6PacketLength = (ushort)(sizeof(IPV6Header) + sizeof(IcmpV6Header) + 32);
15 |
16 | public ushort Id { get; } = id.NextUInt16();
17 |
18 | public ushort SeqNum { get; } = seqNum.NextUInt16();
19 |
20 | ///
21 | /// ping包
22 | ///
23 | ///
24 | ///
25 | public PingPacket(WinDivertRouter router, byte ttl = 128)
26 | : base(v6PacketLength)
27 | {
28 | if (router.DstAddress.AddressFamily == AddressFamily.InterNetwork)
29 | {
30 | var ipHeader = new IPV4Header
31 | {
32 | TTL = ttl,
33 | Version = IPVersion.V4,
34 | DstAddr = router.DstAddress,
35 | SrcAddr = router.SrcAddress,
36 | Protocol = ProtocolType.Icmp,
37 | HdrLength = 5,
38 | Id = IdSeqNum.Shared.NextUInt16(),
39 | Length = v4PacketLength
40 | };
41 |
42 | var icmpHeader = new IcmpV4Header
43 | {
44 | Type = IcmpV4MessageType.EchoRequest,
45 | Identifier = Id,
46 | SequenceNumber = SeqNum,
47 | };
48 |
49 | var writer = GetWriter();
50 | writer.Write(ipHeader);
51 | writer.Write(icmpHeader);
52 | writer.Advance(32);
53 | }
54 | else
55 | {
56 | var ipHeader = new IPV6Header
57 | {
58 | SrcAddr = router.SrcAddress,
59 | DstAddr = router.DstAddress,
60 | Length = v6PacketLength,
61 | HopLimit = ttl,
62 | NextHdr = ProtocolType.IcmpV6,
63 | Version = IPVersion.V6
64 | };
65 | var icmpHeader = new IcmpV6Header
66 | {
67 | Type = IcmpV6MessageType.EchoRequest,
68 | Identifier = Id,
69 | SequenceNumber = SeqNum
70 | };
71 |
72 | var writer = GetWriter();
73 | writer.Write(ipHeader);
74 | writer.Write(icmpHeader);
75 | writer.Advance(32);
76 | }
77 | }
78 |
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/App/Icmp/RouteTracer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.NetworkInformation;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using WindivertDotnet;
10 |
11 | namespace App.Icmp
12 | {
13 | ///
14 | /// 路由信息
15 | ///
16 | static class RouteTracer
17 | {
18 | private record SrcAddrSeqNum(IPAddress SrcAddress, ushort SeqNum);
19 |
20 | ///
21 | /// 测试路由
22 | ///
23 | ///
24 | ///
25 | ///
26 | ///
27 | ///
28 | ///
29 | public async static Task TraceAsync(IPAddress dstAddr, byte ttlCount, TimeSpan waitTime, CancellationToken cancellationToken = default)
30 | {
31 | var filter = Filter.True
32 | .And(f => f.Network.Inbound)
33 | .And(f => f.ICmp.Type == IcmpV4MessageType.TimeExceeded || f.IcmpV6.Type == IcmpV6MessageType.TimeExceeded);
34 |
35 | using var divert = new WinDivert(filter, WinDivertLayer.Network);
36 |
37 | // 开始监听ping的回复
38 | using var waitTokenSource = new CancellationTokenSource();
39 | using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(waitTokenSource.Token, cancellationToken);
40 | var recvTask = RecvRepliesAsync(divert, linkedTokenSource.Token);
41 |
42 | // 开始ttl发ping
43 | var ttls = Enumerable.Range(1, ttlCount).Select(i => (byte)i);
44 | var seqNums = await SendPingsAsync(divert, dstAddr, ttls, cancellationToken);
45 |
46 | // 延时取消监听
47 | waitTokenSource.CancelAfter(waitTime);
48 | var results = await recvTask;
49 |
50 | return results
51 | .Where(item => seqNums.Contains(item.SeqNum))
52 | .Select(item => item.SrcAddress)
53 | .Distinct()
54 | .ToArray();
55 | }
56 |
57 | ///
58 | /// 接收回复信息
59 | ///
60 | ///
61 | ///
62 | ///
63 | private async static Task> RecvRepliesAsync(WinDivert divert, CancellationToken cancellationToken)
64 | {
65 | var results = new List();
66 | using var packet = new WinDivertPacket();
67 | using var addr = new WinDivertAddress();
68 |
69 | while (cancellationToken.IsCancellationRequested == false)
70 | {
71 | try
72 | {
73 | await divert.RecvAsync(packet, addr, cancellationToken);
74 |
75 | if (TryParseReply(packet, out var srcAddrSeqNum))
76 | {
77 | results.Add(srcAddrSeqNum);
78 | }
79 |
80 | await divert.SendAsync(packet, addr, cancellationToken);
81 | }
82 | catch (OperationCanceledException)
83 | {
84 | break;
85 | }
86 | }
87 |
88 | return results;
89 | }
90 |
91 | ///
92 | /// 解析出icmp回复信息
93 | ///
94 | ///
95 | ///
96 | ///
97 | private unsafe static bool TryParseReply(WinDivertPacket packet, [MaybeNullWhen(false)] out SrcAddrSeqNum srcAddrSeqNum)
98 | {
99 | var result = packet.GetParseResult();
100 | if (result.DataLength > 0)
101 | {
102 | var srcAddr = result.IPV4Header != null
103 | ? result.IPV4Header->SrcAddr
104 | : result.IPV6Header->SrcAddr;
105 |
106 | var dataPacket = packet.Slice(packet.Length - result.DataLength, result.DataLength);
107 | dataPacket.ApplyLengthToHeaders();
108 |
109 | var dataResult = dataPacket.GetParseResult();
110 | if (dataResult.IcmpV4Header != null)
111 | {
112 | srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, dataResult.IcmpV4Header->SequenceNumber);
113 | return true;
114 | }
115 | if (dataResult.IcmpV6Header != null)
116 | {
117 | srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, dataResult.IcmpV6Header->SequenceNumber);
118 | return true;
119 | }
120 | }
121 |
122 | srcAddrSeqNum = null;
123 | return false;
124 | }
125 |
126 | ///
127 | /// 发送ping请求包
128 | ///
129 | ///
130 | ///
131 | ///
132 | ///
133 | ///
134 | private async static Task> SendPingsAsync(WinDivert divert, IPAddress dstAddr, IEnumerable ttls, CancellationToken cancellationToken)
135 | {
136 | var router = new WinDivertRouter(dstAddr);
137 | using var addr = router.CreateAddress();
138 | var result = new HashSet();
139 | foreach (var ttl in ttls)
140 | {
141 | using var packet = new PingPacket(router, ttl);
142 | packet.CalcChecksums(addr);
143 | await divert.SendAsync(packet, addr, cancellationToken);
144 | result.Add(packet.SeqNum);
145 | }
146 | return result;
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/App/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using WindivertDotnet;
4 |
5 | namespace App
6 | {
7 | internal class Program
8 | {
9 | static async Task Main(string[] args)
10 | {
11 | var filter = Filter.True.And(f => f.IsIP && f.IsTcp);
12 | using var divert = new WinDivert(filter, WinDivertLayer.Network);
13 | using var packet = new WinDivertPacket();
14 | using var addr = new WinDivertAddress();
15 |
16 | while (true)
17 | {
18 | var recvLength = await divert.RecvAsync(packet, addr);
19 | var result = packet.GetParseResult();
20 |
21 | var checkState = packet.CalcChecksums(addr);
22 | var sendLength = await divert.SendAsync(packet, addr);
23 |
24 | Console.WriteLine($"{result.Protocol} {recvLength} {sendLength}");
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/App/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
54 |
62 |
63 |
64 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WindivertDotnet
2 | 面向对象的[WinDivertv2.2](https://github.com/basil00/Divert)的dotnet异步封装,轻松实现网络数据拦截与修改。
3 |
4 | ### 1 nuget
5 | [WindivertDotnet](https://www.nuget.org/packages/WindivertDotnet)
6 | ```
7 |
8 | ```
9 |
10 | ### 2 功能介绍
11 | * 抓取网络数据包
12 | * 过滤或丢弃网络数据包
13 | * 嗅探网络数据包
14 | * 注入网络数据包
15 | * 修改网络数据包
16 |
17 |
18 | ### 3 如何使用
19 | #### 3.1 抓包改包
20 | ```c#
21 | var filter = Filter.True
22 | .And(f => f.Network.Loopback)
23 | .And(f => f.Tcp.DstPort == 443)
24 | .And(f => f.Tcp.Ack == true);
25 |
26 | using var divert = new WinDivert(filter, WinDivertLayer.Network);
27 | using var packet = new WinDivertPacket();
28 | using var addr = new WinDivertAddress();
29 |
30 | while (true)
31 | {
32 | // 读包
33 | await divert.RecvAsync(packet, addr);
34 |
35 | ProcessPacket(packet, addr);
36 |
37 | // 修改后发出
38 | await divert.SendAsync(packet, addr);
39 | }
40 |
41 | static unsafe void ProcessPacket(WinDivertPacket packet, WinDivertAddress addr)
42 | {
43 | // 解包
44 | var result = packet.GetParseResult();
45 |
46 | // 改包
47 | result.TcpHeader->DstPort = 443;
48 |
49 | // 重算checksums
50 | packet.CalcChecksums(addr);
51 | }
52 | ```
53 |
54 | #### 3.2 注入数据包
55 | ```c#
56 | private async Task SendEchoRequestAsync(IPAddress dstAddr)
57 | {
58 | // 使用router计算将进行通讯的本机地址
59 | var router = new WinDivertRouter(dstAddr);
60 | using var addr = router.CreateAddress();
61 | using var packet = this.CreateIPV4EchoPacket(router.SrcAddress, router.DstAddress);
62 |
63 | packet.CalcChecksums(addr); // 计算checksums,因为创建包时没有计算
64 |
65 | await this.divert.SendAsync(packet, addr);
66 | }
67 |
68 | ///
69 | /// 创建icmp的echo包
70 | ///
71 | ///
72 | ///
73 | ///
74 | private unsafe WinDivertPacket CreateIPV4EchoPacket(IPAddress srcAddr, IPAddress dstAddr)
75 | {
76 | // ipv4头
77 | var ipHeader = new IPV4Header
78 | {
79 | TTL = 128,
80 | Version = IPVersion.V4,
81 | DstAddr = dstAddr,
82 | SrcAddr = srcAddr,
83 | Protocol = ProtocolType.Icmp,
84 | HdrLength = (byte)(sizeof(IPV4Header) / 4),
85 | Id = ++this.id,
86 | Length = (ushort)(sizeof(IPV4Header) + sizeof(IcmpV4Header))
87 | };
88 |
89 | // icmp头
90 | var icmpHeader = new IcmpV4Header
91 | {
92 | Type = IcmpV4MessageType.EchoRequest,
93 | Code = default,
94 | Identifier = ipHeader.Id,
95 | SequenceNumber = ++this.sequenceNumber,
96 | };
97 |
98 | // 将数据写到packet缓冲区
99 | var packet = new WinDivertPacket(ipHeader.Length);
100 |
101 | var writer = packet.GetWriter();
102 | writer.Write(ipHeader);
103 | writer.Write(icmpHeader);
104 |
105 | return packet;
106 | }
107 | ```
108 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/FilterTest.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Net.Sockets;
3 | using System.Runtime.Versioning;
4 | using Xunit;
5 |
6 | namespace WindivertDotnet.Test
7 | {
8 | public class FilterTest
9 | {
10 | [Fact]
11 | public void TrueTest()
12 | {
13 | var filter = Filter.True.ToString();
14 | Assert.Equal("true", filter);
15 | }
16 |
17 | [Fact]
18 | public void FlaseTest()
19 | {
20 | var filter = Filter.False.ToString();
21 | Assert.Equal("false", filter);
22 | }
23 |
24 | [Fact]
25 | public void AndExpressionTest()
26 | {
27 | var filter = Filter.True.And(f => false).ToString();
28 | Assert.Equal("false", filter);
29 | }
30 |
31 | [Fact]
32 | public void OrExpressionTest()
33 | {
34 | var filter = Filter.True.Or(f => false).ToString();
35 | Assert.Equal("true", filter);
36 | }
37 |
38 | [Fact]
39 | public void AndFilterTest()
40 | {
41 | var filter = Filter.True.And(Filter.False).ToString();
42 | Assert.Equal("false", filter);
43 | }
44 |
45 | [Fact]
46 | public void OrFilterTest()
47 | {
48 | var filter = Filter.True.Or(Filter.False).ToString();
49 | Assert.Equal("true", filter);
50 | }
51 |
52 | [Fact]
53 | [SupportedOSPlatform("windows")]
54 | public void BaseTest()
55 | {
56 | var filter = Filter.False
57 | .Or(f => f.IsIcmp)
58 | .Or(f => f.IsIcmpV6)
59 | .Or(f => f.IsIP)
60 | .Or(f => f.IsIPV6)
61 | .Or(f => f.IsTcp)
62 | .Or(f => f.IsUdp)
63 | .Or(f => f.Timestamp > 1)
64 | ;
65 |
66 | var f1 = Filter.Compile(filter.ToString(), WinDivertLayer.Network);
67 | var f2 = Filter.Compile(filter.ToString(), WinDivertLayer.Forward);
68 | var f3 = Filter.Compile(filter.ToString(), WinDivertLayer.Socket);
69 |
70 | Assert.True(f1.Length > 0);
71 | Assert.True(f2.Length > 0);
72 | Assert.True(f3.Length > 0);
73 | }
74 |
75 |
76 | [Fact]
77 | [SupportedOSPlatform("windows")]
78 | public void NetworkTest()
79 | {
80 | var filter = Filter.False
81 | .Or(f => f.Event == Event.PACKET)
82 | .Or(f => f.Network.Fragment)
83 | .Or(f => f.Network.Inbound)
84 | .Or(f => f.Network.Length > 0)
85 | .Or(f => f.Network.IfIdx > 0)
86 | .Or(f => f.Network.Impostor)
87 | .Or(f => f.Network.LocalAddr == IPAddress.Loopback.ToString())
88 | .Or(f => f.Network.LocalPort > 0)
89 | .Or(f => f.Network.Loopback)
90 | .Or(f => f.Network.Outbound)
91 | .Or(f => f.Network.Protocol == ProtocolType.Udp)
92 | .Or(f => f.Network.RemoteAddr == IPAddress.Loopback.ToString())
93 | .Or(f => f.Network.RemotePort == 443)
94 | .Or(f => f.Network.SubIfIdx == 12);
95 |
96 | var f = Filter.Compile(filter.ToString(), WinDivertLayer.Network);
97 | Assert.True(f.Length > 0);
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/WindivertDotnet.Test/HeaderSizeTest.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using Xunit;
3 |
4 | namespace WindivertDotnet.Test
5 | {
6 | public class HeaderSizeTest
7 | {
8 | [Fact]
9 | public void IPV4HeaderSizeTest()
10 | {
11 | Assert.Equal(20, Unsafe.SizeOf());
12 | }
13 |
14 | [Fact]
15 | public void IPV6HeaderSizeTest()
16 | {
17 | Assert.Equal(40, Unsafe.SizeOf());
18 | }
19 |
20 | [Fact]
21 | public void IcmpV4HeaderSizeTest()
22 | {
23 | Assert.Equal(8, Unsafe.SizeOf());
24 | }
25 |
26 | [Fact]
27 | public void IcmpV6HeaderSizeTest()
28 | {
29 | Assert.Equal(8, Unsafe.SizeOf());
30 | }
31 |
32 | [Fact]
33 | public void TcpHeaderHeaderSizeTest()
34 | {
35 | Assert.Equal(20, Unsafe.SizeOf());
36 | }
37 |
38 | [Fact]
39 | public void UdpHeaderHeaderSizeTest()
40 | {
41 | Assert.Equal(8, Unsafe.SizeOf());
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/IPHeaderTest.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Net.Security;
3 | using System.Net.Sockets;
4 | using Xunit;
5 |
6 | namespace WindivertDotnet.Test
7 | {
8 | public class IPHeaderTest
9 | {
10 | [Fact]
11 | public unsafe void IPV4ReadTest()
12 | {
13 | var packet = PacketDotNet.IPPacket.RandomPacket(PacketDotNet.IPVersion.IPv4);
14 | fixed (void* ptr = &packet.Bytes[0])
15 | {
16 | var header = *(IPV4Header*)ptr;
17 | Assert.Equal((byte)packet.Version, (byte)header.Version);
18 | Assert.Equal(packet.HeaderLength, header.HdrLength);
19 | Assert.Equal(packet.TotalLength, header.Length);
20 | Assert.Equal(packet.TimeToLive, header.TTL);
21 | Assert.Equal((byte)packet.Protocol, (byte)header.Protocol);
22 | Assert.Equal(packet.SourceAddress, header.SrcAddr);
23 | Assert.Equal(packet.DestinationAddress, header.DstAddr);
24 | }
25 | }
26 |
27 | [Fact]
28 | public unsafe void IPV4WriteTest()
29 | {
30 | var dstAddr = IPAddress.Parse("1.2.3.4");
31 | var srcAddr = IPAddress.Parse("5.6.7.8");
32 |
33 | var header = new IPV4Header
34 | {
35 | Version = IPVersion.V4,
36 | Checksum = 2,
37 | DstAddr = dstAddr,
38 | SrcAddr = srcAddr,
39 | TOS = 3,
40 | HdrLength = 5,
41 | Id = 6,
42 | Length = 7,
43 | Protocol = ProtocolType.Ggp,
44 | TTL = 8,
45 | FragmentFlags = FragmentFlag.MoreFragments,
46 | FragmentOffset = 9,
47 | };
48 |
49 |
50 | Assert.Equal(IPVersion.V4, header.Version);
51 | Assert.Equal(2, header.Checksum);
52 |
53 | Assert.Equal(dstAddr, header.DstAddr);
54 | Assert.Equal(srcAddr, header.SrcAddr);
55 | Assert.Equal(3, header.TOS);
56 | Assert.Equal(5, header.HdrLength);
57 | Assert.Equal(6, header.Id);
58 |
59 | Assert.Equal(7, header.Length);
60 | Assert.Equal(ProtocolType.Ggp, header.Protocol);
61 | Assert.Equal(8, header.TTL);
62 |
63 | Assert.Equal(FragmentFlag.MoreFragments, header.FragmentFlags);
64 | Assert.Equal(9, header.FragmentOffset);
65 | }
66 |
67 | [Fact]
68 | public unsafe void IPV6ReadTest()
69 | {
70 | var packet = PacketDotNet.IPPacket.RandomPacket(PacketDotNet.IPVersion.IPv6);
71 | fixed (void* ptr = &packet.Bytes[0])
72 | {
73 | var header = *(IPV6Header*)ptr;
74 | Assert.Equal((byte)packet.Version, (byte)header.Version);
75 | Assert.Equal(packet.PayloadLength, header.Length);
76 | Assert.Equal(packet.HopLimit, header.HopLimit);
77 | Assert.Equal((byte)packet.Protocol, (byte)header.NextHdr);
78 | Assert.Equal(packet.SourceAddress, header.SrcAddr);
79 | Assert.Equal(packet.DestinationAddress, header.DstAddr);
80 | }
81 | }
82 |
83 | [Fact]
84 | public unsafe void IPV6WriteTest()
85 | {
86 | var dstAddr = IPAddress.Parse("2409:895a:f138:8189:3c42:fd9f:8ee6:4041");
87 | var srcAddr = IPAddress.IPv6Loopback;
88 |
89 | var header = new IPV6Header
90 | {
91 | Version = IPVersion.V6,
92 | FlowLabel0 = 2,
93 | FlowLabel1 = 3,
94 | HopLimit = 4,
95 | Length = 0,
96 | TrafficClass0 = 6,
97 | TrafficClass1 = 7,
98 | DstAddr = dstAddr,
99 | SrcAddr = srcAddr,
100 | NextHdr = ProtocolType.Ggp,
101 |
102 | };
103 |
104 | Assert.Equal(IPVersion.V6, header.Version);
105 | Assert.Equal(2, header.FlowLabel0);
106 |
107 | Assert.Equal(dstAddr, header.DstAddr);
108 | Assert.Equal(srcAddr, header.SrcAddr);
109 | Assert.Equal(3, header.FlowLabel1);
110 | Assert.Equal(4, header.HopLimit);
111 | Assert.Equal(0, header.Length);
112 | Assert.Equal(6, header.TrafficClass0);
113 |
114 | Assert.Equal(7, header.TrafficClass1);
115 | Assert.Equal(ProtocolType.Ggp, header.NextHdr);
116 |
117 | using var packet = new WinDivertPacket();
118 | packet.GetWriter().Write(header);
119 | var r = packet.GetParseResult();
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/IcmpV4HeaderTest.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace WindivertDotnet.Test
4 | {
5 | public unsafe class IcmpV4HeaderTest
6 | {
7 | [Fact]
8 | public void ReadWriteTest()
9 | {
10 | var header = new IcmpV4Header
11 | {
12 | Checksum = 1,
13 | Code = IcmpV4UnreachableCode.PrecedenceCutoffInEffect,
14 | Type = IcmpV4MessageType.TimeExceeded,
15 | Body = 0x20003
16 | };
17 |
18 | Assert.Equal(1, header.Checksum);
19 | Assert.Equal(0x2, header.Identifier);
20 | Assert.Equal(0x3, header.SequenceNumber);
21 | Assert.Equal(0x20003, (int)header.Body);
22 | Assert.Equal(IcmpV4UnreachableCode.PrecedenceCutoffInEffect, header.Code);
23 | Assert.Equal(IcmpV4MessageType.TimeExceeded, header.Type);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/IcmpV6HeaderTest.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace WindivertDotnet.Test
4 | {
5 | public unsafe class IcmpV6HeaderTest
6 | {
7 | [Fact]
8 | public void ReadWriteTest()
9 | {
10 | var header = new IcmpV6Header
11 | {
12 | Checksum = 1,
13 | Code = IcmpV6UnreachableCode.PortUnreachable,
14 | Type = IcmpV6MessageType.EchoRequest,
15 | Body = 0x20003
16 | };
17 |
18 | Assert.Equal(1, header.Checksum);
19 | Assert.Equal(0x2, header.Identifier);
20 | Assert.Equal(0x3, header.SequenceNumber);
21 | Assert.Equal((uint)0x20003, header.Body);
22 | Assert.Equal(IcmpV6UnreachableCode.PortUnreachable, header.Code);
23 | Assert.Equal(IcmpV6MessageType.EchoRequest, header.Type);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/TcpHeaderTest.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace WindivertDotnet.Test
4 | {
5 | public unsafe class TcpHeaderTest
6 | {
7 | [Fact]
8 | public void ReadTest()
9 | {
10 | var packet = PacketDotNet.TcpPacket.RandomPacket();
11 | fixed (void* ptr = &packet.Bytes[0])
12 | {
13 | var header = *(TcpHeader*)ptr;
14 | Assert.Equal(packet.SourcePort, header.SrcPort);
15 | Assert.Equal(packet.Finished, header.Fin);
16 | Assert.Equal(packet.Checksum, header.Checksum);
17 | Assert.Equal(packet.DestinationPort, header.DstPort);
18 | Assert.Equal(packet.Synchronize, header.Syn);
19 | Assert.Equal(packet.SequenceNumber, header.SeqNum);
20 | Assert.Equal(packet.WindowSize, header.Window);
21 | Assert.Equal(packet.Acknowledgment, header.Ack);
22 | Assert.Equal(packet.AcknowledgmentNumber, header.AckNum);
23 | Assert.Equal(packet.Reset, header.Rst);
24 | Assert.Equal(packet.Push, header.Psh);
25 | Assert.Equal(packet.Urgent, header.Urg);
26 | Assert.Equal(packet.UrgentPointer, header.UrgPtr);
27 | }
28 | }
29 |
30 | [Fact]
31 | public void WriteTest()
32 | {
33 | var header = new TcpHeader
34 | {
35 | Ack = true,
36 | Fin = true,
37 | Psh = true,
38 | Urg = true,
39 | Rst = true,
40 |
41 |
42 |
43 | AckNum = 2,
44 | Checksum = 3,
45 | DstPort = 4,
46 | HdrLength = 6,
47 | UrgPtr = 8,
48 |
49 | SeqNum = 12,
50 | SrcPort = 13,
51 | Window = 16
52 | };
53 |
54 | Assert.True(header.Flags.HasFlag(TcpFlag.Ack));
55 | Assert.True(header.Flags.HasFlag(TcpFlag.Fin));
56 | Assert.True(header.Flags.HasFlag(TcpFlag.Psh));
57 | Assert.True(header.Flags.HasFlag(TcpFlag.Rst));
58 | Assert.True(header.Flags.HasFlag(TcpFlag.Fin));
59 | Assert.False(header.Flags.HasFlag(TcpFlag.Syn));
60 |
61 | Assert.Equal(2U, header.AckNum);
62 | Assert.Equal(3, header.Checksum);
63 | Assert.Equal(4, header.DstPort);
64 | Assert.Equal(6, header.HdrLength);
65 | Assert.Equal(8, header.UrgPtr);
66 |
67 | Assert.Equal(12U, header.SeqNum);
68 | Assert.Equal(13, header.SrcPort);
69 | Assert.Equal(16, header.Window);
70 |
71 |
72 | header.Flags &= ~(TcpFlag.Fin | TcpFlag.Rst);
73 | Assert.False(header.Flags.HasFlag(TcpFlag.Rst));
74 | Assert.False(header.Flags.HasFlag(TcpFlag.Fin));
75 |
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/UdpHeaderTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace WindivertDotnet.Test
9 | {
10 | public unsafe class UdpHeaderTest
11 | {
12 | [Fact]
13 | public void ReadTest()
14 | {
15 | var packet = PacketDotNet.UdpPacket.RandomPacket();
16 | fixed (void* ptr = &packet.Bytes[0])
17 | {
18 | var header = *(UdpHeader*)ptr;
19 | Assert.Equal(packet.SourcePort, header.SrcPort);
20 | Assert.Equal(packet.Length, header.Length);
21 | Assert.Equal(packet.Checksum, header.Checksum);
22 | Assert.Equal(packet.DestinationPort, header.DstPort);
23 | }
24 | }
25 |
26 | [Fact]
27 | public void WriteTest()
28 | {
29 | var header = new UdpHeader
30 | {
31 | Checksum = 1,
32 | DstPort = 2,
33 | SrcPort = 3,
34 | Length = 4,
35 | };
36 | Assert.Equal(1, header.Checksum);
37 | Assert.Equal(2, header.DstPort);
38 | Assert.Equal(3, header.SrcPort);
39 | Assert.Equal(4, header.Length);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/WinDivertAddressTest.cs:
--------------------------------------------------------------------------------
1 | using Xunit;
2 |
3 | namespace WindivertDotnet.Test
4 | {
5 | public class WinDivertAddressTest
6 | {
7 | [Fact]
8 | public void CloneTest()
9 | {
10 | using var addr = new WinDivertAddress();
11 | addr.Flags = WinDivertAddressFlag.Loopback;
12 |
13 | using var clone = addr.Clone();
14 | Assert.Equal(addr.Flags, clone.Flags);
15 |
16 | addr.Flags = WinDivertAddressFlag.IPv6;
17 | Assert.NotEqual(addr.Flags, clone.Flags);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/WinDivertBufferWriterTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Linq;
4 | using System.Runtime.CompilerServices;
5 | using Xunit;
6 |
7 | namespace WindivertDotnet.Test
8 | {
9 | public class WinDivertBufferWriterTest
10 | {
11 | [Fact]
12 | public void WriteByteTest()
13 | {
14 | using var packet = new WinDivertPacket();
15 | var writer = packet.GetWriter();
16 | writer.Write(byte.MaxValue);
17 | Assert.Equal(1, packet.Length);
18 | Assert.Equal(byte.MaxValue, packet.Span[0]);
19 | }
20 |
21 |
22 | [Fact]
23 | public void WriteSpanTest()
24 | {
25 | using var packet = new WinDivertPacket();
26 | var writer = packet.GetWriter();
27 | writer.Write(new byte[] { 1, 2 });
28 | Assert.Equal(2, packet.Length);
29 | Assert.True(packet.Span.SequenceEqual(new byte[] { 1, 2 }));
30 | }
31 |
32 |
33 | [Fact]
34 | public void WriteInt32Test()
35 | {
36 | using var packet = new WinDivertPacket();
37 | var writer = packet.GetWriter();
38 | writer.Write(100);
39 | Assert.Equal(sizeof(int), packet.Length);
40 | Assert.Equal(100, BitConverter.ToInt32(packet.Span));
41 | }
42 |
43 | [Fact]
44 | public void WriteMyStructTest()
45 | {
46 | using var packet = new WinDivertPacket();
47 | var writer = packet.GetWriter();
48 | var myStruct = new MyStruct { Byte1 = 1, Byte2 = 2 };
49 | writer.Write(myStruct);
50 |
51 | Assert.Equal(Unsafe.SizeOf(), packet.Length);
52 | Assert.True(packet.Span[0] == 1 && packet.Span[1] == 2);
53 | }
54 |
55 |
56 | [Fact]
57 | public void WriteReverseInt32Test()
58 | {
59 | using var packet = new WinDivertPacket();
60 | var writer = packet.GetWriter();
61 | writer.WriteReverse(100);
62 | Assert.Equal(sizeof(int), packet.Length);
63 | Assert.Equal(BinaryPrimitives.ReverseEndianness(100), BitConverter.ToInt32(packet.Span));
64 | }
65 |
66 | [Fact]
67 | public void WriteReverseMyStructTest()
68 | {
69 | using var packet = new WinDivertPacket();
70 | var writer = packet.GetWriter();
71 | var myStruct = new MyStruct { Byte1 = 1, Byte2 = 2 };
72 | writer.WriteReverse(myStruct);
73 |
74 | Assert.Equal(Unsafe.SizeOf(), packet.Length);
75 | Assert.True(packet.Span[0] == 2 && packet.Span[1] == 1);
76 | }
77 |
78 | [Fact]
79 | public void OffsetLengthTest()
80 | {
81 | using var packet = new WinDivertPacket();
82 | var writer = packet.GetWriter(10);
83 | writer.Advance(8);
84 |
85 | Assert.Equal(18, packet.Length);
86 | }
87 |
88 | struct MyStruct
89 | {
90 | public byte Byte1;
91 | public byte Byte2;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/WinDivertPacketExtensionsTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using System.Runtime.Versioning;
5 | using Xunit;
6 |
7 | namespace WindivertDotnet.Test
8 | {
9 | [SupportedOSPlatform("windows")]
10 | public class WinDivertPacketExtensionsTest
11 | {
12 | [Fact]
13 | public void CloneTest()
14 | {
15 | using var packet = new WinDivertPacket();
16 | packet.GetWriter().Write(Random.Shared.Next());
17 |
18 | using var clone = packet.Clone();
19 | Assert.False(ReferenceEquals(packet, clone));
20 | Assert.Equal(packet, clone);
21 | }
22 |
23 | [Fact]
24 | public void CopyToTest()
25 | {
26 | using var packet = new WinDivertPacket();
27 | packet.GetWriter().Write(Random.Shared.Next());
28 |
29 | using var dstPacket = new WinDivertPacket(100);
30 | packet.CopyTo(dstPacket);
31 | Assert.Equal(packet, dstPacket);
32 |
33 | using var emptyPacket = new WinDivertPacket(0);
34 | Assert.Throws(() => packet.CopyTo(emptyPacket));
35 | }
36 |
37 | [Fact]
38 | [SupportedOSPlatform("windows")]
39 | public unsafe void IPV4TcpReverseEndPointTest()
40 | {
41 | var ipHeader = new IPV4Header
42 | {
43 | Version = IPVersion.V4,
44 | HdrLength = 5,
45 | Protocol = ProtocolType.Tcp,
46 | Length = 40,
47 | SrcAddr = IPAddress.Loopback,
48 | DstAddr = IPAddress.Parse("1.1.1.1")
49 | };
50 | var tcpHeader = new TcpHeader
51 | {
52 | HdrLength = 5,
53 | SrcPort = 1234,
54 | DstPort = 443
55 | };
56 |
57 | using var packet = new WinDivertPacket();
58 | var writer = packet.GetWriter();
59 | writer.Write(ipHeader);
60 | writer.Write(tcpHeader);
61 |
62 | var state = packet.ReverseEndPoint();
63 | var result = packet.GetParseResult();
64 |
65 | Assert.True(state);
66 | Assert.Equal(ipHeader.SrcAddr, result.IPV4Header->DstAddr);
67 | Assert.Equal(ipHeader.DstAddr, result.IPV4Header->SrcAddr);
68 | Assert.Equal(tcpHeader.SrcPort, result.TcpHeader->DstPort);
69 | Assert.Equal(tcpHeader.DstPort, result.TcpHeader->SrcPort);
70 | }
71 |
72 | [Fact]
73 | public unsafe void IPV6UdpReverseEndPointTest()
74 | {
75 | var ipHeader = new IPV6Header
76 | {
77 | Version = IPVersion.V6,
78 | NextHdr = ProtocolType.Udp,
79 | Length = (ushort)(sizeof(UdpHeader)),
80 | SrcAddr = IPAddress.IPv6Loopback,
81 | DstAddr = IPAddress.Parse("fe80::3d4d:f188:49a5:36c6%12")
82 | };
83 | var udpHeader = new UdpHeader
84 | {
85 | Length = 8,
86 | SrcPort = 1234,
87 | DstPort = 443
88 | };
89 |
90 | using var packet = new WinDivertPacket();
91 | var writer = packet.GetWriter();
92 | writer.Write(ipHeader);
93 | writer.Write(udpHeader);
94 |
95 | var state = packet.ReverseEndPoint();
96 | var result = packet.GetParseResult();
97 |
98 | Assert.True(state);
99 | Assert.Equal(ipHeader.SrcAddr, result.IPV6Header->DstAddr);
100 | Assert.Equal(ipHeader.DstAddr, result.IPV6Header->SrcAddr);
101 | Assert.Equal(udpHeader.SrcPort, result.UdpHeader->DstPort);
102 | Assert.Equal(udpHeader.DstPort, result.UdpHeader->SrcPort);
103 | }
104 |
105 |
106 | [Fact]
107 | public unsafe void ApplyLengthToHeadersIPv4Test()
108 | {
109 | var ipHeader = new IPV4Header
110 | {
111 | Version = IPVersion.V4,
112 | HdrLength = 5,
113 | };
114 |
115 | using var packet = new WinDivertPacket();
116 | packet.GetWriter().Write(ipHeader);
117 | var count = packet.ApplyLengthToHeaders();
118 | var result = packet.GetParseResult();
119 | Assert.Equal(1, count);
120 | Assert.Equal(packet.Length, result.IPV4Header->Length);
121 |
122 | packet.Length += 10;
123 | result = packet.GetParseResult();
124 | count = packet.ApplyLengthToHeaders();
125 | Assert.Equal(1, count);
126 | Assert.Equal(packet.Length, result.IPV4Header->Length);
127 | }
128 |
129 | [Fact]
130 | public unsafe void ApplyLengthToHeadersIPv4UdpTest()
131 | {
132 | var ipHeader = new IPV4Header
133 | {
134 | Version = IPVersion.V4,
135 | HdrLength = 5,
136 | Protocol = ProtocolType.Udp,
137 | };
138 |
139 | var udpHeader = new UdpHeader();
140 |
141 |
142 | using var packet = new WinDivertPacket();
143 | var writer = packet.GetWriter();
144 | writer.Write(ipHeader);
145 | writer.Write(udpHeader);
146 | writer.Advance(255);
147 |
148 | var count = packet.ApplyLengthToHeaders();
149 | var result = packet.GetParseResult();
150 |
151 | Assert.Equal(2, count);
152 | Assert.Equal(packet.Length, result.IPV4Header->Length);
153 | Assert.Equal(packet.Length - sizeof(IPV4Header), result.UdpHeader->Length);
154 | }
155 |
156 | [Fact]
157 | public unsafe void ApplyLengthToHeadersIPv6Test()
158 | {
159 | var ipHeader = new IPV6Header
160 | {
161 | Version = IPVersion.V6,
162 | };
163 |
164 | using var packet = new WinDivertPacket();
165 | packet.GetWriter().Write(ipHeader);
166 | var count = packet.ApplyLengthToHeaders();
167 | var result = packet.GetParseResult();
168 | Assert.Equal(1, count);
169 | Assert.Equal(packet.Length - sizeof(IPV6Header), result.IPV6Header->Length);
170 |
171 | packet.Length += 10;
172 | result = packet.GetParseResult();
173 | count = packet.ApplyLengthToHeaders();
174 | Assert.Equal(1, count);
175 | Assert.Equal(packet.Length - sizeof(IPV6Header), result.IPV6Header->Length);
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/WinDivertPacketTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using System.Runtime.Versioning;
5 | using Xunit;
6 |
7 | namespace WindivertDotnet.Test
8 | {
9 | [SupportedOSPlatform("windows")]
10 | public class WinDivertPacketTest
11 | {
12 | [Fact]
13 | public void CtorTest()
14 | {
15 | using var packet = new WinDivertPacket(10);
16 | Assert.Equal(10, packet.Capacity);
17 | Assert.Equal(0, packet.Length);
18 | Assert.Equal(0, packet.Span.Length);
19 | }
20 |
21 | [Fact]
22 | public void LengthTest()
23 | {
24 | using var packet = new WinDivertPacket(10);
25 | packet.Length = 5;
26 | Assert.Equal(10, packet.Capacity);
27 | Assert.Equal(5, packet.Length);
28 | Assert.Equal(5, packet.Span.Length);
29 |
30 | Assert.Throws(() => packet.Length = 12);
31 | }
32 |
33 | [Fact]
34 | public void GetSpanTest()
35 | {
36 | using var packet = new WinDivertPacket(10);
37 | packet.Length = 1;
38 | Assert.Equal(0, packet.GetSpan(0, 10)[0]);
39 | Assert.Throws(() => packet.GetSpan(1, 10));
40 | }
41 |
42 |
43 | [Fact]
44 | public void SliceTest()
45 | {
46 | using var packet = new WinDivertPacket(10);
47 | var slicePacket = packet.Slice(2, 8);
48 |
49 | var span = packet.GetSpan(0, packet.Capacity);
50 | span[1] = 1;
51 | span[2] = 2;
52 | span[4] = 4;
53 | span[8] = 8;
54 |
55 | Assert.Equal(8, slicePacket.Capacity);
56 | Assert.Equal(8, slicePacket.Length);
57 | Assert.True(span.Slice(2, 8).SequenceEqual(slicePacket.Span));
58 | }
59 |
60 | [Fact]
61 | public unsafe void CalcNetworkIfIdxTest()
62 | {
63 | var router = new WinDivertRouter(IPAddress.Loopback);
64 | using var addr = new WinDivertAddress();
65 |
66 | using var packet = new WinDivertPacket();
67 | var ipv4Header = CreateIPV4Header(router);
68 | packet.GetWriter().Write(ipv4Header);
69 |
70 | packet.CalcNetworkIfIdx(addr);
71 | Assert.Equal(router.InterfaceIndex, addr.Network->IfIdx);
72 | }
73 |
74 | [Fact]
75 | public void CalcLoopbackFlagTest()
76 | {
77 | var router = new WinDivertRouter(IPAddress.Loopback);
78 | using var addr = new WinDivertAddress();
79 |
80 | using var packet = new WinDivertPacket();
81 | var ipv4Header = CreateIPV4Header(router);
82 | packet.GetWriter().Write(ipv4Header);
83 |
84 | packet.CalcLoopbackFlag(addr);
85 | Assert.Equal(router.IsLoopback, addr.Flags.HasFlag(WinDivertAddressFlag.Loopback));
86 | }
87 |
88 |
89 | [Fact]
90 | public void CalcOutboundFlagTest()
91 | {
92 | var router = new WinDivertRouter(IPAddress.Loopback);
93 | using var addr = new WinDivertAddress();
94 |
95 | using var packet = new WinDivertPacket();
96 | var ipv4Header = CreateIPV4Header(router);
97 | packet.GetWriter().Write(ipv4Header);
98 |
99 | packet.CalcOutboundFlag(addr);
100 | Assert.Equal(router.IsOutbound, addr.Flags.HasFlag(WinDivertAddressFlag.Outbound));
101 | }
102 |
103 | [Fact]
104 | public void EqualsTest()
105 | {
106 | using var p1 = new WinDivertPacket();
107 | using var p2 = new WinDivertPacket();
108 | p1.GetWriter().Write(1);
109 | p2.GetWriter().Write(1);
110 |
111 | Assert.Equal(p1, p2);
112 |
113 | p1.GetWriter().Write(3);
114 | p2.GetWriter().Write(4);
115 |
116 | Assert.NotEqual(p1, p2);
117 | }
118 |
119 |
120 | private static IPV4Header CreateIPV4Header(WinDivertRouter router)
121 | {
122 | return new IPV4Header
123 | {
124 | Version = IPVersion.V4,
125 | HdrLength = 5,
126 | Length = 20,
127 | Protocol = ProtocolType.IP,
128 | SrcAddr = router.SrcAddress,
129 | DstAddr = router.DstAddress
130 | };
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/WindivertDotnet.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | True
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 | all
17 |
18 |
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 | all
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/WindivertDotnet.Test/WindivertRouterTest.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Runtime.Versioning;
3 | using Xunit;
4 |
5 | namespace WindivertDotnet.Test
6 | {
7 | [SupportedOSPlatform("windows")]
8 | public class WindivertRouterTest
9 | {
10 | [Fact]
11 | public void IPv4LoopbackTest()
12 | {
13 | var router = new WinDivertRouter(IPAddress.Loopback);
14 | Assert.Equal(IPAddress.Loopback, router.SrcAddress);
15 | Assert.Equal(IPAddress.Loopback, router.DstAddress);
16 | Assert.True(router.IsOutbound);
17 | Assert.True(router.IsLoopback);
18 | }
19 |
20 | [Fact]
21 | public unsafe void IPv4LoopbackAddrTest()
22 | {
23 | var router = new WinDivertRouter(IPAddress.Loopback);
24 | using var addr = router.CreateAddress();
25 |
26 | Assert.Equal(addr.Network->IfIdx, router.InterfaceIndex);
27 | Assert.True(addr.Flags.HasFlag(WinDivertAddressFlag.Outbound));
28 | Assert.True(addr.Flags.HasFlag(WinDivertAddressFlag.Loopback));
29 | }
30 |
31 | [Fact]
32 | public void IPv6LoopbackTest()
33 | {
34 | var router = new WinDivertRouter(IPAddress.IPv6Loopback);
35 | Assert.Equal(IPAddress.IPv6Loopback, router.SrcAddress);
36 | Assert.Equal(IPAddress.IPv6Loopback, router.DstAddress);
37 | Assert.True(router.IsOutbound);
38 | Assert.True(router.IsLoopback);
39 | }
40 |
41 | [Fact]
42 | public void IPv41111Test()
43 | {
44 | var dstAddrr = IPAddress.Parse("1.1.1.1");
45 | var router = new WinDivertRouter(dstAddrr);
46 | Assert.NotEqual(IPAddress.Any, router.SrcAddress);
47 | Assert.Equal(dstAddrr, router.DstAddress);
48 | Assert.True(router.IsOutbound);
49 | Assert.False(router.IsLoopback);
50 | }
51 |
52 | [Fact]
53 | public unsafe void IPv41111AddrTest()
54 | {
55 | var dstAddrr = IPAddress.Parse("1.1.1.1");
56 | var router = new WinDivertRouter(dstAddrr);
57 |
58 | using var addr = new WinDivertAddress
59 | {
60 | Flags = WinDivertAddressFlag.Loopback
61 | };
62 | router.ApplyToAddress(addr);
63 |
64 | Assert.Equal(addr.Network->IfIdx, router.InterfaceIndex);
65 | Assert.True(addr.Flags.HasFlag(WinDivertAddressFlag.Outbound));
66 | Assert.False(addr.Flags.HasFlag(WinDivertAddressFlag.Loopback));
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/WindivertDotnet.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.32912.340
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindivertDotnet", "WindivertDotnet\WindivertDotnet.csproj", "{070A2A01-6E1E-4094-9433-C62E65DBD2FD}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App", "App\App.csproj", "{9EA97509-CBC0-414E-9C76-283AAA379347}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindivertDotnet.Test", "WindivertDotnet.Test\WindivertDotnet.Test.csproj", "{CD236CAC-7444-458B-8C13-2CEF2CC8333C}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {070A2A01-6E1E-4094-9433-C62E65DBD2FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {070A2A01-6E1E-4094-9433-C62E65DBD2FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {070A2A01-6E1E-4094-9433-C62E65DBD2FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {070A2A01-6E1E-4094-9433-C62E65DBD2FD}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {9EA97509-CBC0-414E-9C76-283AAA379347}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {9EA97509-CBC0-414E-9C76-283AAA379347}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {9EA97509-CBC0-414E-9C76-283AAA379347}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {9EA97509-CBC0-414E-9C76-283AAA379347}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {CD236CAC-7444-458B-8C13-2CEF2CC8333C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {CD236CAC-7444-458B-8C13-2CEF2CC8333C}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {CD236CAC-7444-458B-8C13-2CEF2CC8333C}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {CD236CAC-7444-458B-8C13-2CEF2CC8333C}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {23DBE1F7-831E-4D48-93C0-BA0A7596A3F6}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/WindivertDotnet/ChecksumsFlag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WindivertDotnet
4 | {
5 | ///
6 | /// 校验和选项
7 | ///
8 | [Flags]
9 | public enum ChecksumsFlag : ulong
10 | {
11 | ///
12 | /// 全部
13 | ///
14 | All = 0,
15 |
16 | ///
17 | /// 排除IpChecksum
18 | ///
19 | NoIpChecksum = 1,
20 |
21 | ///
22 | /// 排除IcmpChecksum
23 | ///
24 | NoIcmpChecksum = 2,
25 |
26 | ///
27 | /// 排除IcmpV6Checksum
28 | ///
29 | NoIcmpV6Checksum = 4,
30 |
31 | ///
32 | /// 排除TcpChecksum
33 | ///
34 | NoTcpChecksum = 8,
35 |
36 | ///
37 | /// 排除UdpChecksum
38 | ///
39 | NoUdpChecksum = 16,
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/WindivertDotnet/Event.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// 事件类型
5 | ///
6 | public sealed class Event
7 | {
8 | private readonly string name;
9 |
10 | private Event(string name)
11 | {
12 | this.name = name;
13 | }
14 |
15 | ///
16 | /// 转换为文本
17 | ///
18 | ///
19 | public override string ToString()
20 | {
21 | return this.name;
22 | }
23 |
24 | ///
25 | /// 新的网络数据包
26 | ///
27 | public static Event PACKET { get; } = new("PACKET");
28 |
29 | ///
30 | /// 将创建一个新流
31 | ///
32 | public static Event ESTABLISHED { get; } = new("ESTABLISHED");
33 |
34 | ///
35 | /// 旧流将被删除
36 | ///
37 | public static Event DELETED { get; } = new("DELETED");
38 |
39 | ///
40 | /// bind操作
41 | ///
42 | public static Event BIND { get; } = new("BIND");
43 |
44 | ///
45 | /// connect操作
46 | ///
47 | public static Event CONNECT { get; } = new("CONNECT");
48 |
49 | ///
50 | /// listen操作
51 | ///
52 | public static Event LISTEN { get; } = new("LISTEN");
53 |
54 | ///
55 | /// accept操作
56 | ///
57 | public static Event ACCEPT { get; } = new("ACCEPT");
58 |
59 | ///
60 | /// 打开了新的WinDivert实例
61 | ///
62 | public static Event OPEN { get; } = new("OPEN");
63 |
64 | ///
65 | /// 旧的WinDivert实例被关闭或套接字终结点已关闭
66 | ///
67 | public static Event CLOSE { get; } = new("CLOSE");
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/WindivertDotnet/FragmentFlag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WindivertDotnet
4 | {
5 | ///
6 | /// IPv4标记位
7 | ///
8 | [Flags]
9 | public enum FragmentFlag : byte
10 | {
11 | ///
12 | /// 有分片
13 | ///
14 | MoreFragments = 0x1,
15 |
16 | ///
17 | /// 无分片
18 | ///
19 | DontFragment = 0x02,
20 |
21 | ///
22 | /// 保护
23 | ///
24 | Reserved = 0x04,
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/WindivertDotnet/IFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Sockets;
3 |
4 | namespace WindivertDotnet
5 | {
6 | ///
7 | /// 定义filter的属性
8 | ///
9 | public interface IFilter
10 | {
11 | ///
12 | /// 发生的时间戳
13 | ///
14 | [FilterMember("timestamp")]
15 | long Timestamp { get; }
16 |
17 | ///
18 | /// 事件
19 | ///
20 | [FilterMember("event")]
21 | Event Event { get; }
22 |
23 | ///
24 | /// 传入/传出本地计算机的网络数据包
25 | ///
26 | INetwork Network { get; }
27 |
28 | ///
29 | /// 通过本地计算机的网络数据包
30 | ///
31 | IForward Forward { get; }
32 |
33 | ///
34 | /// 网络流已建立/已删除事件
35 | ///
36 | IFlow Flow { get; }
37 |
38 | ///
39 | /// 套接字操作事件
40 | ///
41 | ISocket Socket { get; }
42 |
43 | ///
44 | /// WinDivert处理事件
45 | ///
46 | IReflect Reflect { get; }
47 |
48 | ///
49 | /// 是否为IPv4
50 | ///
51 | [FilterMember("ip")]
52 | bool IsIP { get; }
53 |
54 | ///
55 | /// IPv4的属性集
56 | ///
57 | [FilterMember("ip")]
58 | IIP IP { get; }
59 |
60 | ///
61 | /// 是否IPv6
62 | ///
63 | [FilterMember("ipv6")]
64 | bool IsIPV6 { get; }
65 |
66 | ///
67 | /// IPv6属性集
68 | ///
69 | [FilterMember("ipv6")]
70 | IIPV6 IPV6 { get; }
71 |
72 | ///
73 | /// 是否为tcp协议
74 | ///
75 | [FilterMember("tcp")]
76 | bool IsTcp { get; }
77 |
78 | ///
79 | /// tcp属性集
80 | ///
81 | [FilterMember("tcp")]
82 | ITcp Tcp { get; }
83 |
84 |
85 | ///
86 | /// 是否udp协议
87 | ///
88 | [FilterMember("udp")]
89 | bool IsUdp { get; }
90 |
91 | ///
92 | /// udp属性集
93 | ///
94 | [FilterMember("udp")]
95 | IUdp Udp { get; }
96 |
97 |
98 | ///
99 | /// 是否为icmpV4协议
100 | ///
101 | [FilterMember("icmp")]
102 | bool IsIcmp { get; }
103 |
104 | ///
105 | /// icmpV4属性集
106 | ///
107 | [FilterMember("icmp")]
108 | public IICmp ICmp { get; }
109 |
110 | ///
111 | /// 是否为icmpV6协议
112 | ///
113 | [FilterMember("icmpv6")]
114 | bool IsIcmpV6 { get; }
115 |
116 | ///
117 | /// icmpV6属性集
118 | ///
119 | [FilterMember("icmpv6")]
120 | public IICmpV6 IcmpV6 { get; }
121 |
122 |
123 | ///
124 | /// 传入/传出本地计算机的网络数据包
125 | ///
126 | public interface INetwork
127 | {
128 | ///
129 | /// 是否为出口
130 | ///
131 | [FilterMember("outbound")]
132 | bool Outbound { get; }
133 |
134 | ///
135 | /// 是否为入口
136 | ///
137 | [FilterMember("inbound")]
138 | bool Inbound { get; }
139 |
140 | ///
141 | /// 回环IP
142 | ///
143 | [FilterMember("loopback")]
144 | bool Loopback { get; }
145 |
146 | ///
147 | /// 是否已修改
148 | ///
149 | [FilterMember("impostor")]
150 | bool Impostor { get; }
151 |
152 | ///
153 | /// 是否分片
154 | ///
155 | [FilterMember("fragment")]
156 | bool Fragment { get; }
157 |
158 | ///
159 | /// 网卡索引
160 | ///
161 | [FilterMember("ifIdx")]
162 | int IfIdx { get; }
163 |
164 | ///
165 | /// 子网卡索引
166 | ///
167 | [FilterMember("subIfIdx")]
168 | int SubIfIdx { get; }
169 |
170 | ///
171 | /// 协议类型
172 | /// ProtocolType
173 | ///
174 | [FilterMember("protocol")]
175 | ProtocolType Protocol { get; }
176 |
177 | ///
178 | /// 本机IP地址
179 | ///
180 | [FilterMember("localAddr")]
181 | string LocalAddr { get; }
182 |
183 | ///
184 | /// 本机端口
185 | ///
186 | [FilterMember("localPort")]
187 | int LocalPort { get; }
188 |
189 | ///
190 | /// 远程IP地址
191 | ///
192 | [FilterMember("remoteAddr")]
193 | string RemoteAddr { get; }
194 |
195 | ///
196 | /// 远程端口
197 | ///
198 | [FilterMember("remotePort")]
199 | int RemotePort { get; }
200 |
201 | ///
202 | /// The packet length
203 | ///
204 | [FilterMember("length")]
205 | int Length { get; }
206 | }
207 |
208 | ///
209 | /// 通过本地计算机的网络数据包
210 | ///
211 | public interface IForward
212 | {
213 | ///
214 | /// 网卡索引
215 | ///
216 | [FilterMember("ifIdx")]
217 | int IfIdx { get; }
218 |
219 | ///
220 | /// 子网卡索引
221 | ///
222 | [FilterMember("subIfIdx")]
223 | int SubIfIdx { get; }
224 |
225 | ///
226 | /// 冒名顶替数据包
227 | ///
228 | [FilterMember("impostor")]
229 | bool Impostor { get; }
230 |
231 | ///
232 | /// ip分片包
233 | ///
234 | [FilterMember("fragment")]
235 | bool Fragment { get; }
236 |
237 | ///
238 | /// The packet length
239 | ///
240 | [FilterMember("length")]
241 | int Length { get; }
242 | }
243 |
244 | ///
245 | /// 网络流已建立/已删除事件
246 | ///
247 | public interface IFlow
248 | {
249 | ///
250 | /// 是否为出口
251 | ///
252 | [FilterMember("outbound")]
253 | bool Outbound { get; }
254 |
255 | ///
256 | /// 是否为入口
257 | ///
258 | [FilterMember("inbound")]
259 | bool Inbound { get; }
260 |
261 | ///
262 | /// 环回数据包
263 | ///
264 | [FilterMember("loopback")]
265 | bool Loopback { get; }
266 |
267 | ///
268 | /// 流的终结点ID
269 | ///
270 | [FilterMember("endpointId")]
271 | int EndpointId { get; }
272 |
273 | ///
274 | /// 流的父终结点ID
275 | ///
276 | [FilterMember("parentEndpointId")]
277 | int ParentEndpointId { get; }
278 |
279 | ///
280 | /// 与流关联的进程的ID
281 | ///
282 | [FilterMember("processId")]
283 | int ProcessId { get; }
284 |
285 | ///
286 | /// 本机地址
287 | ///
288 | [FilterMember("localAddr")]
289 | string LocalAddr { get; }
290 |
291 | ///
292 | /// 本机端口
293 | ///
294 | [FilterMember("localPort")]
295 | int LocalPort { get; }
296 |
297 | ///
298 | /// 远程地址
299 | ///
300 | [FilterMember("remoteAddr")]
301 | string RemoteAddr { get; }
302 |
303 | ///
304 | /// 远程端口
305 | ///
306 | [FilterMember("remotePort")]
307 | int RemotePort { get; }
308 |
309 | ///
310 | /// 协议类型(ProtocolType)
311 | ///
312 | [FilterMember("protocol")]
313 | ProtocolType Protocol { get; }
314 | }
315 |
316 | ///
317 | /// 套接字操作事件
318 | ///
319 | public interface ISocket
320 | {
321 | ///
322 | /// 环回数据包
323 | ///
324 | [FilterMember("loopback")]
325 | bool Loopback { get; }
326 |
327 | ///
328 | /// 套接字操作的终结点 ID
329 | ///
330 | [FilterMember("endpointId")]
331 | int EndpointId { get; }
332 |
333 | ///
334 | /// 套接字操作的父终结点 ID
335 | ///
336 | [FilterMember("parentEndpointId")]
337 | int ParentEndpointId { get; }
338 |
339 | ///
340 | /// 与套接字操作关联的进程的 ID
341 | ///
342 | [FilterMember("processId")]
343 | int ProcessId { get; }
344 |
345 | ///
346 | /// 本机地址
347 | ///
348 | [FilterMember("localAddr")]
349 | string LocalAddr { get; }
350 |
351 | ///
352 | /// 本机端口
353 | ///
354 | [FilterMember("localPort")]
355 | int LocalPort { get; }
356 |
357 | ///
358 | /// 远程地址
359 | ///
360 | [FilterMember("remoteAddr")]
361 | string RemoteAddr { get; }
362 |
363 | ///
364 | /// 远程端口
365 | ///
366 | [FilterMember("remotePort")]
367 | int RemotePort { get; }
368 |
369 | ///
370 | /// 协议类型(ProtocolType)
371 | ///
372 | [FilterMember("protocol")]
373 | ProtocolType Protocol { get; }
374 | }
375 |
376 | ///
377 | /// WinDivert处理事件
378 | ///
379 | public interface IReflect
380 | {
381 | ///
382 | /// WinDivert打开句柄的进程的ID
383 | ///
384 | [FilterMember("processId")]
385 | int ProcessId { get; }
386 |
387 | ///
388 | /// WinDivertLayer
389 | ///
390 | [FilterMember("layer")]
391 | string Layer { get; }
392 | ///
393 | /// 优先级
394 | ///
395 | [FilterMember("priority")]
396 | int Priority { get; }
397 | }
398 |
399 | ///
400 | /// IPV4对象
401 | ///
402 | public interface IIP
403 | {
404 | ///
405 | /// 获取或设置Internet Header Length
406 | /// ipv4头总字节为该值的4倍
407 | ///
408 | [FilterMember]
409 | int HdrLength { get; }
410 |
411 | ///
412 | /// 版本
413 | ///
414 | [FilterMember]
415 | int Version { get; }
416 |
417 | ///
418 | /// 服务类型
419 | ///
420 | [FilterMember]
421 | int TOS { get; }
422 |
423 | ///
424 | /// 包长度
425 | ///
426 | [FilterMember]
427 | int Length { get; }
428 |
429 | ///
430 | /// 报文的id
431 | ///
432 | [FilterMember]
433 | int Id { get; }
434 |
435 | ///
436 | /// 生存时间
437 | ///
438 | [FilterMember]
439 | int TTL { get; }
440 |
441 | ///
442 | /// 协议
443 | /// ProtocolType
444 | ///
445 | [FilterMember]
446 | ProtocolType Protocol { get; }
447 |
448 | ///
449 | /// 检验和
450 | ///
451 | [FilterMember]
452 | int Checksum { get; }
453 |
454 | ///
455 | /// 源IP地址
456 | ///
457 | [FilterMember]
458 | string SrcAddr { get; }
459 |
460 | ///
461 | /// 远程IP地址
462 | ///
463 | [FilterMember]
464 | string DstAddr { get; }
465 | }
466 |
467 | ///
468 | /// ipv6
469 | ///
470 | public interface IIPV6
471 | {
472 | ///
473 | /// 版本
474 | ///
475 | [FilterMember]
476 | int Version { get; }
477 |
478 | ///
479 | /// 有效负载长度
480 | ///
481 | [FilterMember]
482 | int Length { get; }
483 |
484 | ///
485 | /// 下一个报头
486 | ///
487 | [FilterMember]
488 | int NextHdr { get; }
489 |
490 | ///
491 | /// 跳跃限制
492 | ///
493 | [FilterMember]
494 | int HopLimit { get; }
495 |
496 | ///
497 | /// 源IP地址
498 | ///
499 | [FilterMember]
500 | string SrcAddr { get; }
501 |
502 | ///
503 | /// 目的IP地址
504 | ///
505 | [FilterMember]
506 | string DstAddr { get; }
507 | }
508 |
509 | ///
510 | /// 传输层
511 | ///
512 | public interface ITransfer
513 | {
514 | ///
515 | /// 校验和
516 | ///
517 | [FilterMember]
518 | int Checksum { get; }
519 |
520 | ///
521 | /// 源端口号
522 | ///
523 | [FilterMember]
524 | int SrcPort { get; }
525 |
526 | ///
527 | /// 目的端口号
528 | ///
529 | [FilterMember]
530 | int DstPort { get; }
531 |
532 | ///
533 | /// 负载数据长度
534 | ///
535 | [FilterMember]
536 | int PayloadLength { get; }
537 |
538 | ///
539 | /// 8位形式的负载数据
540 | /// 使用索引来读取
541 | ///
542 | [FilterMember]
543 | byte[] Payload { get; }
544 |
545 | ///
546 | /// 16位形式的负载数据
547 | /// 使用索引来读取
548 | ///
549 | [FilterMember]
550 | ushort[] Payload16 { get; }
551 |
552 | ///
553 | /// 32位形式的负载数据
554 | /// 使用索引来读取
555 | ///
556 | [FilterMember]
557 | uint[] Payload32 { get; }
558 | }
559 |
560 | ///
561 | /// tcp
562 | ///
563 | public interface ITcp : ITransfer
564 | {
565 | ///
566 | /// 序列码
567 | ///
568 | [FilterMember]
569 | int SeqNum { get; }
570 |
571 | ///
572 | /// 确认码
573 | ///
574 | [FilterMember]
575 | int AckNum { get; }
576 |
577 | ///
578 | /// 结束位
579 | ///
580 | [FilterMember]
581 | bool Fin { get; }
582 |
583 | ///
584 | /// 请求位
585 | ///
586 | [FilterMember]
587 | bool Syn { get; }
588 |
589 | ///
590 | /// 重置位
591 | ///
592 | [FilterMember]
593 | bool Rst { get; }
594 |
595 | ///
596 | /// 确认位
597 | ///
598 | [FilterMember]
599 | bool Ack { get; }
600 |
601 | ///
602 | /// 推送位
603 | ///
604 | [FilterMember]
605 | bool Psh { get; }
606 |
607 | ///
608 | /// 紧急位
609 | ///
610 | [FilterMember]
611 | bool Urg { get; }
612 |
613 | ///
614 | /// 滑动窗口
615 | ///
616 | [FilterMember]
617 | int Window { get; }
618 |
619 | ///
620 | /// 紧急指针
621 | ///
622 | [FilterMember]
623 | int UrgPtr { get; }
624 | }
625 |
626 | ///
627 | /// udp
628 | ///
629 | public interface IUdp : ITransfer
630 | {
631 | ///
632 | /// 获取或设置Udp包长度
633 | /// 含头部的8字节
634 | ///
635 | [FilterMember]
636 | int Length { get; }
637 | }
638 |
639 | ///
640 | /// ICMP
641 | ///
642 | public interface IICmp
643 | {
644 | ///
645 | /// 类型
646 | ///
647 | [FilterMember]
648 | IcmpV4MessageType Type { get; }
649 |
650 | ///
651 | /// 代码
652 | ///
653 | [FilterMember]
654 | IcmpV4UnreachableCode Code { get; }
655 |
656 | ///
657 | /// 检验和
658 | ///
659 | [FilterMember]
660 | int Checksum { get; }
661 |
662 | ///
663 | /// Rest of header
664 | ///
665 | [FilterMember]
666 | int Body { get; }
667 | }
668 |
669 | ///
670 | /// ICMP
671 | ///
672 | public interface IICmpV6
673 | {
674 | ///
675 | /// 类型
676 | ///
677 | [FilterMember]
678 | IcmpV6MessageType Type { get; }
679 |
680 | ///
681 | /// 代码
682 | ///
683 | [FilterMember]
684 | IcmpV6UnreachableCode Code { get; }
685 |
686 | ///
687 | /// 检验和
688 | ///
689 | [FilterMember]
690 | int Checksum { get; }
691 |
692 | ///
693 | /// Rest of header
694 | ///
695 | [FilterMember]
696 | int Body { get; }
697 | }
698 |
699 |
700 | ///
701 | /// Filter成员特性
702 | ///
703 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
704 | public sealed class FilterMemberAttribute : Attribute
705 | {
706 | ///
707 | /// 成员名称
708 | ///
709 | public string? Name { get; }
710 |
711 | ///
712 | /// Filter成员特性
713 | ///
714 | public FilterMemberAttribute()
715 | {
716 | }
717 |
718 | ///
719 | /// Filter成员特性
720 | ///
721 | /// 成员名称
722 | public FilterMemberAttribute(string name)
723 | {
724 | this.Name = name;
725 | }
726 | }
727 | }
728 | }
729 |
--------------------------------------------------------------------------------
/WindivertDotnet/IPV4Header.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Diagnostics;
4 | using System.Net;
5 | using System.Net.Sockets;
6 | using System.Runtime.InteropServices;
7 |
8 | namespace WindivertDotnet
9 | {
10 | ///
11 | /// IPV4头
12 | ///
13 | [DebuggerDisplay("SrcAddr = {SrcAddr}, DstAddr = {DstAddr}, Size = {HdrLength * 4}")]
14 | [StructLayout(LayoutKind.Explicit)]
15 | public struct IPV4Header
16 | {
17 | private const int IPV4_SIZE = sizeof(int);
18 |
19 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
20 | [FieldOffset(0)]
21 | private byte bitfield;
22 |
23 | ///
24 | /// 获取或设置Internet Header Length
25 | /// ipv4头总字节为该值的4倍
26 | ///
27 | public byte HdrLength
28 | {
29 | get => (byte)(bitfield & 0xFu);
30 | set => bitfield = (byte)((bitfield & ~0xFu) | (value & 0xFu));
31 | }
32 |
33 | ///
34 | /// 获取或设置版本
35 | ///
36 | public IPVersion Version
37 | {
38 | get => (IPVersion)((bitfield >> 4) & 0xFu);
39 | set => bitfield = (byte)((bitfield & ~(0xFu << 4)) | (((byte)value & 0xFu) << 4));
40 | }
41 |
42 | ///
43 | /// 获取或设置服务类型
44 | ///
45 | [FieldOffset(1)]
46 | public byte TOS;
47 |
48 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
49 | [FieldOffset(2)]
50 | private ushort length;
51 |
52 | ///
53 | /// 获取或设置IP数据包的长度
54 | /// 包含IPv4头的长度
55 | ///
56 | public ushort Length
57 | {
58 | get => BinaryPrimitives.ReverseEndianness(length);
59 | set => length = BinaryPrimitives.ReverseEndianness(value);
60 | }
61 |
62 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
63 | [FieldOffset(4)]
64 | private ushort id;
65 |
66 | ///
67 | /// 获取或设置报文的id
68 | /// 其初始值由系统随机生成;每发送一个数据报,其值就加1
69 | /// 该值在数据报分片时被复制到每个分片中,因此同一个数据报的所有分片都具有相同的标识值
70 | ///
71 | public ushort Id
72 | {
73 | get => BinaryPrimitives.ReverseEndianness(id);
74 | set => id = BinaryPrimitives.ReverseEndianness(value);
75 | }
76 |
77 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
78 | [FieldOffset(6)]
79 | private byte frag;
80 |
81 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
82 | [FieldOffset(6)]
83 | private ushort fragOff0;
84 |
85 | ///
86 | /// 获取或设置分片偏移
87 | ///
88 | public ushort FragOff0
89 | {
90 | get => BinaryPrimitives.ReverseEndianness(fragOff0);
91 | set => fragOff0 = BinaryPrimitives.ReverseEndianness(value);
92 | }
93 |
94 | ///
95 | /// 标记位
96 | ///
97 | public FragmentFlag FragmentFlags
98 | {
99 | get => (FragmentFlag)(this.frag >> 5);
100 | set => this.frag = (byte)(((uint)((byte)value << 5)) | (this.frag & 0b_0001_1111u));
101 | }
102 |
103 | ///
104 | /// 分片偏移量13位
105 | ///
106 | public ushort FragmentOffset
107 | {
108 | get => (ushort)(this.FragOff0 & 0b_0001_1111_1111_1111u);
109 | set => this.FragOff0 = (ushort)((value & 0b_0001_1111_1111_1111u) | (this.FragOff0 & 0b_1110_0000_0000_0000u));
110 | }
111 |
112 | ///
113 | /// 获取或设置生存时间
114 | ///
115 | [FieldOffset(8)]
116 | public byte TTL;
117 |
118 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
119 | [FieldOffset(9)]
120 | private byte protocol;
121 |
122 |
123 | ///
124 | /// 获取或设置协议类型
125 | ///
126 | public ProtocolType Protocol
127 | {
128 | get => (ProtocolType)protocol;
129 | set => protocol = (byte)value;
130 | }
131 |
132 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
133 | [FieldOffset(10)]
134 | private ushort checksum;
135 |
136 | ///
137 | /// 获取或设置检验和
138 | ///
139 | public ushort Checksum
140 | {
141 | get => BinaryPrimitives.ReverseEndianness(checksum);
142 | set => checksum = BinaryPrimitives.ReverseEndianness(value);
143 | }
144 |
145 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
146 | [FieldOffset(12)]
147 | private unsafe fixed byte srcAddr[IPV4_SIZE];
148 |
149 | ///
150 | /// 获取或设置源IPv4
151 | ///
152 | public unsafe IPAddress SrcAddr
153 | {
154 | get
155 | {
156 | fixed (void* ptr = this.srcAddr)
157 | {
158 | return GetIPAddress(ptr);
159 | }
160 | }
161 | set
162 | {
163 | fixed (void* ptr = this.srcAddr)
164 | {
165 | SetIPAddress(ptr, value);
166 | }
167 | }
168 | }
169 |
170 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
171 | [FieldOffset(16)]
172 | private unsafe fixed byte dstAddr[IPV4_SIZE];
173 |
174 | ///
175 | /// 获取或设置目的IPv4
176 | ///
177 | public unsafe IPAddress DstAddr
178 | {
179 | get
180 | {
181 | fixed (void* ptr = this.dstAddr)
182 | {
183 | return GetIPAddress(ptr);
184 | }
185 | }
186 | set
187 | {
188 | fixed (void* ptr = this.dstAddr)
189 | {
190 | SetIPAddress(ptr, value);
191 | }
192 | }
193 | }
194 |
195 | private unsafe static IPAddress GetIPAddress(void* ptr)
196 | {
197 | var span = new Span(ptr, IPV4_SIZE);
198 | return new IPv4Address(span);
199 | }
200 |
201 | private unsafe static void SetIPAddress(void* ptr, IPAddress value)
202 | {
203 | if (value.AddressFamily != AddressFamily.InterNetwork)
204 | {
205 | throw new ArgumentException($"AddressFamily要求必须为{AddressFamily.InterNetwork}", nameof(value));
206 | }
207 |
208 | var span = new Span(ptr, IPV4_SIZE);
209 | value.TryWriteBytes(span, out _);
210 | }
211 |
212 | private class IPv4Address : IPAddress
213 | {
214 | public IPv4Address(ReadOnlySpan span)
215 | : base(span)
216 | {
217 | }
218 | public override string ToString()
219 | {
220 | return base.ToString();
221 | }
222 | }
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/WindivertDotnet/IPV6Header.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Diagnostics;
4 | using System.Net;
5 | using System.Net.Sockets;
6 |
7 | namespace WindivertDotnet
8 | {
9 | ///
10 | /// IPv6头
11 | ///
12 | [DebuggerDisplay("SrcAddr = {SrcAddr}, DstAddr = {DstAddr}, Size = {sizeof(IPV6Header)}")]
13 | public struct IPV6Header
14 | {
15 | private const int IPV6_SIZE = sizeof(int) * 4;
16 |
17 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
18 | private byte bitfield1;
19 |
20 | ///
21 | /// 获取或设置流量类
22 | ///
23 | public byte TrafficClass0
24 | {
25 | get => (byte)(bitfield1 & 0xFu);
26 | set => bitfield1 = (byte)((bitfield1 & ~0xFu) | (value & 0xFu));
27 | }
28 |
29 | ///
30 | /// 获取或设置版本
31 | ///
32 | public IPVersion Version
33 | {
34 | get => (IPVersion)((bitfield1 >> 4) & 0xFu);
35 | set => bitfield1 = (byte)((bitfield1 & ~(0xFu << 4)) | (((byte)value & 0xFu) << 4));
36 | }
37 |
38 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
39 | private byte bitfield2;
40 |
41 | ///
42 | /// 获取或设置流标签
43 | ///
44 | public byte FlowLabel0
45 | {
46 | get => (byte)(bitfield2 & 0xFu);
47 | set => bitfield2 = (byte)((bitfield2 & ~0xFu) | (value & 0xFu));
48 | }
49 |
50 |
51 | ///
52 | /// 获取或设置流量类
53 | ///
54 | public byte TrafficClass1
55 | {
56 | get => (byte)((bitfield2 >> 4) & 0xFu);
57 | set => bitfield2 = (byte)((bitfield2 & ~(0xFu << 4)) | ((value & 0xFu) << 4));
58 | }
59 |
60 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
61 | private ushort flowLabel1;
62 |
63 | ///
64 | /// 获取或设置流标签
65 | ///
66 | public ushort FlowLabel1
67 | {
68 | get => BinaryPrimitives.ReverseEndianness(flowLabel1);
69 | set => flowLabel1 = BinaryPrimitives.ReverseEndianness(value);
70 | }
71 |
72 |
73 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
74 | private ushort length;
75 |
76 | ///
77 | /// 获取或设置有效负载长度
78 | /// 不包含IPV6头的固定40字节
79 | ///
80 | public ushort Length
81 | {
82 | get => BinaryPrimitives.ReverseEndianness(length);
83 | set => length = BinaryPrimitives.ReverseEndianness(value);
84 | }
85 |
86 | ///
87 | /// 获取或设置下一个报头
88 | ///
89 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
90 | private byte nextHdr;
91 |
92 | ///
93 | /// 获取或设置下一个报头协议
94 | ///
95 | public ProtocolType NextHdr
96 | {
97 | get => (ProtocolType)nextHdr;
98 | set => nextHdr = (byte)value;
99 | }
100 |
101 | ///
102 | /// 获取或设置跳跃限制
103 | ///
104 | public byte HopLimit;
105 |
106 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
107 | private unsafe fixed byte srcAddr[IPV6_SIZE];
108 |
109 | ///
110 | /// 获取或设置源IPv6
111 | ///
112 | public unsafe IPAddress SrcAddr
113 | {
114 | get
115 | {
116 | fixed (void* ptr = this.srcAddr)
117 | {
118 | return GetIPAddress(ptr);
119 | }
120 | }
121 | set
122 | {
123 | fixed (void* ptr = this.srcAddr)
124 | {
125 | SetIPAddress(ptr, value);
126 | }
127 | }
128 | }
129 |
130 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
131 | private unsafe fixed byte dstAddr[IPV6_SIZE];
132 |
133 | ///
134 | /// 获取或设置目的IPV6
135 | ///
136 | public unsafe IPAddress DstAddr
137 | {
138 | get
139 | {
140 | fixed (void* ptr = this.dstAddr)
141 | {
142 | return GetIPAddress(ptr);
143 | }
144 | }
145 | set
146 | {
147 | fixed (void* ptr = this.dstAddr)
148 | {
149 | SetIPAddress(ptr, value);
150 | }
151 | }
152 | }
153 |
154 | private unsafe static IPAddress GetIPAddress(void* ptr)
155 | {
156 | var span = new Span(ptr, IPV6_SIZE);
157 | return new IPv6Address(span);
158 | }
159 |
160 | private unsafe static void SetIPAddress(void* ptr, IPAddress value)
161 | {
162 | if (value.AddressFamily != AddressFamily.InterNetworkV6)
163 | {
164 | throw new ArgumentException($"AddressFamily要求必须为{AddressFamily.InterNetworkV6}", nameof(value));
165 | }
166 |
167 | var span = new Span(ptr, IPV6_SIZE);
168 | value.TryWriteBytes(span, out _);
169 | }
170 |
171 | private class IPv6Address : IPAddress
172 | {
173 | public IPv6Address(ReadOnlySpan span)
174 | : base(span)
175 | {
176 | }
177 | public override string ToString()
178 | {
179 | return base.ToString();
180 | }
181 | }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/WindivertDotnet/IPVersion.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// ip版本
5 | ///
6 | public enum IPVersion : byte
7 | {
8 | ///
9 | /// v4
10 | ///
11 | V4 = 4,
12 |
13 | ///
14 | /// v6
15 | ///
16 | V6 = 6
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/WindivertDotnet/IcmpV4Header.cs:
--------------------------------------------------------------------------------
1 | using System.Buffers.Binary;
2 | using System.Diagnostics;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace WindivertDotnet
6 | {
7 | ///
8 | /// IcmpV4头结构
9 | ///
10 | [DebuggerDisplay("Type = {Type}, Code = {Code}, Size = {sizeof(IcmpV4Header)}")]
11 | [StructLayout(LayoutKind.Explicit)]
12 | public struct IcmpV4Header
13 | {
14 | ///
15 | /// 获取或设置类型
16 | ///
17 | [FieldOffset(0)]
18 | public IcmpV4MessageType Type;
19 |
20 | ///
21 | /// 获取或设置代码
22 | ///
23 | [FieldOffset(1)]
24 | public IcmpV4UnreachableCode Code;
25 |
26 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
27 | [FieldOffset(2)]
28 | private ushort checksum;
29 |
30 | ///
31 | /// 获取或设置检验和
32 | ///
33 | public ushort Checksum
34 | {
35 | get => BinaryPrimitives.ReverseEndianness(checksum);
36 | set => checksum = BinaryPrimitives.ReverseEndianness(value);
37 | }
38 |
39 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
40 | [FieldOffset(4)]
41 | private uint body;
42 |
43 | ///
44 | /// 获取或设置Rest of header
45 | /// 内容因 ICMP 类型和代码而异
46 | ///
47 | public uint Body
48 | {
49 | get => BinaryPrimitives.ReverseEndianness(body);
50 | set => body = BinaryPrimitives.ReverseEndianness(value);
51 | }
52 |
53 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
54 | [FieldOffset(4)]
55 | private ushort identifier;
56 |
57 | ///
58 | /// 获取或设置id
59 | ///
60 | public ushort Identifier
61 | {
62 | get => BinaryPrimitives.ReverseEndianness(identifier);
63 | set => identifier = BinaryPrimitives.ReverseEndianness(value);
64 | }
65 |
66 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
67 | [FieldOffset(6)]
68 | private ushort sequenceNumber;
69 |
70 | ///
71 | /// 获取或设置序列号
72 | ///
73 | public ushort SequenceNumber
74 | {
75 | get => BinaryPrimitives.ReverseEndianness(sequenceNumber);
76 | set => sequenceNumber = BinaryPrimitives.ReverseEndianness(value);
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/WindivertDotnet/IcmpV4MessageType.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// IcmpV4消息类型
5 | ///
6 | public enum IcmpV4MessageType : byte
7 | {
8 | #pragma warning disable 1591
9 | EchoReply = 0,
10 | DestinationUnreachable = 3,
11 | SourceQuench = 4,
12 | RedirectMessage = 5,
13 | EchoRequest = 8,
14 | RouterAdvertisement = 9,
15 | RouterSolicitation = 10,
16 | TimeExceeded = 11,
17 | ParameterProblemBadIPHeader = 12,
18 | Timestamp = 13,
19 | TimestampReply = 14,
20 | InformationRequest = 15,
21 | InformationReply = 16,
22 | AddressMaskRequest = 17,
23 | AddressMaskReply = 18,
24 | Traceroute = 30,
25 | #pragma warning restore 1591
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/WindivertDotnet/IcmpV4UnreachableCode.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// IcmpV4错误码
5 | ///
6 | public enum IcmpV4UnreachableCode : byte
7 | {
8 | #pragma warning disable 1591
9 | DestinationNetworkUnreachable = 0,
10 | DestinationHostUnreachable = 1,
11 | DestinationProtocolUnreachable = 2,
12 | DestinationPortUnreachable = 3,
13 | FragmentationRequiredAndDFFlagSet = 4,
14 | SourceRouteFailed = 5,
15 | DestinationNetworkUnknown = 6,
16 | DestinationHostUnknown = 7,
17 | SourceHostIsolated = 8,
18 | NetworkAdministrativelyProhibited = 9,
19 | HostAdministrativelyProhibited = 10,
20 | NetworkUnreachableForTos = 11,
21 | HostUnreachableForTos = 12,
22 | CommunicationAdministrativelyProhibited = 13,
23 | HostPrecedenceViolation = 14,
24 | PrecedenceCutoffInEffect = 15,
25 | #pragma warning restore 1591
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/WindivertDotnet/IcmpV6Header.cs:
--------------------------------------------------------------------------------
1 | using System.Buffers.Binary;
2 | using System.Diagnostics;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace WindivertDotnet
6 | {
7 | ///
8 | /// IcmpV6头结构
9 | ///
10 | [DebuggerDisplay("Type = {Type}, Code = {Code}, Size = {sizeof(IcmpV6Header)}")]
11 | [StructLayout(LayoutKind.Explicit)]
12 | public struct IcmpV6Header
13 | {
14 | ///
15 | /// 获取或设置类型
16 | ///
17 | [FieldOffset(0)]
18 | public IcmpV6MessageType Type;
19 |
20 | ///
21 | /// 获取或设置代码
22 | ///
23 | [FieldOffset(1)]
24 | public IcmpV6UnreachableCode Code;
25 |
26 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
27 | [FieldOffset(2)]
28 | private ushort checksum;
29 |
30 | ///
31 | /// 获取或设置检验和
32 | ///
33 | public ushort Checksum
34 | {
35 | get => BinaryPrimitives.ReverseEndianness(checksum);
36 | set => checksum = BinaryPrimitives.ReverseEndianness(value);
37 | }
38 |
39 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
40 | [FieldOffset(4)]
41 | private uint body;
42 |
43 | ///
44 | /// 获取或设置Rest of header
45 | /// 内容因 ICMP 类型和代码而异
46 | ///
47 | public uint Body
48 | {
49 | get => BinaryPrimitives.ReverseEndianness(body);
50 | set => body = BinaryPrimitives.ReverseEndianness(value);
51 | }
52 |
53 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
54 | [FieldOffset(4)]
55 | private ushort identifier;
56 |
57 | ///
58 | /// 获取或设置id
59 | ///
60 | public ushort Identifier
61 | {
62 | get => BinaryPrimitives.ReverseEndianness(identifier);
63 | set => identifier = BinaryPrimitives.ReverseEndianness(value);
64 | }
65 |
66 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
67 | [FieldOffset(6)]
68 | private ushort sequenceNumber;
69 |
70 | ///
71 | /// 获取或设置序列号
72 | ///
73 | public ushort SequenceNumber
74 | {
75 | get => BinaryPrimitives.ReverseEndianness(sequenceNumber);
76 | set => sequenceNumber = BinaryPrimitives.ReverseEndianness(value);
77 | }
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/WindivertDotnet/IcmpV6MessageType.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// IcmpV6消息类型
5 | ///
6 | public enum IcmpV6MessageType : byte
7 | {
8 | #pragma warning disable 1591
9 | DestinationUnreachable = 1,
10 | PacketTooBig = 2,
11 | TimeExceeded = 3,
12 | ParameterProblem = 4,
13 | EchoRequest = 128,
14 | EchoReply = 129,
15 | #pragma warning restore 1591
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/WindivertDotnet/IcmpV6UnreachableCode.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// IcmpV6错误码
5 | ///
6 | public enum IcmpV6UnreachableCode : byte
7 | {
8 | #pragma warning disable 1591
9 | NoRouteToDestination = 0,
10 | CommunicationAdministrativelyProhibited = 1,
11 | BeyondScopeOfSourceAddress = 2,
12 | AddressUnreachable = 3,
13 | PortUnreachable = 4,
14 | SourceAddressFailedPolicy = 5,
15 | RejectRouteToDestination = 6,
16 | SourceRoutingHeaderError = 7,
17 | #pragma warning restore 1591
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/WindivertDotnet/IdSeqNum.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | namespace WindivertDotnet
5 | {
6 | ///
7 | /// id或序列号生成器
8 | ///
9 | public sealed class IdSeqNum
10 | {
11 | private static readonly Random random = new();
12 | private int value = random.Next();
13 |
14 | ///
15 | /// 获取公共实例
16 | ///
17 | public static IdSeqNum Shared { get; } = new IdSeqNum();
18 |
19 | ///
20 | /// 生成下一个UInt16值
21 | ///
22 | ///
23 | public ushort NextUInt16() => (ushort)Interlocked.Increment(ref value);
24 |
25 | ///
26 | /// 生成下一个UInt32值
27 | ///
28 | ///
29 | public uint NextUInt32() => (uint)Interlocked.Increment(ref value);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/WindivertDotnet/Runtime/InteropServices/IPHelpApiNative.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Versioning;
2 | using WindivertDotnet;
3 |
4 | namespace System.Runtime.InteropServices
5 | {
6 | [SupportedOSPlatform("windows")]
7 | unsafe static class IPHelpApiNative
8 | {
9 | private const string library = "iphlpapi.dll";
10 |
11 | [DllImport(library)]
12 | public extern static int GetBestInterfaceEx(
13 | ref SockAddress dstSockAddr,
14 | out int index);
15 |
16 | [DllImport(library)]
17 | public static extern int GetBestRoute2(
18 | IntPtr interfaceLuid,
19 | int interfaceIndex,
20 | SockAddress* pSrcSocketAddr,
21 | ref SockAddress dstSocketAddr,
22 | uint addressSortOptions,
23 | byte* pBestRoute, // 103字节
24 | ref SockAddress bestSrcSocketAddr);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/WindivertDotnet/Runtime/InteropServices/Kernel32Native.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Versioning;
2 | using System.Threading;
3 |
4 | namespace System.Runtime.InteropServices
5 | {
6 | [SupportedOSPlatform("windows")]
7 | static class Kernel32Native
8 | {
9 | private const string library = "kernel32.dll";
10 |
11 | [DllImport(library, SetLastError = true, CharSet = CharSet.Ansi)]
12 | public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
13 |
14 |
15 | [DllImport(library, SetLastError = true)]
16 | public unsafe static extern bool CancelIoEx(SafeHandle handle, NativeOverlapped* lpOverlapped);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/WindivertDotnet/Runtime/InteropServices/MemoryNative.cs:
--------------------------------------------------------------------------------
1 | namespace System.Runtime.InteropServices
2 | {
3 | ///
4 | /// 提供非托管内容的申请和释放
5 | ///
6 | static unsafe class MemoryNative
7 | {
8 | #if NET6_0_OR_GREATER
9 | public static IntPtr AllocZeroed(int size)
10 | {
11 | var pointer = NativeMemory.AllocZeroed((uint)size);
12 | return new IntPtr(pointer);
13 | }
14 |
15 | public static void Free(IntPtr handle)
16 | {
17 | NativeMemory.Free(handle.ToPointer());
18 | }
19 | #else
20 | public static IntPtr AllocZeroed(int size)
21 | {
22 | var handle = Marshal.AllocHGlobal(size);
23 | new Span(handle.ToPointer(), size).Clear();
24 | return handle;
25 | }
26 |
27 | public static void Free(IntPtr handle)
28 | {
29 | Marshal.FreeHGlobal(handle);
30 | }
31 | #endif
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/WindivertDotnet/Runtime/InteropServices/WinDivertNative.Loader.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xljiulang/WindivertDotnet/283b5f4c321cb869d39efbd28be1f6f5847d2c83/WindivertDotnet/Runtime/InteropServices/WinDivertNative.Loader.cs
--------------------------------------------------------------------------------
/WindivertDotnet/Runtime/InteropServices/WinDivertNative.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Versioning;
2 | using System.Threading;
3 | using WindivertDotnet;
4 |
5 | namespace System.Runtime.InteropServices
6 | {
7 | [SupportedOSPlatform("windows")]
8 | static unsafe partial class WinDivertNative
9 | {
10 | private const string library = "WinDivert.dll";
11 |
12 |
13 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
14 | public static extern IntPtr WinDivertOpen(
15 | [MarshalAs(UnmanagedType.LPStr)] string filter,
16 | WinDivertLayer layer,
17 | short priority,
18 | WinDivertFlag flags);
19 |
20 |
21 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
22 | public static extern bool WinDivertClose(
23 | IntPtr handle);
24 |
25 |
26 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
27 | public static extern bool WinDivertRecvEx(
28 | WinDivert divert,
29 | WinDivertPacket packet,
30 | int packetLen,
31 | int* pRecvLen,
32 | ulong flags,
33 | WinDivertAddress addr,
34 | int* pAddrLen,
35 | NativeOverlapped* lpOverlapped);
36 |
37 |
38 |
39 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
40 | public static extern bool WinDivertSendEx(
41 | WinDivert divert,
42 | WinDivertPacket packet,
43 | int packetLen,
44 | int* pSendLen,
45 | ulong flags,
46 | WinDivertAddress addr,
47 | int addrLen,
48 | NativeOverlapped* lpOverlapped);
49 |
50 |
51 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
52 | public static extern bool WinDivertShutdown(
53 | WinDivert divert,
54 | WinDivertShutdown how);
55 |
56 |
57 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
58 | public static extern bool WinDivertSetParam(
59 | WinDivert divert,
60 | WinDivertParam param1,
61 | long value);
62 |
63 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
64 | public static extern bool WinDivertGetParam(
65 | WinDivert divert,
66 | WinDivertParam param1,
67 | ref long pValue);
68 |
69 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
70 | public static extern bool WinDivertHelperFormatFilter(
71 | [MarshalAs(UnmanagedType.LPStr)] string filter,
72 | WinDivertLayer layer,
73 | void* buffer,
74 | int bufLen);
75 |
76 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
77 | public static extern bool WinDivertHelperCompileFilter(
78 | [MarshalAs(UnmanagedType.LPStr)] string filter,
79 | WinDivertLayer layer,
80 | void* obj,
81 | int objLen,
82 | out IntPtr errorStr,
83 | out int errorPos);
84 |
85 |
86 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
87 | public static extern int WinDivertHelperHashPacket(
88 | WinDivertPacket packet,
89 | int packetLen,
90 | long seed);
91 |
92 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
93 | public static extern bool WinDivertHelperParsePacket(
94 | WinDivertPacket packet,
95 | int packetLen,
96 | IPV4Header** ppIpHdr,
97 | IPV6Header** ppIpv6Hdr,
98 | byte* pProtocol,
99 | IcmpV4Header** ppIcmpHdr,
100 | IcmpV6Header** ppIcmpv6Hdr,
101 | TcpHeader** ppTcpHdr,
102 | UdpHeader** ppUdpHdr,
103 | byte** ppData,
104 | int* pDataLen,
105 | byte** ppNext,
106 | int* pNextLen);
107 |
108 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
109 | public static extern bool WinDivertHelperCalcChecksums(
110 | WinDivertPacket packet,
111 | int packetLen,
112 | WinDivertAddress addr,
113 | ChecksumsFlag flags);
114 |
115 | [DllImport(library, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
116 | public static extern bool WinDivertHelperDecrementTTL(
117 | WinDivertPacket packet,
118 | int packetLen);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/WindivertDotnet/Runtime/Versioning/SupportedOSPlatformAttribute.cs:
--------------------------------------------------------------------------------
1 | #if NETCOREAPP3_1
2 | namespace System.Runtime.Versioning
3 | {
4 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)]
5 | sealed class SupportedOSPlatformAttribute : Attribute
6 | {
7 | public string PlatformName { get; }
8 |
9 | public SupportedOSPlatformAttribute(string platformName)
10 | {
11 | this.PlatformName = platformName;
12 | }
13 | }
14 | }
15 | #endif
--------------------------------------------------------------------------------
/WindivertDotnet/SockAddress.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Net;
4 | using System.Net.Sockets;
5 |
6 | namespace WindivertDotnet
7 | {
8 | ///
9 | /// SockAddress结构体
10 | ///
11 | unsafe struct SockAddress
12 | {
13 | private const int V4_SIZE = 4;
14 | private const int V6_SIZE = 16;
15 |
16 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
17 | private ushort addressFamily;
18 |
19 | public ushort Port;
20 |
21 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
22 | private fixed byte addressV4[V4_SIZE];
23 |
24 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
25 | private fixed byte addressV6[V6_SIZE];
26 |
27 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
28 | private uint scopeId;
29 |
30 | ///
31 | /// 获取或设置IP
32 | ///
33 | ///
34 | public IPAddress IPAddress
35 | {
36 | get
37 | {
38 | var family = (AddressFamily)this.addressFamily;
39 | if (family == AddressFamily.InterNetwork)
40 | {
41 | fixed (void* ptr = this.addressV4)
42 | {
43 | return new IPAddress(new Span(ptr, V4_SIZE));
44 | }
45 | }
46 | else if (family == AddressFamily.InterNetworkV6)
47 | {
48 | fixed (void* ptr = this.addressV6)
49 | {
50 | return new IPAddress(new Span(ptr, V6_SIZE), this.scopeId);
51 | }
52 | }
53 | throw new NotSupportedException($"不支持的AddressFamily: {family}");
54 | }
55 | set
56 | {
57 | this.addressFamily = (ushort)value.AddressFamily;
58 | if (value.AddressFamily == AddressFamily.InterNetwork)
59 | {
60 | fixed (void* ptr = this.addressV4)
61 | {
62 | var span = new Span(ptr, V4_SIZE);
63 | value.TryWriteBytes(span, out _);
64 | }
65 | }
66 | else if (value.AddressFamily == AddressFamily.InterNetworkV6)
67 | {
68 | fixed (void* ptr = this.addressV6)
69 | {
70 | var span = new Span(ptr, V6_SIZE);
71 | value.TryWriteBytes(span, out _);
72 | this.scopeId = (uint)value.ScopeId;
73 | }
74 | }
75 | else
76 | {
77 | throw new NotSupportedException($"不支持AddressFamily: {value.AddressFamily}");
78 | }
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/WindivertDotnet/TcpFlag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WindivertDotnet
4 | {
5 | ///
6 | /// tcp标记位
7 | ///
8 | [Flags]
9 | public enum TcpFlag : byte
10 | {
11 | ///
12 | /// 结束位
13 | ///
14 | Fin = 0x1,
15 |
16 | ///
17 | /// 请求位
18 | ///
19 | Syn = 0x2,
20 |
21 | ///
22 | /// 重置位
23 | ///
24 | Rst = 0x4,
25 |
26 | ///
27 | /// 推送位
28 | ///
29 | Psh = 0x8,
30 |
31 | ///
32 | /// 确认位
33 | ///
34 | Ack = 0x10,
35 |
36 | ///
37 | /// 紧急位
38 | ///
39 | Urg = 0x20,
40 |
41 | ///
42 | /// 保留1
43 | ///
44 | Reserved1 = 0x40,
45 |
46 | ///
47 | /// 保留2
48 | ///
49 | Reserved2 = 0x80,
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/WindivertDotnet/TcpHeader.cs:
--------------------------------------------------------------------------------
1 | using System.Buffers.Binary;
2 | using System.Diagnostics;
3 |
4 | namespace WindivertDotnet
5 | {
6 | ///
7 | /// Tcp头
8 | ///
9 | [DebuggerDisplay("SrcPort = {SrcPort}, DstPort = {DstPort}, Flags = {Flags}, Size = {HdrLength * 4}")]
10 | public struct TcpHeader
11 | {
12 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
13 | private ushort srcPort;
14 |
15 | ///
16 | /// 获取或设置源端口
17 | ///
18 | public ushort SrcPort
19 | {
20 | get => BinaryPrimitives.ReverseEndianness(srcPort);
21 | set => srcPort = BinaryPrimitives.ReverseEndianness(value);
22 | }
23 |
24 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
25 | private ushort dstPort;
26 |
27 | ///
28 | /// 获取或设置目的端口
29 | ///
30 | public ushort DstPort
31 | {
32 | get => BinaryPrimitives.ReverseEndianness(dstPort);
33 | set => dstPort = BinaryPrimitives.ReverseEndianness(value);
34 | }
35 |
36 |
37 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
38 | private uint seqNum;
39 |
40 | ///
41 | /// 获取或设置序列码
42 | ///
43 | public uint SeqNum
44 | {
45 | get => BinaryPrimitives.ReverseEndianness(seqNum);
46 | set => seqNum = BinaryPrimitives.ReverseEndianness(value);
47 | }
48 |
49 |
50 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
51 | private uint ackNum;
52 |
53 | ///
54 | /// 获取或设置确认码
55 | ///
56 | public uint AckNum
57 | {
58 | get => BinaryPrimitives.ReverseEndianness(ackNum);
59 | set => ackNum = BinaryPrimitives.ReverseEndianness(value);
60 | }
61 |
62 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
63 | private byte bitfield;
64 |
65 |
66 | ///
67 | /// 保留
68 | ///
69 | public byte Reserved1
70 | {
71 | get => (byte)(bitfield & 0xFu);
72 | set => bitfield = (byte)((bitfield & ~0xFu) | (value & 0xFu));
73 | }
74 |
75 | ///
76 | /// 获取或设置Internet Header Length
77 | /// Tcp头总字节为该值的4倍
78 | ///
79 | public byte HdrLength
80 | {
81 | get => (byte)((bitfield >> 4) & 0xFu);
82 | set => bitfield = (byte)((bitfield & ~(0xFu << 4)) | ((value & 0xFu) << 4));
83 | }
84 |
85 | ///
86 | /// 标记位
87 | ///
88 | public TcpFlag Flags;
89 |
90 |
91 | ///
92 | /// 获取或设置结束位
93 | ///
94 | public bool Fin
95 | {
96 | get => this.Flags.HasFlag(TcpFlag.Fin);
97 | set => this.SetFlag(TcpFlag.Fin, value);
98 | }
99 |
100 | ///
101 | /// 获取或设置请求位
102 | ///
103 | public bool Syn
104 | {
105 | get => this.Flags.HasFlag(TcpFlag.Syn);
106 | set => this.SetFlag(TcpFlag.Syn, value);
107 | }
108 |
109 | ///
110 | /// 获取或设置重置位
111 | ///
112 | public bool Rst
113 | {
114 | get => this.Flags.HasFlag(TcpFlag.Rst);
115 | set => this.SetFlag(TcpFlag.Rst, value);
116 | }
117 |
118 | ///
119 | /// 获取或设置推送位
120 | ///
121 | public bool Psh
122 | {
123 | get => this.Flags.HasFlag(TcpFlag.Psh);
124 | set => this.SetFlag(TcpFlag.Psh, value);
125 | }
126 |
127 | ///
128 | /// 获取或设置确认位
129 | ///
130 | public bool Ack
131 | {
132 | get => this.Flags.HasFlag(TcpFlag.Ack);
133 | set => this.SetFlag(TcpFlag.Ack, value);
134 | }
135 |
136 | ///
137 | /// 获取或设置紧急位
138 | ///
139 | public bool Urg
140 | {
141 | get => this.Flags.HasFlag(TcpFlag.Urg);
142 | set => this.SetFlag(TcpFlag.Urg, value);
143 | }
144 |
145 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
146 | private ushort window;
147 |
148 | ///
149 | /// 获取或设置滑动窗口
150 | ///
151 | public ushort Window
152 | {
153 | get => BinaryPrimitives.ReverseEndianness(window);
154 | set => window = BinaryPrimitives.ReverseEndianness(value);
155 | }
156 |
157 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
158 | private ushort checksum;
159 |
160 | ///
161 | /// 获取或设置校验和
162 | ///
163 | public ushort Checksum
164 | {
165 | get => BinaryPrimitives.ReverseEndianness(checksum);
166 | set => checksum = BinaryPrimitives.ReverseEndianness(value);
167 | }
168 |
169 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
170 | private ushort urgPtr;
171 |
172 | ///
173 | /// 获取或设置紧急指针
174 | ///
175 | public ushort UrgPtr
176 | {
177 | get => BinaryPrimitives.ReverseEndianness(urgPtr);
178 | set => urgPtr = BinaryPrimitives.ReverseEndianness(value);
179 | }
180 |
181 | private void SetFlag(TcpFlag flag, bool value)
182 | {
183 | if (value)
184 | {
185 | this.Flags |= flag;
186 | }
187 | else
188 | {
189 | this.Flags &= ~flag;
190 | }
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/WindivertDotnet/UdpHeader.cs:
--------------------------------------------------------------------------------
1 | using System.Buffers.Binary;
2 | using System.Diagnostics;
3 |
4 | namespace WindivertDotnet
5 | {
6 | ///
7 | /// Udp头
8 | ///
9 | [DebuggerDisplay("SrcPort = {SrcPort}, DstPort = {DstPort}, Size = {sizeof(UdpHeader)}")]
10 | public struct UdpHeader
11 | {
12 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
13 | private ushort srcPort;
14 |
15 | ///
16 | /// 获取或设置源端口
17 | ///
18 | public ushort SrcPort
19 | {
20 | get => BinaryPrimitives.ReverseEndianness(srcPort);
21 | set => srcPort = BinaryPrimitives.ReverseEndianness(value);
22 | }
23 |
24 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
25 | private ushort dstPort;
26 |
27 | ///
28 | /// 获取或设置目的端口
29 | ///
30 | public ushort DstPort
31 | {
32 | get => BinaryPrimitives.ReverseEndianness(dstPort);
33 | set => dstPort = BinaryPrimitives.ReverseEndianness(value);
34 | }
35 |
36 |
37 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
38 | private ushort length;
39 |
40 | ///
41 | /// 获取或设置Udp包长度
42 | /// [含头部的8字节]
43 | ///
44 | public ushort Length
45 | {
46 | get => BinaryPrimitives.ReverseEndianness(length);
47 | set => length = BinaryPrimitives.ReverseEndianness(value);
48 | }
49 |
50 |
51 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
52 | private ushort checksum;
53 |
54 |
55 | ///
56 | /// 获取或设置校验和
57 | ///
58 | public ushort Checksum
59 | {
60 | get => BinaryPrimitives.ReverseEndianness(checksum);
61 | set => checksum = BinaryPrimitives.ReverseEndianness(value);
62 | }
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivert.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32.SafeHandles;
2 | using System;
3 | using System.ComponentModel;
4 | using System.Diagnostics;
5 | using System.Runtime.InteropServices;
6 | using System.Runtime.Versioning;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace WindivertDotnet
11 | {
12 | ///
13 | /// 表示WinDivert的操作对象
14 | ///
15 | [SupportedOSPlatform("windows")]
16 | [DebuggerDisplay("Filter = {Filter}, Layer = {Layer}")]
17 | public class WinDivert : SafeHandleZeroOrMinusOneIsInvalid
18 | {
19 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
20 | private readonly Lazy boundHandleLazy;
21 |
22 | ///
23 | /// 获取过滤器
24 | ///
25 | public string Filter { get; }
26 |
27 | ///
28 | /// 获取工作层
29 | ///
30 | public WinDivertLayer Layer { get; }
31 |
32 | ///
33 | /// 获取优先级
34 | ///
35 | public short Priority { get; }
36 |
37 | ///
38 | /// 获取工作方式标志
39 | ///
40 | public WinDivertFlag Flags { get; }
41 |
42 | ///
43 | /// 获取软件版本
44 | ///
45 | public Version Version { get; }
46 |
47 |
48 | ///
49 | /// 获取或设置列队的容量大小
50 | /// 默认为4096,范围为[32,16384]
51 | ///
52 | ///
53 | ///
54 | public long QueueLength
55 | {
56 | get => this.GetParam(WinDivertParam.QueueLength);
57 | set => this.SetParam(WinDivertParam.QueueLength, value);
58 | }
59 |
60 | ///
61 | /// 获取或设自动丢弃数据包之前可以排队的最短时长
62 | /// 默认为2s,范围为100ms到16s
63 | ///
64 | ///
65 | ///
66 | public TimeSpan QueueTime
67 | {
68 | get => TimeSpan.FromMilliseconds(this.GetParam(WinDivertParam.QueueTime));
69 | set => this.SetParam(WinDivertParam.QueueTime, (long)value.TotalMilliseconds);
70 | }
71 |
72 | ///
73 | /// 获取或设置存储在数据包队列中的最大字节数
74 | /// 默认4MB,范围为64KB到32MB
75 | ///
76 | ///
77 | ///
78 | public long QueueSize
79 | {
80 | get => this.GetParam(WinDivertParam.QueueSize);
81 | set => this.SetParam(WinDivertParam.QueueSize, value);
82 | }
83 |
84 | ///
85 | /// 创建一个WinDivert实例
86 | ///
87 | /// 过滤器 https://reqrypt.org/windivert-doc.html#filter_language
88 | /// 工作层
89 | /// 优先级,值越大优先级越高[-30000,30000]
90 | /// 标记
91 | ///
92 | public WinDivert(Filter filter, WinDivertLayer layer, short priority = 0, WinDivertFlag flags = WinDivertFlag.None)
93 | : this(filter.ToString(), layer, priority, flags)
94 | {
95 | }
96 |
97 | ///
98 | /// 创建一个WinDivert实例
99 | ///
100 | /// 过滤器 https://reqrypt.org/windivert-doc.html#filter_language
101 | /// 工作层
102 | /// 优先级,值越大优先级越高[-30000,30000]
103 | /// 标记
104 | ///
105 | ///
106 | ///
107 | public WinDivert(string filter, WinDivertLayer layer, short priority = 0, WinDivertFlag flags = WinDivertFlag.None)
108 | : base(ownsHandle: true)
109 | {
110 | if (layer != WinDivertLayer.Network && flags == WinDivertFlag.None)
111 | {
112 | flags = WinDivertFlag.Sniff | WinDivertFlag.RecvOnly;
113 | }
114 |
115 | var compileFilter = WindivertDotnet.Filter.Compile(filter, layer);
116 |
117 | if (Enum.IsDefined(typeof(WinDivertLayer), layer) == false)
118 | {
119 | throw new ArgumentOutOfRangeException(nameof(layer));
120 | }
121 |
122 | if (priority < -30000 || priority > 30000)
123 | {
124 | throw new ArgumentOutOfRangeException(nameof(priority), "值范围为[-30000,30000]");
125 | }
126 |
127 | this.handle = WinDivertNative.WinDivertOpen(compileFilter, layer, priority, flags);
128 | this.boundHandleLazy = new Lazy(() => ThreadPoolBoundHandle.BindHandle(this));
129 |
130 | if (this.IsInvalid == true)
131 | {
132 | throw new Win32Exception();
133 | }
134 |
135 | this.Filter = WindivertDotnet.Filter.Format(filter, layer);
136 | this.Layer = layer;
137 | this.Priority = priority;
138 | this.Flags = flags;
139 | this.Version = this.GetVersion();
140 | }
141 |
142 | ///
143 | /// 获取版本信息
144 | ///
145 | ///
146 | private Version GetVersion()
147 | {
148 | var major = this.GetParam(WinDivertParam.VersionMajor);
149 | var minor = this.GetParam(WinDivertParam.VersionMinor);
150 | return new Version((int)major, (int)minor);
151 | }
152 |
153 | ///
154 | /// 获取线程池绑定句柄
155 | ///
156 | ///
157 | internal ThreadPoolBoundHandle GetThreadPoolBoundHandle()
158 | {
159 | return this.boundHandleLazy.Value;
160 | }
161 |
162 | ///
163 | /// 同步阻塞读取数据包
164 | ///
165 | /// 数据包
166 | /// 地址信息
167 | ///
168 | ///
169 | public int Recv(WinDivertPacket packet, WinDivertAddress addr)
170 | {
171 | return this.Recv(packet, addr, CancellationToken.None);
172 | }
173 |
174 | ///
175 | /// 同步阻塞读取数据包
176 | ///
177 | /// 数据包
178 | /// 地址信息
179 | /// 取消令牌
180 | ///
181 | ///
182 | ///
183 | public int Recv(WinDivertPacket packet, WinDivertAddress addr, CancellationToken cancellationToken)
184 | {
185 | return this.RecvAsync(packet, addr, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
186 | }
187 |
188 | ///
189 | /// 异步IO读取数据包
190 | ///
191 | /// 数据包
192 | /// 地址信息
193 | ///
194 | ///
195 | public ValueTask RecvAsync(WinDivertPacket packet, WinDivertAddress addr)
196 | {
197 | return this.RecvAsync(packet, addr, CancellationToken.None);
198 | }
199 |
200 | ///
201 | /// 异步IO读取数据包
202 | ///
203 | /// 数据包
204 | /// 地址信息
205 | /// 取消令牌
206 | ///
207 | ///
208 | ///
209 | public async ValueTask RecvAsync(WinDivertPacket packet, WinDivertAddress addr, CancellationToken cancellationToken)
210 | {
211 | using var operation = new WinDivertRecvOperation(this, packet, addr);
212 | return await operation.IOControlAsync(cancellationToken);
213 | }
214 |
215 | ///
216 | /// 同步阻塞发送数据包
217 | ///
218 | /// 数据包
219 | /// 地址信息
220 | ///
221 | ///
222 | ///
223 | public int Send(WinDivertPacket packet, WinDivertAddress addr)
224 | {
225 | return this.Send(packet, addr, CancellationToken.None);
226 | }
227 |
228 | ///
229 | /// 同步阻塞发送数据包
230 | ///
231 | /// 数据包
232 | /// 地址信息
233 | /// 取消令牌
234 | ///
235 | ///
236 | ///
237 | ///
238 | public int Send(WinDivertPacket packet, WinDivertAddress addr, CancellationToken cancellationToken)
239 | {
240 | return this.SendAsync(packet, addr, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
241 | }
242 |
243 | ///
244 | /// 异步IO发送数据包
245 | ///
246 | /// 数据包
247 | /// 地址信息
248 | ///
249 | ///
250 | ///
251 | public ValueTask SendAsync(WinDivertPacket packet, WinDivertAddress addr)
252 | {
253 | return this.SendAsync(packet, addr, CancellationToken.None);
254 | }
255 |
256 | ///
257 | /// 异步IO发送数据包
258 | ///
259 | /// 数据包
260 | /// 地址信息
261 | /// 取消令牌
262 | ///
263 | ///
264 | ///
265 | ///
266 | public async ValueTask SendAsync(WinDivertPacket packet, WinDivertAddress addr, CancellationToken cancellationToken)
267 | {
268 | if (packet.Length == 0)
269 | {
270 | throw new ArgumentOutOfRangeException(nameof(packet), $"{nameof(WinDivertPacket.Length)}不能为0");
271 | }
272 |
273 | using var operation = new WinDivertSendOperation(this, packet, addr);
274 | return await operation.IOControlAsync(cancellationToken);
275 | }
276 |
277 | ///
278 | /// 获取指定的参数值
279 | ///
280 | ///
281 | ///
282 | ///
283 | ///
284 | private long GetParam(WinDivertParam param)
285 | {
286 | if (this.boundHandleLazy.IsValueCreated)
287 | {
288 | throw new InvalidOperationException();
289 | }
290 |
291 | var value = 0L;
292 | var status = WinDivertNative.WinDivertGetParam(this, param, ref value);
293 | return status ? value : throw new Win32Exception();
294 | }
295 |
296 | ///
297 | /// 设置指定的参数值
298 | ///
299 | ///
300 | ///
301 | ///
302 | ///
303 | private void SetParam(WinDivertParam param, long value)
304 | {
305 | if (this.boundHandleLazy.IsValueCreated)
306 | {
307 | throw new InvalidOperationException();
308 | }
309 |
310 | if (param == WinDivertParam.QueueSize)
311 | {
312 | if (value < 65535L || value > 33554432L)
313 | {
314 | throw new ArgumentOutOfRangeException(nameof(value));
315 | }
316 | }
317 |
318 | if (param == WinDivertParam.QueueTime)
319 | {
320 | if (value < 100L || value > 16000L)
321 | {
322 | throw new ArgumentOutOfRangeException(nameof(value));
323 | }
324 | }
325 |
326 | if (param == WinDivertParam.QueueLength)
327 | {
328 | if (value < 32L || value > 16384L)
329 | {
330 | throw new ArgumentOutOfRangeException(nameof(value));
331 | }
332 | }
333 |
334 | if (WinDivertNative.WinDivertSetParam(this, param, value) == false)
335 | {
336 | throw new Win32Exception();
337 | }
338 | }
339 |
340 | ///
341 | /// 关闭
342 | ///
343 | /// 关闭方式
344 | ///
345 | public bool Shutdown(WinDivertShutdown how = WinDivertShutdown.Both)
346 | {
347 | return WinDivertNative.WinDivertShutdown(this, how);
348 | }
349 |
350 | ///
351 | /// 释放句柄
352 | ///
353 | ///
354 | protected override bool ReleaseHandle()
355 | {
356 | return WinDivertNative.WinDivertClose(this.handle);
357 | }
358 |
359 | ///
360 | /// 释放资源
361 | ///
362 | ///
363 | protected override void Dispose(bool disposing)
364 | {
365 | if (this.boundHandleLazy.IsValueCreated)
366 | {
367 | this.boundHandleLazy.Value.Dispose();
368 | }
369 | base.Dispose(disposing);
370 | }
371 | }
372 | }
373 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertAddress.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32.SafeHandles;
2 | using System;
3 | using System.Diagnostics;
4 | using System.Runtime.InteropServices;
5 |
6 | namespace WindivertDotnet
7 | {
8 | ///
9 | /// 表示WinDivert地址信息
10 | ///
11 | [DebuggerDisplay("Flags = {Flags}")]
12 | public unsafe class WinDivertAddress : SafeHandleZeroOrMinusOneIsInvalid, ICloneable
13 | {
14 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
15 | private AddressStruct* Pointer => (AddressStruct*)this.handle.ToPointer();
16 |
17 | ///
18 | /// 获取WinDivertAddress结构大小
19 | ///
20 | public static int Size { get; } = sizeof(AddressStruct);
21 |
22 | ///
23 | /// 获取或设置发生的时间戳
24 | ///
25 | public long Timestamp
26 | {
27 | get => this.Pointer->Timestamp;
28 | set => this.Pointer->Timestamp = value;
29 | }
30 |
31 | ///
32 | /// 获取或设置所在层
33 | ///
34 | public WinDivertLayer Layer
35 | {
36 | get => this.Pointer->Layer;
37 | set => this.Pointer->Layer = value;
38 | }
39 |
40 | ///
41 | /// 获取或设置事件类型
42 | ///
43 | public WinDivertEvent Event
44 | {
45 | get => this.Pointer->Event;
46 | set => this.Pointer->Event = value;
47 | }
48 |
49 | ///
50 | /// 获取或设置标记
51 | ///
52 | public WinDivertAddressFlag Flags
53 | {
54 | get => this.Pointer->Flags;
55 | set => this.Pointer->Flags = value;
56 | }
57 |
58 | ///
59 | /// 获取或设置保留
60 | ///
61 | public byte Reserved
62 | {
63 | get => this.Pointer->Reserved;
64 | set => this.Pointer->Reserved = value;
65 | }
66 |
67 | ///
68 | /// 获取或设置保留
69 | ///
70 | public uint Reserved2
71 | {
72 | get => this.Pointer->Reserved2;
73 | set => this.Pointer->Reserved2 = value;
74 | }
75 |
76 | ///
77 | /// 获取网络信息的指针
78 | ///
79 | public WinDivertDataNetwork* Network => &this.Pointer->Network;
80 |
81 | ///
82 | /// 获取网络流信息的指针
83 | ///
84 | public WinDivertDataFlow* Flow => &this.Pointer->Flow;
85 |
86 | ///
87 | /// 获取套接字信息的指针
88 | ///
89 | public WinDivertDataSocket* Socket => &this.Pointer->Socket;
90 |
91 | ///
92 | /// 获取WinDivert信息的指针
93 | ///
94 | public WinDivertDataReflect* Reflect => &this.Pointer->Reflect;
95 |
96 | ///
97 | /// WinDivert地址信息
98 | ///
99 | public WinDivertAddress()
100 | : base(ownsHandle: true)
101 | {
102 | this.handle = MemoryNative.AllocZeroed(Size);
103 | }
104 |
105 | ///
106 | /// 释放资源
107 | ///
108 | ///
109 | protected override bool ReleaseHandle()
110 | {
111 | MemoryNative.Free(this.handle);
112 | return true;
113 | }
114 |
115 | ///
116 | /// 清除数据
117 | ///
118 | public void Clear()
119 | {
120 | new Span(this.handle.ToPointer(), Size).Clear();
121 | }
122 |
123 | ///
124 | /// 克隆
125 | ///
126 | ///
127 | public WinDivertAddress Clone()
128 | {
129 | var addr = new WinDivertAddress();
130 | *addr.Pointer = *this.Pointer;
131 | return addr;
132 | }
133 |
134 | object ICloneable.Clone()
135 | {
136 | return this.Clone();
137 | }
138 |
139 | [StructLayout(LayoutKind.Explicit)]
140 | private struct AddressStruct
141 | {
142 | [FieldOffset(0)]
143 | public long Timestamp;
144 |
145 | [FieldOffset(8)]
146 | public WinDivertLayer Layer;
147 |
148 | [FieldOffset(9)]
149 | public WinDivertEvent Event;
150 |
151 | [FieldOffset(10)]
152 | public WinDivertAddressFlag Flags;
153 |
154 | [FieldOffset(11)]
155 | public byte Reserved;
156 |
157 | [FieldOffset(12)]
158 | public uint Reserved2;
159 |
160 | [FieldOffset(16)]
161 | public WinDivertDataNetwork Network;
162 |
163 | [FieldOffset(16)]
164 | public WinDivertDataFlow Flow;
165 |
166 | [FieldOffset(16)]
167 | public WinDivertDataSocket Socket;
168 |
169 | [FieldOffset(16)]
170 | public WinDivertDataReflect Reflect;
171 |
172 | [FieldOffset(16)]
173 | public fixed byte Reserved3[64];
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertAddressFlag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WindivertDotnet
4 | {
5 | ///
6 | /// WinDivertAddress标记
7 | ///
8 | [Flags]
9 | public enum WinDivertAddressFlag : byte
10 | {
11 | ///
12 | /// 无
13 | ///
14 | None = 0,
15 |
16 | ///
17 | /// 嗅探事件模式
18 | ///
19 | Sniffed = 0x1,
20 |
21 | ///
22 | /// 出站数据包
23 | ///
24 | Outbound = 0x2,
25 |
26 | ///
27 | /// 环回数据包
28 | ///
29 | Loopback = 0x4,
30 |
31 | ///
32 | /// 冒名顶替数据包
33 | ///
34 | Impostor = 0x8,
35 |
36 | ///
37 | /// IPv6 数据包
38 | ///
39 | IPv6 = 0x10,
40 |
41 | ///
42 | /// IPv4 校验和有效
43 | ///
44 | IPChecksum = 0x20,
45 |
46 | ///
47 | /// tcp 校验和有效
48 | ///
49 | TCPChecksum = 0x40,
50 |
51 | ///
52 | /// udp 校验和有效
53 | ///
54 | UDPChecksum = 0x80
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertDataFlow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Net;
4 | using System.Net.Sockets;
5 |
6 | namespace WindivertDotnet
7 | {
8 | ///
9 | /// 网络流信息
10 | ///
11 | public unsafe struct WinDivertDataFlow
12 | {
13 | private const int IPV6_SIZE = sizeof(int) * 4;
14 |
15 | ///
16 | /// 流的终结点ID
17 | ///
18 | public long EndpointId;
19 |
20 | ///
21 | /// 流的父终结点ID
22 | ///
23 | public long ParentEndpointId;
24 |
25 | ///
26 | /// 与流关联的进程的ID
27 | ///
28 | public int ProcessId;
29 |
30 | ///
31 | /// 本机地址
32 | ///
33 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
34 | private fixed byte localAddr[IPV6_SIZE];
35 |
36 | ///
37 | /// 本机地址
38 | ///
39 | public IPAddress LocalAddr
40 | {
41 | get
42 | {
43 | fixed (void* ptr = this.localAddr)
44 | {
45 | return GetIPAddress(ptr);
46 | }
47 | }
48 | set
49 | {
50 | fixed (void* ptr = this.localAddr)
51 | {
52 | SetIPAddress(ptr, value);
53 | }
54 | }
55 | }
56 |
57 | ///
58 | /// 远程地址
59 | ///
60 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
61 | private fixed byte remoteAddr[IPV6_SIZE];
62 |
63 | ///
64 | /// 远程地址
65 | ///
66 | public IPAddress RemoteAddr
67 | {
68 | get
69 | {
70 | fixed (void* ptr = this.remoteAddr)
71 | {
72 | return GetIPAddress(ptr);
73 | }
74 | }
75 | set
76 | {
77 | fixed (void* ptr = this.remoteAddr)
78 | {
79 | SetIPAddress(ptr, value);
80 | }
81 | }
82 | }
83 |
84 | ///
85 | /// 本机端口
86 | ///
87 | public ushort LocalPort;
88 |
89 | ///
90 | /// 远程端口
91 | ///
92 | public ushort RemotePort;
93 |
94 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
95 | private byte protocol;
96 |
97 | ///
98 | /// 协议类型
99 | ///
100 | public ProtocolType Protocol
101 | {
102 | get => (ProtocolType)protocol;
103 | set => protocol = (byte)value;
104 | }
105 |
106 | private static IPAddress GetIPAddress(void* ptr)
107 | {
108 | var span = new Span(ptr, IPV6_SIZE);
109 | Span value = stackalloc byte[IPV6_SIZE];
110 | span.CopyTo(value);
111 | value.Reverse();
112 | return new IPv6Address(value);
113 | }
114 |
115 | private static void SetIPAddress(void* ptr, IPAddress addr)
116 | {
117 | var span = new Span(ptr, IPV6_SIZE);
118 | addr.TryWriteBytes(span, out _);
119 | span.Reverse();
120 | }
121 |
122 | private class IPv6Address : IPAddress
123 | {
124 | public IPv6Address(ReadOnlySpan value)
125 | : base(value)
126 | {
127 | }
128 | public override string ToString()
129 | {
130 | return base.ToString();
131 | }
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertDataNetwork.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// 网络信息
5 | ///
6 | public struct WinDivertDataNetwork
7 | {
8 | ///
9 | /// 数据包到达的网络接口索引
10 | ///
11 | public int IfIdx;
12 |
13 | ///
14 | /// 数据包到达的网络子接口索引
15 | ///
16 | public int SubIfIdx;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertDataReflect.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// WinDivert信息
5 | ///
6 | public struct WinDivertDataReflect
7 | {
8 | ///
9 | /// WinDivert句柄打开时间的时间戳
10 | ///
11 | public long Timestamp;
12 |
13 | ///
14 | /// WinDivert打开句柄的进程的ID
15 | ///
16 | public int ProcessId;
17 |
18 | ///
19 | /// WinDivert工作层
20 | ///
21 | public WinDivertLayer Layer;
22 |
23 | ///
24 | /// WinDivert的Flags
25 | ///
26 | public WinDivertFlag Flags;
27 |
28 | ///
29 | /// WinDivert的优先级
30 | ///
31 | public short Priority;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertDataSocket.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Net.Sockets;
6 |
7 | namespace WindivertDotnet
8 | {
9 | ///
10 | /// Socket信息
11 | ///
12 | public unsafe struct WinDivertDataSocket
13 | {
14 | private const int IPV6_SIZE = sizeof(int) * 4;
15 |
16 | ///
17 | /// 套接字操作的终结点 ID
18 | ///
19 | public long EndpointId;
20 |
21 | ///
22 | /// 套接字操作的父终结点 ID
23 | ///
24 | public long ParentEndpointId;
25 |
26 | ///
27 | /// 与套接字操作关联的进程的 ID
28 | ///
29 | public int ProcessId;
30 |
31 | ///
32 | /// 本机地址
33 | ///
34 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
35 | private fixed byte localAddr[IPV6_SIZE];
36 |
37 | ///
38 | /// 本机地址
39 | ///
40 | public IPAddress LocalAddr
41 | {
42 | get
43 | {
44 | fixed (void* ptr = this.localAddr)
45 | {
46 | return GetIPAddress(ptr);
47 | }
48 | }
49 | set
50 | {
51 | fixed (void* ptr = this.localAddr)
52 | {
53 | SetIPAddress(ptr, value);
54 | }
55 | }
56 | }
57 |
58 | ///
59 | /// 远程地址
60 | ///
61 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
62 | private fixed byte remoteAddr[IPV6_SIZE];
63 |
64 | ///
65 | /// 远程地址
66 | ///
67 | public IPAddress RemoteAddr
68 | {
69 | get
70 | {
71 | fixed (void* ptr = this.remoteAddr)
72 | {
73 | return GetIPAddress(ptr);
74 | }
75 | }
76 | set
77 | {
78 | fixed (void* ptr = this.remoteAddr)
79 | {
80 | SetIPAddress(ptr, value);
81 | }
82 | }
83 | }
84 |
85 | ///
86 | /// 本机端口
87 | ///
88 | public ushort LocalPort;
89 |
90 | ///
91 | /// 远程端口
92 | ///
93 | public ushort RemotePort;
94 |
95 | [DebuggerBrowsable(DebuggerBrowsableState.Never)]
96 | private byte protocol;
97 |
98 | ///
99 | /// 协议类型
100 | ///
101 | public ProtocolType Protocol
102 | {
103 | get => (ProtocolType)protocol;
104 | set => protocol = (byte)value;
105 | }
106 |
107 | private static IPAddress GetIPAddress(void* ptr)
108 | {
109 | var span = new Span(ptr, IPV6_SIZE);
110 | Span value = stackalloc byte[IPV6_SIZE];
111 | span.CopyTo(value);
112 | value.Reverse();
113 | return new IPv6Address(value);
114 | }
115 |
116 | private static void SetIPAddress(void* ptr, IPAddress addr)
117 | {
118 | var span = new Span(ptr, IPV6_SIZE);
119 | addr.TryWriteBytes(span, out _);
120 | span.Reverse();
121 | }
122 |
123 | private class IPv6Address : IPAddress
124 | {
125 | public IPv6Address(ReadOnlySpan value)
126 | : base(value)
127 | {
128 | }
129 | public override string ToString()
130 | {
131 | return base.ToString();
132 | }
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertEvent.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// 事件类型
5 | ///
6 | public enum WinDivertEvent : byte
7 | {
8 | ///
9 | /// 新的网络数据包
10 | ///
11 | NetworkPacket = 0,
12 |
13 | ///
14 | /// 将创建一个新流
15 | ///
16 | FlowEstablished = 1,
17 |
18 | ///
19 | /// 旧流将被删除
20 | ///
21 | FlowDeleted = 2,
22 |
23 | ///
24 | /// bind操作
25 | ///
26 | SocketBind = 3,
27 |
28 | ///
29 | /// connect操作
30 | ///
31 | SocketConnect = 4,
32 |
33 | ///
34 | /// listen操作
35 | ///
36 | SocketListen = 5,
37 |
38 | ///
39 | /// accept操作
40 | ///
41 | SocketAccept = 6,
42 |
43 | ///
44 | /// 套接字终结点已关闭
45 | ///
46 | SocketClose = 7,
47 |
48 | ///
49 | /// 打开了新的WinDivert实例
50 | ///
51 | ReflectOpen = 8,
52 |
53 | ///
54 | /// 旧的WinDivert实例被关闭
55 | ///
56 | ReflectClose = 9,
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertFlag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace WindivertDotnet
4 | {
5 | ///
6 | /// WinDivert方式标志
7 | ///
8 | [Flags]
9 | public enum WinDivertFlag : ulong
10 | {
11 | ///
12 | ///不指定
13 | ///
14 | None = 0,
15 |
16 | ///
17 | ///此标志在数据包嗅探模式下打开WinDivert句柄
18 | ///在数据包嗅探模式下,原始数据包不会被丢弃和转移(默认值),而是被复制和转移
19 | ///此模式对于实现类似于当前使用的应用程序的数据包探查工具非常有用
20 | ///
21 | Sniff = 0x0001,
22 |
23 | ///
24 | ///此标志指示用户应用程序不打算使用Recv相关方法读取匹配的数据包,而应以静默方式丢弃这些数据包
25 | ///这对于使用WinDivert筛选器语言实现简单的数据包筛选器非常有用
26 | ///
27 | Drop = 0x0002,
28 |
29 | ///
30 | ///此标志强制句柄进入仅接收模式,从而有效地禁用Send相关方法的功能
31 | ///这意味着可以阻止/捕获数据包或事件,但不能注入它们
32 | ///
33 | RecvOnly = 0x0004,
34 |
35 | ///
36 | ///等同于RecvOnly
37 | ///
38 | ReadOnly = RecvOnly,
39 |
40 | ///
41 | ///此标志强制句柄进入仅发送模式,从而有效地禁用Recv相关方法
42 | ///这意味着可以注入数据包或事件,但不能阻止/捕获它们
43 | ///
44 | SendOnly = 0x0008,
45 |
46 | ///
47 | ///等同于SendOnly
48 | ///
49 | WriteOnly = SendOnly,
50 |
51 | ///
52 | ///如果尚未安装WinDivert驱动程序,开启则此标志会产生对应的异常
53 | ///
54 | NoInstall = 0x0010,
55 |
56 | ///
57 | ///开启此标记,句柄将捕获入站IP片段,但不会捕获入站重新组合的IP数据包
58 | ///否则句柄将捕获入站重新组合的IP数据包,但不会捕获入站IP片段
59 | ///
60 | Fragments = 0x0020,
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertLayer.cs:
--------------------------------------------------------------------------------
1 | namespace WindivertDotnet
2 | {
3 | ///
4 | /// 工作层
5 | ///
6 | public enum WinDivertLayer : byte
7 | {
8 | ///
9 | /// 传入/传出本地计算机的网络数据包
10 | ///
11 | Network = 0,
12 |
13 | ///
14 | /// 通过本地计算机的网络数据包
15 | ///
16 | Forward = 1,
17 |
18 | ///
19 | /// 网络流已建立/已删除事件
20 | ///
21 | Flow = 2,
22 |
23 | ///
24 | /// 套接字操作事件
25 | ///
26 | Socket = 3,
27 |
28 | ///
29 | /// WinDivert处理事件
30 | ///
31 | Reflect = 4,
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/WindivertDotnet/WinDivertOperation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Runtime.InteropServices;
4 | using System.Runtime.Versioning;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using System.Threading.Tasks.Sources;
8 |
9 | namespace WindivertDotnet
10 | {
11 | ///
12 | /// Windivert控制器
13 | ///
14 | [SupportedOSPlatform("windows")]
15 | abstract class WinDivertOperation : IDisposable, IValueTaskSource
16 | {
17 | protected readonly WinDivert divert;
18 | private readonly unsafe NativeOverlapped* nativeOverlapped;
19 |
20 | private ManualResetValueTaskSourceCore taskSource; // 不能readonly
21 | private static readonly unsafe IOCompletionCallback completionCallback = new(IOCompletionCallback);
22 |
23 |
24 | ///
25 | /// Windivert控制器
26 | ///
27 | ///
28 | public unsafe WinDivertOperation(WinDivert divert)
29 | {
30 | this.divert = divert;
31 | this.nativeOverlapped = divert.GetThreadPoolBoundHandle().AllocateNativeOverlapped(completionCallback, this, null);
32 | }
33 |
34 | ///
35 | /// io控制
36 | ///
37 | ///
38 | ///
39 | public virtual async ValueTask IOControlAsync(CancellationToken cancellationToken)
40 | {
41 | cancellationToken.ThrowIfCancellationRequested();
42 | using (cancellationToken.Register(CancelIoEx))
43 | {
44 | return await IOControlAsync();
45 | }
46 |
47 | unsafe void CancelIoEx()
48 | {
49 | Kernel32Native.CancelIoEx(this.divert, this.nativeOverlapped);
50 | }
51 |
52 | unsafe ValueTask IOControlAsync()
53 | {
54 | var length = 0; // 如果触发异步回调,回调里不会反写pLength,所以这里可以声明为方法内部变量
55 | return this.IOControl(&length, this.nativeOverlapped)
56 | ? new ValueTask(length)
57 | : new ValueTask(this, this.taskSource.Version);
58 | }
59 | }
60 |
61 | ///
62 | /// io控制
63 | ///
64 | ///
65 | ///
66 | ///
67 | protected abstract unsafe bool IOControl(int* pLength, NativeOverlapped* nativeOverlapped);
68 |
69 | ///
70 | /// io完成回调
71 | ///
72 | ///
73 | ///
74 | ///
75 | private static unsafe void IOCompletionCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP)
76 | {
77 | var operation = (WinDivertOperation)ThreadPoolBoundHandle.GetNativeOverlappedState(pOVERLAP)!;
78 | if (errorCode == 0)
79 | {
80 | operation.taskSource.SetResult((int)numBytes);
81 | }
82 | else if (errorCode == 995) // ERROR_OPERATION_ABORTED
83 | {
84 | var exception = new TaskCanceledException();
85 | operation.taskSource.SetException(exception);
86 | }
87 | else
88 | {
89 | var exception = new Win32Exception((int)errorCode);
90 | operation.taskSource.SetException(exception);
91 | }
92 | }
93 |
94 | ///
95 | /// 释放资源
96 | ///
97 | public virtual unsafe void Dispose()
98 | {
99 | this.divert.GetThreadPoolBoundHandle().FreeNativeOverlapped(this.nativeOverlapped);
100 | }
101 |
102 | int IValueTaskSource.GetResult(short token)
103 | {
104 | return this.taskSource.GetResult(token);
105 | }
106 |
107 | ValueTaskSourceStatus IValueTaskSource.GetStatus(short token)
108 | {
109 | return this.taskSource.GetStatus(token);
110 | }
111 |
112 | void IValueTaskSource.OnCompleted(Action