Ground-Zerro / Phobos Public
Code Issues Pull requests Actions Releases View on GitHub ↗
10.1 KB plaintext
/* 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;
            }
        }
    }
}