/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2026 WireGuard LLC. All Rights Reserved.
*/
using System;
using System.ComponentModel;
using System.Net;
using System.Runtime.InteropServices;
namespace Tunnel
{
public class Driver
{
[DllImport("wireguard.dll", EntryPoint = "WireGuardOpenAdapter", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern IntPtr openAdapter([MarshalAs(UnmanagedType.LPWStr)] string name);
[DllImport("wireguard.dll", EntryPoint = "WireGuardCloseAdapter", CallingConvention = CallingConvention.StdCall)]
private static extern void freeAdapter(IntPtr adapter);
[DllImport("wireguard.dll", EntryPoint = "WireGuardGetConfiguration", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private static extern bool getConfiguration(IntPtr adapter, byte[] iface, ref UInt32 bytes);
public class Adapter
{
private IntPtr _handle;
private UInt32 _lastGetGuess;
public Adapter(string name)
{
_lastGetGuess = 1024;
_handle = openAdapter(name);
if (_handle == IntPtr.Zero)
throw new Win32Exception();
}
~Adapter()
{
freeAdapter(_handle);
}
public unsafe Interface GetConfiguration()
{
var iface = new Interface();
byte[] bytes;
for (; ; )
{
bytes = new byte[_lastGetGuess];
if (getConfiguration(_handle, bytes, ref _lastGetGuess))
break;
if (Marshal.GetLastWin32Error() != 234 /* ERROR_MORE_DATA */)
throw new Win32Exception();
}
fixed (void* start = bytes)
{
var ioctlIface = (IoctlInterface*)start;
if ((ioctlIface->Flags & IoctlInterfaceFlags.HasPublicKey) != 0)
iface.PublicKey = new Key(ioctlIface->PublicKey);
if ((ioctlIface->Flags & IoctlInterfaceFlags.HasPrivateKey) != 0)
iface.PrivateKey = new Key(ioctlIface->PrivateKey);
if ((ioctlIface->Flags & IoctlInterfaceFlags.HasListenPort) != 0)
iface.ListenPort = ioctlIface->ListenPort;
var peers = new Peer[ioctlIface->PeersCount];
var ioctlPeer = (IoctlPeer*)((byte*)ioctlIface + sizeof(IoctlInterface));
for (UInt32 i = 0; i < peers.Length; ++i)
{
var peer = new Peer();
if ((ioctlPeer->Flags & IoctlPeerFlags.HasPublicKey) != 0)
peer.PublicKey = new Key(ioctlPeer->PublicKey);
if ((ioctlPeer->Flags & IoctlPeerFlags.HasPresharedKey) != 0)
peer.PresharedKey = new Key(ioctlPeer->PresharedKey);
if ((ioctlPeer->Flags & IoctlPeerFlags.HasPersistentKeepalive) != 0)
peer.PersistentKeepalive = ioctlPeer->PersistentKeepalive;
if ((ioctlPeer->Flags & IoctlPeerFlags.HasEndpoint) != 0)
{
if (ioctlPeer->Endpoint.si_family == Win32.ADDRESS_FAMILY.AF_INET)
{
var ip = new byte[4];
Marshal.Copy((IntPtr)ioctlPeer->Endpoint.Ipv4.sin_addr.bytes, ip, 0, 4);
peer.Endpoint = new IPEndPoint(new IPAddress(ip), (ushort)IPAddress.NetworkToHostOrder((short)ioctlPeer->Endpoint.Ipv4.sin_port));
}
else if (ioctlPeer->Endpoint.si_family == Win32.ADDRESS_FAMILY.AF_INET6)
{
var ip = new byte[16];
Marshal.Copy((IntPtr)ioctlPeer->Endpoint.Ipv6.sin6_addr.bytes, ip, 0, 16);
peer.Endpoint = new IPEndPoint(new IPAddress(ip), (ushort)IPAddress.NetworkToHostOrder((short)ioctlPeer->Endpoint.Ipv6.sin6_port));
}
}
peer.TxBytes = ioctlPeer->TxBytes;
peer.RxBytes = ioctlPeer->RxBytes;
if (ioctlPeer->LastHandshake != 0)
peer.LastHandshake = DateTime.FromFileTimeUtc((long)ioctlPeer->LastHandshake);
var allowedIPs = new AllowedIP[ioctlPeer->AllowedIPsCount];
var ioctlAllowedIP = (IoctlAllowedIP*)((byte*)ioctlPeer + sizeof(IoctlPeer));
for (UInt32 j = 0; j < allowedIPs.Length; ++j)
{
var allowedIP = new AllowedIP();
if (ioctlAllowedIP->AddressFamily == Win32.ADDRESS_FAMILY.AF_INET)
{
var ip = new byte[4];
Marshal.Copy((IntPtr)ioctlAllowedIP->V4.bytes, ip, 0, 4);
allowedIP.Address = new IPAddress(ip);
}
else if (ioctlAllowedIP->AddressFamily == Win32.ADDRESS_FAMILY.AF_INET6)
{
var ip = new byte[16];
Marshal.Copy((IntPtr)ioctlAllowedIP->V6.bytes, ip, 0, 16);
allowedIP.Address = new IPAddress(ip);
}
allowedIP.Cidr = ioctlAllowedIP->Cidr;
allowedIPs[j] = allowedIP;
ioctlAllowedIP = (IoctlAllowedIP*)((byte*)ioctlAllowedIP + sizeof(IoctlAllowedIP));
}
peer.AllowedIPs = allowedIPs;
peers[i] = peer;
ioctlPeer = (IoctlPeer*)ioctlAllowedIP;
}
iface.Peers = peers;
}
return iface;
}
public class Key
{
private byte[] _bytes;
public byte[] Bytes
{
get
{
return _bytes;
}
set
{
if (value == null || value.Length != 32)
throw new ArgumentException("Keys must be 32 bytes");
_bytes = value;
}
}
public Key(byte[] bytes)
{
Bytes = bytes;
}
public unsafe Key(byte* bytes)
{
_bytes = new byte[32];
Marshal.Copy((IntPtr)bytes, _bytes, 0, 32);
}
public override String ToString()
{
return Convert.ToBase64String(_bytes);
}
}
public class Interface
{
public UInt16 ListenPort { get; set; }
public Key PrivateKey { get; set; }
public Key PublicKey { get; set; }
public Peer[] Peers { get; set; }
}
public class Peer
{
public Key PublicKey { get; set; }
public Key PresharedKey { get; set; }
public UInt16 PersistentKeepalive { get; set; }
public IPEndPoint Endpoint { get; set; }
public UInt64 TxBytes { get; set; }
public UInt64 RxBytes { get; set; }
public DateTime LastHandshake { get; set; }
public AllowedIP[] AllowedIPs { get; set; }
}
public class AllowedIP
{
public IPAddress Address { get; set; }
public byte Cidr { get; set; }
}
private enum IoctlInterfaceFlags : UInt32
{
HasPublicKey = 1 << 0,
HasPrivateKey = 1 << 1,
HasListenPort = 1 << 2,
ReplacePeers = 1 << 3
};
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 80)]
private unsafe struct IoctlInterface
{
public IoctlInterfaceFlags Flags;
public UInt16 ListenPort;
public fixed byte PrivateKey[32];
public fixed byte PublicKey[32];
public UInt32 PeersCount;
};
private enum IoctlPeerFlags : UInt32
{
HasPublicKey = 1 << 0,
HasPresharedKey = 1 << 1,
HasPersistentKeepalive = 1 << 2,
HasEndpoint = 1 << 3,
ReplaceAllowedIPs = 1 << 5,
Remove = 1 << 6,
UpdateOnly = 1 << 7
};
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 136)]
private unsafe struct IoctlPeer
{
public IoctlPeerFlags Flags;
public UInt32 Reserved;
public fixed byte PublicKey[32];
public fixed byte PresharedKey[32];
public UInt16 PersistentKeepalive;
public Win32.SOCKADDR_INET Endpoint;
public UInt64 TxBytes, RxBytes;
public UInt64 LastHandshake;
public UInt32 AllowedIPsCount;
};
[StructLayout(LayoutKind.Explicit, Pack = 8, Size = 24)]
private unsafe struct IoctlAllowedIP
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public Win32.IN_ADDR V4;
[FieldOffset(0)]
[MarshalAs(UnmanagedType.Struct)]
public Win32.IN6_ADDR V6;
[FieldOffset(16)]
public Win32.ADDRESS_FAMILY AddressFamily;
[FieldOffset(20)]
public byte Cidr;
}
}
}
}