safing-portmaster/service/netenv/dbus_linux.go
Daniel Hååvi 80664d1a27
Restructure modules ()
* Move portbase into monorepo

* Add new simple module mgr

* [WIP] Switch to new simple module mgr

* Add StateMgr and more worker variants

* [WIP] Switch more modules

* [WIP] Switch more modules

* [WIP] swtich more modules

* [WIP] switch all SPN modules

* [WIP] switch all service modules

* [WIP] Convert all workers to the new module system

* [WIP] add new task system to module manager

* [WIP] Add second take for scheduling workers

* [WIP] Add FIXME for bugs in new scheduler

* [WIP] Add minor improvements to scheduler

* [WIP] Add new worker scheduler

* [WIP] Fix more bug related to new module system

* [WIP] Fix start handing of the new module system

* [WIP] Improve startup process

* [WIP] Fix minor issues

* [WIP] Fix missing subsystem in settings

* [WIP] Initialize managers in constructor

* [WIP] Move module event initialization to constrictors

* [WIP] Fix setting for enabling and disabling the SPN module

* [WIP] Move API registeration into module construction

* [WIP] Update states mgr for all modules

* [WIP] Add CmdLine operation support

* Add state helper methods to module group and instance

* Add notification and module status handling to status package

* Fix starting issues

* Remove pilot widget and update security lock to new status data

* Remove debug logs

* Improve http server shutdown

* Add workaround for cleanly shutting down firewall+netquery

* Improve logging

* Add syncing states with notifications for new module system

* Improve starting, stopping, shutdown; resolve FIXMEs/TODOs

* [WIP] Fix most unit tests

* Review new module system and fix minor issues

* Push shutdown and restart events again via API

* Set sleep mode via interface

* Update example/template module

* [WIP] Fix spn/cabin unit test

* Remove deprecated UI elements

* Make log output more similar for the logging transition phase

* Switch spn hub and observer cmds to new module system

* Fix log sources

* Make worker mgr less error prone

* Fix tests and minor issues

* Fix observation hub

* Improve shutdown and restart handling

* Split up big connection.go source file

* Move varint and dsd packages to structures repo

* Improve expansion test

* Fix linter warnings

* Fix interception module on windows

* Fix linter errors

---------

Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
2024-08-09 18:15:48 +03:00

223 lines
7.4 KiB
Go

