// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build windows // +build windows package winnet import ( "fmt" "syscall" "unsafe" "github.com/go-ole/go-ole" "github.com/go-ole/go-ole/oleutil" ) const CLSID_NetworkListManager = "{DCB00C01-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 } type INetworkConnection struct { ole.IDispatch } type ConnectionList []*INetworkConnection type INetworkConnectionVtbl struct { ole.IDispatchVtbl GetNetwork uintptr Get_IsConnectedToInternet uintptr Get_IsConnected uintptr GetConnectivity uintptr GetConnectionId uintptr GetAdapterId uintptr GetDomainType uintptr } type INetwork struct { ole.IDispatch } type INetworkVtbl struct { ole.IDispatchVtbl GetName uintptr SetName uintptr GetDescription uintptr SetDescription uintptr GetNetworkId uintptr GetDomainType uintptr GetNetworkConnections uintptr GetTimeCreatedAndConnected uintptr Get_IsConnectedToInternet uintptr Get_IsConnected uintptr GetConnectivity uintptr GetCategory uintptr 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() if err != nil { return nil, err } return &NetworkListManager{ d: d, }, nil } func (m *NetworkListManager) Release() { m.d.Release() } func (cl ConnectionList) Release() { for _, v := range cl { v.Release() } } func asIID(u ole.UnknownLike, iid *ole.GUID) (*ole.IDispatch, error) { if u == nil { return nil, fmt.Errorf("asIID: nil UnknownLike") } d, err := u.QueryInterface(iid) u.Release() if err != nil { return nil, err } return d, nil } func (m *NetworkListManager) GetNetworkConnections() (ConnectionList, error) { ncraw, err := m.d.Call("GetNetworkConnections") if err != nil { return nil, err } nli := ncraw.ToIDispatch() if nli == nil { return nil, fmt.Errorf("GetNetworkConnections: not IDispatch") } cl := ConnectionList{} err = oleutil.ForEach(nli, func(v *ole.VARIANT) error { nc, err := asIID(v.ToIUnknown(), IID_INetworkConnection) if err != nil { return err } nco := (*INetworkConnection)(unsafe.Pointer(nc)) cl = append(cl, nco) return nil }) if err != nil { cl.Release() return nil, err } return cl, nil } func (n *INetwork) GetName() (string, error) { v, err := n.CallMethod("GetName") if err != nil { return "", err } return v.ToString(), err } func (n *INetwork) GetCategory() (int32, error) { var result int32 r, _, _ := syscall.SyscallN( n.VTable().GetCategory, uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(&result)), ) if int32(r) < 0 { return 0, ole.NewError(r) } return result, nil } func (n *INetwork) SetCategory(v int32) error { r, _, _ := syscall.SyscallN( n.VTable().SetCategory, uintptr(unsafe.Pointer(n)), uintptr(v), ) if int32(r) < 0 { return ole.NewError(r) } return nil } func (n *INetwork) VTable() *INetworkVtbl { return (*INetworkVtbl)(unsafe.Pointer(n.RawVTable)) } func (v *INetworkConnection) VTable() *INetworkConnectionVtbl { return (*INetworkConnectionVtbl)(unsafe.Pointer(v.RawVTable)) } func (v *INetworkConnection) GetNetwork() (*INetwork, error) { var result *INetwork r, _, _ := syscall.SyscallN( v.VTable().GetNetwork, uintptr(unsafe.Pointer(v)), uintptr(unsafe.Pointer(&result)), ) if int32(r) < 0 { return nil, ole.NewError(r) } return result, nil }