mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-05 20:26:47 +02:00
query for connectivity
Signed-off-by: Aaron Klotz <aaron@tailscale.com>
This commit is contained in:
parent
b7658a4ad2
commit
7a76b22972
@ -4,7 +4,9 @@
|
||||
package netmon
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"log"
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strings"
|
||||
@ -15,6 +17,7 @@ import (
|
||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||
"tailscale.com/feature/buildfeatures"
|
||||
"tailscale.com/tsconst"
|
||||
"tailscale.com/util/winutil/winnet"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -22,12 +25,118 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
altNetInterfaces = altNetInterfacesWindows
|
||||
likelyHomeRouterIP = likelyHomeRouterIPWindows
|
||||
if buildfeatures.HasUseProxy {
|
||||
getPAC = getPACWindows
|
||||
}
|
||||
}
|
||||
|
||||
func altNetInterfacesWindows() ([]Interface, error) {
|
||||
adapterAddrs, err := getInterfaces(windows.AF_UNSPEC, winipcfg.GAAFlagIncludePrefix|winipcfg.GAAFlagIncludeGateways, notTailscaleInterface)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]Interface, 0, len(adapterAddrs))
|
||||
|
||||
for _, aa := range adapterAddrs {
|
||||
curIface := net.Interface{
|
||||
Index: int(cmp.Or(aa.IfIndex, aa.IPv6IfIndex)),
|
||||
Name: aa.FriendlyName(),
|
||||
}
|
||||
|
||||
if aa.OperStatus == windows.IfOperStatusUp {
|
||||
curIface.Flags |= net.FlagUp
|
||||
curIface.Flags |= net.FlagRunning
|
||||
}
|
||||
|
||||
platFlags := connectivityFlags(aa.NetworkGUID)
|
||||
|
||||
entry, err := ifEntry(aa.LUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eat := entry.AccessType
|
||||
if eat&winipcfg.NetIfAccessLoopback != 0 {
|
||||
curIface.Flags |= net.FlagLoopback
|
||||
}
|
||||
if eat&winipcfg.NetIfAccessBroadcast != 0 {
|
||||
curIface.Flags |= net.FlagBroadcast
|
||||
}
|
||||
if eat&winipcfg.NetIfAccessPointToPoint != 0 {
|
||||
curIface.Flags |= net.FlagPointToPoint
|
||||
}
|
||||
if eat&winipcfg.NetIfAccessPointToMultiPoint != 0 {
|
||||
curIface.Flags |= net.FlagMulticast
|
||||
}
|
||||
|
||||
if aa.MTU == 0xffffffff {
|
||||
curIface.MTU = -1
|
||||
} else {
|
||||
curIface.MTU = int(aa.MTU)
|
||||
}
|
||||
|
||||
if physAddr := aa.PhysicalAddress(); len(physAddr) > 0 {
|
||||
curIface.HardwareAddr = make([]byte, len(physAddr))
|
||||
copy(curIface.HardwareAddr, physAddr)
|
||||
}
|
||||
|
||||
result = append(result, Interface{
|
||||
Interface: &curIface,
|
||||
Desc: aa.Description(),
|
||||
PlatFlags: platFlags,
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func connectivityFlags(ifGUID windows.GUID) (flags PlatFlags) {
|
||||
nlm, err := winnet.GetNetworkListManager()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
network, err := nlm.GetNetwork(ifGUID)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
defer network.Release()
|
||||
|
||||
connectivity, err := network.GetConnectivity()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if connectivity&winnet.NLM_CONNECTIVITY_IPV4_INTERNET == 0 {
|
||||
flags |= PlatFlagNoIPv4InternetConnectivity
|
||||
}
|
||||
|
||||
if connectivity&winnet.NLM_CONNECTIVITY_IPV6_INTERNET == 0 {
|
||||
flags |= PlatFlagNoIPv6InternetConnectivity
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func ifEntry(ifLUID winipcfg.LUID) (*winipcfg.MibIfRow2, error) {
|
||||
row := &winipcfg.MibIfRow2{
|
||||
InterfaceLUID: ifLUID,
|
||||
}
|
||||
if procGetIfEntry2Ex.Find() == nil {
|
||||
if err := getIfEntry2Ex(_MibIfEntryNormalWithoutStatistics, row); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := getIfEntry2(row); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func likelyHomeRouterIPWindows() (ret netip.Addr, _ netip.Addr, ok bool) {
|
||||
rs, err := winipcfg.GetIPForwardTable2(windows.AF_INET)
|
||||
if err != nil {
|
||||
|
||||
17
net/netmon/mksyscall.go
Normal file
17
net/netmon/mksyscall.go
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package netmon
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go mksyscall.go
|
||||
//go:generate go run golang.org/x/tools/cmd/goimports -w zsyscall_windows.go
|
||||
|
||||
type _MIB_IF_ENTRY_LEVEL int32
|
||||
|
||||
const (
|
||||
_MibIfEntryNormal _MIB_IF_ENTRY_LEVEL = 0
|
||||
_MibIfEntryNormalWithoutStatistics _MIB_IF_ENTRY_LEVEL = 2
|
||||
)
|
||||
|
||||
//sys getIfEntry2(row *winipcfg.MibIfRow2) (ret error) = iphlpapi.GetIfEntry2
|
||||
//sys getIfEntry2Ex(level _MIB_IF_ENTRY_LEVEL, row *winipcfg.MibIfRow2) (ret error) = iphlpapi.GetIfEntry2Ex
|
||||
@ -142,15 +142,29 @@ func sortIPs(s []netip.Addr) {
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Less(s[j]) })
|
||||
}
|
||||
|
||||
type PlatFlags uint
|
||||
|
||||
const (
|
||||
PlatFlagNoIPv4InternetConnectivity PlatFlags = 1 << iota
|
||||
PlatFlagNoIPv6InternetConnectivity
|
||||
)
|
||||
|
||||
// Interface is a wrapper around Go's net.Interface with some extra methods.
|
||||
type Interface struct {
|
||||
*net.Interface
|
||||
AltAddrs []net.Addr // if non-nil, returned by Addrs
|
||||
Desc string // extra description (used on Windows)
|
||||
AltAddrs []net.Addr // if non-nil, returned by Addrs
|
||||
Desc string // extra description (used on Windows)
|
||||
PlatFlags PlatFlags // flags with additional connectivity information
|
||||
}
|
||||
|
||||
func (i Interface) IsLoopback() bool {
|
||||
return isLoopback(i.Interface)
|
||||
}
|
||||
|
||||
func (i Interface) IsUp() bool {
|
||||
return isUp(i.Interface)
|
||||
}
|
||||
|
||||
func (i Interface) IsLoopback() bool { return isLoopback(i.Interface) }
|
||||
func (i Interface) IsUp() bool { return isUp(i.Interface) }
|
||||
func (i Interface) Addrs() ([]net.Addr, error) {
|
||||
if i.AltAddrs != nil {
|
||||
return i.AltAddrs, nil
|
||||
@ -158,6 +172,14 @@ func (i Interface) Addrs() ([]net.Addr, error) {
|
||||
return i.Interface.Addrs()
|
||||
}
|
||||
|
||||
func (i Interface) isNoIPv6InternetConnectivity() bool {
|
||||
return i.PlatFlags&PlatFlagNoIPv6InternetConnectivity != 0
|
||||
}
|
||||
|
||||
func (i Interface) isNoIPv4InternetConnectivity() bool {
|
||||
return i.PlatFlags&PlatFlagNoIPv6InternetConnectivity != 0
|
||||
}
|
||||
|
||||
// ForeachInterfaceAddress is a wrapper for GetList, then
|
||||
// List.ForeachInterfaceAddress.
|
||||
func ForeachInterfaceAddress(fn func(Interface, netip.Prefix)) error {
|
||||
@ -492,8 +514,8 @@ func getState(optTSInterfaceName string) (*State, error) {
|
||||
if pfx.Addr().IsLoopback() {
|
||||
continue
|
||||
}
|
||||
s.HaveV6 = s.HaveV6 || isUsableV6(pfx.Addr())
|
||||
s.HaveV4 = s.HaveV4 || isUsableV4(pfx.Addr())
|
||||
s.HaveV6 = s.HaveV6 || (isUsableV6(pfx.Addr()) && !ni.isNoIPv6InternetConnectivity())
|
||||
s.HaveV4 = s.HaveV4 || (isUsableV4(pfx.Addr()) && !ni.isNoIPv4InternetConnectivity())
|
||||
}
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
|
||||
62
net/netmon/zsyscall_windows.go
Normal file
62
net/netmon/zsyscall_windows.go
Normal file
@ -0,0 +1,62 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package netmon
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||
)
|
||||
|
||||
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)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
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")
|
||||
|
||||
procGetIfEntry2 = modiphlpapi.NewProc("GetIfEntry2")
|
||||
procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex")
|
||||
)
|
||||
|
||||
func getIfEntry2(row *winipcfg.MibIfRow2) (ret error) {
|
||||
r0, _, _ := syscall.Syscall(procGetIfEntry2.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||
if r0 != 0 {
|
||||
ret = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getIfEntry2Ex(level _MIB_IF_ENTRY_LEVEL, row *winipcfg.MibIfRow2) (ret error) {
|
||||
r0, _, _ := syscall.Syscall(procGetIfEntry2Ex.Addr(), 2, uintptr(level), uintptr(unsafe.Pointer(row)), 0)
|
||||
if r0 != 0 {
|
||||
ret = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -1,27 +1,67 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build windows
|
||||
|
||||
// Package winnet contains Windows-specific networking code.
|
||||
package winnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/go-ole/go-ole/oleutil"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const CLSID_NetworkListManager = "{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"
|
||||
type NLM_CONNECTIVITY int32
|
||||
|
||||
const (
|
||||
NLM_CONNECTIVITY_DISCONNECTED NLM_CONNECTIVITY = 0
|
||||
NLM_CONNECTIVITY_IPV4_NOTRAFFIC NLM_CONNECTIVITY = 0x1
|
||||
NLM_CONNECTIVITY_IPV6_NOTRAFFIC NLM_CONNECTIVITY = 0x2
|
||||
NLM_CONNECTIVITY_IPV4_SUBNET NLM_CONNECTIVITY = 0x10
|
||||
NLM_CONNECTIVITY_IPV4_LOCALNETWORK NLM_CONNECTIVITY = 0x20
|
||||
NLM_CONNECTIVITY_IPV4_INTERNET NLM_CONNECTIVITY = 0x40
|
||||
NLM_CONNECTIVITY_IPV6_SUBNET NLM_CONNECTIVITY = 0x100
|
||||
NLM_CONNECTIVITY_IPV6_LOCALNETWORK NLM_CONNECTIVITY = 0x200
|
||||
NLM_CONNECTIVITY_IPV6_INTERNET NLM_CONNECTIVITY = 0x400
|
||||
)
|
||||
|
||||
var CLSID_NetworkListManager = ole.NewGUID("{DCB00C01-570F-4A9B-8D69-199FDBA5723B}")
|
||||
|
||||
var IID_INetworkListManager = ole.NewGUID("{DCB00000-570F-4A9B-8D69-199FDBA5723B}")
|
||||
var IID_INetwork = ole.NewGUID("{8A40A45D-055C-4B62-ABD7-6D613E2CEAEC}")
|
||||
var IID_INetworkConnection = ole.NewGUID("{DCB00005-570F-4A9B-8D69-199FDBA5723B}")
|
||||
|
||||
type NetworkListManager struct {
|
||||
d *ole.Dispatch
|
||||
i *INetworkListManager
|
||||
}
|
||||
|
||||
func (m *NetworkListManager) GetNetwork(networkID windows.GUID) (*INetwork, error) {
|
||||
return m.i.GetNetwork(networkID)
|
||||
}
|
||||
|
||||
type INetworkListManager struct {
|
||||
ole.IUnknown
|
||||
}
|
||||
|
||||
func (i *INetworkListManager) VTable() *INetworkListManagerVtbl {
|
||||
return (*INetworkListManagerVtbl)(unsafe.Pointer(i.RawVTable))
|
||||
}
|
||||
|
||||
type INetworkListManagerVtbl struct {
|
||||
ole.IDispatchVtbl
|
||||
GetNetworks uintptr
|
||||
GetNetwork uintptr
|
||||
GetNetworkConnections uintptr
|
||||
GetNetworkConnection uintptr
|
||||
Get_IsConnectedToInternet uintptr
|
||||
Get_IsConnected uintptr
|
||||
GetConnectivity uintptr
|
||||
SetSimulatedProfileInfo uintptr
|
||||
ClearSimulatedProfileInfo uintptr
|
||||
}
|
||||
|
||||
type INetworkConnection struct {
|
||||
@ -62,25 +102,29 @@ type INetworkVtbl struct {
|
||||
SetCategory uintptr
|
||||
}
|
||||
|
||||
func NewNetworkListManager(c *ole.Connection) (*NetworkListManager, error) {
|
||||
err := c.Create(CLSID_NetworkListManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.Release()
|
||||
|
||||
d, err := c.Dispatch()
|
||||
func newNetworkListManager() (*NetworkListManager, error) {
|
||||
unk, err := ole.CreateInstance(CLSID_NetworkListManager, IID_INetworkListManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nlm := (*INetworkListManager)(unsafe.Pointer(unk))
|
||||
return &NetworkListManager{
|
||||
d: d,
|
||||
i: nlm,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *NetworkListManager) Release() {
|
||||
m.d.Release()
|
||||
var (
|
||||
once sync.Once
|
||||
nlm *NetworkListManager
|
||||
nlmErr error
|
||||
)
|
||||
|
||||
func GetNetworkListManager() (*NetworkListManager, error) {
|
||||
once.Do(func() {
|
||||
nlm, nlmErr = newNetworkListManager()
|
||||
})
|
||||
return nlm, nlmErr
|
||||
}
|
||||
|
||||
func (cl ConnectionList) Release() {
|
||||
@ -103,7 +147,10 @@ func asIID(u ole.UnknownLike, iid *ole.GUID) (*ole.IDispatch, error) {
|
||||
}
|
||||
|
||||
func (m *NetworkListManager) GetNetworkConnections() (ConnectionList, error) {
|
||||
ncraw, err := m.d.Call("GetNetworkConnections")
|
||||
d := ole.Dispatch{
|
||||
Object: (*ole.IDispatch)(unsafe.Pointer(m.i)),
|
||||
}
|
||||
ncraw, err := d.Call("GetNetworkConnections")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -168,6 +215,20 @@ func (n *INetwork) SetCategory(v int32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *INetwork) GetConnectivity() (c NLM_CONNECTIVITY, _ error) {
|
||||
r, _, _ := syscall.SyscallN(
|
||||
n.VTable().GetConnectivity,
|
||||
uintptr(unsafe.Pointer(n)),
|
||||
uintptr(unsafe.Pointer(&c)),
|
||||
)
|
||||
|
||||
if int32(r) < 0 {
|
||||
return 0, ole.NewError(r)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (n *INetwork) VTable() *INetworkVtbl {
|
||||
return (*INetworkVtbl)(unsafe.Pointer(n.RawVTable))
|
||||
}
|
||||
@ -190,3 +251,17 @@ func (v *INetworkConnection) GetNetwork() (*INetwork, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (v *INetworkConnection) GetAdapterId() (string, error) {
|
||||
buf := ole.GUID{}
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().GetAdapterId,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&buf)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
return "", fmt.Errorf("GetAdapterId failed: %08x", hr)
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
32
util/winutil/winnet/winnet_windows_386.go
Normal file
32
util/winutil/winnet/winnet_windows_386.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package winnet
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
ole "github.com/go-ole/go-ole"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (i *INetworkListManager) GetNetwork(networkID windows.GUID) (*INetwork, error) {
|
||||
words := (*[4]uintptr)(unsafe.Pointer(&networkID))
|
||||
var result *INetwork
|
||||
r, _, _ := syscall.SyscallN(
|
||||
i.VTable().GetNetwork,
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
words[0],
|
||||
words[1],
|
||||
words[2],
|
||||
words[3],
|
||||
uintptr(unsafe.Pointer(&result)),
|
||||
)
|
||||
|
||||
if int32(r) < 0 {
|
||||
return nil, ole.NewError(r)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
30
util/winutil/winnet/winnet_windows_not386.go
Normal file
30
util/winutil/winnet/winnet_windows_not386.go
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build windows && !386
|
||||
|
||||
package winnet
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
ole "github.com/go-ole/go-ole"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (i *INetworkListManager) GetNetwork(networkID windows.GUID) (*INetwork, error) {
|
||||
var result *INetwork
|
||||
r, _, _ := syscall.SyscallN(
|
||||
i.VTable().GetNetwork,
|
||||
uintptr(unsafe.Pointer(i)),
|
||||
uintptr(unsafe.Pointer(&networkID)),
|
||||
uintptr(unsafe.Pointer(&result)),
|
||||
)
|
||||
|
||||
if int32(r) < 0 {
|
||||
return nil, ole.NewError(r)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@ -18,10 +18,9 @@ import (
|
||||
"tailscale.com/net/netmon"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/net/tstun"
|
||||
"tailscale.com/util/winutil/winnet"
|
||||
"tailscale.com/wgengine/router"
|
||||
"tailscale.com/wgengine/winnet"
|
||||
|
||||
ole "github.com/go-ole/go-ole"
|
||||
"github.com/tailscale/wireguard-go/tun"
|
||||
"go4.org/netipx"
|
||||
"golang.org/x/sys/windows"
|
||||
@ -175,15 +174,10 @@ func setPrivateNetwork(ifcLUID winipcfg.LUID) (bool, error) {
|
||||
return false, fmt.Errorf("ifcLUID.GUID: %v", err)
|
||||
}
|
||||
|
||||
// aaron: DO NOT call Initialize() or Uninitialize() on c!
|
||||
// We've already handled that process-wide.
|
||||
var c ole.Connection
|
||||
|
||||
m, err := winnet.NewNetworkListManager(&c)
|
||||
m, err := winnet.GetNetworkListManager()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("winnet.NewNetworkListManager: %v", err)
|
||||
}
|
||||
defer m.Release()
|
||||
|
||||
cl, err := m.GetNetworkConnections()
|
||||
if err != nil {
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package winnet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
)
|
||||
|
||||
func (v *INetworkConnection) GetAdapterId() (string, error) {
|
||||
buf := ole.GUID{}
|
||||
hr, _, _ := syscall.Syscall(
|
||||
v.VTable().GetAdapterId,
|
||||
2,
|
||||
uintptr(unsafe.Pointer(v)),
|
||||
uintptr(unsafe.Pointer(&buf)),
|
||||
0)
|
||||
if hr != 0 {
|
||||
return "", fmt.Errorf("GetAdapterId failed: %08x", hr)
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user