From 68fea631d8e97aa7395b944916e569cf66553e73 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Wed, 21 Aug 2019 08:40:44 +0200 Subject: [PATCH] wintun: use nci.dll directly instead of buggy netshell --- tun/wintun/nci/mksyscall.go | 8 ++++ tun/wintun/nci/nci_windows.go | 28 ++++++++++++ tun/wintun/nci/zsyscall_windows.go | 60 +++++++++++++++++++++++++ tun/wintun/netshell/netshell_windows.go | 32 ------------- tun/wintun/wintun_windows.go | 58 +++--------------------- 5 files changed, 102 insertions(+), 84 deletions(-) create mode 100644 tun/wintun/nci/mksyscall.go create mode 100644 tun/wintun/nci/nci_windows.go create mode 100644 tun/wintun/nci/zsyscall_windows.go delete mode 100644 tun/wintun/netshell/netshell_windows.go diff --git a/tun/wintun/nci/mksyscall.go b/tun/wintun/nci/mksyscall.go new file mode 100644 index 0000000..15408f4 --- /dev/null +++ b/tun/wintun/nci/mksyscall.go @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. + */ + +package nci + +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go nci_windows.go diff --git a/tun/wintun/nci/nci_windows.go b/tun/wintun/nci/nci_windows.go new file mode 100644 index 0000000..9dc6699 --- /dev/null +++ b/tun/wintun/nci/nci_windows.go @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019 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 +} diff --git a/tun/wintun/nci/zsyscall_windows.go b/tun/wintun/nci/zsyscall_windows.go new file mode 100644 index 0000000..2a7b79e --- /dev/null +++ b/tun/wintun/nci/zsyscall_windows.go @@ -0,0 +1,60 @@ +// 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 +} diff --git a/tun/wintun/netshell/netshell_windows.go b/tun/wintun/netshell/netshell_windows.go deleted file mode 100644 index 3122d87..0000000 --- a/tun/wintun/netshell/netshell_windows.go +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package netshell - -import ( - "syscall" - "unsafe" - - "golang.org/x/sys/windows" -) - -var ( - modnetshell = windows.NewLazySystemDLL("netshell.dll") - procHrRenameConnection = modnetshell.NewProc("HrRenameConnection") -) - -func HrRenameConnection(guid *windows.GUID, newName *uint16) (err error) { - err = procHrRenameConnection.Find() - if err != nil { - // Missing from servercore, so we can't presume it's always there. - return - } - - ret, _, _ := syscall.Syscall(procHrRenameConnection.Addr(), 2, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(newName)), 0) - if ret != 0 { - err = syscall.Errno(ret) - } - return -} diff --git a/tun/wintun/wintun_windows.go b/tun/wintun/wintun_windows.go index 35ec89e..f6a230a 100644 --- a/tun/wintun/wintun_windows.go +++ b/tun/wintun/wintun_windows.go @@ -15,7 +15,7 @@ import ( "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" - "golang.zx2c4.com/wireguard/tun/wintun/netshell" + "golang.zx2c4.com/wireguard/tun/wintun/nci" registryEx "golang.zx2c4.com/wireguard/tun/wintun/registry" "golang.zx2c4.com/wireguard/tun/wintun/setupapi" ) @@ -348,28 +348,6 @@ func CreateInterface(description string, requestedGUID *windows.GUID) (wintun *W return } - // Wait for network registry key to emerge and populate. - netRegKey, err := registryEx.OpenKeyWait( - registry.LOCAL_MACHINE, - wintun.netRegKeyName(), - registry.QUERY_VALUE|registry.NOTIFY, - waitForRegistryTimeout) - if err != nil { - err = fmt.Errorf("makeWintun failed: %v", err) - return - } - defer netRegKey.Close() - _, err = registryEx.GetStringValueWait(netRegKey, "Name", waitForRegistryTimeout) - if err != nil { - err = fmt.Errorf("GetStringValueWait(Name) failed: %v", err) - return - } - _, err = registryEx.GetStringValueWait(netRegKey, "PnPInstanceId", waitForRegistryTimeout) - if err != nil { - err = fmt.Errorf("GetStringValueWait(PnPInstanceId) failed: %v", err) - return - } - // Wait for TCP/IP adapter registry key to emerge and populate. tcpipAdapterRegKey, err := registryEx.OpenKeyWait( registry.LOCAL_MACHINE, @@ -539,29 +517,17 @@ func setQuietInstall(deviceInfoSet setupapi.DevInfo, deviceInfoData *setupapi.De // InterfaceName returns the name of the Wintun interface. func (wintun *Wintun) InterfaceName() (string, error) { - key, err := registry.OpenKey(registry.LOCAL_MACHINE, wintun.netRegKeyName(), registry.QUERY_VALUE) - if err != nil { - return "", fmt.Errorf("Network-specific registry key open failed: %v", err) - } - defer key.Close() - - // Get the interface name. - return registryEx.GetStringValue(key, "Name") + return nci.ConnectionName(&wintun.cfgInstanceID) } // SetInterfaceName sets name of the Wintun interface. func (wintun *Wintun) SetInterfaceName(ifname string) error { - netRegKey, err := registry.OpenKey(registry.LOCAL_MACHINE, wintun.netRegKeyName(), registry.SET_VALUE) + err := nci.SetConnectionName(&wintun.cfgInstanceID, ifname) if err != nil { - return fmt.Errorf("Network-specific registry key open failed: %v", err) - } - defer netRegKey.Close() - err = netRegKey.SetStringValue("Name", ifname) - if err != nil { - return err + return fmt.Errorf("NciSetConnectionName failed: %v", err) } - // TODO: This only sometimes works. + // TODO: This should use NetSetup2 so that it doesn't get unset. deviceRegKey, err := registry.OpenKey(registry.LOCAL_MACHINE, wintun.deviceRegKeyName(), registry.SET_VALUE) if err != nil { return fmt.Errorf("Device-level registry key open failed: %v", err) @@ -569,23 +535,11 @@ func (wintun *Wintun) SetInterfaceName(ifname string) error { defer deviceRegKey.Close() err = deviceRegKey.SetStringValue("FriendlyName", deviceTypeName) if err != nil { - return err + return fmt.Errorf("SetStringValue(FriendlyName) failed: %v", err) } - - // We have to tell the various runtime COM services about the new name too. We ignore the - // error because netshell isn't available on servercore. - // TODO: netsh.exe falls back to NciSetConnection in this case. If somebody complains, maybe - // we should do the same. - // TODO: This only sometimes works. - netshell.HrRenameConnection(&wintun.cfgInstanceID, windows.StringToUTF16Ptr(ifname)) return nil } -// netRegKeyName returns the interface-specific network registry key name. -func (wintun *Wintun) netRegKeyName() string { - return fmt.Sprintf("SYSTEM\\CurrentControlSet\\Control\\Network\\%v\\%v\\Connection", deviceClassNetGUID, wintun.cfgInstanceID) -} - // tcpipAdapterRegKeyName returns the adapter-specific TCP/IP network registry key name. func (wintun *Wintun) tcpipAdapterRegKeyName() string { return fmt.Sprintf("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Adapters\\%v", wintun.cfgInstanceID)