mirror of
https://github.com/netbirdio/wireguard-go.git
synced 2026-05-22 17:08:51 -07:00
wintun: migrate to wintun.dll API
Rather than having every application using Wintun driver reinvent the wheel, the Wintun device/adapter/interface management has been moved from wireguard-go to wintun.dll deployed with Wintun itself. Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
+18
-10
@@ -33,7 +33,7 @@ type rateJuggler struct {
|
||||
}
|
||||
|
||||
type NativeTun struct {
|
||||
wt *wintun.Interface
|
||||
wt *wintun.Adapter
|
||||
handle windows.Handle
|
||||
close bool
|
||||
events chan Event
|
||||
@@ -44,7 +44,15 @@ type NativeTun struct {
|
||||
writeLock sync.Mutex
|
||||
}
|
||||
|
||||
const WintunPool = wintun.Pool("WireGuard")
|
||||
var WintunPool *wintun.Pool
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
WintunPool, err = wintun.MakePool("WireGuard")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Failed to make pool: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
//go:linkname procyield runtime.procyield
|
||||
func procyield(cycles uint32)
|
||||
@@ -66,18 +74,18 @@ func CreateTUN(ifname string, mtu int) (Device, error) {
|
||||
//
|
||||
func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu int) (Device, error) {
|
||||
var err error
|
||||
var wt *wintun.Interface
|
||||
var wt *wintun.Adapter
|
||||
|
||||
// Does an interface with this name already exist?
|
||||
wt, err = WintunPool.GetInterface(ifname)
|
||||
wt, err = WintunPool.OpenAdapter(ifname)
|
||||
if err == nil {
|
||||
// If so, we delete it, in case it has weird residual configuration.
|
||||
_, err = wt.DeleteInterface()
|
||||
_, err = wt.Delete(true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error deleting already existing interface: %v", err)
|
||||
}
|
||||
}
|
||||
wt, _, err = WintunPool.CreateInterface(ifname, requestedGUID)
|
||||
wt, _, err = WintunPool.CreateAdapter(ifname, requestedGUID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating interface: %v", err)
|
||||
}
|
||||
@@ -132,7 +140,7 @@ func (tun *NativeTun) Close() error {
|
||||
tun.rings.Close()
|
||||
var err error
|
||||
if tun.wt != nil {
|
||||
_, err = tun.wt.DeleteInterface()
|
||||
_, err = tun.wt.Delete(false)
|
||||
}
|
||||
close(tun.events)
|
||||
return err
|
||||
@@ -254,9 +262,9 @@ func (tun *NativeTun) LUID() uint64 {
|
||||
return tun.wt.LUID()
|
||||
}
|
||||
|
||||
// Version returns the version of the Wintun driver and NDIS system currently loaded.
|
||||
func (tun *NativeTun) Version() (driverVersion string, ndisVersion string, err error) {
|
||||
return tun.wt.Version()
|
||||
// RunningVersion returns the running version of the Wintun driver.
|
||||
func (tun *NativeTun) RunningVersion() (version uint32, err error) {
|
||||
return wintun.RunningVersion()
|
||||
}
|
||||
|
||||
func (rate *rateJuggler) update(packetLen uint64) {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package iphlpapi
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
//sys convertInterfaceLUIDToGUID(interfaceLUID *uint64, interfaceGUID *windows.GUID) (ret error) = iphlpapi.ConvertInterfaceLuidToGuid
|
||||
//sys convertInterfaceAliasToLUID(interfaceAlias *uint16, interfaceLUID *uint64) (ret error) = iphlpapi.ConvertInterfaceAliasToLuid
|
||||
|
||||
func InterfaceGUIDFromAlias(alias string) (*windows.GUID, error) {
|
||||
var luid uint64
|
||||
var guid windows.GUID
|
||||
err := convertInterfaceAliasToLUID(windows.StringToUTF16Ptr(alias), &luid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = convertInterfaceLUIDToGUID(&luid, &guid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &guid, nil
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package iphlpapi
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go conversion_windows.go
|
||||
@@ -1,60 +0,0 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package iphlpapi
|
||||
|
||||
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 (
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
|
||||
procConvertInterfaceLuidToGuid = modiphlpapi.NewProc("ConvertInterfaceLuidToGuid")
|
||||
procConvertInterfaceAliasToLuid = modiphlpapi.NewProc("ConvertInterfaceAliasToLuid")
|
||||
)
|
||||
|
||||
func convertInterfaceLUIDToGUID(interfaceLUID *uint64, interfaceGUID *windows.GUID) (ret error) {
|
||||
r0, _, _ := syscall.Syscall(procConvertInterfaceLuidToGuid.Addr(), 2, uintptr(unsafe.Pointer(interfaceLUID)), uintptr(unsafe.Pointer(interfaceGUID)), 0)
|
||||
if r0 != 0 {
|
||||
ret = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertInterfaceAliasToLUID(interfaceAlias *uint16, interfaceLUID *uint64) (ret error) {
|
||||
r0, _, _ := syscall.Syscall(procConvertInterfaceAliasToLuid.Addr(), 2, uintptr(unsafe.Pointer(interfaceAlias)), uintptr(unsafe.Pointer(interfaceLUID)), 0)
|
||||
if r0 != 0 {
|
||||
ret = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/crypto/blake2s"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/namespaceapi"
|
||||
)
|
||||
|
||||
var (
|
||||
wintunObjectSecurityAttributes *windows.SecurityAttributes
|
||||
hasInitializedNamespace bool
|
||||
initializingNamespace sync.Mutex
|
||||
)
|
||||
|
||||
func initializeNamespace() error {
|
||||
initializingNamespace.Lock()
|
||||
defer initializingNamespace.Unlock()
|
||||
if hasInitializedNamespace {
|
||||
return nil
|
||||
}
|
||||
sd, err := windows.SecurityDescriptorFromString("O:SYD:P(A;;GA;;;SY)")
|
||||
if err != nil {
|
||||
return fmt.Errorf("SddlToSecurityDescriptor failed: %v", err)
|
||||
}
|
||||
wintunObjectSecurityAttributes = &windows.SecurityAttributes{
|
||||
Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{})),
|
||||
SecurityDescriptor: sd,
|
||||
}
|
||||
sid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CreateWellKnownSid(LOCAL_SYSTEM) failed: %v", err)
|
||||
}
|
||||
|
||||
boundary, err := namespaceapi.CreateBoundaryDescriptor("Wintun")
|
||||
if err != nil {
|
||||
return fmt.Errorf("CreateBoundaryDescriptor failed: %v", err)
|
||||
}
|
||||
err = boundary.AddSid(sid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("AddSIDToBoundaryDescriptor failed: %v", err)
|
||||
}
|
||||
for {
|
||||
_, err = namespaceapi.CreatePrivateNamespace(wintunObjectSecurityAttributes, boundary, "Wintun")
|
||||
if err == windows.ERROR_ALREADY_EXISTS {
|
||||
_, err = namespaceapi.OpenPrivateNamespace(boundary, "Wintun")
|
||||
if err == windows.ERROR_PATH_NOT_FOUND {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenPrivateNamespace failed: %v", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("CreatePrivateNamespace failed: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
hasInitializedNamespace = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pool Pool) takeNameMutex() (windows.Handle, error) {
|
||||
err := initializeNamespace()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
const mutexLabel = "WireGuard Adapter Name Mutex Stable Suffix v1 jason@zx2c4.com"
|
||||
b2, _ := blake2s.New256(nil)
|
||||
b2.Write([]byte(mutexLabel))
|
||||
b2.Write(norm.NFC.Bytes([]byte(string(pool))))
|
||||
mutexName := `Wintun\Wintun-Name-Mutex-` + hex.EncodeToString(b2.Sum(nil))
|
||||
mutex, err := windows.CreateMutex(wintunObjectSecurityAttributes, false, windows.StringToUTF16Ptr(mutexName))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error creating name mutex: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
event, err := windows.WaitForSingleObject(mutex, windows.INFINITE)
|
||||
if err != nil {
|
||||
windows.CloseHandle(mutex)
|
||||
return 0, fmt.Errorf("Error waiting on name mutex: %v", err)
|
||||
}
|
||||
if event != windows.WAIT_OBJECT_0 && event != windows.WAIT_ABANDONED {
|
||||
windows.CloseHandle(mutex)
|
||||
return 0, errors.New("Error with event trigger of name mutex")
|
||||
}
|
||||
return mutex, nil
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package namespaceapi
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go namespaceapi_windows.go
|
||||
@@ -1,83 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package namespaceapi
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
//sys createBoundaryDescriptor(name *uint16, flags uint32) (handle windows.Handle, err error) = kernel32.CreateBoundaryDescriptorW
|
||||
//sys deleteBoundaryDescriptor(boundaryDescriptor windows.Handle) = kernel32.DeleteBoundaryDescriptor
|
||||
//sys addSIDToBoundaryDescriptor(boundaryDescriptor *windows.Handle, requiredSid *windows.SID) (err error) = kernel32.AddSIDToBoundaryDescriptor
|
||||
//sys createPrivateNamespace(privateNamespaceAttributes *windows.SecurityAttributes, boundaryDescriptor windows.Handle, aliasPrefix *uint16) (handle windows.Handle, err error) = kernel32.CreatePrivateNamespaceW
|
||||
//sys openPrivateNamespace(boundaryDescriptor windows.Handle, aliasPrefix *uint16) (handle windows.Handle, err error) = kernel32.OpenPrivateNamespaceW
|
||||
//sys closePrivateNamespace(handle windows.Handle, flags uint32) (err error) = kernel32.ClosePrivateNamespace
|
||||
|
||||
// BoundaryDescriptor represents a boundary that defines how the objects in the namespace are to be isolated.
|
||||
type BoundaryDescriptor windows.Handle
|
||||
|
||||
// CreateBoundaryDescriptor creates a boundary descriptor.
|
||||
func CreateBoundaryDescriptor(name string) (BoundaryDescriptor, error) {
|
||||
name16, err := windows.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
handle, err := createBoundaryDescriptor(name16, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return BoundaryDescriptor(handle), nil
|
||||
}
|
||||
|
||||
// Delete deletes the specified boundary descriptor.
|
||||
func (bd BoundaryDescriptor) Delete() {
|
||||
deleteBoundaryDescriptor(windows.Handle(bd))
|
||||
}
|
||||
|
||||
// AddSid adds a security identifier (SID) to the specified boundary descriptor.
|
||||
func (bd *BoundaryDescriptor) AddSid(requiredSid *windows.SID) error {
|
||||
return addSIDToBoundaryDescriptor((*windows.Handle)(bd), requiredSid)
|
||||
}
|
||||
|
||||
// PrivateNamespace represents a private namespace.
|
||||
type PrivateNamespace windows.Handle
|
||||
|
||||
// CreatePrivateNamespace creates a private namespace.
|
||||
func CreatePrivateNamespace(privateNamespaceAttributes *windows.SecurityAttributes, boundaryDescriptor BoundaryDescriptor, aliasPrefix string) (PrivateNamespace, error) {
|
||||
aliasPrefix16, err := windows.UTF16PtrFromString(aliasPrefix)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
handle, err := createPrivateNamespace(privateNamespaceAttributes, windows.Handle(boundaryDescriptor), aliasPrefix16)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return PrivateNamespace(handle), nil
|
||||
}
|
||||
|
||||
// OpenPrivateNamespace opens a private namespace.
|
||||
func OpenPrivateNamespace(boundaryDescriptor BoundaryDescriptor, aliasPrefix string) (PrivateNamespace, error) {
|
||||
aliasPrefix16, err := windows.UTF16PtrFromString(aliasPrefix)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
handle, err := openPrivateNamespace(windows.Handle(boundaryDescriptor), aliasPrefix16)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return PrivateNamespace(handle), nil
|
||||
}
|
||||
|
||||
// ClosePrivateNamespaceFlags describes flags that are used by PrivateNamespace's Close() method.
|
||||
type ClosePrivateNamespaceFlags uint32
|
||||
|
||||
const (
|
||||
// PrivateNamespaceFlagDestroy makes the close to destroy the namespace.
|
||||
PrivateNamespaceFlagDestroy = ClosePrivateNamespaceFlags(0x1)
|
||||
)
|
||||
|
||||
// Close closes an open namespace handle.
|
||||
func (pns PrivateNamespace) Close(flags ClosePrivateNamespaceFlags) error {
|
||||
return closePrivateNamespace(windows.Handle(pns), uint32(flags))
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package namespaceapi
|
||||
|
||||
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 (
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
|
||||
procCreateBoundaryDescriptorW = modkernel32.NewProc("CreateBoundaryDescriptorW")
|
||||
procDeleteBoundaryDescriptor = modkernel32.NewProc("DeleteBoundaryDescriptor")
|
||||
procAddSIDToBoundaryDescriptor = modkernel32.NewProc("AddSIDToBoundaryDescriptor")
|
||||
procCreatePrivateNamespaceW = modkernel32.NewProc("CreatePrivateNamespaceW")
|
||||
procOpenPrivateNamespaceW = modkernel32.NewProc("OpenPrivateNamespaceW")
|
||||
procClosePrivateNamespace = modkernel32.NewProc("ClosePrivateNamespace")
|
||||
)
|
||||
|
||||
func createBoundaryDescriptor(name *uint16, flags uint32) (handle windows.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procCreateBoundaryDescriptorW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(flags), 0)
|
||||
handle = windows.Handle(r0)
|
||||
if handle == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func deleteBoundaryDescriptor(boundaryDescriptor windows.Handle) {
|
||||
syscall.Syscall(procDeleteBoundaryDescriptor.Addr(), 1, uintptr(boundaryDescriptor), 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func addSIDToBoundaryDescriptor(boundaryDescriptor *windows.Handle, requiredSid *windows.SID) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procAddSIDToBoundaryDescriptor.Addr(), 2, uintptr(unsafe.Pointer(boundaryDescriptor)), uintptr(unsafe.Pointer(requiredSid)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createPrivateNamespace(privateNamespaceAttributes *windows.SecurityAttributes, boundaryDescriptor windows.Handle, aliasPrefix *uint16) (handle windows.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procCreatePrivateNamespaceW.Addr(), 3, uintptr(unsafe.Pointer(privateNamespaceAttributes)), uintptr(boundaryDescriptor), uintptr(unsafe.Pointer(aliasPrefix)))
|
||||
handle = windows.Handle(r0)
|
||||
if handle == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func openPrivateNamespace(boundaryDescriptor windows.Handle, aliasPrefix *uint16) (handle windows.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procOpenPrivateNamespaceW.Addr(), 2, uintptr(boundaryDescriptor), uintptr(unsafe.Pointer(aliasPrefix)), 0)
|
||||
handle = windows.Handle(r0)
|
||||
if handle == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func closePrivateNamespace(handle windows.Handle, flags uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procClosePrivateNamespace.Addr(), 2, uintptr(handle), uintptr(flags), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package nci
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go nci_windows.go
|
||||
@@ -1,28 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package nci
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
//sys nciSetConnectionName(guid *windows.GUID, newName *uint16) (ret error) = nci.NciSetConnectionName
|
||||
//sys nciGetConnectionName(guid *windows.GUID, destName *uint16, inDestNameBytes uint32, outDestNameBytes *uint32) (ret error) = nci.NciGetConnectionName
|
||||
|
||||
func SetConnectionName(guid *windows.GUID, newName string) error {
|
||||
newName16, err := windows.UTF16PtrFromString(newName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nciSetConnectionName(guid, newName16)
|
||||
}
|
||||
|
||||
func ConnectionName(guid *windows.GUID) (string, error) {
|
||||
var name [0x400]uint16
|
||||
err := nciGetConnectionName(guid, &name[0], uint32(len(name)*2), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return windows.UTF16ToString(name[:]), nil
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package nci
|
||||
|
||||
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 (
|
||||
modnci = windows.NewLazySystemDLL("nci.dll")
|
||||
|
||||
procNciSetConnectionName = modnci.NewProc("NciSetConnectionName")
|
||||
procNciGetConnectionName = modnci.NewProc("NciGetConnectionName")
|
||||
)
|
||||
|
||||
func nciSetConnectionName(guid *windows.GUID, newName *uint16) (ret error) {
|
||||
r0, _, _ := syscall.Syscall(procNciSetConnectionName.Addr(), 2, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(newName)), 0)
|
||||
if r0 != 0 {
|
||||
ret = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func nciGetConnectionName(guid *windows.GUID, destName *uint16, inDestNameBytes uint32, outDestNameBytes *uint32) (ret error) {
|
||||
r0, _, _ := syscall.Syscall6(procNciGetConnectionName.Addr(), 4, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(destName)), uintptr(inDestNameBytes), uintptr(unsafe.Pointer(outDestNameBytes)), 0, 0)
|
||||
if r0 != 0 {
|
||||
ret = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package registry
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zregistry_windows.go registry_windows.go
|
||||
@@ -1,272 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
// REG_NOTIFY_CHANGE_NAME notifies the caller if a subkey is added or deleted.
|
||||
REG_NOTIFY_CHANGE_NAME uint32 = 0x00000001
|
||||
|
||||
// REG_NOTIFY_CHANGE_ATTRIBUTES notifies the caller of changes to the attributes of the key, such as the security descriptor information.
|
||||
REG_NOTIFY_CHANGE_ATTRIBUTES uint32 = 0x00000002
|
||||
|
||||
// REG_NOTIFY_CHANGE_LAST_SET notifies the caller of changes to a value of the key. This can include adding or deleting a value, or changing an existing value.
|
||||
REG_NOTIFY_CHANGE_LAST_SET uint32 = 0x00000004
|
||||
|
||||
// REG_NOTIFY_CHANGE_SECURITY notifies the caller of changes to the security descriptor of the key.
|
||||
REG_NOTIFY_CHANGE_SECURITY uint32 = 0x00000008
|
||||
|
||||
// REG_NOTIFY_THREAD_AGNOSTIC indicates that the lifetime of the registration must not be tied to the lifetime of the thread issuing the RegNotifyChangeKeyValue call. Note: This flag value is only supported in Windows 8 and later.
|
||||
REG_NOTIFY_THREAD_AGNOSTIC uint32 = 0x10000000
|
||||
)
|
||||
|
||||
//sys regNotifyChangeKeyValue(key windows.Handle, watchSubtree bool, notifyFilter uint32, event windows.Handle, asynchronous bool) (regerrno error) = advapi32.RegNotifyChangeKeyValue
|
||||
|
||||
func OpenKeyWait(k registry.Key, path string, access uint32, timeout time.Duration) (registry.Key, error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
deadline := time.Now().Add(timeout)
|
||||
pathSpl := strings.Split(path, "\\")
|
||||
for i := 0; ; i++ {
|
||||
keyName := pathSpl[i]
|
||||
isLast := i+1 == len(pathSpl)
|
||||
|
||||
event, err := windows.CreateEvent(nil, 0, 0, nil)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Error creating event: %v", err)
|
||||
}
|
||||
defer windows.CloseHandle(event)
|
||||
|
||||
var key registry.Key
|
||||
for {
|
||||
err = regNotifyChangeKeyValue(windows.Handle(k), false, REG_NOTIFY_CHANGE_NAME, windows.Handle(event), true)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Setting up change notification on registry key failed: %v", err)
|
||||
}
|
||||
|
||||
var accessFlags uint32
|
||||
if isLast {
|
||||
accessFlags = access
|
||||
} else {
|
||||
accessFlags = registry.NOTIFY
|
||||
}
|
||||
key, err = registry.OpenKey(k, keyName, accessFlags)
|
||||
if err == windows.ERROR_FILE_NOT_FOUND || err == windows.ERROR_PATH_NOT_FOUND {
|
||||
timeout := time.Until(deadline) / time.Millisecond
|
||||
if timeout < 0 {
|
||||
timeout = 0
|
||||
}
|
||||
s, err := windows.WaitForSingleObject(event, uint32(timeout))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("Unable to wait on registry key: %v", err)
|
||||
}
|
||||
if s == uint32(windows.WAIT_TIMEOUT) { // windows.WAIT_TIMEOUT status const is misclassified as error in golang.org/x/sys/windows
|
||||
return 0, errors.New("Timeout waiting for registry key")
|
||||
}
|
||||
} else if err != nil {
|
||||
return 0, fmt.Errorf("Error opening registry key %v: %v", path, err)
|
||||
} else {
|
||||
if isLast {
|
||||
return key, nil
|
||||
}
|
||||
defer key.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
k = key
|
||||
}
|
||||
}
|
||||
|
||||
func WaitForKey(k registry.Key, path string, timeout time.Duration) error {
|
||||
key, err := OpenKeyWait(k, path, registry.NOTIFY, timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
// getValue is more or less the same as windows/registry's getValue.
|
||||
//
|
||||
func getValue(k registry.Key, name string, buf []byte) (value []byte, valueType uint32, err error) {
|
||||
var name16 *uint16
|
||||
name16, err = windows.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n := uint32(len(buf))
|
||||
for {
|
||||
err = windows.RegQueryValueEx(windows.Handle(k), name16, nil, &valueType, (*byte)(unsafe.Pointer(&buf[0])), &n)
|
||||
if err == nil {
|
||||
value = buf[:n]
|
||||
return
|
||||
}
|
||||
if err != windows.ERROR_MORE_DATA {
|
||||
return
|
||||
}
|
||||
if n <= uint32(len(buf)) {
|
||||
return
|
||||
}
|
||||
buf = make([]byte, n)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// getValueRetry function reads any value from registry. It waits for
|
||||
// the registry value to become available or returns error on timeout.
|
||||
//
|
||||
// Key must be opened with at least QUERY_VALUE|NOTIFY access.
|
||||
//
|
||||
func getValueRetry(key registry.Key, name string, buf []byte, timeout time.Duration) ([]byte, uint32, error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
event, err := windows.CreateEvent(nil, 0, 0, nil)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Error creating event: %v", err)
|
||||
}
|
||||
defer windows.CloseHandle(event)
|
||||
|
||||
deadline := time.Now().Add(timeout)
|
||||
for {
|
||||
err := regNotifyChangeKeyValue(windows.Handle(key), false, REG_NOTIFY_CHANGE_LAST_SET, windows.Handle(event), true)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Setting up change notification on registry value failed: %v", err)
|
||||
}
|
||||
|
||||
buf, valueType, err := getValue(key, name, buf)
|
||||
if err == windows.ERROR_FILE_NOT_FOUND || err == windows.ERROR_PATH_NOT_FOUND {
|
||||
timeout := time.Until(deadline) / time.Millisecond
|
||||
if timeout < 0 {
|
||||
timeout = 0
|
||||
}
|
||||
s, err := windows.WaitForSingleObject(event, uint32(timeout))
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Unable to wait on registry value: %v", err)
|
||||
}
|
||||
if s == uint32(windows.WAIT_TIMEOUT) { // windows.WAIT_TIMEOUT status const is misclassified as error in golang.org/x/sys/windows
|
||||
return nil, 0, errors.New("Timeout waiting for registry value")
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, 0, fmt.Errorf("Error reading registry value %v: %v", name, err)
|
||||
} else {
|
||||
return buf, valueType, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toString(buf []byte, valueType uint32, err error) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var value string
|
||||
switch valueType {
|
||||
case registry.SZ, registry.EXPAND_SZ, registry.MULTI_SZ:
|
||||
if len(buf) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
value = windows.UTF16PtrToString((*uint16)(unsafe.Pointer(&buf[0])))
|
||||
|
||||
default:
|
||||
return "", registry.ErrUnexpectedType
|
||||
}
|
||||
|
||||
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 toInteger(buf []byte, valueType uint32, err error) (uint64, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch valueType {
|
||||
case registry.DWORD:
|
||||
if len(buf) != 4 {
|
||||
return 0, errors.New("DWORD value is not 4 bytes long")
|
||||
}
|
||||
var val uint32
|
||||
copy((*[4]byte)(unsafe.Pointer(&val))[:], buf)
|
||||
return uint64(val), nil
|
||||
|
||||
case registry.QWORD:
|
||||
if len(buf) != 8 {
|
||||
return 0, errors.New("QWORD value is not 8 bytes long")
|
||||
}
|
||||
var val uint64
|
||||
copy((*[8]byte)(unsafe.Pointer(&val))[:], buf)
|
||||
return val, nil
|
||||
|
||||
default:
|
||||
return 0, registry.ErrUnexpectedType
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GetStringValueWait function reads a string value from registry. It waits
|
||||
// for the registry value to become available or returns error on timeout.
|
||||
//
|
||||
// Key must be opened with at least QUERY_VALUE|NOTIFY access.
|
||||
//
|
||||
// If the value type is REG_EXPAND_SZ the environment variables are expanded.
|
||||
// Should expanding fail, original string value and nil error are returned.
|
||||
//
|
||||
// If the value type is REG_MULTI_SZ only the first string is returned.
|
||||
//
|
||||
func GetStringValueWait(key registry.Key, name string, timeout time.Duration) (string, error) {
|
||||
return toString(getValueRetry(key, name, make([]byte, 256), timeout))
|
||||
}
|
||||
|
||||
//
|
||||
// GetStringValue function reads a string value from registry.
|
||||
//
|
||||
// Key must be opened with at least QUERY_VALUE access.
|
||||
//
|
||||
// If the value type is REG_EXPAND_SZ the environment variables are expanded.
|
||||
// Should expanding fail, original string value and nil error are returned.
|
||||
//
|
||||
// If the value type is REG_MULTI_SZ only the first string is returned.
|
||||
//
|
||||
func GetStringValue(key registry.Key, name string) (string, error) {
|
||||
return toString(getValue(key, name, make([]byte, 256)))
|
||||
}
|
||||
|
||||
//
|
||||
// GetIntegerValueWait function reads a DWORD32 or QWORD value from registry.
|
||||
// It waits for the registry value to become available or returns error on
|
||||
// timeout.
|
||||
//
|
||||
// Key must be opened with at least QUERY_VALUE|NOTIFY access.
|
||||
//
|
||||
func GetIntegerValueWait(key registry.Key, name string, timeout time.Duration) (uint64, error) {
|
||||
return toInteger(getValueRetry(key, name, make([]byte, 8), timeout))
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
const keyRoot = registry.CURRENT_USER
|
||||
const pathRoot = "Software\\WireGuardRegistryTest"
|
||||
const path = pathRoot + "\\foobar"
|
||||
const pathFake = pathRoot + "\\raboof"
|
||||
|
||||
func Test_WaitForKey(t *testing.T) {
|
||||
registry.DeleteKey(keyRoot, path)
|
||||
registry.DeleteKey(keyRoot, pathRoot)
|
||||
go func() {
|
||||
time.Sleep(time.Second * 1)
|
||||
key, _, err := registry.CreateKey(keyRoot, pathFake, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
t.Errorf("Error creating registry key: %v", err)
|
||||
}
|
||||
key.Close()
|
||||
registry.DeleteKey(keyRoot, pathFake)
|
||||
|
||||
key, _, err = registry.CreateKey(keyRoot, path, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
t.Errorf("Error creating registry key: %v", err)
|
||||
}
|
||||
key.Close()
|
||||
}()
|
||||
err := WaitForKey(keyRoot, path, time.Second*2)
|
||||
if err != nil {
|
||||
t.Errorf("Error waiting for registry key: %v", err)
|
||||
}
|
||||
registry.DeleteKey(keyRoot, path)
|
||||
registry.DeleteKey(keyRoot, pathRoot)
|
||||
|
||||
err = WaitForKey(keyRoot, path, time.Second*1)
|
||||
if err == nil {
|
||||
t.Error("Registry key notification expected to timeout but it succeeded.")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_GetValueWait(t *testing.T) {
|
||||
registry.DeleteKey(keyRoot, path)
|
||||
registry.DeleteKey(keyRoot, pathRoot)
|
||||
go func() {
|
||||
time.Sleep(time.Second * 1)
|
||||
key, _, err := registry.CreateKey(keyRoot, path, registry.SET_VALUE)
|
||||
if err != nil {
|
||||
t.Errorf("Error creating registry key: %v", err)
|
||||
}
|
||||
time.Sleep(time.Second * 1)
|
||||
key.SetStringValue("name1", "eulav")
|
||||
key.SetExpandStringValue("name2", "value")
|
||||
time.Sleep(time.Second * 1)
|
||||
key.SetDWordValue("name3", ^uint32(123))
|
||||
key.SetDWordValue("name4", 123)
|
||||
key.Close()
|
||||
}()
|
||||
|
||||
key, err := OpenKeyWait(keyRoot, path, registry.QUERY_VALUE|registry.NOTIFY, time.Second*2)
|
||||
if err != nil {
|
||||
t.Errorf("Error waiting for registry key: %v", err)
|
||||
}
|
||||
|
||||
valueStr, err := GetStringValueWait(key, "name2", time.Second*2)
|
||||
if err != nil {
|
||||
t.Errorf("Error waiting for registry value: %v", err)
|
||||
}
|
||||
if valueStr != "value" {
|
||||
t.Errorf("Wrong value read: %v", valueStr)
|
||||
}
|
||||
|
||||
_, err = GetStringValueWait(key, "nonexisting", time.Second*1)
|
||||
if err == nil {
|
||||
t.Error("Registry value notification expected to timeout but it succeeded.")
|
||||
}
|
||||
|
||||
valueInt, err := GetIntegerValueWait(key, "name4", time.Second*2)
|
||||
if err != nil {
|
||||
t.Errorf("Error waiting for registry value: %v", err)
|
||||
}
|
||||
if valueInt != 123 {
|
||||
t.Errorf("Wrong value read: %v", valueInt)
|
||||
}
|
||||
|
||||
_, err = GetIntegerValueWait(key, "nonexisting", time.Second*1)
|
||||
if err == nil {
|
||||
t.Error("Registry value notification expected to timeout but it succeeded.")
|
||||
}
|
||||
|
||||
key.Close()
|
||||
registry.DeleteKey(keyRoot, path)
|
||||
registry.DeleteKey(keyRoot, pathRoot)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package registry
|
||||
|
||||
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 (
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
|
||||
procRegNotifyChangeKeyValue = modadvapi32.NewProc("RegNotifyChangeKeyValue")
|
||||
)
|
||||
|
||||
func regNotifyChangeKeyValue(key windows.Handle, watchSubtree bool, notifyFilter uint32, event windows.Handle, asynchronous bool) (regerrno error) {
|
||||
var _p0 uint32
|
||||
if watchSubtree {
|
||||
_p0 = 1
|
||||
} else {
|
||||
_p0 = 0
|
||||
}
|
||||
var _p1 uint32
|
||||
if asynchronous {
|
||||
_p1 = 1
|
||||
} else {
|
||||
_p1 = 0
|
||||
}
|
||||
r0, _, _ := syscall.Syscall6(procRegNotifyChangeKeyValue.Addr(), 5, uintptr(key), uintptr(_p0), uintptr(notifyFilter), uintptr(event), uintptr(_p1), 0)
|
||||
if r0 != 0 {
|
||||
regerrno = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -103,8 +103,8 @@ func (descriptor *RingDescriptor) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
func (wintun *Interface) Register(descriptor *RingDescriptor) (windows.Handle, error) {
|
||||
handle, err := wintun.handle()
|
||||
func (wintun *Adapter) Register(descriptor *RingDescriptor) (windows.Handle, error) {
|
||||
handle, err := wintun.OpenAdapterDeviceObject()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsetupapi_windows.go setupapi_windows.go
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,488 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var deviceClassNetGUID = windows.GUID{Data1: 0x4d36e972, Data2: 0xe325, Data3: 0x11ce, Data4: [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 {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
|
||||
} else {
|
||||
devInfoList.Close()
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
|
||||
} else {
|
||||
devInfoList.Close()
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiCreateDeviceInfoListEx(nil, 0, "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx(nil): %s", err.Error())
|
||||
} else {
|
||||
devInfoList.Close()
|
||||
}
|
||||
}
|
||||
|
||||
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.DeviceInfoListDetail()
|
||||
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.DeviceInfoListDetail()
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
data = &DevInfoListDetailData{}
|
||||
data.SetRemoteMachineName("foobar")
|
||||
if data.RemoteMachineName() != "foobar" {
|
||||
t.Error("DevInfoListDetailData.(Get|Set)RemoteMachineName() differ")
|
||||
}
|
||||
}
|
||||
|
||||
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.(windows.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.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if data.ClassGUID != deviceClassNetGUID {
|
||||
t.Error("SetupDiEnumDeviceInfo returned different class GUID")
|
||||
}
|
||||
|
||||
_, err = devInfoList.DeviceInstanceID(data)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceInstanceId: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.(windows.Errno); ok && errWin == windows.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 *DrvInfoData
|
||||
for j := 0; true; j++ {
|
||||
driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, j)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
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.DriverInfoDetail(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")
|
||||
}
|
||||
a := driverDetailData.CompatIDs()
|
||||
for k := range a {
|
||||
if !driverDetailData.IsCompatible(strings.ToUpper(a[k])) {
|
||||
t.Error("HWID compatibitlity missed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedDriverData2, err := devInfoList.SelectedDriver(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")
|
||||
}
|
||||
}
|
||||
|
||||
data := &DrvInfoData{}
|
||||
data.SetDescription("foobar")
|
||||
if data.Description() != "foobar" {
|
||||
t.Error("DrvInfoData.(Get|Set)Description() differ")
|
||||
}
|
||||
data.SetMfgName("foobar")
|
||||
if data.MfgName() != "foobar" {
|
||||
t.Error("DrvInfoData.(Get|Set)MfgName() differ")
|
||||
}
|
||||
data.SetProviderName("foobar")
|
||||
if data.ProviderName() != "foobar" {
|
||||
t.Error("DrvInfoData.(Get|Set)ProviderName() differ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetClassDevsEx(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "PCI", 0, DIGCF_PRESENT, DevInfo(0), computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
} else {
|
||||
devInfoList.Close()
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiGetClassDevsEx(nil, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
if errWin, ok := err.(windows.Errno); !ok || errWin != windows.ERROR_INVALID_PARAMETER {
|
||||
t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail with ERROR_INVALID_PARAMETER")
|
||||
}
|
||||
} else {
|
||||
devInfoList.Close()
|
||||
t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail")
|
||||
}
|
||||
}
|
||||
|
||||
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.(windows.Errno); ok && errWin == windows.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.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := devInfoList.DeviceRegistryProperty(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.DeviceRegistryProperty(data, SPDRP_CLASSGUID)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID): %s", err.Error())
|
||||
} else if valStr, ok := val.(string); !ok {
|
||||
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID) should return string")
|
||||
} else {
|
||||
classGUID, err := windows.GUIDFromString(valStr)
|
||||
if err != nil {
|
||||
t.Errorf("Error parsing GUID returned by SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID): %s", err.Error())
|
||||
} else if classGUID != deviceClassNetGUID {
|
||||
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID) should return %x", deviceClassNetGUID)
|
||||
}
|
||||
}
|
||||
|
||||
val, err = devInfoList.DeviceRegistryProperty(data, SPDRP_COMPATIBLEIDS)
|
||||
if err != nil {
|
||||
// Some devices have no SPDRP_COMPATIBLEIDS.
|
||||
if errWin, ok := err.(windows.Errno); !ok || errWin != windows.ERROR_INVALID_DATA {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_COMPATIBLEIDS): %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
val, err = devInfoList.DeviceRegistryProperty(data, SPDRP_CONFIGFLAGS)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CONFIGFLAGS): %s", err.Error())
|
||||
}
|
||||
|
||||
val, err = devInfoList.DeviceRegistryProperty(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.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = devInfoList.DeviceInstallParams(data)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceInstallParams: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
params := &DevInstallParams{}
|
||||
params.SetDriverPath("foobar")
|
||||
if params.DriverPath() != "foobar" {
|
||||
t.Error("DevInstallParams.(Get|Set)DriverPath() differ")
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
if errWin, ok := err.(windows.Errno); !ok || errWin != windows.ERROR_INVALID_USER_BUFFER {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail with ERROR_INVALID_USER_BUFFER")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail")
|
||||
}
|
||||
}
|
||||
|
||||
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.(windows.Errno); ok && errWin == windows.ERROR_NO_MORE_ITEMS {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = devInfoList.SetSelectedDevice(data)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiSetSelectedDevice: %s", err.Error())
|
||||
}
|
||||
|
||||
data2, err := devInfoList.SelectedDevice()
|
||||
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 {
|
||||
if errWin, ok := err.(windows.Errno); !ok || errWin != windows.ERROR_INVALID_PARAMETER {
|
||||
t.Errorf("SetupDiSetSelectedDevice(nil) should fail with ERROR_INVALID_USER_BUFFER")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("SetupDiSetSelectedDevice(nil) should fail")
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
runtime.KeepAlive(buf)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// +build 386 arm
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
const (
|
||||
sizeofDevInfoListDetailData uint32 = 550
|
||||
sizeofDrvInfoDetailData uint32 = 1570
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user