commit 75591bce9741a19f9825dd47d43330cdf39ea58f Author: Tom Wambold Date: Tue May 24 20:58:18 2016 -0400 Initial commit with not a whole lot of coverage. diff --git a/AccessPoint.go b/AccessPoint.go new file mode 100644 index 0000000..0d6341e --- /dev/null +++ b/AccessPoint.go @@ -0,0 +1,29 @@ +package gonetworkmanager + +import ( + "github.com/godbus/dbus" +) + +const ( + AccessPointInterface = NetworkManagerInterface + ".AccessPoint" + + AccessPointPropertySSID = AccessPointInterface + ".Ssid" +) + +type AccessPoint interface { + // GetSSID returns the Service Set Identifier identifying the access point. + GetSSID() string +} + +func NewAccessPoint(objectPath dbus.ObjectPath) (AccessPoint, error) { + var a accessPoint + return &a, a.init(NetworkManagerInterface, objectPath) +} + +type accessPoint struct { + dbusBase +} + +func (a *accessPoint) GetSSID() string { + return string(a.getSliceByteProperty(AccessPointPropertySSID)) +} diff --git a/ActiveConnection.go b/ActiveConnection.go new file mode 100644 index 0000000..3444eaa --- /dev/null +++ b/ActiveConnection.go @@ -0,0 +1,4 @@ +package gonetworkmanager + +type ActiveConnection interface { +} diff --git a/Connection.go b/Connection.go new file mode 100644 index 0000000..5882f5b --- /dev/null +++ b/Connection.go @@ -0,0 +1,48 @@ +package gonetworkmanager + +import ( + "github.com/godbus/dbus" +) + +const ( + ConnectionInterface = SettingsInterface + ".Connection" + + ConnectionGetSettings = ConnectionInterface + ".GetSettings" +) + +//type ConnectionSettings map[string]map[string]interface{} +type ConnectionSettings map[string]map[string]interface{} + +type Connection interface { + // GetSettings gets the settings maps describing this network configuration. + // This will never include any secrets required for connection to the + // network, as those are often protected. Secrets must be requested + // separately using the GetSecrets() call. + GetSettings() ConnectionSettings +} + +func NewConnection(objectPath dbus.ObjectPath) (Connection, error) { + var c connection + return &c, c.init(NetworkManagerInterface, objectPath) +} + +type connection struct { + dbusBase +} + +func (c *connection) GetSettings() ConnectionSettings { + var settings map[string]map[string]dbus.Variant + c.call(&settings, ConnectionGetSettings) + + rv := make(ConnectionSettings) + + for k1, v1 := range settings { + rv[k1] = make(map[string]interface{}) + + for k2, v2 := range v1 { + rv[k1][k2] = v2.Value() + } + } + + return rv +} diff --git a/Device.go b/Device.go new file mode 100644 index 0000000..e397d33 --- /dev/null +++ b/Device.go @@ -0,0 +1,101 @@ +package gonetworkmanager + +import ( + "github.com/godbus/dbus" +) + +const ( + DeviceInterface = NetworkManagerInterface + ".Device" + + DevicePropertyInterface = DeviceInterface + ".Interface" + DevicePropertyState = DeviceInterface + ".State" + DevicePropertyIP4Config = DeviceInterface + ".Ip4Config" + DevicePropertyDeviceType = DeviceInterface + ".DeviceType" + DevicePropertyAvailableConnections = DeviceInterface + ".AvailableConnections" +) + +func DeviceFactory(objectPath dbus.ObjectPath) (Device, error) { + d, err := NewDevice(objectPath) + if err != nil { + return nil, err + } + + switch d.GetDeviceType() { + case NmDeviceTypeWifi: + return NewWirelessDevice(objectPath) + } + + return d, nil +} + +type Device interface { + // GetInterface gets the name of the device's control (and often data) + // interface. + GetInterface() string + + // GetState gets the current state of the device. + GetState() NmDeviceState + + // GetIP4Config gets the Ip4Config object describing the configuration of the + // device. Only valid when the device is in the NM_DEVICE_STATE_ACTIVATED + // state. + GetIP4Config() IP4Config + + // GetDeviceType gets the general type of the network device; ie Ethernet, + // WiFi, etc. + GetDeviceType() NmDeviceType + + // GetAvailableConnections gets an array of object paths of every configured + // connection that is currently 'available' through this device. + GetAvailableConnections() []Connection +} + +func NewDevice(objectPath dbus.ObjectPath) (Device, error) { + var d device + return &d, d.init(NetworkManagerInterface, objectPath) +} + +type device struct { + dbusBase +} + +func (d *device) GetInterface() string { + return d.getStringProperty(DevicePropertyInterface) +} + +func (d *device) GetState() NmDeviceState { + return NmDeviceState(d.getUint32Property(DevicePropertyState)) +} + +func (d *device) GetIP4Config() IP4Config { + path := d.getObjectProperty(DevicePropertyIP4Config) + if path == "/" { + return nil + } + + cfg, err := NewIP4Config(path) + if err != nil { + panic(err) + } + + return cfg +} + +func (d *device) GetDeviceType() NmDeviceType { + return NmDeviceType(d.getUint32Property(DevicePropertyDeviceType)) +} + +func (d *device) GetAvailableConnections() []Connection { + connPaths := d.getSliceObjectProperty(DevicePropertyAvailableConnections) + conns := make([]Connection, len(connPaths)) + + var err error + for i, path := range connPaths { + conns[i], err = NewConnection(path) + if err != nil { + panic(err) + } + } + + return conns +} diff --git a/IP4Config.go b/IP4Config.go new file mode 100644 index 0000000..a674538 --- /dev/null +++ b/IP4Config.go @@ -0,0 +1,102 @@ +package gonetworkmanager + +import ( + "github.com/godbus/dbus" +) + +const ( + IP4ConfigInterface = NetworkManagerInterface + ".IP4Config" + + IP4ConfigPropertyAddresses = IP4ConfigInterface + ".Addresses" + IP4ConfigPropertyRoutes = IP4ConfigInterface + ".Routes" + IP4ConfigPropertyNameservers = IP4ConfigInterface + ".Nameservers" + IP4ConfigPropertyDomains = IP4ConfigInterface + ".Domains" +) + +type IP4Address struct { + Address string + Prefix uint8 + Gateway string +} + +type IP4Route struct { + Route string + Prefix uint8 + NextHop string + Metric uint8 +} + +type IP4Config interface { + // GetAddresses gets an array of tuples of IPv4 address/prefix/gateway. All 3 + // elements of each tuple are in network byte order. Essentially: [(addr, + // prefix, gateway), (addr, prefix, gateway), ...] + GetAddresses() []IP4Address + + // GetRoutes gets tuples of IPv4 route/prefix/next-hop/metric. All 4 elements + // of each tuple are in network byte order. 'route' and 'next hop' are IPv4 + // addresses, while prefix and metric are simple unsigned integers. + // Essentially: [(route, prefix, next-hop, metric), (route, prefix, next-hop, + // metric), ...] + GetRoutes() []IP4Route + + // GetNameservers gets the nameservers in use. + GetNameservers() []string + + // GetDomains gets a list of domains this address belongs to. + GetDomains() []string +} + +func NewIP4Config(objectPath dbus.ObjectPath) (IP4Config, error) { + var c ip4Config + return &c, c.init(NetworkManagerInterface, objectPath) +} + +type ip4Config struct { + dbusBase +} + +func (c *ip4Config) GetAddresses() []IP4Address { + addresses := c.getSliceSliceUint32Property(IP4ConfigPropertyAddresses) + ret := make([]IP4Address, len(addresses)) + + for i, parts := range addresses { + ret[i] = IP4Address{ + Address: ip4ToString(parts[0]), + Prefix: uint8(parts[1]), + Gateway: ip4ToString(parts[2]), + } + } + + return ret +} + +func (c *ip4Config) GetRoutes() []IP4Route { + routes := c.getSliceSliceUint32Property(IP4ConfigPropertyRoutes) + ret := make([]IP4Route, len(routes)) + + for i, parts := range routes { + ret[i] = IP4Route{ + Route: ip4ToString(parts[0]), + Prefix: uint8(parts[1]), + NextHop: ip4ToString(parts[2]), + Metric: uint8(parts[3]), + } + } + + return ret +} + +func (c *ip4Config) GetNameservers() []string { + nameservers := c.getSliceUint32Property(IP4ConfigPropertyNameservers) + ret := make([]string, len(nameservers)) + + for i, ns := range nameservers { + ret[i] = ip4ToString(ns) + } + + return ret +} + +func (c *ip4Config) GetDomains() []string { + return c.getSliceStringProperty(IP4ConfigPropertyDomains) +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..86e5a3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Bellerophon Mobile + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NetworkManager.go b/NetworkManager.go new file mode 100644 index 0000000..07e0138 --- /dev/null +++ b/NetworkManager.go @@ -0,0 +1,54 @@ +package gonetworkmanager + +import ( + "github.com/godbus/dbus" +) + +const ( + NetworkManagerInterface = "org.freedesktop.NetworkManager" + NetworkManagerObjectPath = "/org/freedesktop/NetworkManager" + + NetworkManagerGetDevices = NetworkManagerInterface + ".GetDevices" + NetworkManagerPropertyState = NetworkManagerInterface + ".state" +) + +type NetworkManager interface { + + // GetDevices gets the list of network devices. + GetDevices() []Device + + // GetState returns the overall networking state as determined by the + // NetworkManager daemon, based on the state of network devices under it's + // management. + GetState() NmState +} + +func NewNetworkManager() (NetworkManager, error) { + var nm networkManager + return &nm, nm.init(NetworkManagerInterface, NetworkManagerObjectPath) +} + +type networkManager struct { + dbusBase +} + +func (n *networkManager) GetDevices() []Device { + var devicePaths []dbus.ObjectPath + + n.call(&devicePaths, NetworkManagerGetDevices) + devices := make([]Device, len(devicePaths)) + + var err error + for i, path := range devicePaths { + devices[i], err = DeviceFactory(path) + if err != nil { + panic(err) + } + } + + return devices +} + +func (n *networkManager) GetState() NmState { + return NmState(n.getUint32Property(NetworkManagerPropertyState)) +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..1693ce1 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +gonetworkmanager +================ + +Go D-Bus bindings for NetworkManager. + +Currently tested with NetworkManager 0.9.8.10. + +(https://godoc.org/github.com/BellerophonMobile/gonetworkmanager?status.svg)](https://godoc.org/github.com/BellerophonMobile/gonetworkmanager) + +[NetworkManager 0.9 D-Bus Spec](https://developer.gnome.org/NetworkManager/0.9/spec.html) diff --git a/Settings.go b/Settings.go new file mode 100644 index 0000000..4b3df30 --- /dev/null +++ b/Settings.go @@ -0,0 +1,5 @@ +package gonetworkmanager + +const ( + SettingsInterface = NetworkManagerInterface + ".Settings" +) diff --git a/WirelessDevice.go b/WirelessDevice.go new file mode 100644 index 0000000..a712de0 --- /dev/null +++ b/WirelessDevice.go @@ -0,0 +1,47 @@ +package gonetworkmanager + +import ( + "github.com/godbus/dbus" +) + +const ( + WirelessDeviceInterface = DeviceInterface + ".Wireless" + + WirelessDeviceGetAccessPoints = WirelessDeviceInterface + ".GetAccessPoints" +) + +type WirelessDevice interface { + Device + + // GetAccessPoints gets the list of access points visible to this device. + // Note that this list does not include access points which hide their SSID. + // To retrieve a list of all access points (including hidden ones) use the + // GetAllAccessPoints() method. + GetAccessPoints() []AccessPoint +} + +func NewWirelessDevice(objectPath dbus.ObjectPath) (WirelessDevice, error) { + var d wirelessDevice + return &d, d.init(NetworkManagerInterface, objectPath) +} + +type wirelessDevice struct { + device +} + +func (d *wirelessDevice) GetAccessPoints() []AccessPoint { + var apPaths []dbus.ObjectPath + + d.call(&apPaths, WirelessDeviceGetAccessPoints) + aps := make([]AccessPoint, len(apPaths)) + + var err error + for i, path := range apPaths { + aps[i], err = NewAccessPoint(path) + if err != nil { + panic(err) + } + } + + return aps +} diff --git a/enums.go b/enums.go new file mode 100644 index 0000000..6bf3d3d --- /dev/null +++ b/enums.go @@ -0,0 +1,67 @@ +package gonetworkmanager + +//go:generate stringer -type=NmConnectivity +type NmConnectivity uint32 + +const ( + NmConnectivityUnknown NmConnectivity = 0 + NmConnectivityNone NmConnectivity = 1 + NmConnectivityPortal NmConnectivity = 2 + NmConnectivityLimited NmConnectivity = 3 + NmConnectivityFull NmConnectivity = 4 +) + +//go:generate stringer -type=NmState +type NmState uint32 + +const ( + NmStateUnknown NmState = 0 + NmStateAsleep NmState = 10 + NmStateDisconnected NmState = 20 + NmStateDisconnecting NmState = 30 + NmStateConnecting NmState = 40 + NmStateConnectedLocal NmState = 50 + NmStateConnectedSite NmState = 60 + NmStateConnectedGlobal NmState = 70 +) + +//go:generate stringer -type=NmDeviceState +type NmDeviceState uint32 + +const ( + NmDeviceStateUnknown NmDeviceState = 0 + NmDeviceStateUnmanaged NmDeviceState = 10 + NmDeviceStateUnavailable NmDeviceState = 20 + NmDeviceStateDisconnected NmDeviceState = 30 + NmDeviceStatePrepare NmDeviceState = 40 + NmDeviceStateConfig NmDeviceState = 50 + NmDeviceStateNeed_auth NmDeviceState = 60 + NmDeviceStateIp_config NmDeviceState = 70 + NmDeviceStateIp_check NmDeviceState = 80 + NmDeviceStateSecondaries NmDeviceState = 90 + NmDeviceStateActivated NmDeviceState = 100 + NmDeviceStateDeactivating NmDeviceState = 110 + NmDeviceStateFailed NmDeviceState = 120 +) + +//go:generate stringer -type=NmDeviceType +type NmDeviceType uint32 + +const ( + NmDeviceTypeUnknown NmDeviceType = 0 + NmDeviceTypeEthernet NmDeviceType = 1 + NmDeviceTypeWifi NmDeviceType = 2 + NmDeviceTypeUnused1 NmDeviceType = 3 + NmDeviceTypeUnused2 NmDeviceType = 4 + NmDeviceTypeBt NmDeviceType = 5 + NmDeviceTypeOlpcMesh NmDeviceType = 6 + NmDeviceTypeWimax NmDeviceType = 7 + NmDeviceTypeModem NmDeviceType = 8 + NmDeviceTypeInfiniband NmDeviceType = 9 + NmDeviceTypeBond NmDeviceType = 10 + NmDeviceTypeVlan NmDeviceType = 11 + NmDeviceTypeAdsl NmDeviceType = 12 + NmDeviceTypeBridge NmDeviceType = 13 + NmDeviceTypeGeneric NmDeviceType = 14 + NmDeviceTypeTeam NmDeviceType = 15 +) diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..990e6ad --- /dev/null +++ b/errors.go @@ -0,0 +1,9 @@ +package gonetworkmanager + +import ( + "errors" +) + +var ( + ErrVariantType = errors.New("unexpected variant type") +) diff --git a/nmconnectivity_string.go b/nmconnectivity_string.go new file mode 100644 index 0000000..c9bc0e1 --- /dev/null +++ b/nmconnectivity_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=NmConnectivity"; DO NOT EDIT + +package gonetworkmanager + +import "fmt" + +const _NmConnectivity_name = "NmConnectivityUnknownNmConnectivityNoneNmConnectivityPortalNmConnectivityLimitedNmConnectivityFull" + +var _NmConnectivity_index = [...]uint8{0, 21, 39, 59, 80, 98} + +func (i NmConnectivity) String() string { + if i >= NmConnectivity(len(_NmConnectivity_index)-1) { + return fmt.Sprintf("NmConnectivity(%d)", i) + } + return _NmConnectivity_name[_NmConnectivity_index[i]:_NmConnectivity_index[i+1]] +} diff --git a/nmdevicestate_string.go b/nmdevicestate_string.go new file mode 100644 index 0000000..c88842d --- /dev/null +++ b/nmdevicestate_string.go @@ -0,0 +1,30 @@ +// Code generated by "stringer -type=NmDeviceState"; DO NOT EDIT + +package gonetworkmanager + +import "fmt" + +const _NmDeviceState_name = "NmDeviceStateUnknownNmDeviceStateUnmanagedNmDeviceStateUnavailableNmDeviceStateDisconnectedNmDeviceStatePrepareNmDeviceStateConfigNmDeviceStateNeed_authNmDeviceStateIp_configNmDeviceStateIp_checkNmDeviceStateSecondariesNmDeviceStateActivatedNmDeviceStateDeactivatingNmDeviceStateFailed" + +var _NmDeviceState_map = map[NmDeviceState]string{ + 0: _NmDeviceState_name[0:20], + 10: _NmDeviceState_name[20:42], + 20: _NmDeviceState_name[42:66], + 30: _NmDeviceState_name[66:91], + 40: _NmDeviceState_name[91:111], + 50: _NmDeviceState_name[111:130], + 60: _NmDeviceState_name[130:152], + 70: _NmDeviceState_name[152:174], + 80: _NmDeviceState_name[174:195], + 90: _NmDeviceState_name[195:219], + 100: _NmDeviceState_name[219:241], + 110: _NmDeviceState_name[241:266], + 120: _NmDeviceState_name[266:285], +} + +func (i NmDeviceState) String() string { + if str, ok := _NmDeviceState_map[i]; ok { + return str + } + return fmt.Sprintf("NmDeviceState(%d)", i) +} diff --git a/nmdevicetype_string.go b/nmdevicetype_string.go new file mode 100644 index 0000000..af9645f --- /dev/null +++ b/nmdevicetype_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=NmDeviceType"; DO NOT EDIT + +package gonetworkmanager + +import "fmt" + +const _NmDeviceType_name = "NmDeviceTypeUnknownNmDeviceTypeEthernetNmDeviceTypeWifiNmDeviceTypeUnused1NmDeviceTypeUnused2NmDeviceTypeBtNmDeviceTypeOlpcMeshNmDeviceTypeWimaxNmDeviceTypeModemNmDeviceTypeInfinibandNmDeviceTypeBondNmDeviceTypeVlanNmDeviceTypeAdslNmDeviceTypeBridgeNmDeviceTypeGenericNmDeviceTypeTeam" + +var _NmDeviceType_index = [...]uint16{0, 19, 39, 55, 74, 93, 107, 127, 144, 161, 183, 199, 215, 231, 249, 268, 284} + +func (i NmDeviceType) String() string { + if i >= NmDeviceType(len(_NmDeviceType_index)-1) { + return fmt.Sprintf("NmDeviceType(%d)", i) + } + return _NmDeviceType_name[_NmDeviceType_index[i]:_NmDeviceType_index[i+1]] +} diff --git a/nmstate_string.go b/nmstate_string.go new file mode 100644 index 0000000..42c467a --- /dev/null +++ b/nmstate_string.go @@ -0,0 +1,50 @@ +// Code generated by "stringer -type=NmState"; DO NOT EDIT + +package gonetworkmanager + +import "fmt" + +const ( + _NmState_name_0 = "NmStateUnknown" + _NmState_name_1 = "NmStateAsleep" + _NmState_name_2 = "NmStateDisconnected" + _NmState_name_3 = "NmStateDisconnecting" + _NmState_name_4 = "NmStateConnecting" + _NmState_name_5 = "NmStateConnectedLocal" + _NmState_name_6 = "NmStateConnectedSite" + _NmState_name_7 = "NmStateConnectedGlobal" +) + +var ( + _NmState_index_0 = [...]uint8{0, 14} + _NmState_index_1 = [...]uint8{0, 13} + _NmState_index_2 = [...]uint8{0, 19} + _NmState_index_3 = [...]uint8{0, 20} + _NmState_index_4 = [...]uint8{0, 17} + _NmState_index_5 = [...]uint8{0, 21} + _NmState_index_6 = [...]uint8{0, 20} + _NmState_index_7 = [...]uint8{0, 22} +) + +func (i NmState) String() string { + switch { + case i == 0: + return _NmState_name_0 + case i == 10: + return _NmState_name_1 + case i == 20: + return _NmState_name_2 + case i == 30: + return _NmState_name_3 + case i == 40: + return _NmState_name_4 + case i == 50: + return _NmState_name_5 + case i == 60: + return _NmState_name_6 + case i == 70: + return _NmState_name_7 + default: + return fmt.Sprintf("NmState(%d)", i) + } +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..2f94979 --- /dev/null +++ b/utils.go @@ -0,0 +1,115 @@ +package gonetworkmanager + +import ( + "encoding/binary" + "net" + + "github.com/godbus/dbus" +) + +type dbusBase struct { + conn *dbus.Conn + obj dbus.BusObject +} + +func (d *dbusBase) init(iface string, objectPath dbus.ObjectPath) error { + var err error + + d.conn, err = dbus.SystemBus() + if err != nil { + return err + } + + d.obj = d.conn.Object(iface, objectPath) + + return nil +} + +func (d *dbusBase) call(value interface{}, method string, args ...interface{}) { + err := d.callError(value, method, args...) + if err != nil { + panic(err) + } +} + +func (d *dbusBase) callError(value interface{}, method string, args ...interface{}) error { + return d.obj.Call(method, 0, args...).Store(value) +} + +func (d *dbusBase) getProperty(iface string) interface{} { + variant, err := d.obj.GetProperty(iface) + if err != nil { + panic(err) + } + return variant.Value() +} + +func (d *dbusBase) getObjectProperty(iface string) dbus.ObjectPath { + value, ok := d.getProperty(iface).(dbus.ObjectPath) + if !ok { + panic(ErrVariantType) + } + return value +} + +func (d *dbusBase) getSliceObjectProperty(iface string) []dbus.ObjectPath { + value, ok := d.getProperty(iface).([]dbus.ObjectPath) + if !ok { + panic(ErrVariantType) + } + return value +} + +func (d *dbusBase) getStringProperty(iface string) string { + value, ok := d.getProperty(iface).(string) + if !ok { + panic(ErrVariantType) + } + return value +} + +func (d *dbusBase) getSliceStringProperty(iface string) []string { + value, ok := d.getProperty(iface).([]string) + if !ok { + panic(ErrVariantType) + } + return value +} + +func (d *dbusBase) getUint32Property(iface string) uint32 { + value, ok := d.getProperty(iface).(uint32) + if !ok { + panic(ErrVariantType) + } + return value +} + +func (d *dbusBase) getSliceUint32Property(iface string) []uint32 { + value, ok := d.getProperty(iface).([]uint32) + if !ok { + panic(ErrVariantType) + } + return value +} + +func (d *dbusBase) getSliceSliceUint32Property(iface string) [][]uint32 { + value, ok := d.getProperty(iface).([][]uint32) + if !ok { + panic(ErrVariantType) + } + return value +} + +func (d *dbusBase) getSliceByteProperty(iface string) []byte { + value, ok := d.getProperty(iface).([]byte) + if !ok { + panic(ErrVariantType) + } + return value +} + +func ip4ToString(ip uint32) string { + bs := []byte{0, 0, 0, 0} + binary.LittleEndian.PutUint32(bs, ip) + return net.IP(bs).String() +}