//go:build !server
package netenv
import (
"errors"
"fmt"
"net"
"sync"
"github.com/godbus/dbus/v5"
"github.com/safing/portmaster/base/log"
)
var (
dbusConn *dbus.Conn
dbusConnLock sync.Mutex
)
func getNameserversFromDbus() ([]Nameserver, error) { //nolint:gocognit // TODO
// cmdline tool for exploring: gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager
var ns []Nameserver
var err error
dbusConnLock.Lock()
defer dbusConnLock.Unlock()
if dbusConn == nil {
dbusConn, err = dbus.SystemBus()
}
if err != nil {
return nil, err
}
primaryConnectionVariant, err := getNetworkManagerProperty(dbusConn, dbus.ObjectPath("/org/freedesktop/NetworkManager"), "org.freedesktop.NetworkManager.PrimaryConnection")
if err != nil {
return nil, fmt.Errorf("dbus: failed to access NetworkManager.PrimaryConnection: %w", err)
}
primaryConnection, ok := primaryConnectionVariant.Value().(dbus.ObjectPath)
if !ok {
return nil, errors.New("dbus: could not assert type of /org/freedesktop/NetworkManager:org.freedesktop.NetworkManager.PrimaryConnection")
}
activeConnectionsVariant, err := getNetworkManagerProperty(dbusConn, dbus.ObjectPath("/org/freedesktop/NetworkManager"), "org.freedesktop.NetworkManager.ActiveConnections")
if err != nil {
return nil, fmt.Errorf("dbus: failed to access NetworkManager.ActiveConnections: %w", err)
}
activeConnections, ok := activeConnectionsVariant.Value().([]dbus.ObjectPath)
if !ok {
return nil, errors.New("dbus: could not assert type of /org/freedesktop/NetworkManager:org.freedesktop.NetworkManager.ActiveConnections")
}
sortedConnections := []dbus.ObjectPath{primaryConnection}
for _, activeConnection := range activeConnections {
if !objectPathInSlice(activeConnection, sortedConnections) {
sortedConnections = append(sortedConnections, activeConnection)
}
}
for _, activeConnection := range sortedConnections {
newNameservers, err := dbusGetInterfaceNameservers(dbusConn, activeConnection, 4)
if err != nil {
log.Warningf("failed to get nameserver: %s", err)
} else {
ns = append(ns, newNameservers...)
}
newNameservers, err = dbusGetInterfaceNameservers(dbusConn, activeConnection, 6)
if err != nil {
log.Warningf("failed to get nameserver: %s", err)
} else {
ns = append(ns, newNameservers...)
}
}
return ns, nil
}
func dbusGetInterfaceNameservers(dbusConn *dbus.Conn, interfaceObject dbus.ObjectPath, ipVersion uint8) ([]Nameserver, error) {
ipConfigPropertyKey := fmt.Sprintf("org.freedesktop.NetworkManager.Connection.Active.Ip%dConfig", ipVersion)
nameserversIPsPropertyKey := fmt.Sprintf("org.freedesktop.NetworkManager.IP%dConfig.Nameservers", ipVersion)
nameserversDomainsPropertyKey := fmt.Sprintf("org.freedesktop.NetworkManager.IP%dConfig.Domains", ipVersion)
nameserversSearchesPropertyKey := fmt.Sprintf("org.freedesktop.NetworkManager.IP%dConfig.Searches", ipVersion)
// Get Interface Configuration.
ipConfigVariant, err := getNetworkManagerProperty(dbusConn, interfaceObject, ipConfigPropertyKey)
if err != nil {
return nil, fmt.Errorf("failed to access %s:%s: %w", interfaceObject, ipConfigPropertyKey, err)
}
ipConfig, ok := ipConfigVariant.Value().(dbus.ObjectPath)
if !ok {
return nil, fmt.Errorf("could not assert type of %s:%s", interfaceObject, ipConfigPropertyKey)
}
// Check if interface is active in the selected IP version
if !ipConfig.IsValid() || ipConfig == "/" {
return nil, nil
}
// Get Nameserver IPs
nameserverIPsVariant, err := getNetworkManagerProperty(dbusConn, ipConfig, nameserversIPsPropertyKey)
if err != nil {
return nil, fmt.Errorf("failed to access %s:%s: %w", ipConfig, nameserversIPsPropertyKey, err)
}
var nameserverIPs []net.IP
switch ipVersion {
case 4:
nameserverIP4s, ok := nameserverIPsVariant.Value().([]uint32)
if !ok {
return nil, fmt.Errorf("could not assert type of %s:%s", ipConfig, nameserversIPsPropertyKey)
}
for _, ip := range nameserverIP4s {
a := uint8(ip / 16777216)
b := uint8((ip % 16777216) / 65536)
c := uint8((ip % 65536) / 256)
d := uint8(ip % 256)
nameserverIPs = append(nameserverIPs, net.IPv4(d, c, b, a))
}
case 6:
nameserverIP6s, ok := nameserverIPsVariant.Value().([][]byte)
if !ok {
return nil, fmt.Errorf("could not assert type of %s:%s", ipConfig, nameserversIPsPropertyKey)
}
for _, ip := range nameserverIP6s {
if len(ip) != 16 {
return nil, fmt.Errorf("query returned IPv6 address with invalid length: %q", ip)
}
nameserverIPs = append(nameserverIPs, net.IP(ip))
}
}
// Get Nameserver Domains
nameserverDomainsVariant, err := getNetworkManagerProperty(dbusConn, ipConfig, nameserversDomainsPropertyKey)
if err != nil {
return nil, fmt.Errorf("failed to access %s:%s: %w", ipConfig, nameserversDomainsPropertyKey, err)
}
nameserverDomains, ok := nameserverDomainsVariant.Value().([]string)
if !ok {
return nil, fmt.Errorf("could not assert type of %s:%s", ipConfig, nameserversDomainsPropertyKey)
}
// Get Nameserver Searches
nameserverSearchesVariant, err := getNetworkManagerProperty(dbusConn, ipConfig, nameserversSearchesPropertyKey)
if err != nil {
return nil, fmt.Errorf("failed to access %s:%s: %w", ipConfig, nameserversSearchesPropertyKey, err)
}
nameserverSearches, ok := nameserverSearchesVariant.Value().([]string)
if !ok {
return nil, fmt.Errorf("could not assert type of %s:%s", ipConfig, nameserversSearchesPropertyKey)
}
ns := make([]Nameserver, 0, len(nameserverIPs))
searchDomains := append(nameserverDomains, nameserverSearches...) //nolint:gocritic
for _, nameserverIP := range nameserverIPs {
ns = append(ns, Nameserver{
IP: nameserverIP,
Search: searchDomains,
})
}
return ns, nil
}
func getConnectivityStateFromDbus() (OnlineStatus, error) {
var err error
dbusConnLock.Lock()
defer dbusConnLock.Unlock()
if dbusConn == nil {
dbusConn, err = dbus.SystemBus()
}
if err != nil {
return 0, err
}
connectivityStateVariant, err := getNetworkManagerProperty(dbusConn, dbus.ObjectPath("/org/freedesktop/NetworkManager"), "org.freedesktop.NetworkManager.Connectivity")
if err != nil {
return 0, err
}
connectivityState, ok := connectivityStateVariant.Value().(uint32)
if !ok {
return 0, errors.New("dbus: could not assert type of /org/freedesktop/NetworkManager:org.freedesktop.NetworkManager.Connectivity")
}
// NMConnectivityState
// NM_CONNECTIVITY_UNKNOWN = 0 Network connectivity is unknown.
// NM_CONNECTIVITY_NONE = 1 The host is not connected to any network.
// NM_CONNECTIVITY_PORTAL = 2 The host is behind a captive portal and cannot reach the full Internet.
// NM_CONNECTIVITY_LIMITED = 3 The host is connected to a network, but does not appear to be able to reach the full Internet.
// NM_CONNECTIVITY_FULL = 4 The host is connected to a network, and appears to be able to reach the full Internet.
switch connectivityState {
case 0:
return StatusUnknown, nil
case 1:
return StatusOffline, nil
case 2:
return StatusPortal, nil
case 3:
return StatusLimited, nil
case 4:
return StatusOnline, nil
}
return StatusUnknown, nil
}
func getNetworkManagerProperty(conn *dbus.Conn, objectPath dbus.ObjectPath, property string) (dbus.Variant, error) {
object := conn.Object("org.freedesktop.NetworkManager", objectPath)
return object.GetProperty(property)
}
func objectPathInSlice(a dbus.ObjectPath, list []dbus.ObjectPath) bool {
for _, b := range list {
if string(b) == string(a) {
return true
}
}
return false
}