summaryrefslogtreecommitdiffhomepage
path: root/tun
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-02-07 04:18:27 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2019-02-07 04:39:59 +0100
commitea59177f1c6ab9031d5d30771410c9514fa551d9 (patch)
treecfe5c11f9340d468dfd5f80b76db78584a9ef20a /tun
parent306d08e692bc9b2249078c0b4ab7eb3e42078230 (diff)
wintun: Introduce new package for obscuring Windows bits
Diffstat (limited to 'tun')
-rw-r--r--tun/tun_windows.go493
-rw-r--r--tun/wintun/guid/guid_windows.go44
-rw-r--r--tun/wintun/guid/mksyscall.go8
-rw-r--r--tun/wintun/guid/zguid_windows.go (renamed from tun/ztun_windows.go)2
-rw-r--r--tun/wintun/setupapi/mksyscall.go8
-rw-r--r--tun/wintun/setupapi/setupapi_windows.go476
-rw-r--r--tun/wintun/setupapi/setupapi_windows_test.go452
-rw-r--r--tun/wintun/setupapi/types_windows.go571
-rw-r--r--tun/wintun/setupapi/zsetupapi_windows.go370
-rw-r--r--tun/wintun/setupapi/zsetupapi_windows_test.go20
-rw-r--r--tun/wintun/wintun_windows.go461
11 files changed, 2424 insertions, 481 deletions
diff --git a/tun/tun_windows.go b/tun/tun_windows.go
index d7cd762..6449974 100644
--- a/tun/tun_windows.go
+++ b/tun/tun_windows.go
@@ -5,20 +5,14 @@
package tun
-//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output ztun_windows.go tun_windows.go
-
import (
"errors"
"fmt"
"os"
- "strings"
- "syscall"
- "time"
"unsafe"
- "git.zx2c4.com/wireguard-go/setupapi"
+ "git.zx2c4.com/wireguard-go/tun/wintun"
"golang.org/x/sys/windows"
- "golang.org/x/sys/windows/registry"
)
const (
@@ -34,10 +28,6 @@ const (
TUN_SIGNAL_MAX = 2
)
-var deviceClassNetGUID = windows.GUID{0x4d36e972, 0xe325, 0x11ce, [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
-
-const TUN_HWID = "Wintun"
-
type tunPacket struct {
size uint32
data [TUN_MAX_PACKET_SIZE]byte
@@ -50,7 +40,7 @@ type tunRWQueue struct {
}
type nativeTun struct {
- ifid *windows.GUID
+ wt *wintun.Wintun
tunName string
signalName *uint16
tunFile *os.File
@@ -64,30 +54,28 @@ type nativeTun struct {
func CreateTUN(ifname string) (TUNDevice, error) {
// Does an interface with this name already exist?
- ifid, err := getInterface(ifname, 0)
- if ifid == nil || err != nil {
+ wt, err := wintun.GetInterface(ifname, 0)
+ if wt == nil || err != nil {
// Interface does not exist or an error occured. Create one.
- ifid, _, err = createInterface("WireGuard Tunnel Adapter", 0)
+ wt, _, err = wintun.CreateInterface("WireGuard Tunnel Adapter", 0)
if err != nil {
return nil, err
}
// Set interface name. (Ignore errors.)
- setInterfaceName(ifid, ifname)
+ wt.SetInterfaceName(ifname)
}
- ifidStr := guidToString(ifid)
-
- signalNameUTF16, err := windows.UTF16PtrFromString(fmt.Sprintf("Global\\WINTUN_EVENT_%s", ifidStr))
+ signalNameUTF16, err := windows.UTF16PtrFromString(wt.SignalEventName())
if err != nil {
- deleteInterface(ifid, 0)
+ wt.DeleteInterface(0)
return nil, err
}
// Create instance.
tun := &nativeTun{
- ifid: ifid,
- tunName: fmt.Sprintf("\\\\.\\Global\\WINTUN_DEVICE_%s", ifidStr),
+ wt: wt,
+ tunName: wt.DataFileName(),
signalName: signalNameUTF16,
events: make(chan TUNEvent, 10),
errors: make(chan error, 1),
@@ -96,7 +84,7 @@ func CreateTUN(ifname string) (TUNDevice, error) {
// Create close event.
tun.signals[TUN_SIGNAL_CLOSE], err = windows.CreateEvent(nil, 1 /*TRUE*/, 0 /*FALSE*/, nil)
if err != nil {
- deleteInterface(ifid, 0)
+ wt.DeleteInterface(0)
return nil, err
}
@@ -160,7 +148,7 @@ func (tun *nativeTun) closeTUN() (err error) {
}
func (tun *nativeTun) Name() (string, error) {
- return getInterfaceName(tun.ifid)
+ return tun.wt.GetInterfaceName()
}
func (tun *nativeTun) File() *os.File {
@@ -184,7 +172,7 @@ func (tun *nativeTun) Close() error {
close(tun.events)
}
- _, _, e = deleteInterface(tun.ifid, 0)
+ _, _, e = tun.wt.DeleteInterface(0)
if err == nil {
err = e
}
@@ -317,458 +305,3 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
// Flush write buffer.
return len(buff) - offset, tun.flush()
}
-
-//
-// getInterface finds interface ID by name.
-//
-// hwndParent is a handle to the top-level window to use for any user
-// interface that is related to non-device-specific actions (such as a select-
-// device dialog box that uses the global class driver list). This handle is
-// optional and can be 0. If a specific top-level window is not required, set
-// hwndParent to 0.
-//
-// Function returns interface ID when the interface was found, or nil
-// otherwise.
-//
-func getInterface(ifname string, hwndParent uintptr) (*windows.GUID, error) {
- // Create a list of network devices.
- devInfoList, err := setupapi.SetupDiGetClassDevsEx(&deviceClassNetGUID, "", hwndParent, setupapi.DIGCF_PRESENT, setupapi.DevInfo(0), "")
- if err != nil {
- return nil, err
- }
- defer devInfoList.Close()
-
- // Retrieve information associated with a device information set.
- // TODO: Is this really necessary?
- _, err = devInfoList.GetDeviceInfoListDetail()
- if err != nil {
- return nil, err
- }
-
- // TODO: If we're certain we want case-insensitive name comparison, please document the rationale.
- ifname = strings.ToLower(ifname)
-
- // Iterate.
- for index := 0; ; index++ {
- // Get the device from the list.
- deviceData, err := devInfoList.EnumDeviceInfo(index)
- if err != nil {
- if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
- break
- }
- // Something is wrong with this device. Skip it.
- continue
- }
-
- // Get interface ID.
- ifid, err := getInterfaceId(devInfoList, deviceData, 1)
- if err != nil {
- // Something is wrong with this device. Skip it.
- continue
- }
-
- // Get interface name.
- ifname2, err := getInterfaceName(ifid)
- if err != nil {
- // Something is wrong with this device. Skip it.
- continue
- }
-
- if ifname == strings.ToLower(ifname2) {
- // Interface name found.
- return ifid, nil
- }
- }
-
- return nil, nil
-}
-
-//
-// createInterface creates a TUN interface.
-//
-// description is a string that supplies the text description of the device.
-// Description is optional and can be "".
-//
-// hwndParent is a handle to the top-level window to use for any user
-// interface that is related to non-device-specific actions (such as a select-
-// device dialog box that uses the global class driver list). This handle is
-// optional and can be 0. If a specific top-level window is not required, set
-// hwndParent to 0.
-//
-// Function returns the network interface ID and a flag if reboot is required.
-//
-func createInterface(description string, hwndParent uintptr) (*windows.GUID, bool, error) {
- // Create an empty device info set for network adapter device class.
- devInfoList, err := setupapi.SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, hwndParent, "")
- if err != nil {
- return nil, false, err
- }
-
- // Get the device class name from GUID.
- className, err := setupapi.SetupDiClassNameFromGuidEx(&deviceClassNetGUID, "")
- if err != nil {
- return nil, false, err
- }
-
- // Create a new device info element and add it to the device info set.
- deviceData, err := devInfoList.CreateDeviceInfo(className, &deviceClassNetGUID, description, hwndParent, setupapi.DICD_GENERATE_ID)
- if err != nil {
- return nil, false, err
- }
-
- // Set a device information element as the selected member of a device information set.
- err = devInfoList.SetSelectedDevice(deviceData)
- if err != nil {
- return nil, false, err
- }
-
- // Set Plug&Play device hardware ID property.
- hwid, err := syscall.UTF16FromString(TUN_HWID)
- if err != nil {
- return nil, false, err
- }
- err = devInfoList.SetDeviceRegistryProperty(deviceData, setupapi.SPDRP_HARDWAREID, setupapi.UTF16ToBuf(append(hwid, 0)))
- if err != nil {
- return nil, false, err
- }
-
- // Search for the driver.
- const driverType = setupapi.SPDIT_CLASSDRIVER
- err = devInfoList.BuildDriverInfoList(deviceData, driverType)
- if err != nil {
- return nil, false, err
- }
- defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
-
- driverDate := windows.Filetime{}
- driverVersion := uint64(0)
- for index := 0; ; index++ {
- // Get a driver from the list.
- driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, index)
- if err != nil {
- if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
- break
- }
- // Something is wrong with this driver. Skip it.
- continue
- }
-
- // Check the driver version first, since the check is trivial and will save us iterating over hardware IDs for any driver versioned prior our best match.
- if driverData.IsNewer(driverDate, driverVersion) {
- // Get driver info details.
- driverDetailData, err := devInfoList.GetDriverInfoDetail(deviceData, driverData)
- if err != nil {
- // Something is wrong with this driver. Skip it.
- continue
- }
-
- if driverDetailData.IsCompatible(TUN_HWID) {
- // Matching hardware ID found. Select the driver.
- err := devInfoList.SetSelectedDriver(deviceData, driverData)
- if err != nil {
- // Something is wrong with this driver. Skip it.
- continue
- }
-
- driverDate = driverData.DriverDate
- driverVersion = driverData.DriverVersion
- }
- }
- }
-
- if driverVersion == 0 {
- return nil, false, fmt.Errorf("No driver for device \"%v\" installed", TUN_HWID)
- }
-
- // Call appropriate class installer.
- err = devInfoList.CallClassInstaller(setupapi.DIF_REGISTERDEVICE, deviceData)
- if err != nil {
- return nil, false, err
- }
-
- // Register device co-installers if any.
- devInfoList.CallClassInstaller(setupapi.DIF_REGISTER_COINSTALLERS, deviceData)
-
- // Install interfaces if any.
- devInfoList.CallClassInstaller(setupapi.DIF_INSTALLINTERFACES, deviceData)
-
- var ifid *windows.GUID
- var rebootRequired bool
-
- // Install the device.
- err = devInfoList.CallClassInstaller(setupapi.DIF_INSTALLDEVICE, deviceData)
- if err == nil {
- // Check if a system reboot is required. (Ignore errors)
- if ret, _ := checkReboot(devInfoList, deviceData); ret {
- rebootRequired = true
- }
-
- // Get network interface ID from registry. Retry for max 30sec.
- ifid, err = getInterfaceId(devInfoList, deviceData, 30)
- }
-
- if err == nil {
- return ifid, rebootRequired, nil
- }
-
- // The interface failed to install, or the interface ID was unobtainable. Clean-up.
- removeDeviceParams := setupapi.SP_REMOVEDEVICE_PARAMS{
- ClassInstallHeader: setupapi.SP_CLASSINSTALL_HEADER{
- InstallFunction: setupapi.DIF_REMOVE,
- },
- Scope: setupapi.DI_REMOVEDEVICE_GLOBAL,
- }
- removeDeviceParams.ClassInstallHeader.Size = uint32(unsafe.Sizeof(removeDeviceParams.ClassInstallHeader))
-
- // Set class installer parameters for DIF_REMOVE.
- if devInfoList.SetClassInstallParams(deviceData, &removeDeviceParams.ClassInstallHeader, uint32(unsafe.Sizeof(removeDeviceParams))) == nil {
- // Call appropriate class installer.
- if devInfoList.CallClassInstaller(setupapi.DIF_REMOVE, deviceData) == nil {
- // Check if a system reboot is required. (Ignore errors)
- if ret, _ := checkReboot(devInfoList, deviceData); ret {
- rebootRequired = true
- }
- }
- }
-
- return nil, false, err
-}
-
-//
-// deleteInterface deletes a TUN interface.
-//
-// hwndParent is a handle to the top-level window to use for any user
-// interface that is related to non-device-specific actions (such as a select-
-// device dialog box that uses the global class driver list). This handle is
-// optional and can be 0. If a specific top-level window is not required, set
-// hwndParent to 0.
-//
-// Function returns true if the interface was found and deleted and a flag if
-// reboot is required.
-//
-func deleteInterface(ifid *windows.GUID, hwndParent uintptr) (bool, bool, error) {
- // Create a list of network devices.
- devInfoList, err := setupapi.SetupDiGetClassDevsEx(&deviceClassNetGUID, "", hwndParent, setupapi.DIGCF_PRESENT, setupapi.DevInfo(0), "")
- if err != nil {
- return false, false, err
- }
- defer devInfoList.Close()
-
- // Retrieve information associated with a device information set.
- // TODO: Is this really necessary?
- _, err = devInfoList.GetDeviceInfoListDetail()
- if err != nil {
- return false, false, err
- }
-
- // Iterate.
- for index := 0; ; index++ {
- // Get the device from the list.
- deviceData, err := devInfoList.EnumDeviceInfo(index)
- if err != nil {
- if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
- break
- }
- // Something is wrong with this device. Skip it.
- continue
- }
-
- // Get interface ID.
- ifid2, err := getInterfaceId(devInfoList, deviceData, 1)
- if err != nil {
- // Something is wrong with this device. Skip it.
- continue
- }
-
- if ifid == ifid2 {
- // Remove the device.
- removeDeviceParams := setupapi.SP_REMOVEDEVICE_PARAMS{
- ClassInstallHeader: setupapi.SP_CLASSINSTALL_HEADER{
- InstallFunction: setupapi.DIF_REMOVE,
- },
- Scope: setupapi.DI_REMOVEDEVICE_GLOBAL,
- }
- removeDeviceParams.ClassInstallHeader.Size = uint32(unsafe.Sizeof(removeDeviceParams.ClassInstallHeader))
-
- // Set class installer parameters for DIF_REMOVE.
- err = devInfoList.SetClassInstallParams(deviceData, &removeDeviceParams.ClassInstallHeader, uint32(unsafe.Sizeof(removeDeviceParams)))
- if err != nil {
- return false, false, err
- }
-
- // Call appropriate class installer.
- err = devInfoList.CallClassInstaller(setupapi.DIF_REMOVE, deviceData)
- if err != nil {
- return false, false, err
- }
-
- // Check if a system reboot is required. (Ignore errors)
- if ret, _ := checkReboot(devInfoList, deviceData); ret {
- return true, true, nil
- }
-
- return true, false, nil
- }
- }
-
- return false, false, nil
-}
-
-///
-/// checkReboot checks device install parameters if a system reboot is required.
-///
-func checkReboot(deviceInfoSet setupapi.DevInfo, deviceInfoData *setupapi.SP_DEVINFO_DATA) (bool, error) {
- devInstallParams, err := deviceInfoSet.GetDeviceInstallParams(deviceInfoData)
- if err != nil {
- return false, err
- }
-
- if (devInstallParams.Flags & (setupapi.DI_NEEDREBOOT | setupapi.DI_NEEDRESTART)) != 0 {
- return true, nil
- }
-
- return false, nil
-}
-
-// getInterfaceId returns network interface ID.
-//
-// After the device is created, it might take some time before the registry
-// key is populated. numAttempts parameter specifies the number of attempts
-// to read NetCfgInstanceId value from registry. A 1sec sleep is inserted
-// between retry attempts.
-//
-// Function returns the network interface ID.
-//
-func getInterfaceId(deviceInfoSet setupapi.DevInfo, deviceInfoData *setupapi.SP_DEVINFO_DATA, numAttempts int) (*windows.GUID, error) {
- if numAttempts < 1 {
- return nil, fmt.Errorf("Invalid numAttempts (expected: >=1, provided: %v)", numAttempts)
- }
-
- // Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> registry key.
- key, err := deviceInfoSet.OpenDevRegKey(deviceInfoData, setupapi.DICS_FLAG_GLOBAL, 0, setupapi.DIREG_DRV, registry.READ)
- if err != nil {
- return nil, errors.New("Device-specific registry key open failed: " + err.Error())
- }
- defer key.Close()
-
- for {
- // Query the NetCfgInstanceId value. Using get_reg_string() right on might clutter the output with error messages while the registry is still being populated.
- _, _, err = key.GetValue("NetCfgInstanceId", nil)
- if err != nil {
- if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_FILE_NOT_FOUND {
- numAttempts--
- if numAttempts > 0 {
- // Wait and retry.
- // TODO: Wait for a cancellable event instead.
- time.Sleep(1000 * time.Millisecond)
- continue
- }
- }
-
- return nil, errors.New("RegQueryValueEx(\"NetCfgInstanceId\") failed: " + err.Error())
- }
-
- // Read the NetCfgInstanceId value now.
- value, err := getRegStringValue(key, "NetCfgInstanceId")
- if err != nil {
- return nil, errors.New("RegQueryStringValue(\"NetCfgInstanceId\") failed: " + err.Error())
- }
-
- // Convert to windows.GUID.
- ifid, err := stringToGUID(value)
- if err != nil {
- return nil, fmt.Errorf("NetCfgInstanceId registry value is not a GUID (expected: \"{...}\", provided: \"%v\")", value)
- }
-
- return ifid, err
- }
-}
-
-//
-// getInterfaceName returns network interface name.
-//
-func getInterfaceName(ifid *windows.GUID) (string, error) {
- // Open network interface registry key.
- key, err := registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Control\\Network\\%v\\%v\\Connection", guidToString(&deviceClassNetGUID), guidToString(ifid)), registry.QUERY_VALUE)
- if err != nil {
- return "", errors.New("Network-specific registry key open failed: " + err.Error())
- }
- defer key.Close()
-
- // Get the interface name.
- return getRegStringValue(key, "Name")
-}
-
-//
-// setInterfaceName sets network interface name.
-//
-func setInterfaceName(ifid *windows.GUID, ifname string) error {
- // Open network interface registry key.
- key, err := registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Control\\Network\\%v\\%v\\Connection", guidToString(&deviceClassNetGUID), guidToString(ifid)), registry.SET_VALUE)
- if err != nil {
- return errors.New("Network-specific registry key open failed: " + err.Error())
- }
- defer key.Close()
-
- // Set the interface name.
- return key.SetStringValue("Name", ifname)
-}
-
-//sys clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) = ole32.CLSIDFromString
-
-//
-// stringToGUID parses "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" string to GUID.
-//
-func stringToGUID(str string) (*windows.GUID, error) {
- strUTF16, err := syscall.UTF16PtrFromString(str)
- if err != nil {
- return nil, err
- }
-
- guid := &windows.GUID{}
-
- hr := clsidFromString(strUTF16, guid)
- if hr < 0 {
- return nil, syscall.Errno(hr)
- }
-
- return guid, nil
-}
-
-//
-// guidToString function converts GUID to string
-// "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
-//
-// The resulting string is uppercase.
-//
-func guidToString(guid *windows.GUID) string {
- return fmt.Sprintf("{%06X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:])
-}
-
-//
-// getRegStringValue function reads a string value from registry.
-//
-// If the value type is REG_EXPAND_SZ the environment variables are expanded.
-// Should expanding fail, original string value and nil error are returned.
-//
-func getRegStringValue(key registry.Key, name string) (string, error) {
- // Read string value.
- value, valueType, err := key.GetStringValue(name)
- if err != nil {
- return "", err
- }
-
- if valueType != registry.EXPAND_SZ {
- // Value does not require expansion.
- return value, nil
- }
-
- valueExp, err := registry.ExpandString(value)
- if err != nil {
- // Expanding failed: return original sting value.
- return value, nil
- }
-
- // Return expanded value.
- return valueExp, nil
-}
diff --git a/tun/wintun/guid/guid_windows.go b/tun/wintun/guid/guid_windows.go
new file mode 100644
index 0000000..0078d2c
--- /dev/null
+++ b/tun/wintun/guid/guid_windows.go
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package guid
+
+import (
+ "fmt"
+ "syscall"
+
+ "golang.org/x/sys/windows"
+)
+
+//sys clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) = ole32.CLSIDFromString
+
+//
+// FromString parses "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" string to GUID.
+//
+func FromString(str string) (*windows.GUID, error) {
+ strUTF16, err := syscall.UTF16PtrFromString(str)
+ if err != nil {
+ return nil, err
+ }
+
+ guid := &windows.GUID{}
+
+ hr := clsidFromString(strUTF16, guid)
+ if hr < 0 {
+ return nil, syscall.Errno(hr)
+ }
+
+ return guid, nil
+}
+
+//
+// ToString function converts GUID to string
+// "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
+//
+// The resulting string is uppercase.
+//
+func ToString(guid *windows.GUID) string {
+ return fmt.Sprintf("{%06X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:])
+}
diff --git a/tun/wintun/guid/mksyscall.go b/tun/wintun/guid/mksyscall.go
new file mode 100644
index 0000000..5c956cf
--- /dev/null
+++ b/tun/wintun/guid/mksyscall.go
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package guid
+
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zguid_windows.go guid_windows.go
diff --git a/tun/ztun_windows.go b/tun/wintun/guid/zguid_windows.go
index 9aa6e11..5467849 100644
--- a/tun/ztun_windows.go
+++ b/tun/wintun/guid/zguid_windows.go
@@ -1,6 +1,6 @@
// Code generated by 'go generate'; DO NOT EDIT.
-package tun
+package guid
import (
"syscall"
diff --git a/tun/wintun/setupapi/mksyscall.go b/tun/wintun/setupapi/mksyscall.go
new file mode 100644
index 0000000..3447dee
--- /dev/null
+++ b/tun/wintun/setupapi/mksyscall.go
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package setupapi
+
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsetupapi_windows.go setupapi_windows.go
diff --git a/tun/wintun/setupapi/setupapi_windows.go b/tun/wintun/setupapi/setupapi_windows.go
new file mode 100644
index 0000000..72d4bd8
--- /dev/null
+++ b/tun/wintun/setupapi/setupapi_windows.go
@@ -0,0 +1,476 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package setupapi
+
+import (
+ "encoding/binary"
+ "fmt"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/registry"
+)
+
+//sys setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiCreateDeviceInfoListExW
+
+// SetupDiCreateDeviceInfoListEx function creates an empty device information set on a remote or a local computer and optionally associates the set with a device setup class.
+func SetupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName string) (deviceInfoSet DevInfo, err error) {
+ var machineNameUTF16 *uint16
+ if machineName != "" {
+ machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
+ if err != nil {
+ return
+ }
+ }
+ return setupDiCreateDeviceInfoListEx(classGUID, hwndParent, machineNameUTF16, 0)
+}
+
+//sys setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *_SP_DEVINFO_LIST_DETAIL_DATA) (err error) = setupapi.SetupDiGetDeviceInfoListDetailW
+
+// SetupDiGetDeviceInfoListDetail function retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
+func SetupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo) (deviceInfoSetDetailData *DevInfoListDetailData, err error) {
+ var _data _SP_DEVINFO_LIST_DETAIL_DATA
+ _data.Size = uint32(unsafe.Sizeof(_data))
+
+ err = setupDiGetDeviceInfoListDetail(deviceInfoSet, &_data)
+ if err != nil {
+ return
+ }
+
+ return _data.toGo(), nil
+}
+
+// GetDeviceInfoListDetail method retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
+func (deviceInfoSet DevInfo) GetDeviceInfoListDetail() (*DevInfoListDetailData, error) {
+ return SetupDiGetDeviceInfoListDetail(deviceInfoSet)
+}
+
+//sys setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *SP_DEVINFO_DATA) (err error) = setupapi.SetupDiCreateDeviceInfoW
+
+// SetupDiCreateDeviceInfo function creates a new device information element and adds it as a new member to the specified device information set.
+func SetupDiCreateDeviceInfo(deviceInfoSet DevInfo, deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (deviceInfoData *SP_DEVINFO_DATA, err error) {
+ deviceNameUTF16, err := syscall.UTF16PtrFromString(deviceName)
+ if err != nil {
+ return
+ }
+
+ var deviceDescriptionUTF16 *uint16
+ if deviceDescription != "" {
+ deviceDescriptionUTF16, err = syscall.UTF16PtrFromString(deviceDescription)
+ if err != nil {
+ return
+ }
+ }
+
+ data := SP_DEVINFO_DATA{}
+ data.Size = uint32(unsafe.Sizeof(data))
+
+ return &data, setupDiCreateDeviceInfo(deviceInfoSet, deviceNameUTF16, classGUID, deviceDescriptionUTF16, hwndParent, creationFlags, &data)
+}
+
+// CreateDeviceInfo method creates a new device information element and adds it as a new member to the specified device information set.
+func (deviceInfoSet DevInfo) CreateDeviceInfo(deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (*SP_DEVINFO_DATA, error) {
+ return SetupDiCreateDeviceInfo(deviceInfoSet, deviceName, classGUID, deviceDescription, hwndParent, creationFlags)
+}
+
+//sys setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *SP_DEVINFO_DATA) (err error) = setupapi.SetupDiEnumDeviceInfo
+
+// SetupDiEnumDeviceInfo function returns a SP_DEVINFO_DATA structure that specifies a device information element in a device information set.
+func SetupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex int) (*SP_DEVINFO_DATA, error) {
+ data := SP_DEVINFO_DATA{}
+ data.Size = uint32(unsafe.Sizeof(data))
+
+ return &data, setupDiEnumDeviceInfo(deviceInfoSet, uint32(memberIndex), &data)
+}
+
+// EnumDeviceInfo method returns a SP_DEVINFO_DATA structure that specifies a device information element in a device information set.
+func (deviceInfoSet DevInfo) EnumDeviceInfo(memberIndex int) (*SP_DEVINFO_DATA, error) {
+ return SetupDiEnumDeviceInfo(deviceInfoSet, memberIndex)
+}
+
+// SetupDiDestroyDeviceInfoList function deletes a device information set and frees all associated memory.
+//sys SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiDestroyDeviceInfoList
+
+// Close method deletes a device information set and frees all associated memory.
+func (deviceInfoSet DevInfo) Close() error {
+ return SetupDiDestroyDeviceInfoList(deviceInfoSet)
+}
+
+//sys SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT) (err error) = setupapi.SetupDiBuildDriverInfoList
+
+// BuildDriverInfoList method builds a list of drivers that is associated with a specific device or with the global class driver list for a device information set.
+func (deviceInfoSet DevInfo) BuildDriverInfoList(deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT) error {
+ return SetupDiBuildDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
+}
+
+//sys SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiCancelDriverInfoSearch
+
+// CancelDriverInfoSearch method cancels a driver list search that is currently in progress in a different thread.
+func (deviceInfoSet DevInfo) CancelDriverInfoSearch() error {
+ return SetupDiCancelDriverInfoSearch(deviceInfoSet)
+}
+
+//sys setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT, memberIndex uint32, driverInfoData *SP_DRVINFO_DATA) (err error) = setupapi.SetupDiEnumDriverInfoW
+
+// SetupDiEnumDriverInfo function enumerates the members of a driver list.
+func SetupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT, memberIndex int) (*SP_DRVINFO_DATA, error) {
+ data := &SP_DRVINFO_DATA{}
+ data.Size = uint32(unsafe.Sizeof(*data))
+
+ return data, setupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, uint32(memberIndex), data)
+}
+
+// EnumDriverInfo method enumerates the members of a driver list.
+func (deviceInfoSet DevInfo) EnumDriverInfo(deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT, memberIndex int) (*SP_DRVINFO_DATA, error) {
+ return SetupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, memberIndex)
+}
+
+//sys setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverInfoData *SP_DRVINFO_DATA) (err error) = setupapi.SetupDiGetSelectedDriverW
+
+// SetupDiGetSelectedDriver function retrieves the selected driver for a device information set or a particular device information element.
+func SetupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA) (*SP_DRVINFO_DATA, error) {
+ data := &SP_DRVINFO_DATA{}
+ data.Size = uint32(unsafe.Sizeof(*data))
+
+ return data, setupDiGetSelectedDriver(deviceInfoSet, deviceInfoData, data)
+}
+
+// GetSelectedDriver method retrieves the selected driver for a device information set or a particular device information element.
+func (deviceInfoSet DevInfo) GetSelectedDriver(deviceInfoData *SP_DEVINFO_DATA) (*SP_DRVINFO_DATA, error) {
+ return SetupDiGetSelectedDriver(deviceInfoSet, deviceInfoData)
+}
+
+//sys SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverInfoData *SP_DRVINFO_DATA) (err error) = setupapi.SetupDiSetSelectedDriverW
+
+// SetSelectedDriver method sets, or resets, the selected driver for a device information element or the selected class driver for a device information set.
+func (deviceInfoSet DevInfo) SetSelectedDriver(deviceInfoData *SP_DEVINFO_DATA, driverInfoData *SP_DRVINFO_DATA) error {
+ return SetupDiSetSelectedDriver(deviceInfoSet, deviceInfoData, driverInfoData)
+}
+
+//sys setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverInfoData *SP_DRVINFO_DATA, driverInfoDetailData *_SP_DRVINFO_DETAIL_DATA, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDriverInfoDetailW
+
+// SetupDiGetDriverInfoDetail function retrieves driver information detail for a device information set or a particular device information element in the device information set.
+func SetupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverInfoData *SP_DRVINFO_DATA) (driverInfoDetailData *DrvInfoDetailData, err error) {
+ const bufCapacity = 0x800
+ buf := [bufCapacity]byte{}
+ var bufLen uint32
+
+ _data := (*_SP_DRVINFO_DETAIL_DATA)(unsafe.Pointer(&buf[0]))
+ _data.Size = uint32(unsafe.Sizeof(*_data))
+
+ err = setupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData, _data, bufCapacity, &bufLen)
+ if err == nil {
+ // The buffer was was sufficiently big.
+ return _data.toGo(bufLen), nil
+ }
+
+ if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
+ // The buffer was too small. Now that we got the required size, create another one big enough and retry.
+ buf := make([]byte, bufLen)
+ _data := (*_SP_DRVINFO_DETAIL_DATA)(unsafe.Pointer(&buf[0]))
+ _data.Size = uint32(unsafe.Sizeof(*_data))
+
+ err = setupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData, _data, bufLen, &bufLen)
+ if err == nil {
+ return _data.toGo(bufLen), nil
+ }
+ }
+
+ return
+}
+
+// GetDriverInfoDetail method retrieves driver information detail for a device information set or a particular device information element in the device information set.
+func (deviceInfoSet DevInfo) GetDriverInfoDetail(deviceInfoData *SP_DEVINFO_DATA, driverInfoData *SP_DRVINFO_DATA) (*DrvInfoDetailData, error) {
+ return SetupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData)
+}
+
+//sys SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT) (err error) = setupapi.SetupDiDestroyDriverInfoList
+
+// DestroyDriverInfoList method deletes a driver list.
+func (deviceInfoSet DevInfo) DestroyDriverInfoList(deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT) error {
+ return SetupDiDestroyDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
+}
+
+//sys setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiGetClassDevsExW
+
+// SetupDiGetClassDevsEx function returns a handle to a device information set that contains requested device information elements for a local or a remote computer.
+func SetupDiGetClassDevsEx(classGUID *windows.GUID, enumerator string, hwndParent uintptr, flags DIGCF, deviceInfoSet DevInfo, machineName string) (handle DevInfo, err error) {
+ var enumeratorUTF16 *uint16
+ if enumerator != "" {
+ enumeratorUTF16, err = syscall.UTF16PtrFromString(enumerator)
+ if err != nil {
+ return
+ }
+ }
+ var machineNameUTF16 *uint16
+ if machineName != "" {
+ machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
+ if err != nil {
+ return
+ }
+ }
+ return setupDiGetClassDevsEx(classGUID, enumeratorUTF16, hwndParent, flags, deviceInfoSet, machineNameUTF16, 0)
+}
+
+// SetupDiCallClassInstaller function calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
+//sys SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA) (err error) = setupapi.SetupDiCallClassInstaller
+
+// CallClassInstaller member calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
+func (deviceInfoSet DevInfo) CallClassInstaller(installFunction DI_FUNCTION, deviceInfoData *SP_DEVINFO_DATA) error {
+ return SetupDiCallClassInstaller(installFunction, deviceInfoSet, deviceInfoData)
+}
+
+//sys setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) [failretval==windows.InvalidHandle] = setupapi.SetupDiOpenDevRegKey
+
+// SetupDiOpenDevRegKey function opens a registry key for device-specific configuration information.
+func SetupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, scope DICS_FLAG, hwProfile uint32, keyType DIREG, samDesired uint32) (registry.Key, error) {
+ handle, err := setupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, scope, hwProfile, keyType, samDesired)
+ return registry.Key(handle), err
+}
+
+// OpenDevRegKey method opens a registry key for device-specific configuration information.
+func (deviceInfoSet DevInfo) OpenDevRegKey(DeviceInfoData *SP_DEVINFO_DATA, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (registry.Key, error) {
+ return SetupDiOpenDevRegKey(deviceInfoSet, DeviceInfoData, Scope, HwProfile, KeyType, samDesired)
+}
+
+//sys setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceRegistryPropertyW
+
+// SetupDiGetDeviceRegistryProperty function retrieves a specified Plug and Play device property.
+func SetupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, property SPDRP) (value interface{}, err error) {
+ buf := make([]byte, 0x100)
+ var dataType, bufLen uint32
+ err = setupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
+ if err == nil {
+ // The buffer was sufficiently big.
+ return getRegistryValue(buf[:bufLen], dataType)
+ }
+
+ if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
+ // The buffer was too small. Now that we got the required size, create another one big enough and retry.
+ buf = make([]byte, bufLen)
+ err = setupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
+ if err == nil {
+ return getRegistryValue(buf[:bufLen], dataType)
+ }
+ }
+
+ return
+}
+
+func getRegistryValue(buf []byte, dataType uint32) (interface{}, error) {
+ switch dataType {
+ case windows.REG_SZ:
+ return windows.UTF16ToString(BufToUTF16(buf)), nil
+ case windows.REG_EXPAND_SZ:
+ return registry.ExpandString(windows.UTF16ToString(BufToUTF16(buf)))
+ case windows.REG_BINARY:
+ return buf, nil
+ case windows.REG_DWORD_LITTLE_ENDIAN:
+ return binary.LittleEndian.Uint32(buf), nil
+ case windows.REG_DWORD_BIG_ENDIAN:
+ return binary.BigEndian.Uint32(buf), nil
+ case windows.REG_MULTI_SZ:
+ bufW := BufToUTF16(buf)
+ a := []string{}
+ for i := 0; i < len(bufW); {
+ j := i + wcslen(bufW[i:])
+ if i < j {
+ a = append(a, windows.UTF16ToString(bufW[i:j]))
+ }
+ i = j + 1
+ }
+ return a, nil
+ case windows.REG_QWORD_LITTLE_ENDIAN:
+ return binary.LittleEndian.Uint64(buf), nil
+ default:
+ return nil, fmt.Errorf("Unsupported registry value type: %v", dataType)
+ }
+}
+
+// BufToUTF16 function reinterprets []byte buffer as []uint16
+func BufToUTF16(buf []byte) []uint16 {
+ sl := struct {
+ addr *uint16
+ len int
+ cap int
+ }{(*uint16)(unsafe.Pointer(&buf[0])), len(buf) / 2, cap(buf) / 2}
+ return *(*[]uint16)(unsafe.Pointer(&sl))
+}
+
+// UTF16ToBuf function reinterprets []uint16 as []byte
+func UTF16ToBuf(buf []uint16) []byte {
+ sl := struct {
+ addr *byte
+ len int
+ cap int
+ }{(*byte)(unsafe.Pointer(&buf[0])), len(buf) * 2, cap(buf) * 2}
+ return *(*[]byte)(unsafe.Pointer(&sl))
+}
+
+func wcslen(str []uint16) int {
+ for i := 0; i < len(str); i++ {
+ if str[i] == 0 {
+ return i
+ }
+ }
+ return len(str)
+}
+
+// GetDeviceRegistryProperty method retrieves a specified Plug and Play device property.
+func (deviceInfoSet DevInfo) GetDeviceRegistryProperty(deviceInfoData *SP_DEVINFO_DATA, property SPDRP) (interface{}, error) {
+ return SetupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property)
+}
+
+//sys setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) = setupapi.SetupDiSetDeviceRegistryPropertyW
+
+// SetupDiSetDeviceRegistryProperty function sets a Plug and Play device property for a device.
+func SetupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, property SPDRP, propertyBuffers []byte) error {
+ return setupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &propertyBuffers[0], uint32(len(propertyBuffers)))
+}
+
+// SetDeviceRegistryProperty function sets a Plug and Play device property for a device.
+func (deviceInfoSet DevInfo) SetDeviceRegistryProperty(deviceInfoData *SP_DEVINFO_DATA, property SPDRP, propertyBuffers []byte) error {
+ return SetupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, propertyBuffers)
+}
+
+//sys setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, deviceInstallParams *_SP_DEVINSTALL_PARAMS) (err error) = setupapi.SetupDiGetDeviceInstallParamsW
+
+// SetupDiGetDeviceInstallParams function retrieves device installation parameters for a device information set or a particular device information element.
+func SetupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA) (deviceInstallParams *DevInstallParams, err error) {
+ var _data _SP_DEVINSTALL_PARAMS
+ _data.Size = uint32(unsafe.Sizeof(_data))
+
+ err = setupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData, &_data)
+ if err != nil {
+ return
+ }
+
+ return _data.toGo(), nil
+}
+
+// GetDeviceInstallParams method retrieves device installation parameters for a device information set or a particular device information element.
+func (deviceInfoSet DevInfo) GetDeviceInstallParams(deviceInfoData *SP_DEVINFO_DATA) (*DevInstallParams, error) {
+ return SetupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData)
+}
+
+// SetupDiGetClassInstallParams function retrieves class installation parameters for a device information set or a particular device information element.
+//sys SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, classInstallParams *SP_CLASSINSTALL_HEADER, classInstallParamsSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetClassInstallParamsW
+
+// GetClassInstallParams method retrieves class installation parameters for a device information set or a particular device information element.
+func (deviceInfoSet DevInfo) GetClassInstallParams(deviceInfoData *SP_DEVINFO_DATA, classInstallParams *SP_CLASSINSTALL_HEADER, classInstallParamsSize uint32, requiredSize *uint32) error {
+ return SetupDiGetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize, requiredSize)
+}
+
+//sys setupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, deviceInstallParams *_SP_DEVINSTALL_PARAMS) (err error) = setupapi.SetupDiSetDeviceInstallParamsW
+
+// SetupDiSetDeviceInstallParams function sets device installation parameters for a device information set or a particular device information element.
+func SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, deviceInstallParams *DevInstallParams) (err error) {
+ _data, err := deviceInstallParams.toWindows()
+ if err != nil {
+ return
+ }
+
+ return setupDiSetDeviceInstallParams(deviceInfoSet, deviceInfoData, _data)
+}
+
+// SetDeviceInstallParams member sets device installation parameters for a device information set or a particular device information element.
+func (deviceInfoSet DevInfo) SetDeviceInstallParams(deviceInfoData *SP_DEVINFO_DATA, deviceInstallParams *DevInstallParams) error {
+ return SetupDiSetDeviceInstallParams(deviceInfoSet, deviceInfoData, deviceInstallParams)
+}
+
+// SetupDiSetClassInstallParams function sets or clears class install parameters for a device information set or a particular device information element.
+//sys SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, classInstallParams *SP_CLASSINSTALL_HEADER, classInstallParamsSize uint32) (err error) = setupapi.SetupDiSetClassInstallParamsW
+
+// SetClassInstallParams method sets or clears class install parameters for a device information set or a particular device information element.
+func (deviceInfoSet DevInfo) SetClassInstallParams(deviceInfoData *SP_DEVINFO_DATA, classInstallParams *SP_CLASSINSTALL_HEADER, classInstallParamsSize uint32) error {
+ return SetupDiSetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize)
+}
+
+//sys setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassNameFromGuidExW
+
+// SetupDiClassNameFromGuidEx function retrieves the class name associated with a class GUID. The class can be installed on a local or remote computer.
+func SetupDiClassNameFromGuidEx(classGUID *windows.GUID, machineName string) (className string, err error) {
+ var classNameUTF16 [MAX_CLASS_NAME_LEN]uint16
+
+ var machineNameUTF16 *uint16
+ if machineName != "" {
+ machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
+ if err != nil {
+ return
+ }
+ }
+
+ err = setupDiClassNameFromGuidEx(classGUID, &classNameUTF16[0], MAX_CLASS_NAME_LEN, nil, machineNameUTF16, 0)
+ if err != nil {
+ return
+ }
+
+ className = windows.UTF16ToString(classNameUTF16[:])
+ return
+}
+
+//sys setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassGuidsFromNameExW
+
+// SetupDiClassGuidsFromNameEx function retrieves the GUIDs associated with the specified class name. This resulting list contains the classes currently installed on a local or remote computer.
+func SetupDiClassGuidsFromNameEx(className string, machineName string) (classGuidLists []windows.GUID, err error) {
+ classNameUTF16, err := syscall.UTF16PtrFromString(className)
+ if err != nil {
+ return
+ }
+
+ const bufCapacity = 4
+ var buf [bufCapacity]windows.GUID
+ var bufLen uint32
+
+ var machineNameUTF16 *uint16
+ if machineName != "" {
+ machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
+ if err != nil {
+ return
+ }
+ }
+
+ err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufCapacity, &bufLen, machineNameUTF16, 0)
+ if err == nil {
+ // The GUID array was sufficiently big. Return its slice.
+ return buf[:bufLen], nil
+ }
+
+ if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
+ // The GUID array was too small. Now that we got the required size, create another one big enough and retry.
+ buf := make([]windows.GUID, bufLen)
+ err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufLen, &bufLen, machineNameUTF16, 0)
+ if err == nil {
+ return buf[:bufLen], nil
+ }
+ }
+
+ return
+}
+
+//sys setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA) (err error) = setupapi.SetupDiGetSelectedDevice
+
+// SetupDiGetSelectedDevice function retrieves the selected device information element in a device information set.
+func SetupDiGetSelectedDevice(deviceInfoSet DevInfo) (*SP_DEVINFO_DATA, error) {
+ data := SP_DEVINFO_DATA{}
+ data.Size = uint32(unsafe.Sizeof(data))
+
+ return &data, setupDiGetSelectedDevice(deviceInfoSet, &data)
+}
+
+// GetSelectedDevice method retrieves the selected device information element in a device information set.
+func (deviceInfoSet DevInfo) GetSelectedDevice() (*SP_DEVINFO_DATA, error) {
+ return SetupDiGetSelectedDevice(deviceInfoSet)
+}
+
+// SetupDiSetSelectedDevice function sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
+//sys SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA) (err error) = setupapi.SetupDiSetSelectedDevice
+
+// SetSelectedDevice method sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
+func (deviceInfoSet DevInfo) SetSelectedDevice(deviceInfoData *SP_DEVINFO_DATA) error {
+ return SetupDiSetSelectedDevice(deviceInfoSet, deviceInfoData)
+}
diff --git a/tun/wintun/setupapi/setupapi_windows_test.go b/tun/wintun/setupapi/setupapi_windows_test.go
new file mode 100644
index 0000000..e6b00c9
--- /dev/null
+++ b/tun/wintun/setupapi/setupapi_windows_test.go
@@ -0,0 +1,452 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package setupapi
+
+import (
+ "strings"
+ "syscall"
+ "testing"
+
+ "golang.org/x/sys/windows"
+)
+
+var deviceClassNetGUID = windows.GUID{0x4d36e972, 0xe325, 0x11ce, [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
+var computerName string
+
+func init() {
+ computerName, _ = windows.ComputerName()
+}
+
+func TestSetupDiCreateDeviceInfoListEx(t *testing.T) {
+ devInfoList, err := SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, "")
+ if err == nil {
+ devInfoList.Close()
+ } else {
+ t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
+ }
+
+ devInfoList, err = SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, computerName)
+ if err == nil {
+ devInfoList.Close()
+ } else {
+ t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
+ }
+
+ devInfoList, err = SetupDiCreateDeviceInfoListEx(nil, 0, "")
+ if err == nil {
+ devInfoList.Close()
+ } else {
+ t.Errorf("Error calling SetupDiCreateDeviceInfoListEx(nil): %s", err.Error())
+ }
+}
+
+func TestSetupDiGetDeviceInfoListDetail(t *testing.T) {
+ devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
+ }
+ defer devInfoList.Close()
+
+ data, err := devInfoList.GetDeviceInfoListDetail()
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetDeviceInfoListDetail: %s", err.Error())
+ } else {
+ if data.ClassGUID != deviceClassNetGUID {
+ t.Error("SetupDiGetDeviceInfoListDetail returned different class GUID")
+ }
+
+ if data.RemoteMachineHandle != windows.Handle(0) {
+ t.Error("SetupDiGetDeviceInfoListDetail returned non-NULL remote machine handle")
+ }
+
+ if data.RemoteMachineName != "" {
+ t.Error("SetupDiGetDeviceInfoListDetail returned non-NULL remote machine name")
+ }
+ }
+
+ devInfoList, err = SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), computerName)
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
+ }
+ defer devInfoList.Close()
+
+ data, err = devInfoList.GetDeviceInfoListDetail()
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetDeviceInfoListDetail: %s", err.Error())
+ } else {
+ if data.ClassGUID != deviceClassNetGUID {
+ t.Error("SetupDiGetDeviceInfoListDetail returned different class GUID")
+ }
+
+ if data.RemoteMachineHandle == windows.Handle(0) {
+ t.Error("SetupDiGetDeviceInfoListDetail returned NULL remote machine handle")
+ }
+
+ if data.RemoteMachineName != computerName {
+ t.Error("SetupDiGetDeviceInfoListDetail returned different remote machine name")
+ }
+ }
+}
+
+func TestSetupDiCreateDeviceInfo(t *testing.T) {
+ devInfoList, err := SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, computerName)
+ if err != nil {
+ t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
+ }
+ defer devInfoList.Close()
+
+ deviceClassNetName, err := SetupDiClassNameFromGuidEx(&deviceClassNetGUID, computerName)
+ if err != nil {
+ t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
+ }
+
+ devInfoData, err := devInfoList.CreateDeviceInfo(deviceClassNetName, &deviceClassNetGUID, "This is a test device", 0, DICD_GENERATE_ID)
+ if err != nil {
+ // Access denied is expected, as the SetupDiCreateDeviceInfo() require elevation to succeed.
+ if errWin, ok := err.(syscall.Errno); !ok || errWin != windows.ERROR_ACCESS_DENIED {
+ t.Errorf("Error calling SetupDiCreateDeviceInfo: %s", err.Error())
+ }
+ } else if devInfoData.ClassGUID != deviceClassNetGUID {
+ t.Error("SetupDiCreateDeviceInfo returned different class GUID")
+ }
+}
+
+func TestSetupDiEnumDeviceInfo(t *testing.T) {
+ devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
+ }
+ defer devInfoList.Close()
+
+ for i := 0; true; i++ {
+ data, err := devInfoList.EnumDeviceInfo(i)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ continue
+ }
+
+ if data.ClassGUID != deviceClassNetGUID {
+ t.Error("SetupDiEnumDeviceInfo returned different class GUID")
+ }
+ }
+}
+
+func TestDevInfo_BuildDriverInfoList(t *testing.T) {
+ devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
+ }
+ defer devInfoList.Close()
+
+ for i := 0; true; i++ {
+ deviceData, err := devInfoList.EnumDeviceInfo(i)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ continue
+ }
+
+ const driverType SPDIT = SPDIT_COMPATDRIVER
+ err = devInfoList.BuildDriverInfoList(deviceData, driverType)
+ if err != nil {
+ t.Errorf("Error calling SetupDiBuildDriverInfoList: %s", err.Error())
+ }
+ defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
+
+ var selectedDriverData *SP_DRVINFO_DATA
+ for j := 0; true; j++ {
+ driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, j)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ continue
+ }
+
+ if driverData2, err2 := driverData.toGo().toWindows(); err2 != nil || *driverData2 != *driverData {
+ t.Error("Error converting between SP_DRVINFO_DATA and DrvInfoData")
+ }
+
+ if driverData.DriverType == 0 {
+ continue
+ }
+
+ if !driverData.IsNewer(windows.Filetime{}, 0) {
+ t.Error("Driver should have non-zero date and version")
+ }
+ if !driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime}, 0) {
+ t.Error("Driver should have non-zero date and version")
+ }
+ if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime + 1}, 0) {
+ t.Error("Driver should report newer version on high-date-time")
+ }
+ if !driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, 0) {
+ t.Error("Driver should have non-zero version")
+ }
+ if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime + 1}, 0) {
+ t.Error("Driver should report newer version on low-date-time")
+ }
+ if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, driverData.DriverVersion) {
+ t.Error("Driver should not be newer than itself")
+ }
+ if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, driverData.DriverVersion+1) {
+ t.Error("Driver should report newer version on version")
+ }
+
+ err = devInfoList.SetSelectedDriver(deviceData, driverData)
+ if err != nil {
+ t.Errorf("Error calling SetupDiSetSelectedDriver: %s", err.Error())
+ } else {
+ selectedDriverData = driverData
+ }
+
+ driverDetailData, err := devInfoList.GetDriverInfoDetail(deviceData, driverData)
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetDriverInfoDetail: %s", err.Error())
+ }
+
+ if driverDetailData.IsCompatible("foobar-aab6e3a4-144e-4786-88d3-6cec361e1edd") {
+ t.Error("Invalid HWID compatibitlity reported")
+ }
+ if !driverDetailData.IsCompatible(strings.ToUpper(driverDetailData.HardwareID)) {
+ t.Error("HWID compatibitlity missed")
+ }
+ for k := range driverDetailData.CompatIDs {
+ if !driverDetailData.IsCompatible(strings.ToUpper(driverDetailData.CompatIDs[k])) {
+ t.Error("HWID compatibitlity missed")
+ }
+ }
+ }
+
+ selectedDriverData2, err := devInfoList.GetSelectedDriver(deviceData)
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetSelectedDriver: %s", err.Error())
+ } else if *selectedDriverData != *selectedDriverData2 {
+ t.Error("SetupDiGetSelectedDriver should return driver selected with SetupDiSetSelectedDriver")
+ }
+ }
+}
+
+func TestSetupDiGetClassDevsEx(t *testing.T) {
+ devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "PCI", 0, DIGCF_PRESENT, DevInfo(0), computerName)
+ if err == nil {
+ devInfoList.Close()
+ } else {
+ t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
+ }
+
+ devInfoList, err = SetupDiGetClassDevsEx(nil, "", 0, DIGCF_PRESENT, DevInfo(0), "")
+ if err == nil {
+ devInfoList.Close()
+ t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail")
+ } else {
+ if errWin, ok := err.(syscall.Errno); !ok || errWin != 87 /*ERROR_INVALID_PARAMETER*/ {
+ t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail with ERROR_INVALID_PARAMETER")
+ }
+ }
+}
+
+func TestSetupDiOpenDevRegKey(t *testing.T) {
+ devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
+ }
+ defer devInfoList.Close()
+
+ for i := 0; true; i++ {
+ data, err := devInfoList.EnumDeviceInfo(i)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ continue
+ }
+
+ key, err := devInfoList.OpenDevRegKey(data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, windows.KEY_READ)
+ if err != nil {
+ t.Errorf("Error calling SetupDiOpenDevRegKey: %s", err.Error())
+ }
+ defer key.Close()
+ }
+}
+
+func TestSetupDiGetDeviceRegistryProperty(t *testing.T) {
+ devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
+ }
+ defer devInfoList.Close()
+
+ for i := 0; true; i++ {
+ data, err := devInfoList.EnumDeviceInfo(i)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ continue
+ }
+
+ val, err := devInfoList.GetDeviceRegistryProperty(data, SPDRP_CLASS)
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CLASS): %s", err.Error())
+ } else if class, ok := val.(string); !ok || strings.ToLower(class) != "net" {
+ t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASS) should return \"Net\"")
+ }
+
+ val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_CLASSGUID)
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID): %s", err.Error())
+ } /* TODO: Parse GUID string: else if classGUID, ok := val.(string); !ok || parseGUID(classGUID) != deviceClassNetGUID {
+ t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID) should return %x", deviceClassNetGUID)
+ }*/
+
+ val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_COMPATIBLEIDS)
+ if err != nil {
+ // Some devices have no SPDRP_COMPATIBLEIDS.
+ if errWin, ok := err.(syscall.Errno); !ok || errWin != 13 /*windows.ERROR_INVALID_DATA*/ {
+ t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_COMPATIBLEIDS): %s", err.Error())
+ }
+ }
+
+ val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_CONFIGFLAGS)
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CONFIGFLAGS): %s", err.Error())
+ }
+
+ val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_DEVICE_POWER_DATA)
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_DEVICE_POWER_DATA): %s", err.Error())
+ }
+ }
+}
+
+func TestSetupDiGetDeviceInstallParams(t *testing.T) {
+ devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
+ }
+ defer devInfoList.Close()
+
+ for i := 0; true; i++ {
+ data, err := devInfoList.EnumDeviceInfo(i)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ continue
+ }
+
+ _, err = devInfoList.GetDeviceInstallParams(data)
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetDeviceInstallParams: %s", err.Error())
+ }
+ }
+}
+
+func TestSetupDiClassNameFromGuidEx(t *testing.T) {
+ deviceClassNetName, err := SetupDiClassNameFromGuidEx(&deviceClassNetGUID, "")
+ if err != nil {
+ t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
+ } else if strings.ToLower(deviceClassNetName) != "net" {
+ t.Errorf("SetupDiClassNameFromGuidEx(%x) should return \"Net\"", deviceClassNetGUID)
+ }
+
+ deviceClassNetName, err = SetupDiClassNameFromGuidEx(&deviceClassNetGUID, computerName)
+ if err != nil {
+ t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
+ } else if strings.ToLower(deviceClassNetName) != "net" {
+ t.Errorf("SetupDiClassNameFromGuidEx(%x) should return \"Net\"", deviceClassNetGUID)
+ }
+
+ _, err = SetupDiClassNameFromGuidEx(nil, "")
+ if err == nil {
+ t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail")
+ } else {
+ if errWin, ok := err.(syscall.Errno); !ok || errWin != 1784 /*ERROR_INVALID_USER_BUFFER*/ {
+ t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail with ERROR_INVALID_USER_BUFFER")
+ }
+ }
+}
+
+func TestSetupDiClassGuidsFromNameEx(t *testing.T) {
+ ClassGUIDs, err := SetupDiClassGuidsFromNameEx("Net", "")
+ if err != nil {
+ t.Errorf("Error calling SetupDiClassGuidsFromNameEx: %s", err.Error())
+ } else {
+ found := false
+ for i := range ClassGUIDs {
+ if ClassGUIDs[i] == deviceClassNetGUID {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("SetupDiClassGuidsFromNameEx(\"Net\") should return %x", deviceClassNetGUID)
+ }
+ }
+
+ ClassGUIDs, err = SetupDiClassGuidsFromNameEx("foobar-34274a51-a6e6-45f0-80d6-c62be96dd5fe", computerName)
+ if err != nil {
+ t.Errorf("Error calling SetupDiClassGuidsFromNameEx: %s", err.Error())
+ } else if len(ClassGUIDs) != 0 {
+ t.Errorf("SetupDiClassGuidsFromNameEx(\"foobar-34274a51-a6e6-45f0-80d6-c62be96dd5fe\") should return an empty GUID set")
+ }
+}
+
+func TestSetupDiGetSelectedDevice(t *testing.T) {
+ devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
+ }
+ defer devInfoList.Close()
+
+ for i := 0; true; i++ {
+ data, err := devInfoList.EnumDeviceInfo(i)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ continue
+ }
+
+ err = devInfoList.SetSelectedDevice(data)
+ if err != nil {
+ t.Errorf("Error calling SetupDiSetSelectedDevice: %s", err.Error())
+ }
+
+ data2, err := devInfoList.GetSelectedDevice()
+ if err != nil {
+ t.Errorf("Error calling SetupDiGetSelectedDevice: %s", err.Error())
+ } else if *data != *data2 {
+ t.Error("SetupDiGetSelectedDevice returned different data than was set by SetupDiSetSelectedDevice")
+ }
+ }
+
+ err = devInfoList.SetSelectedDevice(nil)
+ if err == nil {
+ t.Errorf("SetupDiSetSelectedDevice(nil) should fail")
+ } else {
+ if errWin, ok := err.(syscall.Errno); !ok || errWin != 87 /*ERROR_INVALID_PARAMETER*/ {
+ t.Errorf("SetupDiSetSelectedDevice(nil) should fail with ERROR_INVALID_USER_BUFFER")
+ }
+ }
+}
+
+func TestUTF16ToBuf(t *testing.T) {
+ buf := []uint16{0x0123, 0x4567, 0x89ab, 0xcdef}
+ buf2 := UTF16ToBuf(buf)
+ if len(buf)*2 != len(buf2) ||
+ cap(buf)*2 != cap(buf2) ||
+ buf2[0] != 0x23 || buf2[1] != 0x01 ||
+ buf2[2] != 0x67 || buf2[3] != 0x45 ||
+ buf2[4] != 0xab || buf2[5] != 0x89 ||
+ buf2[6] != 0xef || buf2[7] != 0xcd {
+ t.Errorf("SetupDiSetSelectedDevice(nil) should fail with ERROR_INVALID_USER_BUFFER")
+ }
+}
diff --git a/tun/wintun/setupapi/types_windows.go b/tun/wintun/setupapi/types_windows.go
new file mode 100644
index 0000000..c4aeff9
--- /dev/null
+++ b/tun/wintun/setupapi/types_windows.go
@@ -0,0 +1,571 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package setupapi
+
+import (
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+const (
+ MAX_DEVICE_ID_LEN = 200
+ MAX_DEVNODE_ID_LEN = MAX_DEVICE_ID_LEN
+ MAX_GUID_STRING_LEN = 39 // 38 chars + terminator null
+ MAX_CLASS_NAME_LEN = 32
+ MAX_PROFILE_LEN = 80
+ MAX_CONFIG_VALUE = 9999
+ MAX_INSTANCE_VALUE = 9999
+ CONFIGMG_VERSION = 0x0400
+)
+
+//
+// Define maximum string length constants
+//
+const (
+ LINE_LEN = 256 // Windows 9x-compatible maximum for displayable strings coming from a device INF.
+ MAX_INF_STRING_LENGTH = 4096 // Actual maximum size of an INF string (including string substitutions).
+ MAX_INF_SECTION_NAME_LENGTH = 255 // For Windows 9x compatibility, INF section names should be constrained to 32 characters.
+ MAX_TITLE_LEN = 60
+ MAX_INSTRUCTION_LEN = 256
+ MAX_LABEL_LEN = 30
+ MAX_SERVICE_NAME_LEN = 256
+ MAX_SUBTITLE_LEN = 256
+)
+
+const (
+ // SP_MAX_MACHINENAME_LENGTH defines maximum length of a machine name in the format expected by ConfigMgr32 CM_Connect_Machine (i.e., "\\\\MachineName\0").
+ SP_MAX_MACHINENAME_LENGTH = windows.MAX_PATH + 3
+)
+
+// HSPFILEQ is type for setup file queue
+type HSPFILEQ uintptr
+
+// DevInfo holds reference to device information set
+type DevInfo windows.Handle
+
+// SP_DEVINFO_DATA is a device information structure (references a device instance that is a member of a device information set)
+type SP_DEVINFO_DATA struct {
+ Size uint32
+ ClassGUID windows.GUID
+ DevInst uint32 // DEVINST handle
+ _ uintptr
+}
+
+type _SP_DEVINFO_LIST_DETAIL_DATA struct {
+ Size uint32
+ ClassGUID windows.GUID
+ RemoteMachineHandle windows.Handle
+ RemoteMachineName [SP_MAX_MACHINENAME_LENGTH]uint16
+}
+
+func (_data *_SP_DEVINFO_LIST_DETAIL_DATA) toGo() *DevInfoListDetailData {
+ return &DevInfoListDetailData{
+ ClassGUID: _data.ClassGUID,
+ RemoteMachineHandle: _data.RemoteMachineHandle,
+ RemoteMachineName: windows.UTF16ToString(_data.RemoteMachineName[:]),
+ }
+}
+
+// DevInfoListDetailData is a structure for detailed information on a device information set (used for SetupDiGetDeviceInfoListDetail which supercedes the functionality of SetupDiGetDeviceInfoListClass).
+type DevInfoListDetailData struct {
+ ClassGUID windows.GUID
+ RemoteMachineHandle windows.Handle
+ RemoteMachineName string
+}
+
+// DI_FUNCTION is function type for device installer
+type DI_FUNCTION uint32
+
+const (
+ DIF_SELECTDEVICE DI_FUNCTION = 0x00000001
+ DIF_INSTALLDEVICE DI_FUNCTION = 0x00000002
+ DIF_ASSIGNRESOURCES DI_FUNCTION = 0x00000003
+ DIF_PROPERTIES DI_FUNCTION = 0x00000004
+ DIF_REMOVE DI_FUNCTION = 0x00000005
+ DIF_FIRSTTIMESETUP DI_FUNCTION = 0x00000006
+ DIF_FOUNDDEVICE DI_FUNCTION = 0x00000007
+ DIF_SELECTCLASSDRIVERS DI_FUNCTION = 0x00000008
+ DIF_VALIDATECLASSDRIVERS DI_FUNCTION = 0x00000009
+ DIF_INSTALLCLASSDRIVERS DI_FUNCTION = 0x0000000A
+ DIF_CALCDISKSPACE DI_FUNCTION = 0x0000000B
+ DIF_DESTROYPRIVATEDATA DI_FUNCTION = 0x0000000C
+ DIF_VALIDATEDRIVER DI_FUNCTION = 0x0000000D
+ DIF_DETECT DI_FUNCTION = 0x0000000F
+ DIF_INSTALLWIZARD DI_FUNCTION = 0x00000010
+ DIF_DESTROYWIZARDDATA DI_FUNCTION = 0x00000011
+ DIF_PROPERTYCHANGE DI_FUNCTION = 0x00000012
+ DIF_ENABLECLASS DI_FUNCTION = 0x00000013
+ DIF_DETECTVERIFY DI_FUNCTION = 0x00000014
+ DIF_INSTALLDEVICEFILES DI_FUNCTION = 0x00000015
+ DIF_UNREMOVE DI_FUNCTION = 0x00000016
+ DIF_SELECTBESTCOMPATDRV DI_FUNCTION = 0x00000017
+ DIF_ALLOW_INSTALL DI_FUNCTION = 0x00000018
+ DIF_REGISTERDEVICE DI_FUNCTION = 0x00000019
+ DIF_NEWDEVICEWIZARD_PRESELECT DI_FUNCTION = 0x0000001A
+ DIF_NEWDEVICEWIZARD_SELECT DI_FUNCTION = 0x0000001B
+ DIF_NEWDEVICEWIZARD_PREANALYZE DI_FUNCTION = 0x0000001C
+ DIF_NEWDEVICEWIZARD_POSTANALYZE DI_FUNCTION = 0x0000001D
+ DIF_NEWDEVICEWIZARD_FINISHINSTALL DI_FUNCTION = 0x0000001E
+ DIF_INSTALLINTERFACES DI_FUNCTION = 0x00000020
+ DIF_DETECTCANCEL DI_FUNCTION = 0x00000021
+ DIF_REGISTER_COINSTALLERS DI_FUNCTION = 0x00000022
+ DIF_ADDPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000023
+ DIF_ADDPROPERTYPAGE_BASIC DI_FUNCTION = 0x00000024
+ DIF_TROUBLESHOOTER DI_FUNCTION = 0x00000026
+ DIF_POWERMESSAGEWAKE DI_FUNCTION = 0x00000027
+ DIF_ADDREMOTEPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000028
+ DIF_UPDATEDRIVER_UI DI_FUNCTION = 0x00000029
+ DIF_FINISHINSTALL_ACTION DI_FUNCTION = 0x0000002A
+)
+
+type _SP_DEVINSTALL_PARAMS struct {
+ Size uint32
+ Flags DI_FLAGS
+ FlagsEx DI_FLAGSEX
+ hwndParent uintptr
+ InstallMsgHandler uintptr
+ InstallMsgHandlerContext uintptr
+ FileQueue HSPFILEQ
+ _ uintptr
+ _ uint32
+ DriverPath [windows.MAX_PATH]uint16
+}
+
+func (_data *_SP_DEVINSTALL_PARAMS) toGo() *DevInstallParams {
+ return &DevInstallParams{
+ Flags: _data.Flags,
+ FlagsEx: _data.FlagsEx,
+ hwndParent: _data.hwndParent,
+ InstallMsgHandler: _data.InstallMsgHandler,
+ InstallMsgHandlerContext: _data.InstallMsgHandlerContext,
+ FileQueue: _data.FileQueue,
+ DriverPath: windows.UTF16ToString(_data.DriverPath[:]),
+ }
+}
+
+// DevInstallParams is device installation parameters structure (associated with a particular device information element, or globally with a device information set)
+type DevInstallParams struct {
+ Flags DI_FLAGS
+ FlagsEx DI_FLAGSEX
+ hwndParent uintptr
+ InstallMsgHandler uintptr
+ InstallMsgHandlerContext uintptr
+ FileQueue HSPFILEQ
+ DriverPath string
+}
+
+func (DeviceInstallParams *DevInstallParams) toWindows() (_data *_SP_DEVINSTALL_PARAMS, err error) {
+ _data = &_SP_DEVINSTALL_PARAMS{
+ Flags: DeviceInstallParams.Flags,
+ FlagsEx: DeviceInstallParams.FlagsEx,
+ hwndParent: DeviceInstallParams.hwndParent,
+ InstallMsgHandler: DeviceInstallParams.InstallMsgHandler,
+ InstallMsgHandlerContext: DeviceInstallParams.InstallMsgHandlerContext,
+ FileQueue: DeviceInstallParams.FileQueue,
+ }
+ _data.Size = uint32(unsafe.Sizeof(*_data))
+
+ driverPathUTF16, err := syscall.UTF16FromString(DeviceInstallParams.DriverPath)
+ if err != nil {
+ return
+ }
+ copy(_data.DriverPath[:], driverPathUTF16)
+
+ return
+}
+
+// DI_FLAGS is SP_DEVINSTALL_PARAMS.Flags values
+type DI_FLAGS uint32
+
+const (
+ // Flags for choosing a device
+ DI_SHOWOEM DI_FLAGS = 0x00000001 // support Other... button
+ DI_SHOWCOMPAT DI_FLAGS = 0x00000002 // show compatibility list
+ DI_SHOWCLASS DI_FLAGS = 0x00000004 // show class list
+ DI_SHOWALL DI_FLAGS = 0x00000007 // both class & compat list shown
+ DI_NOVCP DI_FLAGS = 0x00000008 // don't create a new copy queue--use caller-supplied FileQueue
+ DI_DIDCOMPAT DI_FLAGS = 0x00000010 // Searched for compatible devices
+ DI_DIDCLASS DI_FLAGS = 0x00000020 // Searched for class devices
+ DI_AUTOASSIGNRES DI_FLAGS = 0x00000040 // No UI for resources if possible
+
+ // Flags returned by DiInstallDevice to indicate need to reboot/restart
+ DI_NEEDRESTART DI_FLAGS = 0x00000080 // Reboot required to take effect
+ DI_NEEDREBOOT DI_FLAGS = 0x00000100 // ""
+
+ // Flags for device installation
+ DI_NOBROWSE DI_FLAGS = 0x00000200 // no Browse... in InsertDisk
+
+ // Flags set by DiBuildDriverInfoList
+ DI_MULTMFGS DI_FLAGS = 0x00000400 // Set if multiple manufacturers in class driver list
+
+ // Flag indicates that device is disabled
+ DI_DISABLED DI_FLAGS = 0x00000800 // Set if device disabled
+
+ // Flags for Device/Class Properties
+ DI_GENERALPAGE_ADDED DI_FLAGS = 0x00001000
+ DI_RESOURCEPAGE_ADDED DI_FLAGS = 0x00002000
+
+ // Flag to indicate the setting properties for this Device (or class) caused a change so the Dev Mgr UI probably needs to be updated.
+ DI_PROPERTIES_CHANGE DI_FLAGS = 0x00004000
+
+ // Flag to indicate that the sorting from the INF file should be used.
+ DI_INF_IS_SORTED DI_FLAGS = 0x00008000
+
+ // Flag to indicate that only the the INF specified by SP_DEVINSTALL_PARAMS.DriverPath should be searched.
+ DI_ENUMSINGLEINF DI_FLAGS = 0x00010000
+
+ // Flag that prevents ConfigMgr from removing/re-enumerating devices during device
+ // registration, installation, and deletion.
+ DI_DONOTCALLCONFIGMG DI_FLAGS = 0x00020000
+
+ // The following flag can be used to install a device disabled
+ DI_INSTALLDISABLED DI_FLAGS = 0x00040000
+
+ // Flag that causes SetupDiBuildDriverInfoList to build a device's compatible driver
+ // list from its existing class driver list, instead of the normal INF search.
+ DI_COMPAT_FROM_CLASS DI_FLAGS = 0x00080000
+
+ // This flag is set if the Class Install params should be used.
+ DI_CLASSINSTALLPARAMS DI_FLAGS = 0x00100000
+
+ // This flag is set if the caller of DiCallClassInstaller does NOT want the internal default action performed if the Class installer returns ERROR_DI_DO_DEFAULT.
+ DI_NODI_DEFAULTACTION DI_FLAGS = 0x00200000
+
+ // Flags for device installation
+ DI_QUIETINSTALL DI_FLAGS = 0x00800000 // don't confuse the user with questions or excess info
+ DI_NOFILECOPY DI_FLAGS = 0x01000000 // No file Copy necessary
+ DI_FORCECOPY DI_FLAGS = 0x02000000 // Force files to be copied from install path
+ DI_DRIVERPAGE_ADDED DI_FLAGS = 0x04000000 // Prop provider added Driver page.
+ DI_USECI_SELECTSTRINGS DI_FLAGS = 0x08000000 // Use Class Installer Provided strings in the Select Device Dlg
+ DI_OVERRIDE_INFFLAGS DI_FLAGS = 0x10000000 // Override INF flags
+ DI_PROPS_NOCHANGEUSAGE DI_FLAGS = 0x20000000 // No Enable/Disable in General Props
+
+ DI_NOSELECTICONS DI_FLAGS = 0x40000000 // No small icons in select device dialogs
+
+ DI_NOWRITE_IDS DI_FLAGS = 0x80000000 // Don't write HW & Compat IDs on install
+)
+
+// DI_FLAGSEX is SP_DEVINSTALL_PARAMS.FlagsEx values
+type DI_FLAGSEX uint32
+
+const (
+ DI_FLAGSEX_CI_FAILED DI_FLAGSEX = 0x00000004 // Failed to Load/Call class installer
+ DI_FLAGSEX_FINISHINSTALL_ACTION DI_FLAGSEX = 0x00000008 // Class/co-installer wants to get a DIF_FINISH_INSTALL action in client context.
+ DI_FLAGSEX_DIDINFOLIST DI_FLAGSEX = 0x00000010 // Did the Class Info List
+ DI_FLAGSEX_DIDCOMPATINFO DI_FLAGSEX = 0x00000020 // Did the Compat Info List
+ DI_FLAGSEX_FILTERCLASSES DI_FLAGSEX = 0x00000040
+ DI_FLAGSEX_SETFAILEDINSTALL DI_FLAGSEX = 0x00000080
+ DI_FLAGSEX_DEVICECHANGE DI_FLAGSEX = 0x00000100
+ DI_FLAGSEX_ALWAYSWRITEIDS DI_FLAGSEX = 0x00000200
+ DI_FLAGSEX_PROPCHANGE_PENDING DI_FLAGSEX = 0x00000400 // One or more device property sheets have had changes made to them, and need to have a DIF_PROPERTYCHANGE occur.
+ DI_FLAGSEX_ALLOWEXCLUDEDDRVS DI_FLAGSEX = 0x00000800
+ DI_FLAGSEX_NOUIONQUERYREMOVE DI_FLAGSEX = 0x00001000
+ DI_FLAGSEX_USECLASSFORCOMPAT DI_FLAGSEX = 0x00002000 // Use the device's class when building compat drv list. (Ignored if DI_COMPAT_FROM_CLASS flag is specified.)
+ DI_FLAGSEX_NO_DRVREG_MODIFY DI_FLAGSEX = 0x00008000 // Don't run AddReg and DelReg for device's software (driver) key.
+ DI_FLAGSEX_IN_SYSTEM_SETUP DI_FLAGSEX = 0x00010000 // Installation is occurring during initial system setup.
+ DI_FLAGSEX_INET_DRIVER DI_FLAGSEX = 0x00020000 // Driver came from Windows Update
+ DI_FLAGSEX_APPENDDRIVERLIST DI_FLAGSEX = 0x00040000 // Cause SetupDiBuildDriverInfoList to append a new driver list to an existing list.
+ DI_FLAGSEX_PREINSTALLBACKUP DI_FLAGSEX = 0x00080000 // not used
+ DI_FLAGSEX_BACKUPONREPLACE DI_FLAGSEX = 0x00100000 // not used
+ DI_FLAGSEX_DRIVERLIST_FROM_URL DI_FLAGSEX = 0x00200000 // build driver list from INF(s) retrieved from URL specified in SP_DEVINSTALL_PARAMS.DriverPath (empty string means Windows Update website)
+ DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS DI_FLAGSEX = 0x00800000 // Don't include old Internet drivers when building a driver list. Ignored on Windows Vista and later.
+ DI_FLAGSEX_POWERPAGE_ADDED DI_FLAGSEX = 0x01000000 // class installer added their own power page
+ DI_FLAGSEX_FILTERSIMILARDRIVERS DI_FLAGSEX = 0x02000000 // only include similar drivers in class list
+ DI_FLAGSEX_INSTALLEDDRIVER DI_FLAGSEX = 0x04000000 // only add the installed driver to the class or compat driver list. Used in calls to SetupDiBuildDriverInfoList
+ DI_FLAGSEX_NO_CLASSLIST_NODE_MERGE DI_FLAGSEX = 0x08000000 // Don't remove identical driver nodes from the class list
+ DI_FLAGSEX_ALTPLATFORM_DRVSEARCH DI_FLAGSEX = 0x10000000 // Build driver list based on alternate platform information specified in associated file queue
+ DI_FLAGSEX_RESTART_DEVICE_ONLY DI_FLAGSEX = 0x20000000 // only restart the device drivers are being installed on as opposed to restarting all devices using those drivers.
+ DI_FLAGSEX_RECURSIVESEARCH DI_FLAGSEX = 0x40000000 // Tell SetupDiBuildDriverInfoList to do a recursive search
+ DI_FLAGSEX_SEARCH_PUBLISHED_INFS DI_FLAGSEX = 0x80000000 // Tell SetupDiBuildDriverInfoList to do a "published INF" search
+)
+
+// SP_CLASSINSTALL_HEADER is the first member of any class install parameters structure. It contains the device installation request code that defines the format of the rest of the install parameters structure.
+type SP_CLASSINSTALL_HEADER struct {
+ Size uint32
+ InstallFunction DI_FUNCTION
+}
+
+// DICS_FLAG specifies the scope of a device property change
+type DICS_FLAG uint32
+
+const (
+ DICS_FLAG_GLOBAL DICS_FLAG = 0x00000001 // make change in all hardware profiles
+ DICS_FLAG_CONFIGSPECIFIC DICS_FLAG = 0x00000002 // make change in specified profile only
+ DICS_FLAG_CONFIGGENERAL DICS_FLAG = 0x00000004 // 1 or more hardware profile-specific changes to follow
+)
+
+// DI_REMOVEDEVICE specifies the scope of the device removal
+type DI_REMOVEDEVICE uint32
+
+const (
+ DI_REMOVEDEVICE_GLOBAL DI_REMOVEDEVICE = 0x00000001 // Make this change in all hardware profiles. Remove information about the device from the registry.
+ DI_REMOVEDEVICE_CONFIGSPECIFIC DI_REMOVEDEVICE = 0x00000002 // Make this change to only the hardware profile specified by HwProfile. this flag only applies to root-enumerated devices. When Windows removes the device from the last hardware profile in which it was configured, Windows performs a global removal.
+)
+
+// SP_REMOVEDEVICE_PARAMS is a structure corresponding to a DIF_REMOVE install function.
+type SP_REMOVEDEVICE_PARAMS struct {
+ ClassInstallHeader SP_CLASSINSTALL_HEADER
+ Scope DI_REMOVEDEVICE
+ HwProfile uint32
+}
+
+type SP_DRVINFO_DATA struct {
+ Size uint32
+ DriverType uint32
+ _ uintptr
+ Description [LINE_LEN]uint16
+ MfgName [LINE_LEN]uint16
+ ProviderName [LINE_LEN]uint16
+ DriverDate windows.Filetime
+ DriverVersion uint64
+}
+
+func (data *SP_DRVINFO_DATA) toGo() *DrvInfoData {
+ return &DrvInfoData{
+ DriverType: data.DriverType,
+ Description: windows.UTF16ToString(data.Description[:]),
+ MfgName: windows.UTF16ToString(data.MfgName[:]),
+ ProviderName: windows.UTF16ToString(data.ProviderName[:]),
+ DriverDate: data.DriverDate,
+ DriverVersion: data.DriverVersion,
+ }
+}
+
+// IsNewer method returns true if SP_DRVINFO_DATA date and version is newer than supplied parameters.
+func (data *SP_DRVINFO_DATA) IsNewer(driverDate windows.Filetime, driverVersion uint64) bool {
+ if data.DriverDate.HighDateTime > driverDate.HighDateTime {
+ return true
+ }
+ if data.DriverDate.HighDateTime < driverDate.HighDateTime {
+ return false
+ }
+
+ if data.DriverDate.LowDateTime > driverDate.LowDateTime {
+ return true
+ }
+ if data.DriverDate.LowDateTime < driverDate.LowDateTime {
+ return false
+ }
+
+ if data.DriverVersion > driverVersion {
+ return true
+ }
+ if data.DriverVersion < driverVersion {
+ return false
+ }
+
+ return false
+}
+
+// DrvInfoData is driver information structure (member of a driver info list that may be associated with a particular device instance, or (globally) with a device information set)
+type DrvInfoData struct {
+ DriverType uint32
+ Description string
+ MfgName string
+ ProviderName string
+ DriverDate windows.Filetime
+ DriverVersion uint64
+}
+
+func (driverInfoData *DrvInfoData) toWindows() (data *SP_DRVINFO_DATA, err error) {
+ data = &SP_DRVINFO_DATA{
+ DriverType: driverInfoData.DriverType,
+ DriverDate: driverInfoData.DriverDate,
+ DriverVersion: driverInfoData.DriverVersion,
+ }
+ data.Size = uint32(unsafe.Sizeof(*data))
+
+ DescriptionUTF16, err := syscall.UTF16FromString(driverInfoData.Description)
+ if err != nil {
+ return
+ }
+ copy(data.Description[:], DescriptionUTF16)
+
+ MfgNameUTF16, err := syscall.UTF16FromString(driverInfoData.MfgName)
+ if err != nil {
+ return
+ }
+ copy(data.MfgName[:], MfgNameUTF16)
+
+ ProviderNameUTF16, err := syscall.UTF16FromString(driverInfoData.ProviderName)
+ if err != nil {
+ return
+ }
+ copy(data.ProviderName[:], ProviderNameUTF16)
+
+ return
+}
+
+type _SP_DRVINFO_DETAIL_DATA struct {
+ Size uint32
+ InfDate windows.Filetime
+ CompatIDsOffset uint32
+ CompatIDsLength uint32
+ _ uintptr
+ SectionName [LINE_LEN]uint16
+ InfFileName [windows.MAX_PATH]uint16
+ DrvDescription [LINE_LEN]uint16
+ HardwareID [1]uint16
+}
+
+func (_data *_SP_DRVINFO_DETAIL_DATA) toGo(bufLen uint32) (DriverInfoDetailData *DrvInfoDetailData) {
+ DriverInfoDetailData = &DrvInfoDetailData{
+ InfDate: _data.InfDate,
+ SectionName: windows.UTF16ToString(_data.SectionName[:]),
+ InfFileName: windows.UTF16ToString(_data.InfFileName[:]),
+ DrvDescription: windows.UTF16ToString(_data.DrvDescription[:]),
+ CompatIDs: []string{},
+ }
+
+ bufW := _data.getBuf(bufLen)
+
+ if _data.CompatIDsOffset > 1 {
+ DriverInfoDetailData.HardwareID = windows.UTF16ToString(bufW[:wcslen(bufW)])
+ }
+
+ if _data.CompatIDsLength > 0 {
+ bufW = bufW[_data.CompatIDsOffset : _data.CompatIDsOffset+_data.CompatIDsLength]
+ for i := 0; i < len(bufW); {
+ j := i + wcslen(bufW[i:])
+ if i < j {
+ DriverInfoDetailData.CompatIDs = append(DriverInfoDetailData.CompatIDs, windows.UTF16ToString(bufW[i:j]))
+ }
+ i = j + 1
+ }
+ }
+
+ return
+}
+
+func (_data *_SP_DRVINFO_DETAIL_DATA) getBuf(bufLen uint32) []uint16 {
+ len := (bufLen - uint32(unsafe.Offsetof(_data.HardwareID))) / 2
+ sl := struct {
+ addr *uint16
+ len int
+ cap int
+ }{&_data.HardwareID[0], int(len), int(len)}
+ return *(*[]uint16)(unsafe.Pointer(&sl))
+}
+
+// DrvInfoDetailData is driver information details structure (provides detailed information about a particular driver information structure)
+type DrvInfoDetailData struct {
+ InfDate windows.Filetime
+ SectionName string
+ InfFileName string
+ DrvDescription string
+ HardwareID string
+ CompatIDs []string
+}
+
+// IsCompatible method tests if given hardware ID matches the driver or is listed on the compatible ID list.
+func (driverInfoDetailData *DrvInfoDetailData) IsCompatible(hwid string) bool {
+ hwidLC := strings.ToLower(hwid)
+ if strings.ToLower(driverInfoDetailData.HardwareID) == hwidLC {
+ return true
+ }
+ for i := range driverInfoDetailData.CompatIDs {
+ if strings.ToLower(driverInfoDetailData.CompatIDs[i]) == hwidLC {
+ return true
+ }
+ }
+
+ return false
+}
+
+// DICD flags control SetupDiCreateDeviceInfo
+type DICD uint32
+
+const (
+ DICD_GENERATE_ID DICD = 0x00000001
+ DICD_INHERIT_CLASSDRVS DICD = 0x00000002
+)
+
+//
+// SPDIT flags to distinguish between class drivers and
+// device drivers.
+// (Passed in 'DriverType' parameter of driver information list APIs)
+//
+type SPDIT uint32
+
+const (
+ SPDIT_NODRIVER SPDIT = 0x00000000
+ SPDIT_CLASSDRIVER SPDIT = 0x00000001
+ SPDIT_COMPATDRIVER SPDIT = 0x00000002
+)
+
+// DIGCF flags control what is included in the device information set built by SetupDiGetClassDevs
+type DIGCF uint32
+
+const (
+ DIGCF_DEFAULT DIGCF = 0x00000001 // only valid with DIGCF_DEVICEINTERFACE
+ DIGCF_PRESENT DIGCF = 0x00000002
+ DIGCF_ALLCLASSES DIGCF = 0x00000004
+ DIGCF_PROFILE DIGCF = 0x00000008
+ DIGCF_DEVICEINTERFACE DIGCF = 0x00000010
+)
+
+// DIREG specifies values for SetupDiCreateDevRegKey, SetupDiOpenDevRegKey, and SetupDiDeleteDevRegKey.
+type DIREG uint32
+
+const (
+ DIREG_DEV DIREG = 0x00000001 // Open/Create/Delete device key
+ DIREG_DRV DIREG = 0x00000002 // Open/Create/Delete driver key
+ DIREG_BOTH DIREG = 0x00000004 // Delete both driver and Device key
+)
+
+//
+// SPDRP specifies device registry property codes
+// (Codes marked as read-only (R) may only be used for
+// SetupDiGetDeviceRegistryProperty)
+//
+// These values should cover the same set of registry properties
+// as defined by the CM_DRP codes in cfgmgr32.h.
+//
+// Note that SPDRP codes are zero based while CM_DRP codes are one based!
+//
+type SPDRP uint32
+
+const (
+ SPDRP_DEVICEDESC SPDRP = 0x00000000 // DeviceDesc (R/W)
+ SPDRP_HARDWAREID SPDRP = 0x00000001 // HardwareID (R/W)
+ SPDRP_COMPATIBLEIDS SPDRP = 0x00000002 // CompatibleIDs (R/W)
+ SPDRP_SERVICE SPDRP = 0x00000004 // Service (R/W)
+ SPDRP_CLASS SPDRP = 0x00000007 // Class (R--tied to ClassGUID)
+ SPDRP_CLASSGUID SPDRP = 0x00000008 // ClassGUID (R/W)
+ SPDRP_DRIVER SPDRP = 0x00000009 // Driver (R/W)
+ SPDRP_CONFIGFLAGS SPDRP = 0x0000000A // ConfigFlags (R/W)
+ SPDRP_MFG SPDRP = 0x0000000B // Mfg (R/W)
+ SPDRP_FRIENDLYNAME SPDRP = 0x0000000C // FriendlyName (R/W)
+ SPDRP_LOCATION_INFORMATION SPDRP = 0x0000000D // LocationInformation (R/W)
+ SPDRP_PHYSICAL_DEVICE_OBJECT_NAME SPDRP = 0x0000000E // PhysicalDeviceObjectName (R)
+ SPDRP_CAPABILITIES SPDRP = 0x0000000F // Capabilities (R)
+ SPDRP_UI_NUMBER SPDRP = 0x00000010 // UiNumber (R)
+ SPDRP_UPPERFILTERS SPDRP = 0x00000011 // UpperFilters (R/W)
+ SPDRP_LOWERFILTERS SPDRP = 0x00000012 // LowerFilters (R/W)
+ SPDRP_BUSTYPEGUID SPDRP = 0x00000013 // BusTypeGUID (R)
+ SPDRP_LEGACYBUSTYPE SPDRP = 0x00000014 // LegacyBusType (R)
+ SPDRP_BUSNUMBER SPDRP = 0x00000015 // BusNumber (R)
+ SPDRP_ENUMERATOR_NAME SPDRP = 0x00000016 // Enumerator Name (R)
+ SPDRP_SECURITY SPDRP = 0x00000017 // Security (R/W, binary form)
+ SPDRP_SECURITY_SDS SPDRP = 0x00000018 // Security (W, SDS form)
+ SPDRP_DEVTYPE SPDRP = 0x00000019 // Device Type (R/W)
+ SPDRP_EXCLUSIVE SPDRP = 0x0000001A // Device is exclusive-access (R/W)
+ SPDRP_CHARACTERISTICS SPDRP = 0x0000001B // Device Characteristics (R/W)
+ SPDRP_ADDRESS SPDRP = 0x0000001C // Device Address (R)
+ SPDRP_UI_NUMBER_DESC_FORMAT SPDRP = 0x0000001D // UiNumberDescFormat (R/W)
+ SPDRP_DEVICE_POWER_DATA SPDRP = 0x0000001E // Device Power Data (R)
+ SPDRP_REMOVAL_POLICY SPDRP = 0x0000001F // Removal Policy (R)
+ SPDRP_REMOVAL_POLICY_HW_DEFAULT SPDRP = 0x00000020 // Hardware Removal Policy (R)
+ SPDRP_REMOVAL_POLICY_OVERRIDE SPDRP = 0x00000021 // Removal Policy Override (RW)
+ SPDRP_INSTALL_STATE SPDRP = 0x00000022 // Device Install State (R)
+ SPDRP_LOCATION_PATHS SPDRP = 0x00000023 // Device Location Paths (R)
+ SPDRP_BASE_CONTAINERID SPDRP = 0x00000024 // Base ContainerID (R)
+
+ SPDRP_MAXIMUM_PROPERTY SPDRP = 0x00000025 // Upper bound on ordinals
+)
diff --git a/tun/wintun/setupapi/zsetupapi_windows.go b/tun/wintun/setupapi/zsetupapi_windows.go
new file mode 100644
index 0000000..853b6a7
--- /dev/null
+++ b/tun/wintun/setupapi/zsetupapi_windows.go
@@ -0,0 +1,370 @@
+// Code generated by 'go generate'; DO NOT EDIT.
+
+package setupapi
+
+import (
+ "syscall"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+var _ unsafe.Pointer
+
+// Do the interface allocations only once for common
+// Errno values.
+const (
+ errnoERROR_IO_PENDING = 997
+)
+
+var (
+ errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+func errnoErr(e syscall.Errno) error {
+ switch e {
+ case 0:
+ return nil
+ case errnoERROR_IO_PENDING:
+ return errERROR_IO_PENDING
+ }
+ // TODO: add more here, after collecting data on the common
+ // error values see on Windows. (perhaps when running
+ // all.bat?)
+ return e
+}
+
+var (
+ modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
+
+ procSetupDiCreateDeviceInfoListExW = modsetupapi.NewProc("SetupDiCreateDeviceInfoListExW")
+ procSetupDiGetDeviceInfoListDetailW = modsetupapi.NewProc("SetupDiGetDeviceInfoListDetailW")
+ procSetupDiCreateDeviceInfoW = modsetupapi.NewProc("SetupDiCreateDeviceInfoW")
+ procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo")
+ procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList")
+ procSetupDiBuildDriverInfoList = modsetupapi.NewProc("SetupDiBuildDriverInfoList")
+ procSetupDiCancelDriverInfoSearch = modsetupapi.NewProc("SetupDiCancelDriverInfoSearch")
+ procSetupDiEnumDriverInfoW = modsetupapi.NewProc("SetupDiEnumDriverInfoW")
+ procSetupDiGetSelectedDriverW = modsetupapi.NewProc("SetupDiGetSelectedDriverW")
+ procSetupDiSetSelectedDriverW = modsetupapi.NewProc("SetupDiSetSelectedDriverW")
+ procSetupDiGetDriverInfoDetailW = modsetupapi.NewProc("SetupDiGetDriverInfoDetailW")
+ procSetupDiDestroyDriverInfoList = modsetupapi.NewProc("SetupDiDestroyDriverInfoList")
+ procSetupDiGetClassDevsExW = modsetupapi.NewProc("SetupDiGetClassDevsExW")
+ procSetupDiCallClassInstaller = modsetupapi.NewProc("SetupDiCallClassInstaller")
+ procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey")
+ procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW")
+ procSetupDiSetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiSetDeviceRegistryPropertyW")
+ procSetupDiGetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiGetDeviceInstallParamsW")
+ procSetupDiGetClassInstallParamsW = modsetupapi.NewProc("SetupDiGetClassInstallParamsW")
+ procSetupDiSetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiSetDeviceInstallParamsW")
+ procSetupDiSetClassInstallParamsW = modsetupapi.NewProc("SetupDiSetClassInstallParamsW")
+ procSetupDiClassNameFromGuidExW = modsetupapi.NewProc("SetupDiClassNameFromGuidExW")
+ procSetupDiClassGuidsFromNameExW = modsetupapi.NewProc("SetupDiClassGuidsFromNameExW")
+ procSetupDiGetSelectedDevice = modsetupapi.NewProc("SetupDiGetSelectedDevice")
+ procSetupDiSetSelectedDevice = modsetupapi.NewProc("SetupDiSetSelectedDevice")
+)
+
+func setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
+ r0, _, e1 := syscall.Syscall6(procSetupDiCreateDeviceInfoListExW.Addr(), 4, uintptr(unsafe.Pointer(classGUID)), uintptr(hwndParent), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0)
+ handle = DevInfo(r0)
+ if handle == DevInfo(windows.InvalidHandle) {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *_SP_DEVINFO_LIST_DETAIL_DATA) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInfoListDetailW.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoSetDetailData)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *SP_DEVINFO_DATA) (err error) {
+ r1, _, e1 := syscall.Syscall9(procSetupDiCreateDeviceInfoW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(DeviceName)), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(DeviceDescription)), uintptr(hwndParent), uintptr(CreationFlags), uintptr(unsafe.Pointer(deviceInfoData)), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *SP_DEVINFO_DATA) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(deviceInfoSet), uintptr(memberIndex), uintptr(unsafe.Pointer(deviceInfoData)))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(deviceInfoSet), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiBuildDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiCancelDriverInfoSearch.Addr(), 1, uintptr(deviceInfoSet), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT, memberIndex uint32, driverInfoData *SP_DRVINFO_DATA) (err error) {
+ r1, _, e1 := syscall.Syscall6(procSetupDiEnumDriverInfoW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType), uintptr(memberIndex), uintptr(unsafe.Pointer(driverInfoData)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverInfoData *SP_DRVINFO_DATA) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverInfoData *SP_DRVINFO_DATA) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverInfoData *SP_DRVINFO_DATA, driverInfoDetailData *_SP_DRVINFO_DETAIL_DATA, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procSetupDiGetDriverInfoDetailW.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)), uintptr(unsafe.Pointer(driverInfoDetailData)), uintptr(driverInfoDetailDataSize), uintptr(unsafe.Pointer(requiredSize)))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, driverType SPDIT) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiDestroyDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
+ r0, _, e1 := syscall.Syscall9(procSetupDiGetClassDevsExW.Addr(), 7, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(Enumerator)), uintptr(hwndParent), uintptr(Flags), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0)
+ handle = DevInfo(r0)
+ if handle == DevInfo(windows.InvalidHandle) {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiCallClassInstaller.Addr(), 3, uintptr(installFunction), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) {
+ r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(Scope), uintptr(HwProfile), uintptr(KeyType), uintptr(samDesired))
+ key = windows.Handle(r0)
+ if key == windows.InvalidHandle {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyRegDataType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procSetupDiSetDeviceRegistryPropertyW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, deviceInstallParams *_SP_DEVINSTALL_PARAMS) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, classInstallParams *SP_CLASSINSTALL_HEADER, classInstallParamsSize uint32, requiredSize *uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procSetupDiGetClassInstallParamsW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), uintptr(unsafe.Pointer(requiredSize)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, deviceInstallParams *_SP_DEVINSTALL_PARAMS) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiSetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA, classInstallParams *SP_CLASSINSTALL_HEADER, classInstallParamsSize uint32) (err error) {
+ r1, _, e1 := syscall.Syscall6(procSetupDiSetClassInstallParamsW.Addr(), 4, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), 0, 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall6(procSetupDiClassNameFromGuidExW.Addr(), 6, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(className)), uintptr(classNameSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
+ r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameExW.Addr(), 6, uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(classGuidList)), uintptr(classGuidListSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+func SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *SP_DEVINFO_DATA) (err error) {
+ r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0)
+ if r1 == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
diff --git a/tun/wintun/setupapi/zsetupapi_windows_test.go b/tun/wintun/setupapi/zsetupapi_windows_test.go
new file mode 100644
index 0000000..09b9195
--- /dev/null
+++ b/tun/wintun/setupapi/zsetupapi_windows_test.go
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package setupapi
+
+import (
+ "syscall"
+ "testing"
+
+ "golang.org/x/sys/windows"
+)
+
+func TestSetupDiDestroyDeviceInfoList(t *testing.T) {
+ err := SetupDiDestroyDeviceInfoList(DevInfo(windows.InvalidHandle))
+ if errWin, ok := err.(syscall.Errno); !ok || errWin != 6 /*ERROR_INVALID_HANDLE*/ {
+ t.Errorf("SetupDiDestroyDeviceInfoList(nil, ...) should fail with ERROR_INVALID_HANDLE")
+ }
+}
diff --git a/tun/wintun/wintun_windows.go b/tun/wintun/wintun_windows.go
new file mode 100644
index 0000000..1413c24
--- /dev/null
+++ b/tun/wintun/wintun_windows.go
@@ -0,0 +1,461 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package wintun
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+ "syscall"
+ "time"
+ "unsafe"
+
+ "git.zx2c4.com/wireguard-go/tun/wintun/guid"
+ "git.zx2c4.com/wireguard-go/tun/wintun/setupapi"
+ "golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/registry"
+)
+
+type Wintun windows.GUID
+
+var deviceClassNetGUID = windows.GUID{0x4d36e972, 0xe325, 0x11ce, [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
+
+const TUN_HWID = "Wintun"
+
+//
+// GetInterface finds interface ID by name.
+//
+// hwndParent is a handle to the top-level window to use for any user
+// interface that is related to non-device-specific actions (such as a select-
+// device dialog box that uses the global class driver list). This handle is
+// optional and can be 0. If a specific top-level window is not required, set
+// hwndParent to 0.
+//
+// Function returns interface ID when the interface was found, or nil
+// otherwise.
+//
+func GetInterface(ifname string, hwndParent uintptr) (*Wintun, error) {
+ // Create a list of network devices.
+ devInfoList, err := setupapi.SetupDiGetClassDevsEx(&deviceClassNetGUID, "", hwndParent, setupapi.DIGCF_PRESENT, setupapi.DevInfo(0), "")
+ if err != nil {
+ return nil, err
+ }
+ defer devInfoList.Close()
+
+ // Retrieve information associated with a device information set.
+ // TODO: Is this really necessary?
+ _, err = devInfoList.GetDeviceInfoListDetail()
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO: If we're certain we want case-insensitive name comparison, please document the rationale.
+ ifname = strings.ToLower(ifname)
+
+ // Iterate.
+ for index := 0; ; index++ {
+ // Get the device from the list.
+ deviceData, err := devInfoList.EnumDeviceInfo(index)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ // Something is wrong with this device. Skip it.
+ continue
+ }
+
+ // Get interface ID.
+ ifid, err := getInterfaceId(devInfoList, deviceData, 1)
+ if err != nil {
+ // Something is wrong with this device. Skip it.
+ continue
+ }
+
+ // Get interface name.
+ ifname2, err := ((*Wintun)(ifid)).GetInterfaceName()
+ if err != nil {
+ // Something is wrong with this device. Skip it.
+ continue
+ }
+
+ if ifname == strings.ToLower(ifname2) {
+ // Interface name found.
+ return (*Wintun)(ifid), nil
+ }
+ }
+
+ return nil, nil
+}
+
+//
+// CreateInterface creates a TUN interface.
+//
+// description is a string that supplies the text description of the device.
+// description is optional and can be "".
+//
+// hwndParent is a handle to the top-level window to use for any user
+// interface that is related to non-device-specific actions (such as a select-
+// device dialog box that uses the global class driver list). This handle is
+// optional and can be 0. If a specific top-level window is not required, set
+// hwndParent to 0.
+//
+// Function returns the network interface ID and a flag if reboot is required.
+//
+func CreateInterface(description string, hwndParent uintptr) (*Wintun, bool, error) {
+ // Create an empty device info set for network adapter device class.
+ devInfoList, err := setupapi.SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, hwndParent, "")
+ if err != nil {
+ return nil, false, err
+ }
+
+ // Get the device class name from GUID.
+ className, err := setupapi.SetupDiClassNameFromGuidEx(&deviceClassNetGUID, "")
+ if err != nil {
+ return nil, false, err
+ }
+
+ // Create a new device info element and add it to the device info set.
+ deviceData, err := devInfoList.CreateDeviceInfo(className, &deviceClassNetGUID, description, hwndParent, setupapi.DICD_GENERATE_ID)
+ if err != nil {
+ return nil, false, err
+ }
+
+ // Set a device information element as the selected member of a device information set.
+ err = devInfoList.SetSelectedDevice(deviceData)
+ if err != nil {
+ return nil, false, err
+ }
+
+ // Set Plug&Play device hardware ID property.
+ hwid, err := syscall.UTF16FromString(TUN_HWID)
+ if err != nil {
+ return nil, false, err
+ }
+ err = devInfoList.SetDeviceRegistryProperty(deviceData, setupapi.SPDRP_HARDWAREID, setupapi.UTF16ToBuf(append(hwid, 0)))
+ if err != nil {
+ return nil, false, err
+ }
+
+ // Search for the driver.
+ const driverType = setupapi.SPDIT_CLASSDRIVER
+ err = devInfoList.BuildDriverInfoList(deviceData, driverType)
+ if err != nil {
+ return nil, false, err
+ }
+ defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
+
+ driverDate := windows.Filetime{}
+ driverVersion := uint64(0)
+ for index := 0; ; index++ {
+ // Get a driver from the list.
+ driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, index)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ // Something is wrong with this driver. Skip it.
+ continue
+ }
+
+ // Check the driver version first, since the check is trivial and will save us iterating over hardware IDs for any driver versioned prior our best match.
+ if driverData.IsNewer(driverDate, driverVersion) {
+ // Get driver info details.
+ driverDetailData, err := devInfoList.GetDriverInfoDetail(deviceData, driverData)
+ if err != nil {
+ // Something is wrong with this driver. Skip it.
+ continue
+ }
+
+ if driverDetailData.IsCompatible(TUN_HWID) {
+ // Matching hardware ID found. Select the driver.
+ err := devInfoList.SetSelectedDriver(deviceData, driverData)
+ if err != nil {
+ // Something is wrong with this driver. Skip it.
+ continue
+ }
+
+ driverDate = driverData.DriverDate
+ driverVersion = driverData.DriverVersion
+ }
+ }
+ }
+
+ if driverVersion == 0 {
+ return nil, false, fmt.Errorf("No driver for device \"%v\" installed", TUN_HWID)
+ }
+
+ // Call appropriate class installer.
+ err = devInfoList.CallClassInstaller(setupapi.DIF_REGISTERDEVICE, deviceData)
+ if err != nil {
+ return nil, false, err
+ }
+
+ // Register device co-installers if any.
+ devInfoList.CallClassInstaller(setupapi.DIF_REGISTER_COINSTALLERS, deviceData)
+
+ // Install interfaces if any.
+ devInfoList.CallClassInstaller(setupapi.DIF_INSTALLINTERFACES, deviceData)
+
+ var ifid *windows.GUID
+ var rebootRequired bool
+
+ // Install the device.
+ err = devInfoList.CallClassInstaller(setupapi.DIF_INSTALLDEVICE, deviceData)
+ if err == nil {
+ // Check if a system reboot is required. (Ignore errors)
+ if ret, _ := checkReboot(devInfoList, deviceData); ret {
+ rebootRequired = true
+ }
+
+ // Get network interface ID from registry. Retry for max 30sec.
+ ifid, err = getInterfaceId(devInfoList, deviceData, 30)
+ }
+
+ if err == nil {
+ return (*Wintun)(ifid), rebootRequired, nil
+ }
+
+ // The interface failed to install, or the interface ID was unobtainable. Clean-up.
+ removeDeviceParams := setupapi.SP_REMOVEDEVICE_PARAMS{
+ ClassInstallHeader: setupapi.SP_CLASSINSTALL_HEADER{
+ InstallFunction: setupapi.DIF_REMOVE,
+ },
+ Scope: setupapi.DI_REMOVEDEVICE_GLOBAL,
+ }
+ removeDeviceParams.ClassInstallHeader.Size = uint32(unsafe.Sizeof(removeDeviceParams.ClassInstallHeader))
+
+ // Set class installer parameters for DIF_REMOVE.
+ if devInfoList.SetClassInstallParams(deviceData, &removeDeviceParams.ClassInstallHeader, uint32(unsafe.Sizeof(removeDeviceParams))) == nil {
+ // Call appropriate class installer.
+ if devInfoList.CallClassInstaller(setupapi.DIF_REMOVE, deviceData) == nil {
+ // Check if a system reboot is required. (Ignore errors)
+ if ret, _ := checkReboot(devInfoList, deviceData); ret {
+ rebootRequired = true
+ }
+ }
+ }
+
+ return nil, false, err
+}
+
+//
+// DeleteInterface deletes a TUN interface.
+//
+// hwndParent is a handle to the top-level window to use for any user
+// interface that is related to non-device-specific actions (such as a select-
+// device dialog box that uses the global class driver list). This handle is
+// optional and can be 0. If a specific top-level window is not required, set
+// hwndParent to 0.
+//
+// Function returns true if the interface was found and deleted and a flag if
+// reboot is required.
+//
+func (wintun *Wintun) DeleteInterface(hwndParent uintptr) (bool, bool, error) {
+ ifid := (*windows.GUID)(wintun)
+ // Create a list of network devices.
+ devInfoList, err := setupapi.SetupDiGetClassDevsEx(&deviceClassNetGUID, "", hwndParent, setupapi.DIGCF_PRESENT, setupapi.DevInfo(0), "")
+ if err != nil {
+ return false, false, err
+ }
+ defer devInfoList.Close()
+
+ // Retrieve information associated with a device information set.
+ // TODO: Is this really necessary?
+ _, err = devInfoList.GetDeviceInfoListDetail()
+ if err != nil {
+ return false, false, err
+ }
+
+ // Iterate.
+ for index := 0; ; index++ {
+ // Get the device from the list.
+ deviceData, err := devInfoList.EnumDeviceInfo(index)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
+ break
+ }
+ // Something is wrong with this device. Skip it.
+ continue
+ }
+
+ // Get interface ID.
+ ifid2, err := getInterfaceId(devInfoList, deviceData, 1)
+ if err != nil {
+ // Something is wrong with this device. Skip it.
+ continue
+ }
+
+ if ifid == ifid2 {
+ // Remove the device.
+ removeDeviceParams := setupapi.SP_REMOVEDEVICE_PARAMS{
+ ClassInstallHeader: setupapi.SP_CLASSINSTALL_HEADER{
+ InstallFunction: setupapi.DIF_REMOVE,
+ },
+ Scope: setupapi.DI_REMOVEDEVICE_GLOBAL,
+ }
+ removeDeviceParams.ClassInstallHeader.Size = uint32(unsafe.Sizeof(removeDeviceParams.ClassInstallHeader))
+
+ // Set class installer parameters for DIF_REMOVE.
+ err = devInfoList.SetClassInstallParams(deviceData, &removeDeviceParams.ClassInstallHeader, uint32(unsafe.Sizeof(removeDeviceParams)))
+ if err != nil {
+ return false, false, err
+ }
+
+ // Call appropriate class installer.
+ err = devInfoList.CallClassInstaller(setupapi.DIF_REMOVE, deviceData)
+ if err != nil {
+ return false, false, err
+ }
+
+ // Check if a system reboot is required. (Ignore errors)
+ if ret, _ := checkReboot(devInfoList, deviceData); ret {
+ return true, true, nil
+ }
+
+ return true, false, nil
+ }
+ }
+
+ return false, false, nil
+}
+
+///
+/// checkReboot checks device install parameters if a system reboot is required.
+///
+func checkReboot(deviceInfoSet setupapi.DevInfo, deviceInfoData *setupapi.SP_DEVINFO_DATA) (bool, error) {
+ devInstallParams, err := deviceInfoSet.GetDeviceInstallParams(deviceInfoData)
+ if err != nil {
+ return false, err
+ }
+
+ if (devInstallParams.Flags & (setupapi.DI_NEEDREBOOT | setupapi.DI_NEEDRESTART)) != 0 {
+ return true, nil
+ }
+
+ return false, nil
+}
+
+// getInterfaceId returns network interface ID.
+//
+// After the device is created, it might take some time before the registry
+// key is populated. numAttempts parameter specifies the number of attempts
+// to read NetCfgInstanceId value from registry. A 1sec sleep is inserted
+// between retry attempts.
+//
+// Function returns the network interface ID.
+//
+func getInterfaceId(deviceInfoSet setupapi.DevInfo, deviceInfoData *setupapi.SP_DEVINFO_DATA, numAttempts int) (*windows.GUID, error) {
+ if numAttempts < 1 {
+ return nil, fmt.Errorf("Invalid numAttempts (expected: >=1, provided: %v)", numAttempts)
+ }
+
+ // Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> registry key.
+ key, err := deviceInfoSet.OpenDevRegKey(deviceInfoData, setupapi.DICS_FLAG_GLOBAL, 0, setupapi.DIREG_DRV, registry.READ)
+ if err != nil {
+ return nil, errors.New("Device-specific registry key open failed: " + err.Error())
+ }
+ defer key.Close()
+
+ for {
+ // Query the NetCfgInstanceId value. Using get_reg_string() right on might clutter the output with error messages while the registry is still being populated.
+ _, _, err = key.GetValue("NetCfgInstanceId", nil)
+ if err != nil {
+ if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_FILE_NOT_FOUND {
+ numAttempts--
+ if numAttempts > 0 {
+ // Wait and retry.
+ // TODO: Wait for a cancellable event instead.
+ time.Sleep(1000 * time.Millisecond)
+ continue
+ }
+ }
+
+ return nil, errors.New("RegQueryValueEx(\"NetCfgInstanceId\") failed: " + err.Error())
+ }
+
+ // Read the NetCfgInstanceId value now.
+ value, err := getRegStringValue(key, "NetCfgInstanceId")
+ if err != nil {
+ return nil, errors.New("RegQueryStringValue(\"NetCfgInstanceId\") failed: " + err.Error())
+ }
+
+ // Convert to windows.GUID.
+ ifid, err := guid.FromString(value)
+ if err != nil {
+ return nil, fmt.Errorf("NetCfgInstanceId registry value is not a GUID (expected: \"{...}\", provided: \"%v\")", value)
+ }
+
+ return ifid, err
+ }
+}
+
+//
+// GetInterfaceName returns network interface name.
+//
+func (wintun *Wintun) GetInterfaceName() (string, error) {
+ ifid := (*windows.GUID)(wintun)
+ // Open network interface registry key.
+ key, err := registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Control\\Network\\%v\\%v\\Connection", guid.ToString(&deviceClassNetGUID), guid.ToString(ifid)), registry.QUERY_VALUE)
+ if err != nil {
+ return "", errors.New("Network-specific registry key open failed: " + err.Error())
+ }
+ defer key.Close()
+
+ // Get the interface name.
+ return getRegStringValue(key, "Name")
+}
+
+//
+// SetInterfaceName sets network interface name.
+//
+func (wintun *Wintun) SetInterfaceName(ifname string) error {
+ ifid := (*windows.GUID)(wintun)
+ // Open network interface registry key.
+ key, err := registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Control\\Network\\%v\\%v\\Connection", guid.ToString(&deviceClassNetGUID), guid.ToString(ifid)), registry.SET_VALUE)
+ if err != nil {
+ return errors.New("Network-specific registry key open failed: " + err.Error())
+ }
+ defer key.Close()
+
+ // Set the interface name.
+ return key.SetStringValue("Name", ifname)
+}
+
+//
+// getRegStringValue function reads a string value from registry.
+//
+// If the value type is REG_EXPAND_SZ the environment variables are expanded.
+// Should expanding fail, original string value and nil error are returned.
+//
+func getRegStringValue(key registry.Key, name string) (string, error) {
+ // Read string value.
+ value, valueType, err := key.GetStringValue(name)
+ if err != nil {
+ return "", err
+ }
+
+ if valueType != registry.EXPAND_SZ {
+ // Value does not require expansion.
+ return value, nil
+ }
+
+ valueExp, err := registry.ExpandString(value)
+ if err != nil {
+ // Expanding failed: return original sting value.
+ return value, nil
+ }
+
+ // Return expanded value.
+ return valueExp, nil
+}
+
+func (wintun *Wintun) SignalEventName() string {
+ return fmt.Sprintf("Global\\WINTUN_EVENT_%s", guid.ToString((*windows.GUID)(wintun)))
+}
+
+func (wintun *Wintun) DataFileName() string {
+ return fmt.Sprintf("\\\\.\\Global\\WINTUN_DEVICE_%s", guid.ToString((*windows.GUID)(wintun)))
+} \ No newline at end of file