Table of Contents

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

adapterSettings WinDivertVpnAdapterSettings

The 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

short

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

string

Always null: there is no app filter.

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

bool

Always false: there is no interface to check.

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

bool

Always false.

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

bool

Always false: a socket is protected by its TTL.

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

bool

Always false.

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

cancellationToken CancellationToken

The 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

cancellationToken CancellationToken

The 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

ipNetwork IpNetwork

The interface's address with its network.

cancellationToken CancellationToken

The 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

ipNetwork IpNetwork

The virtual network.

cancellationToken CancellationToken

The 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

ipNetwork IpNetwork

The network.

cancellationToken CancellationToken

The 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

ipVersion IpVersion

The IP version.

Returns

bool

true if the adapter translates it; otherwise, false.

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

ipPacket IpPacket

The 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

socket Socket

The socket, not bound yet.

Returns

bool

Always true.

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

socket Socket

The socket, not bound yet.

ipAddress IPAddress

Not used.

Returns

bool

Always true.

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

buffer byte[]

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

mtu int

Not used: WinDivert sizes the packet.

Returns

IpPacket

The packet, which owns its bytes; never null.

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

packageIds IEnumerable<string>

The apps' IDs, this app's own included.

cancellationToken CancellationToken

The 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

packageIds IEnumerable<string>

The apps' IDs, never this app's own.

cancellationToken CancellationToken

The 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

dnsServers IReadOnlyList<IPAddress>

The servers, only of the IP versions the interface carries; may be empty.

cancellationToken CancellationToken

The 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

metric int

The metric.

ipV4 bool

true if the interface carries IPv4 in this run.

ipV6 bool

true if the interface carries IPv6 in this run.

cancellationToken CancellationToken

The 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

mtu int

The MTU in bytes.

ipV4 bool

true if the interface carries IPv4 in this run.

ipV6 bool

true if the interface carries IPv6 in this run.

cancellationToken CancellationToken

The 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

sessionName string

The name.

cancellationToken CancellationToken

The 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

ipPacket IpPacket

The packet, changed in place, with its checksums updated.

Returns

bool

Always true.

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

ipPacket IpPacket

The packet.

outbound bool

true 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.