From ea59177f1c6ab9031d5d30771410c9514fa551d9 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 7 Feb 2019 04:18:27 +0100 Subject: wintun: Introduce new package for obscuring Windows bits --- setupapi/mksyscall.go | 8 - setupapi/setupapi.go | 6 - setupapi/setupapi_windows.go | 476 --------------------- setupapi/setupapi_windows_test.go | 452 -------------------- setupapi/types_windows.go | 571 -------------------------- setupapi/zsetupapi_windows.go | 370 ----------------- setupapi/zsetupapi_windows_test.go | 20 - tun/tun_windows.go | 493 +--------------------- tun/wintun/guid/guid_windows.go | 44 ++ tun/wintun/guid/mksyscall.go | 8 + tun/wintun/guid/zguid_windows.go | 49 +++ tun/wintun/setupapi/mksyscall.go | 8 + tun/wintun/setupapi/setupapi_windows.go | 476 +++++++++++++++++++++ tun/wintun/setupapi/setupapi_windows_test.go | 452 ++++++++++++++++++++ tun/wintun/setupapi/types_windows.go | 571 ++++++++++++++++++++++++++ tun/wintun/setupapi/zsetupapi_windows.go | 370 +++++++++++++++++ tun/wintun/setupapi/zsetupapi_windows_test.go | 20 + tun/wintun/wintun_windows.go | 461 +++++++++++++++++++++ tun/ztun_windows.go | 49 --- 19 files changed, 2472 insertions(+), 2432 deletions(-) delete mode 100644 setupapi/mksyscall.go delete mode 100644 setupapi/setupapi.go delete mode 100644 setupapi/setupapi_windows.go delete mode 100644 setupapi/setupapi_windows_test.go delete mode 100644 setupapi/types_windows.go delete mode 100644 setupapi/zsetupapi_windows.go delete mode 100644 setupapi/zsetupapi_windows_test.go create mode 100644 tun/wintun/guid/guid_windows.go create mode 100644 tun/wintun/guid/mksyscall.go create mode 100644 tun/wintun/guid/zguid_windows.go create mode 100644 tun/wintun/setupapi/mksyscall.go create mode 100644 tun/wintun/setupapi/setupapi_windows.go create mode 100644 tun/wintun/setupapi/setupapi_windows_test.go create mode 100644 tun/wintun/setupapi/types_windows.go create mode 100644 tun/wintun/setupapi/zsetupapi_windows.go create mode 100644 tun/wintun/setupapi/zsetupapi_windows_test.go create mode 100644 tun/wintun/wintun_windows.go delete mode 100644 tun/ztun_windows.go diff --git a/setupapi/mksyscall.go b/setupapi/mksyscall.go deleted file mode 100644 index 3447dee..0000000 --- a/setupapi/mksyscall.go +++ /dev/null @@ -1,8 +0,0 @@ -/* 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/setupapi/setupapi.go b/setupapi/setupapi.go deleted file mode 100644 index 69703b3..0000000 --- a/setupapi/setupapi.go +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package setupapi diff --git a/setupapi/setupapi_windows.go b/setupapi/setupapi_windows.go deleted file mode 100644 index 72d4bd8..0000000 --- a/setupapi/setupapi_windows.go +++ /dev/null @@ -1,476 +0,0 @@ -/* 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/setupapi/setupapi_windows_test.go b/setupapi/setupapi_windows_test.go deleted file mode 100644 index e6b00c9..0000000 --- a/setupapi/setupapi_windows_test.go +++ /dev/null @@ -1,452 +0,0 @@ -/* 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/setupapi/types_windows.go b/setupapi/types_windows.go deleted file mode 100644 index c4aeff9..0000000 --- a/setupapi/types_windows.go +++ /dev/null @@ -1,571 +0,0 @@ -/* 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/setupapi/zsetupapi_windows.go b/setupapi/zsetupapi_windows.go deleted file mode 100644 index 853b6a7..0000000 --- a/setupapi/zsetupapi_windows.go +++ /dev/null @@ -1,370 +0,0 @@ -// 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/setupapi/zsetupapi_windows_test.go b/setupapi/zsetupapi_windows_test.go deleted file mode 100644 index 09b9195..0000000 --- a/setupapi/zsetupapi_windows_test.go +++ /dev/null @@ -1,20 +0,0 @@ -/* 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/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\\ 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/wintun/guid/zguid_windows.go b/tun/wintun/guid/zguid_windows.go new file mode 100644 index 0000000..5467849 --- /dev/null +++ b/tun/wintun/guid/zguid_windows.go @@ -0,0 +1,49 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package guid + +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 ( + modole32 = windows.NewLazySystemDLL("ole32.dll") + + procCLSIDFromString = modole32.NewProc("CLSIDFromString") +) + +func clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) { + r0, _, _ := syscall.Syscall(procCLSIDFromString.Addr(), 2, uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)), 0) + hr = int32(r0) + return +} 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\\ 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 diff --git a/tun/ztun_windows.go b/tun/ztun_windows.go deleted file mode 100644 index 9aa6e11..0000000 --- a/tun/ztun_windows.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by 'go generate'; DO NOT EDIT. - -package tun - -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 ( - modole32 = windows.NewLazySystemDLL("ole32.dll") - - procCLSIDFromString = modole32.NewProc("CLSIDFromString") -) - -func clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) { - r0, _, _ := syscall.Syscall(procCLSIDFromString.Addr(), 2, uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)), 0) - hr = int32(r0) - return -} -- cgit v1.2.3-54-g00ecf