Class WinDivertVpnAdapter
- Namespace
- VpnHood.Net.VpnAdapters.WinDivert
- Assembly
- VpnHood.Net.VpnAdapters.WinDivert.dll
Provides a Windows VPN adapter on WinDivert, a packet capture driver, with no interface of its own: it takes the host's outbound packets to the routed networks before they leave, and puts the VPN's packets back in as inbound.
public class WinDivertVpnAdapter : TunVpnAdapter, IVpnAdapter, IPacketTransport, IDisposable
- Inheritance
-
WinDivertVpnAdapter
- Implements
- Inherited Members
Remarks
It serves tests and debugging; a Windows app runs on WinTunVpnAdapter. A captured packet's source becomes
the adapter's virtual address, and a packet the VPN sends back goes in as inbound, addressed to the host's primary
address. The host's DNS settings stay as they are: with SimulateDns,
every outbound UDP packet to port 53, wherever it goes, is captured too, but loopback ones and those of
ProtectedTtl, and sent to one of the adapter's DNS servers of its IP version, if it has one; an answer
comes back from the address the latest query from its port was sent to, while that is kept.
It runs as an administrator only, on x64 Windows in an x64 process: the first start in a process installs the WinDivert DLL and driver it carries into a protected folder of their own under Common Files, or takes the ones installed there, and the driver stays loaded until a reboot or until someone stops it. A socket is kept out of the capture by its TTL, ProtectedTtl. It has no NAT, no app filter and no interface, so no address, MTU, metric or session name of its own: the virtual address only rewrites the captured packets. It writes no packet before it has captured one, whose header the written packets reuse.
Constructors
WinDivertVpnAdapter(WinDivertVpnAdapterSettings)
Provides a Windows VPN adapter on WinDivert, a packet capture driver, with no interface of its own: it takes the host's outbound packets to the routed networks before they leave, and puts the VPN's packets back in as inbound.
public WinDivertVpnAdapter(WinDivertVpnAdapterSettings adapterSettings)
Parameters
adapterSettingsWinDivertVpnAdapterSettingsThe adapter's settings.
Remarks
It serves tests and debugging; a Windows app runs on WinTunVpnAdapter. A captured packet's source becomes
the adapter's virtual address, and a packet the VPN sends back goes in as inbound, addressed to the host's primary
address. The host's DNS settings stay as they are: with SimulateDns,
every outbound UDP packet to port 53, wherever it goes, is captured too, but loopback ones and those of
ProtectedTtl, and sent to one of the adapter's DNS servers of its IP version, if it has one; an answer
comes back from the address the latest query from its port was sent to, while that is kept.
It runs as an administrator only, on x64 Windows in an x64 process: the first start in a process installs the WinDivert DLL and driver it carries into a protected folder of their own under Common Files, or takes the ones installed there, and the driver stays loaded until a reboot or until someone stops it. A socket is kept out of the capture by its TTL, ProtectedTtl. It has no NAT, no app filter and no interface, so no address, MTU, metric or session name of its own: the virtual address only rewrites the captured packets. It writes no packet before it has captured one, whose header the written packets reuse.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
Fields
ProtectedTtl
The TTL, or IPv6 hop limit, that marks a protected socket's packets, which the capture leaves alone: 111.
public const short ProtectedTtl = 111
Field Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
Properties
AppPackageId
Gets the ID the app filter knows this app by, which it always lets into the tunnel, or null where there is no app filter.
protected override string? AppPackageId { get; }
Property Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
CheckAdapterInterfaceAfterNetworkChanged
Gets a value indicating whether the adapter checks, at each change of the host's network, that its interface is still there, and stops when it is not. The check is IsAdapterInterfaceEnabled(), which by default looks for the interface by name or address only.
protected override bool CheckAdapterInterfaceAfterNetworkChanged { get; }
Property Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
IsAppFilterSupported
Gets a value indicating whether the adapter can include or exclude apps from the tunnel (IncludeApps, ExcludeApps).
public override bool IsAppFilterSupported { get; }
Property Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
IsSocketProtectedByBind
Gets a value indicating whether the adapter keeps a socket out of the tunnel by binding it to the primary address, rather than by an OS call of its own.
protected override bool IsSocketProtectedByBind { get; }
Property Value
Remarks
When true, the routes leave out the probe networks the adapter aims at to find the primary addresses, and that search runs on an unprotected socket after a network change.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
RestartAfterNetworkAddressChanged
Gets a value indicating whether the adapter restarts when the host moves to other networks. When false, it takes the new primary addresses and raises PrimaryNetworkChanged.
protected override bool RestartAfterNetworkAddressChanged { get; }
Property Value
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
Methods
AdapterAdd(CancellationToken)
Makes the interface: the first step of a start.
protected override Task AdapterAdd(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
There is no interface to make: the first start in a process loads WinDivert, installing it first where needed.
Exceptions
- PlatformNotSupportedException
The OS is not x64 Windows, or the process is not x64.
- UnauthorizedAccessException
The process is not an administrator's, or Common Files or the install folder is a link or one others than administrators may change, so WinDivert cannot be installed.
- InvalidOperationException
The package's WinDivert files are missing, or the installed ones are not the ones the package carries, are links, or are files others than administrators may change.
AdapterClose()
Closes the interface for reading and writing: the first step of a stop, under the base's stop lock. It may run after a partial start.
protected override void AdapterClose()
Remarks
A failure to close the capture is logged, not thrown.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
AdapterOpen(CancellationToken)
Opens the interface for reading and writing packets: the last step of a start, before the reader starts.
protected override Task AdapterOpen(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It opens the capture of the host's outbound packets, but loopback ones and those of ProtectedTtl: those to the routes' networks, less the local networks with ExcludeLocalNetwork, and, with SimulateDns, every UDP packet to port 53, local or not. The first open loads WinDivert's driver.
Exceptions
- InvalidOperationException
A stop removed the capture while the start was under way.
- Exception
The open failed with an error whose message says, in English, that access is denied, as when the process is not an administrator's.
AdapterRemove()
Removes the interface and what the start added with it: the last step of a stop, after AdapterClose(). When the base calls it, it runs under the base's stop lock and must not wait for the reader or a sender to take that lock. It may run after a partial start, and after a close that failed.
protected override void AdapterRemove()
Remarks
It closes and releases the capture.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
AddAddress(IpNetwork, CancellationToken)
Gives the interface its address in a virtual network. A start calls it once per configured IP version.
protected override Task AddAddress(IpNetwork ipNetwork, CancellationToken cancellationToken)
Parameters
ipNetworkIpNetworkThe interface's address with its network.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It does nothing: there is no interface.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
AddNat(IpNetwork, CancellationToken)
Translates a virtual network to the host's primary address (NAT). A start calls it with UseNat, for each IP version the adapter's NAT translates and the host has a primary address of.
protected override Task AddNat(IpNetwork ipNetwork, CancellationToken cancellationToken)
Parameters
ipNetworkIpNetworkThe virtual network.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Exceptions
- NotSupportedException
Always: there is no NAT.
AddRoute(IpNetwork, CancellationToken)
Routes a network through the interface. A start calls it once per network, for each IP version the interface carries.
protected override Task AddRoute(IpNetwork ipNetwork, CancellationToken cancellationToken)
Parameters
ipNetworkIpNetworkThe network.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It adds the network to the capture, which the open then makes.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
DisposeManaged()
Stops watching the host's network changes, raises Disposed, then disposes the transport.
protected override void DisposeManaged()
Remarks
It also releases the DNS queries' destinations kept for their answers.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
~WinDivertVpnAdapter()
Runs DisposeUnmanaged() for an adapter that was not disposed. This adapter has no unmanaged resource of its own: the finalizer does not close the capture, so stop or dispose the adapter.
protected ~WinDivertVpnAdapter()
Remarks
The adapter's handlers on the host's network-change events keep it reachable until it is disposed, so dispose it rather than count on this.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
IsNatSupported(IpVersion)
Determines whether the adapter's NAT translates an IP version. A server forwards a version the adapter does not translate by its own forwarders.
public override bool IsNatSupported(IpVersion ipVersion)
Parameters
ipVersionIpVersionThe IP version.
Returns
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
ProcessReadPacket(IpPacket)
Rewrites a captured packet for the VPN: its source becomes the adapter's virtual address of its IP version, and, with SimulateDns, a DNS query's destination one of the adapter's DNS servers of that version, chosen at random, if it has one.
protected virtual void ProcessReadPacket(IpPacket ipPacket)
Parameters
ipPacketIpPacketThe packet, changed in place, with its checksums updated.
Remarks
A packet of an IP version the adapter has no virtual address of keeps its source. A query's destination is kept for its answer by its IP version and source port, the latest for each, until 30 seconds pass with no query or answer using it.
Exceptions
- ArgumentException
The packet's payload is invalid for its protocol, which the rewrite's checksums read.
- InvalidDataException
With SimulateDns and DNS servers, the packet is a UDP fragment, which the DNS rewrite cannot take apart.
ProtectSocket(Socket)
Keeps a socket's traffic out of the capture by binding it to any address and giving it ProtectedTtl. Call it before the socket is bound or connected.
public override bool ProtectSocket(Socket socket)
Parameters
socketSocketThe socket, not bound yet.
Returns
Exceptions
- SocketException
The socket could not be bound, as when it is bound already.
- ObjectDisposedException
The socket is closed.
ProtectSocket(Socket, IPAddress)
Keeps a socket's traffic out of the capture, as ProtectSocket(Socket) does: the remote address does not matter, loopback included. Call it before the socket is bound or connected.
public override bool ProtectSocket(Socket socket, IPAddress ipAddress)
Parameters
Returns
Exceptions
- SocketException
The socket could not be bound, as when it is bound already.
- ObjectDisposedException
The socket is closed.
ReadPacket(byte[])
Not used: the adapter reads with ReadPacket(int), which WinDivert sizes.
protected override int ReadPacket(byte[] buffer)
Parameters
bufferbyte[]Not used.
Returns
- int
Never returns.
Exceptions
- InvalidOperationException
Always.
ReadPacket(int)
Blocks until WinDivert captures a packet, then returns it as ProcessReadPacket(IpPacket) rewrites it.
protected override IpPacket ReadPacket(int mtu)
Parameters
mtuintNot used: WinDivert sizes the packet.
Returns
Exceptions
- InvalidOperationException
The capture is not made, or WinDivert gave no packet, as after the capture closed.
- ArgumentException
The captured bytes are no valid IP packet, or its payload is invalid for its protocol, which the rewrite's checksums read.
- InvalidDataException
With SimulateDns and DNS servers, the packet is a UDP fragment, which the DNS rewrite cannot take apart.
SetAllowedApps(IEnumerable<string>, CancellationToken)
Lets only these apps' traffic into the tunnel. A start calls it with IncludeApps, where IsAppFilterSupported is true.
protected override Task SetAllowedApps(IEnumerable<string> packageIds, CancellationToken cancellationToken)
Parameters
packageIdsIEnumerable<string>The apps' IDs, this app's own included.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Exceptions
- NotSupportedException
Always: there is no app filter.
SetDisallowedApps(IEnumerable<string>, CancellationToken)
Keeps these apps' traffic out of the tunnel. A start calls it with ExcludeApps, where IsAppFilterSupported is true.
protected override Task SetDisallowedApps(IEnumerable<string> packageIds, CancellationToken cancellationToken)
Parameters
packageIdsIEnumerable<string>The apps' IDs, never this app's own.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Exceptions
- NotSupportedException
Always: there is no app filter.
SetDnsServers(IReadOnlyList<IPAddress>, CancellationToken)
Sets the DNS servers the interface gives the host. A start calls it every time.
protected override Task SetDnsServers(IReadOnlyList<IPAddress> dnsServers, CancellationToken cancellationToken)
Parameters
dnsServersIReadOnlyList<IPAddress>The servers, only of the IP versions the interface carries; may be empty.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It keeps the servers for SimulateDns; the host's own DNS settings stay as they are.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
SetMetric(int, bool, bool, CancellationToken)
Sets the interface's route metric. A start calls it when Metric is set.
protected override Task SetMetric(int metric, bool ipV4, bool ipV6, CancellationToken cancellationToken)
Parameters
metricintThe metric.
ipV4booltrue if the interface carries IPv4 in this run.
ipV6booltrue if the interface carries IPv6 in this run.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It does nothing: there is no interface.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
SetMtu(int, bool, bool, CancellationToken)
Sets the interface's MTU. A start calls it when Mtu is set.
protected override Task SetMtu(int mtu, bool ipV4, bool ipV6, CancellationToken cancellationToken)
Parameters
mtuintThe MTU in bytes.
ipV4booltrue if the interface carries IPv4 in this run.
ipV6booltrue if the interface carries IPv6 in this run.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It does nothing: there is no interface MTU to set.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
SetSessionName(string, CancellationToken)
Names the session where the OS shows it. A start calls it when SessionName is not empty.
protected override Task SetSessionName(string sessionName, CancellationToken cancellationToken)
Parameters
sessionNamestringThe name.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Remarks
It does nothing: there is no interface.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
WaitForTunRead()
Blocks until a packet may be ready, after a read found none. It runs on the reader's task.
protected override void WaitForTunRead()
Remarks
It does nothing: the read itself blocks.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
WaitForTunWrite()
Blocks until the interface may take a packet again, between the retries of a write it refused.
protected override void WaitForTunWrite()
Remarks
It does nothing: the write itself blocks.
Exceptions
- ArgumentOutOfRangeException
The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.
WritePacket(IpPacket)
Writes one packet to the host as inbound, and blocks until WinDivert takes it: its destination becomes the host's primary address and, with SimulateDns, a DNS answer's source becomes the address the latest query from its port was sent to, while that is kept.
protected override bool WritePacket(IpPacket ipPacket)
Parameters
ipPacketIpPacketThe packet, changed in place, with its checksums updated.
Returns
Exceptions
- InvalidOperationException
No packet has been captured yet, the capture is not made, or the host has no primary address of the packet's IP version.
- ArgumentException
The packet's payload is invalid for its protocol, which the rewrite's checksums read.
- InvalidDataException
With SimulateDns and DNS servers, the packet is a UDP fragment.
WritePacketToAdapter(IpPacket, bool)
Writes a packet to WinDivert as it is, with the header of the last packet captured, whose interface WinDivert uses for an inbound packet.
protected void WritePacketToAdapter(IpPacket ipPacket, bool outbound)
Parameters
ipPacketIpPacketThe packet.
outboundbooltrue to send it out from the host; false to deliver it to the host as inbound.
Exceptions
- InvalidOperationException
No packet has been captured yet, or the capture is not made.