Move network/environment to netenv
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/safing/portmaster/network/netutils"
|
||||
)
|
||||
|
||||
// GetAssignedAddresses returns the assigned IPv4 and IPv6 addresses of the host.
|
||||
func GetAssignedAddresses() (ipv4 []net.IP, ipv6 []net.IP, err error) {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
ip := net.ParseIP(strings.Split(addr.String(), "/")[0])
|
||||
if ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
ipv4 = append(ipv4, ip4)
|
||||
} else {
|
||||
ipv6 = append(ipv6, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetAssignedGlobalAddresses returns the assigned global IPv4 and IPv6 addresses of the host.
|
||||
func GetAssignedGlobalAddresses() (ipv4 []net.IP, ipv6 []net.IP, err error) {
|
||||
allv4, allv6, err := GetAssignedAddresses()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, ip4 := range allv4 {
|
||||
if netutils.IPIsGlobal(ip4) {
|
||||
ipv4 = append(ipv4, ip4)
|
||||
}
|
||||
}
|
||||
for _, ip6 := range allv6 {
|
||||
if netutils.IPIsGlobal(ip6) {
|
||||
ipv6 = append(ipv6, ip6)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetAssignedAddresses(t *testing.T) {
|
||||
ipv4, ipv6, err := GetAssignedAddresses()
|
||||
fmt.Printf("all v4: %v", ipv4)
|
||||
fmt.Printf("all v6: %v", ipv6)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get addresses: %s", err)
|
||||
}
|
||||
if len(ipv4) == 0 && len(ipv6) == 0 {
|
||||
t.Fatal("GetAssignedAddresses did not return any addresses")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAssignedGlobalAddresses(t *testing.T) {
|
||||
ipv4, ipv6, err := GetAssignedGlobalAddresses()
|
||||
fmt.Printf("all global v4: %v", ipv4)
|
||||
fmt.Printf("all global v6: %v", ipv6)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get addresses: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
// +build !server
|
||||
|
||||
package environment
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
var (
|
||||
dbusConn *dbus.Conn
|
||||
dbusConnLock sync.Mutex
|
||||
)
|
||||
|
||||
func getNameserversFromDbus() ([]Nameserver, error) {
|
||||
// cmdline tool for exploring: gdbus introspect --system --dest org.freedesktop.NetworkManager --object-path /org/freedesktop/NetworkManager
|
||||
|
||||
var nameservers []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, 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, 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 {
|
||||
|
||||
ip4ConfigVariant, err := getNetworkManagerProperty(dbusConn, activeConnection, "org.freedesktop.NetworkManager.Connection.Active.Ip4Config")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ip4Config, ok := ip4ConfigVariant.Value().(dbus.ObjectPath)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dbus: could not assert type of %s:org.freedesktop.NetworkManager.Connection.Active.Ip4Config", activeConnection)
|
||||
}
|
||||
|
||||
nameserverIP4sVariant, err := getNetworkManagerProperty(dbusConn, ip4Config, "org.freedesktop.NetworkManager.IP4Config.Nameservers")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameserverIP4s, ok := nameserverIP4sVariant.Value().([]uint32)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dbus: could not assert type of %s:org.freedesktop.NetworkManager.IP4Config.Nameservers", ip4Config)
|
||||
}
|
||||
|
||||
nameserverDomainsVariant, err := getNetworkManagerProperty(dbusConn, ip4Config, "org.freedesktop.NetworkManager.IP4Config.Domains")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameserverDomains, ok := nameserverDomainsVariant.Value().([]string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dbus: could not assert type of %s:org.freedesktop.NetworkManager.IP4Config.Domains", ip4Config)
|
||||
}
|
||||
|
||||
nameserverSearchesVariant, err := getNetworkManagerProperty(dbusConn, ip4Config, "org.freedesktop.NetworkManager.IP4Config.Searches")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameserverSearches, ok := nameserverSearchesVariant.Value().([]string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dbus: could not assert type of %s:org.freedesktop.NetworkManager.IP4Config.Searches", ip4Config)
|
||||
}
|
||||
|
||||
for _, ip := range nameserverIP4s {
|
||||
a := uint8(ip / 16777216)
|
||||
b := uint8((ip % 16777216) / 65536)
|
||||
c := uint8((ip % 65536) / 256)
|
||||
d := uint8(ip % 256)
|
||||
nameservers = append(nameservers, Nameserver{
|
||||
IP: net.IPv4(d, c, b, a),
|
||||
Search: append(nameserverDomains, nameserverSearches...),
|
||||
})
|
||||
}
|
||||
|
||||
ip6ConfigVariant, err := getNetworkManagerProperty(dbusConn, activeConnection, "org.freedesktop.NetworkManager.Connection.Active.Ip6Config")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ip6Config, ok := ip6ConfigVariant.Value().(dbus.ObjectPath)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dbus: could not assert type of %s:org.freedesktop.NetworkManager.Connection.Active.Ip6Config", activeConnection)
|
||||
}
|
||||
|
||||
nameserverIP6sVariant, err := getNetworkManagerProperty(dbusConn, ip6Config, "org.freedesktop.NetworkManager.IP6Config.Nameservers")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameserverIP6s, ok := nameserverIP6sVariant.Value().([][]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dbus: could not assert type of %s:org.freedesktop.NetworkManager.IP6Config.Nameservers", ip6Config)
|
||||
}
|
||||
|
||||
nameserverDomainsVariant, err = getNetworkManagerProperty(dbusConn, ip6Config, "org.freedesktop.NetworkManager.IP6Config.Domains")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameserverDomains, ok = nameserverDomainsVariant.Value().([]string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dbus: could not assert type of %s:org.freedesktop.NetworkManager.IP6Config.Domains", ip6Config)
|
||||
}
|
||||
|
||||
nameserverSearchesVariant, err = getNetworkManagerProperty(dbusConn, ip6Config, "org.freedesktop.NetworkManager.IP6Config.Searches")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameserverSearches, ok = nameserverSearchesVariant.Value().([]string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dbus: could not assert type of %s:org.freedesktop.NetworkManager.IP6Config.Searches", ip6Config)
|
||||
}
|
||||
|
||||
for _, ip := range nameserverIP6s {
|
||||
if len(ip) != 16 {
|
||||
return nil, fmt.Errorf("dbus: query returned IPv6 address (%s) with invalid length", ip)
|
||||
}
|
||||
nameservers = append(nameservers, Nameserver{
|
||||
IP: net.IP(ip),
|
||||
Search: append(nameserverDomains, nameserverSearches...),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nameservers, 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
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
package environment
|
||||
|
||||
func getNameserversFromDbus() ([]Nameserver, error) {
|
||||
var nameservers []Nameserver
|
||||
return nameservers, nil
|
||||
}
|
||||
|
||||
func getConnectivityStateFromDbus() (uint8, error) {
|
||||
return StatusUnknown, nil
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDbus(t *testing.T) {
|
||||
if _, err := os.Stat("/var/run/dbus/system_bus_socket"); os.IsNotExist(err) {
|
||||
t.Logf("skipping dbus tests, as dbus does not seem to be installed: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
nameservers, err := getNameserversFromDbus()
|
||||
if err != nil {
|
||||
t.Errorf("getNameserversFromDbus failed: %s", err)
|
||||
}
|
||||
t.Logf("getNameserversFromDbus: %v", nameservers)
|
||||
|
||||
connectivityState, err := getConnectivityStateFromDbus()
|
||||
if err != nil {
|
||||
t.Errorf("getConnectivityStateFromDbus failed: %s", err)
|
||||
}
|
||||
t.Logf("getConnectivityStateFromDbus: %v", connectivityState)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package environment
|
||||
|
||||
import "net"
|
||||
|
||||
var (
|
||||
localAddrFactory func(network string) net.Addr
|
||||
)
|
||||
|
||||
// SetLocalAddrFactory supplies the environment package with a function to get permitted local addresses for connections.
|
||||
func SetLocalAddrFactory(laf func(network string) net.Addr) {
|
||||
if localAddrFactory == nil {
|
||||
localAddrFactory = laf
|
||||
}
|
||||
}
|
||||
|
||||
func getLocalAddr(network string) net.Addr {
|
||||
if localAddrFactory != nil {
|
||||
return localAddrFactory(network)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TODO: find a good way to identify a network
|
||||
// best options until now:
|
||||
// MAC of gateway
|
||||
// domain parameter of dhcp
|
||||
|
||||
// TODO: get dhcp servers on windows:
|
||||
// windows: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365917
|
||||
// this info might already be included in the interfaces api provided by golang!
|
||||
|
||||
const (
|
||||
gatewaysRecheck = 2 * time.Second
|
||||
nameserversRecheck = 2 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
// interfaces = make(map[*net.IP]net.Flags)
|
||||
// interfacesLock sync.Mutex
|
||||
// interfacesExpires = time.Now()
|
||||
|
||||
gateways = make([]*net.IP, 0)
|
||||
gatewaysLock sync.Mutex
|
||||
gatewaysExpires = time.Now()
|
||||
|
||||
nameservers = make([]Nameserver, 0)
|
||||
nameserversLock sync.Mutex
|
||||
nameserversExpires = time.Now()
|
||||
)
|
||||
|
||||
// Nameserver describes a system assigned namserver.
|
||||
type Nameserver struct {
|
||||
IP net.IP
|
||||
Search []string
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package environment
|
||||
|
||||
import "net"
|
||||
|
||||
func Nameservers() []Nameserver {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Gateways() []*net.IP {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: implement using
|
||||
// ifconfig
|
||||
// scutil --nwi
|
||||
// scutil --proxy
|
||||
// networksetup -listallnetworkservices
|
||||
// networksetup -listnetworkserviceorder
|
||||
// networksetup -getdnsservers "Wi-Fi"
|
||||
// networksetup -getsearchdomains <networkservice>
|
||||
// networksetup -getftpproxy <networkservice>
|
||||
// networksetup -getwebproxy <networkservice>
|
||||
// networksetup -getsecurewebproxy <networkservice>
|
||||
// networksetup -getstreamingproxy <networkservice>
|
||||
// networksetup -getgopherproxy <networkservice>
|
||||
// networksetup -getsocksfirewallproxy <networkservice>
|
||||
// route -n get default
|
||||
@@ -1,205 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/network/netutils"
|
||||
)
|
||||
|
||||
// Gateways returns the currently active gateways
|
||||
func Gateways() []*net.IP {
|
||||
// locking
|
||||
gatewaysLock.Lock()
|
||||
defer gatewaysLock.Unlock()
|
||||
// cache
|
||||
if gatewaysExpires.After(time.Now()) {
|
||||
return gateways
|
||||
}
|
||||
// update cache expiry when finished
|
||||
defer func() {
|
||||
gatewaysExpires = time.Now().Add(gatewaysRecheck)
|
||||
}()
|
||||
// logic
|
||||
|
||||
newGateways := make([]*net.IP, 0)
|
||||
var decoded []byte
|
||||
|
||||
// open file
|
||||
route, err := os.Open("/proc/net/route")
|
||||
if err != nil {
|
||||
log.Warningf("environment: could not read /proc/net/route: %s", err)
|
||||
return newGateways
|
||||
}
|
||||
defer route.Close()
|
||||
|
||||
// file scanner
|
||||
scanner := bufio.NewScanner(route)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
// parse
|
||||
for scanner.Scan() {
|
||||
line := strings.SplitN(scanner.Text(), "\t", 4)
|
||||
if len(line) < 4 {
|
||||
continue
|
||||
}
|
||||
if line[1] == "00000000" {
|
||||
decoded, err = hex.DecodeString(line[2])
|
||||
if err != nil {
|
||||
log.Warningf("environment: could not parse gateway %s from /proc/net/route: %s", line[2], err)
|
||||
continue
|
||||
}
|
||||
if len(decoded) != 4 {
|
||||
log.Warningf("environment: decoded gateway %s from /proc/net/route has wrong length", decoded)
|
||||
continue
|
||||
}
|
||||
gate := net.IPv4(decoded[3], decoded[2], decoded[1], decoded[0])
|
||||
newGateways = append(newGateways, &gate)
|
||||
}
|
||||
}
|
||||
|
||||
// open file
|
||||
v6route, err := os.Open("/proc/net/ipv6_route")
|
||||
if err != nil {
|
||||
log.Warningf("environment: could not read /proc/net/ipv6_route: %s", err)
|
||||
return newGateways
|
||||
}
|
||||
defer v6route.Close()
|
||||
|
||||
// file scanner
|
||||
scanner = bufio.NewScanner(v6route)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
// parse
|
||||
for scanner.Scan() {
|
||||
line := strings.SplitN(scanner.Text(), " ", 6)
|
||||
if len(line) < 6 {
|
||||
continue
|
||||
}
|
||||
if line[0] == "00000000000000000000000000000000" && line[4] != "00000000000000000000000000000000" {
|
||||
decoded, err := hex.DecodeString(line[4])
|
||||
if err != nil {
|
||||
log.Warningf("environment: could not parse gateway %s from /proc/net/ipv6_route: %s", line[2], err)
|
||||
continue
|
||||
}
|
||||
if len(decoded) != 16 {
|
||||
log.Warningf("environment: decoded gateway %s from /proc/net/ipv6_route has wrong length", decoded)
|
||||
continue
|
||||
}
|
||||
gate := net.IP(decoded)
|
||||
newGateways = append(newGateways, &gate)
|
||||
}
|
||||
}
|
||||
|
||||
return newGateways
|
||||
}
|
||||
|
||||
// Nameservers returns the currently active nameservers
|
||||
func Nameservers() []Nameserver {
|
||||
// locking
|
||||
nameserversLock.Lock()
|
||||
defer nameserversLock.Unlock()
|
||||
// cache
|
||||
if nameserversExpires.After(time.Now()) {
|
||||
return nameservers
|
||||
}
|
||||
// update cache expiry when finished
|
||||
defer func() {
|
||||
nameserversExpires = time.Now().Add(nameserversRecheck)
|
||||
}()
|
||||
|
||||
// logic
|
||||
// TODO: try:
|
||||
// 1. NetworkManager DBUS
|
||||
// 2. /etc/resolv.conf
|
||||
// 2.1. if /etc/resolv.conf has localhost nameserver, check for dnsmasq config (are there others?)
|
||||
nameservers = make([]Nameserver, 0)
|
||||
|
||||
// get nameservers from DBUS
|
||||
dbusNameservers, err := getNameserversFromDbus()
|
||||
if err != nil {
|
||||
log.Warningf("environment: could not get nameservers from dbus: %s", err)
|
||||
} else {
|
||||
nameservers = addNameservers(nameservers, dbusNameservers)
|
||||
}
|
||||
|
||||
// get nameservers from /etc/resolv.conf
|
||||
resolvconfNameservers, err := getNameserversFromResolvconf()
|
||||
if err != nil {
|
||||
log.Warningf("environment: could not get nameservers from resolvconf: %s", err)
|
||||
} else {
|
||||
nameservers = addNameservers(nameservers, resolvconfNameservers)
|
||||
}
|
||||
|
||||
return nameservers
|
||||
}
|
||||
|
||||
func getNameserversFromResolvconf() ([]Nameserver, error) {
|
||||
// open file
|
||||
resolvconf, err := os.Open("/etc/resolv.conf")
|
||||
if err != nil {
|
||||
log.Warningf("environment: could not read /etc/resolv.conf: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
defer resolvconf.Close()
|
||||
|
||||
// file scanner
|
||||
scanner := bufio.NewScanner(resolvconf)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
var searchDomains []string
|
||||
var servers []net.IP
|
||||
|
||||
// parse
|
||||
for scanner.Scan() {
|
||||
line := strings.SplitN(scanner.Text(), " ", 3)
|
||||
if len(line) < 2 {
|
||||
continue
|
||||
}
|
||||
switch line[0] {
|
||||
case "search":
|
||||
if netutils.IsValidFqdn(dns.Fqdn(line[1])) {
|
||||
searchDomains = append(searchDomains, line[1])
|
||||
}
|
||||
case "nameserver":
|
||||
ip := net.ParseIP(line[1])
|
||||
if ip != nil {
|
||||
servers = append(servers, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build array
|
||||
nameservers := make([]Nameserver, 0, len(servers))
|
||||
for _, server := range servers {
|
||||
nameservers = append(nameservers, Nameserver{
|
||||
IP: server,
|
||||
Search: searchDomains,
|
||||
})
|
||||
}
|
||||
return nameservers, nil
|
||||
|
||||
}
|
||||
|
||||
func addNameservers(nameservers, newNameservers []Nameserver) []Nameserver {
|
||||
for _, newNameserver := range newNameservers {
|
||||
found := false
|
||||
for _, nameserver := range nameservers {
|
||||
if nameserver.IP.Equal(newNameserver.IP) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
nameservers = append(nameservers, newNameserver)
|
||||
}
|
||||
}
|
||||
return nameservers
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package environment
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestEnvironment(t *testing.T) {
|
||||
|
||||
nameserversTest, err := getNameserversFromResolvconf()
|
||||
if err != nil {
|
||||
t.Errorf("failed to get namerservers from resolvconf: %s", err)
|
||||
}
|
||||
t.Logf("nameservers from resolvconf: %v", nameserversTest)
|
||||
|
||||
nameserversTest = Nameservers()
|
||||
t.Logf("nameservers: %v", nameserversTest)
|
||||
|
||||
gatewaysTest := Gateways()
|
||||
t.Logf("gateways: %v", gatewaysTest)
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package environment
|
||||
|
||||
import "net"
|
||||
|
||||
func Nameservers() []Nameserver {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Gateways() []*net.IP {
|
||||
return nil
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portmaster/network/netutils"
|
||||
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
// TODO: Create IPv6 version of GetApproximateInternetLocation
|
||||
|
||||
// GetApproximateInternetLocation returns the IP-address of the nearest ping-answering internet node
|
||||
//nolint:gocognit // TODO
|
||||
func GetApproximateInternetLocation() (net.IP, error) {
|
||||
// TODO: first check if we have a public IP
|
||||
// net.InterfaceAddrs()
|
||||
|
||||
// Traceroute example
|
||||
|
||||
dst := net.IPAddr{
|
||||
IP: net.IPv4(1, 1, 1, 1),
|
||||
}
|
||||
|
||||
c, err := net.ListenPacket("ip4:icmp", "0.0.0.0") // ICMP for IPv4
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
p := ipv4.NewPacketConn(c)
|
||||
err = p.SetControlMessage(ipv4.FlagTTL|ipv4.FlagSrc|ipv4.FlagDst|ipv4.FlagInterface, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wm := icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho, Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: os.Getpid() & 0xffff,
|
||||
Data: []byte{0},
|
||||
},
|
||||
}
|
||||
rb := make([]byte, 1500)
|
||||
|
||||
next:
|
||||
for i := 1; i <= 64; i++ { // up to 64 hops
|
||||
repeat:
|
||||
for j := 1; j <= 5; j++ {
|
||||
wm.Body.(*icmp.Echo).Seq = i
|
||||
|
||||
wb, err := wm.Marshal(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = p.SetTTL(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = p.WriteTo(wb, nil, &dst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = p.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// n, cm, peer, err := p.ReadFrom(rb)
|
||||
// readping:
|
||||
for {
|
||||
|
||||
n, _, peer, err := p.ReadFrom(rb)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
continue repeat
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rm, err := icmp.ParseMessage(1, rb[:n])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
switch rm.Type {
|
||||
case ipv4.ICMPTypeTimeExceeded:
|
||||
ip := net.ParseIP(peer.String())
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("failed to parse IP: %s", peer.String())
|
||||
}
|
||||
if !netutils.IPIsLAN(ip) {
|
||||
return ip, nil
|
||||
}
|
||||
continue next
|
||||
case ipv4.ICMPTypeEchoReply:
|
||||
continue next
|
||||
default:
|
||||
// log.Tracef("unknown ICMP message: %+v\n", rm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// +build root
|
||||
|
||||
package environment
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetApproximateInternetLocation(t *testing.T) {
|
||||
ip, err := GetApproximateInternetLocation()
|
||||
if err != nil {
|
||||
t.Errorf("GetApproximateInternetLocation failed: %s", err)
|
||||
}
|
||||
t.Logf("GetApproximateInternetLocation: %s", ip.String())
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
)
|
||||
|
||||
const (
|
||||
networkChangedEvent = "network changed"
|
||||
onlineStatusChangedEvent = "online status changed"
|
||||
)
|
||||
|
||||
var (
|
||||
module *modules.Module
|
||||
)
|
||||
|
||||
// InitSubModule initializes module specific things with the given module. Intended to be used as part of the "network" module.
|
||||
func InitSubModule(m *modules.Module) {
|
||||
module = m
|
||||
module.RegisterEvent(networkChangedEvent)
|
||||
module.RegisterEvent(onlineStatusChangedEvent)
|
||||
}
|
||||
|
||||
// StartSubModule starts module specific things with the given module. Intended to be used as part of the "network" module.
|
||||
func StartSubModule() error {
|
||||
if module == nil {
|
||||
return errors.New("not initialized")
|
||||
}
|
||||
|
||||
module.StartServiceWorker(
|
||||
"monitor network changes",
|
||||
0,
|
||||
monitorNetworkChanges,
|
||||
)
|
||||
|
||||
module.StartServiceWorker(
|
||||
"monitor online status",
|
||||
0,
|
||||
monitorOnlineStatus,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/core"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// setup
|
||||
tmpDir, err := core.InitForTesting()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// setup package
|
||||
netModule := modules.Register("network", nil, nil, nil, "core")
|
||||
InitSubModule(netModule)
|
||||
err = StartSubModule()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// run tests
|
||||
rv := m.Run()
|
||||
|
||||
// teardown
|
||||
core.StopTesting()
|
||||
_ = os.RemoveAll(tmpDir)
|
||||
|
||||
// exit with test run return value
|
||||
os.Exit(rv)
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha1" //nolint:gosec // not used for security
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
)
|
||||
|
||||
var (
|
||||
networkChangeCheckTrigger = make(chan struct{}, 1)
|
||||
)
|
||||
|
||||
func triggerNetworkChangeCheck() {
|
||||
select {
|
||||
case networkChangeCheckTrigger <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func monitorNetworkChanges(ctx context.Context) error {
|
||||
var lastNetworkChecksum []byte
|
||||
|
||||
serviceLoop:
|
||||
for {
|
||||
trigger := false
|
||||
|
||||
// wait for trigger
|
||||
if GetOnlineStatus() == StatusOnline {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-networkChangeCheckTrigger:
|
||||
case <-time.After(1 * time.Minute):
|
||||
trigger = true
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-networkChangeCheckTrigger:
|
||||
case <-time.After(1 * time.Second):
|
||||
trigger = true
|
||||
}
|
||||
}
|
||||
|
||||
// check network for changes
|
||||
// create hashsum of current network config
|
||||
hasher := sha1.New() //nolint:gosec // not used for security
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
log.Warningf("environment: failed to get interfaces: %s", err)
|
||||
continue
|
||||
}
|
||||
for _, iface := range interfaces {
|
||||
_, _ = io.WriteString(hasher, iface.Name)
|
||||
// log.Tracef("adding: %s", iface.Name)
|
||||
_, _ = io.WriteString(hasher, iface.Flags.String())
|
||||
// log.Tracef("adding: %s", iface.Flags.String())
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
log.Warningf("environment: failed to get addrs from interface %s: %s", iface.Name, err)
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
_, _ = io.WriteString(hasher, addr.String())
|
||||
// log.Tracef("adding: %s", addr.String())
|
||||
}
|
||||
}
|
||||
newChecksum := hasher.Sum(nil)
|
||||
|
||||
// compare checksum with last
|
||||
if !bytes.Equal(lastNetworkChecksum, newChecksum) {
|
||||
if len(lastNetworkChecksum) == 0 {
|
||||
lastNetworkChecksum = newChecksum
|
||||
continue serviceLoop
|
||||
}
|
||||
lastNetworkChecksum = newChecksum
|
||||
|
||||
if trigger {
|
||||
triggerOnlineStatusInvestigation()
|
||||
}
|
||||
module.TriggerEvent(networkChangedEvent, nil)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
|
||||
Intel:
|
||||
- First ever request: use first resolver as selected
|
||||
- If resolver fails:
|
||||
- stop all requesting
|
||||
- get network status
|
||||
- if failed: do nothing, return offline error
|
||||
- check list front to back, use first resolver that resolves one.one.one.one correctly
|
||||
|
||||
NetEnv:
|
||||
- check for intercepted HTTP Request requests
|
||||
- if fails on:
|
||||
- connection establishment: OFFLINE
|
||||
-
|
||||
- check for intercepted HTTPS Request requests
|
||||
|
||||
|
||||
- check for intercepted DNS requests
|
||||
@@ -1,353 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/network/netutils"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
// OnlineStatus represent a state of connectivity to the Internet.
|
||||
type OnlineStatus uint8
|
||||
|
||||
// Online Status Values
|
||||
const (
|
||||
StatusUnknown OnlineStatus = 0
|
||||
StatusOffline OnlineStatus = 1
|
||||
StatusLimited OnlineStatus = 2 // local network only
|
||||
StatusPortal OnlineStatus = 3 // there seems to be an internet connection, but we are being intercepted, possibly by a captive portal
|
||||
StatusSemiOnline OnlineStatus = 4 // we seem to online, but without full connectivity
|
||||
StatusOnline OnlineStatus = 5
|
||||
)
|
||||
|
||||
// Online Status and Resolver
|
||||
const (
|
||||
HTTPTestURL = "http://detectportal.firefox.com/success.txt"
|
||||
HTTPExpectedContent = "success"
|
||||
HTTPSTestURL = "https://one.one.one.one/"
|
||||
|
||||
ResolverTestFqdn = "one.one.one.one."
|
||||
ResolverTestRRType = dns.TypeA
|
||||
ResolverTestExpectedResponse = "1.1.1.1"
|
||||
)
|
||||
|
||||
var (
|
||||
parsedHTTPTestURL *url.URL
|
||||
parsedHTTPSTestURL *url.URL
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
parsedHTTPTestURL, err = url.Parse(HTTPTestURL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
parsedHTTPSTestURL, err = url.Parse(HTTPSTestURL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// IsOnlineStatusTestDomain checks whether the given fqdn is used for testing online status.
|
||||
func IsOnlineStatusTestDomain(domain string) bool {
|
||||
switch domain {
|
||||
case "detectportal.firefox.com.":
|
||||
return true
|
||||
case "one.one.one.one.":
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// GetResolverTestingRequestData returns request information that should be used to test DNS resolvers for availability and basic correct behaviour.
|
||||
func GetResolverTestingRequestData() (fqdn string, rrType uint16, expectedResponse string) {
|
||||
return ResolverTestFqdn, ResolverTestRRType, ResolverTestExpectedResponse
|
||||
}
|
||||
|
||||
func (os OnlineStatus) String() string {
|
||||
switch os {
|
||||
default:
|
||||
return "Unknown"
|
||||
case StatusOffline:
|
||||
return "Offline"
|
||||
case StatusLimited:
|
||||
return "Limited"
|
||||
case StatusPortal:
|
||||
return "Portal"
|
||||
case StatusSemiOnline:
|
||||
return "SemiOnline"
|
||||
case StatusOnline:
|
||||
return "Online"
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
onlineStatus *int32
|
||||
onlineStatusQuickCheck = abool.NewBool(false)
|
||||
|
||||
onlineStatusInvestigationTrigger = make(chan struct{}, 1)
|
||||
onlineStatusInvestigationInProgress = abool.NewBool(false)
|
||||
onlineStatusInvestigationWg sync.WaitGroup
|
||||
|
||||
captivePortalURL string
|
||||
captivePortalLock sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
var onlineStatusValue int32
|
||||
onlineStatus = &onlineStatusValue
|
||||
}
|
||||
|
||||
// Online returns true if online status is either SemiOnline or Online.
|
||||
func Online() bool {
|
||||
return onlineStatusQuickCheck.IsSet()
|
||||
}
|
||||
|
||||
// GetOnlineStatus returns the current online stats.
|
||||
func GetOnlineStatus() OnlineStatus {
|
||||
return OnlineStatus(atomic.LoadInt32(onlineStatus))
|
||||
}
|
||||
|
||||
// CheckAndGetOnlineStatus triggers a new online status check and returns the result
|
||||
func CheckAndGetOnlineStatus() OnlineStatus {
|
||||
// trigger new investigation
|
||||
triggerOnlineStatusInvestigation()
|
||||
// wait for completion
|
||||
onlineStatusInvestigationWg.Wait()
|
||||
// return current status
|
||||
return GetOnlineStatus()
|
||||
}
|
||||
|
||||
func updateOnlineStatus(status OnlineStatus, portalURL, comment string) {
|
||||
changed := false
|
||||
|
||||
// status
|
||||
currentStatus := atomic.LoadInt32(onlineStatus)
|
||||
if status != OnlineStatus(currentStatus) && atomic.CompareAndSwapInt32(onlineStatus, currentStatus, int32(status)) {
|
||||
// status changed!
|
||||
onlineStatusQuickCheck.SetTo(
|
||||
status == StatusOnline || status == StatusSemiOnline,
|
||||
)
|
||||
changed = true
|
||||
}
|
||||
|
||||
// captive portal
|
||||
captivePortalLock.Lock()
|
||||
defer captivePortalLock.Unlock()
|
||||
if portalURL != captivePortalURL {
|
||||
captivePortalURL = portalURL
|
||||
changed = true
|
||||
}
|
||||
|
||||
// trigger event
|
||||
if changed {
|
||||
module.TriggerEvent(onlineStatusChangedEvent, nil)
|
||||
if status == StatusPortal {
|
||||
log.Infof(`network: setting online status to %s at "%s" (%s)`, status, captivePortalURL, comment)
|
||||
} else {
|
||||
log.Infof("network: setting online status to %s (%s)", status, comment)
|
||||
}
|
||||
triggerNetworkChangeCheck()
|
||||
}
|
||||
}
|
||||
|
||||
// GetCaptivePortalURL returns the current captive portal url as a string.
|
||||
func GetCaptivePortalURL() string {
|
||||
captivePortalLock.Lock()
|
||||
defer captivePortalLock.Unlock()
|
||||
|
||||
return captivePortalURL
|
||||
}
|
||||
|
||||
// ReportSuccessfulConnection hints the online status monitoring system that a connection attempt was successful.
|
||||
func ReportSuccessfulConnection() {
|
||||
if !onlineStatusQuickCheck.IsSet() {
|
||||
triggerOnlineStatusInvestigation()
|
||||
}
|
||||
}
|
||||
|
||||
// ReportFailedConnection hints the online status monitoring system that a connection attempt has failed. This function has extremely low overhead and may be called as much as wanted.
|
||||
func ReportFailedConnection() {
|
||||
if onlineStatusQuickCheck.IsSet() {
|
||||
triggerOnlineStatusInvestigation()
|
||||
}
|
||||
}
|
||||
|
||||
func triggerOnlineStatusInvestigation() {
|
||||
if onlineStatusInvestigationInProgress.SetToIf(false, true) {
|
||||
onlineStatusInvestigationWg.Add(1)
|
||||
}
|
||||
|
||||
select {
|
||||
case onlineStatusInvestigationTrigger <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func monitorOnlineStatus(ctx context.Context) error {
|
||||
for {
|
||||
// wait for trigger
|
||||
if GetOnlineStatus() == StatusOnline {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-onlineStatusInvestigationTrigger:
|
||||
case <-time.After(1 * time.Minute):
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-onlineStatusInvestigationTrigger:
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
}
|
||||
|
||||
// enable waiting
|
||||
if onlineStatusInvestigationInProgress.SetToIf(false, true) {
|
||||
onlineStatusInvestigationWg.Add(1)
|
||||
}
|
||||
|
||||
checkOnlineStatus(ctx)
|
||||
|
||||
// finished!
|
||||
onlineStatusInvestigationWg.Done()
|
||||
onlineStatusInvestigationInProgress.UnSet()
|
||||
}
|
||||
}
|
||||
|
||||
func checkOnlineStatus(ctx context.Context) {
|
||||
// TODO: implement more methods
|
||||
/*status, err := getConnectivityStateFromDbus()
|
||||
if err != nil {
|
||||
log.Warningf("environment: could not get connectivity: %s", err)
|
||||
setConnectivity(StatusUnknown)
|
||||
return StatusUnknown
|
||||
}*/
|
||||
|
||||
// 1) check for addresses
|
||||
|
||||
ipv4, ipv6, err := GetAssignedAddresses()
|
||||
if err != nil {
|
||||
log.Warningf("network: failed to get assigned network addresses: %s", err)
|
||||
} else {
|
||||
var lan bool
|
||||
for _, ip := range ipv4 {
|
||||
switch netutils.ClassifyIP(ip) {
|
||||
case netutils.SiteLocal:
|
||||
lan = true
|
||||
case netutils.Global:
|
||||
// we _are_ the Internet ;)
|
||||
updateOnlineStatus(StatusOnline, "", "global IPv4 interface detected")
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, ip := range ipv6 {
|
||||
switch netutils.ClassifyIP(ip) {
|
||||
case netutils.SiteLocal, netutils.Global:
|
||||
// IPv6 global addresses are also used in local networks
|
||||
lan = true
|
||||
}
|
||||
}
|
||||
if !lan {
|
||||
updateOnlineStatus(StatusOffline, "", "no local or global interfaces detected")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 2) try a http request
|
||||
|
||||
// TODO: find (array of) alternatives to detectportal.firefox.com
|
||||
// TODO: find something about usage terms of detectportal.firefox.com
|
||||
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
LocalAddr: getLocalAddr("tcp"),
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
DisableKeepAlives: true,
|
||||
DisableCompression: true,
|
||||
WriteBufferSize: 1024,
|
||||
ReadBufferSize: 1024,
|
||||
},
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
request := (&http.Request{
|
||||
Method: "GET",
|
||||
URL: parsedHTTPTestURL,
|
||||
Close: true,
|
||||
}).WithContext(ctx)
|
||||
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
updateOnlineStatus(StatusLimited, "", "http request failed")
|
||||
return
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
// check location
|
||||
portalURL, err := response.Location()
|
||||
if err == nil {
|
||||
updateOnlineStatus(StatusPortal, portalURL.String(), "http request succeeded with redirect")
|
||||
return
|
||||
}
|
||||
|
||||
// read the body
|
||||
data, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
log.Warningf("network: failed to read http body of captive portal testing response: %s", err)
|
||||
// assume we are online nonetheless
|
||||
updateOnlineStatus(StatusOnline, "", "http request succeeded, albeit failing later")
|
||||
return
|
||||
}
|
||||
|
||||
// check body contents
|
||||
if strings.TrimSpace(string(data)) == HTTPExpectedContent {
|
||||
updateOnlineStatus(StatusOnline, "", "http request succeeded")
|
||||
} else {
|
||||
// something is interfering with the website content
|
||||
// this might be a weird captive portal, just direct the user there
|
||||
updateOnlineStatus(StatusPortal, "detectportal.firefox.com", "http request succeeded, response content not as expected")
|
||||
}
|
||||
|
||||
// 3) try a https request
|
||||
|
||||
request = (&http.Request{
|
||||
Method: "HEAD",
|
||||
URL: parsedHTTPSTestURL,
|
||||
Close: true,
|
||||
}).WithContext(ctx)
|
||||
|
||||
// only test if we can get the headers
|
||||
response, err = client.Do(request)
|
||||
if err != nil {
|
||||
// if we fail, something is really weird
|
||||
updateOnlineStatus(StatusSemiOnline, "", "http request failed")
|
||||
return
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
// finally
|
||||
updateOnlineStatus(StatusOnline, "", "all checks successful")
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckOnlineStatus(t *testing.T) {
|
||||
checkOnlineStatus(context.Background())
|
||||
t.Logf("online status: %s", GetOnlineStatus())
|
||||
t.Logf("captive portal: %s", GetCaptivePortalURL())
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package network
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/network/environment"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -10,8 +9,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("network", nil, start, nil, "core")
|
||||
environment.InitSubModule(module)
|
||||
module = modules.Register("network", nil, start, nil, "core", "processes")
|
||||
}
|
||||
|
||||
func start() error {
|
||||
@@ -22,5 +20,5 @@ func start() error {
|
||||
|
||||
go cleaner()
|
||||
|
||||
return environment.StartSubModule()
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user