From bbf9d7ca721181af15946eaeb6e312835d9c1762 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 17 Nov 2021 14:13:16 +0100 Subject: [PATCH] Improve online status and location checks --- netenv/location.go | 31 +++++++++----------- netenv/online-status.go | 63 +++++++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 25 deletions(-) diff --git a/netenv/location.go b/netenv/location.go index a80c510c..d4c03604 100644 --- a/netenv/location.go +++ b/netenv/location.go @@ -241,7 +241,7 @@ func addLocation(dl *DeviceLocation) { func GetApproximateInternetLocation() (net.IP, error) { loc, ok := GetInternetLocation() if !ok || loc.Best() == nil { - return nil, errors.New("no location data available") + return nil, errors.New("no device location data available") } return loc.Best().IP, nil } @@ -259,7 +259,7 @@ func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) { // Get all assigned addresses. v4s, v6s, err := GetAssignedAddresses() if err != nil { - log.Warningf("netenv: failed to get assigned addresses: %s", err) + log.Warningf("netenv: failed to get assigned addresses for device location: %s", err) return nil, false } @@ -267,27 +267,24 @@ func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) { v4ok, v6ok := getLocationFromInterfaces() // Try other methods for missing locations. - if len(v4s) > 0 { - if !v4ok { - _, err = getLocationFromTraceroute() - if err != nil { - log.Warningf("netenv: failed to get IPv4 from traceroute: %s", err) - } else { - v4ok = true - } + if len(v4s) > 0 && !v4ok { + _, err = getLocationFromTraceroute() + if err != nil { + log.Warningf("netenv: failed to get IPv4 device location from traceroute: %s", err) + } else { + v4ok = true } + + // Get location from timezone as final fallback. if !v4ok { - v4ok = getLocationFromTimezone(packet.IPv4) + getLocationFromTimezone(packet.IPv4) } } if len(v6s) > 0 && !v6ok { - // TODO - log.Warningf("netenv: could not get IPv6 location") - } + // TODO: Find more ways to get IPv6 device location - // Check if we have any locations. - if !v4ok && !v6ok { - return nil, false + // Get location from timezone as final fallback. + getLocationFromTimezone(packet.IPv6) } // Return gathered locations. diff --git a/netenv/online-status.go b/netenv/online-status.go index db36a805..ddad55e2 100644 --- a/netenv/online-status.go +++ b/netenv/online-status.go @@ -145,6 +145,7 @@ var ( onlineStatusInvestigationTrigger = make(chan struct{}, 1) onlineStatusInvestigationInProgress = abool.NewBool(false) onlineStatusInvestigationWg sync.WaitGroup + onlineStatusNotification *notifications.Notification captivePortal = &CaptivePortal{} captivePortalLock sync.Mutex @@ -186,7 +187,7 @@ func CheckAndGetOnlineStatus() OnlineStatus { func updateOnlineStatus(status OnlineStatus, portalURL *url.URL, comment string) { changed := false - // status + // Update online status. currentStatus := atomic.LoadInt32(onlineStatus) if status != OnlineStatus(currentStatus) && atomic.CompareAndSwapInt32(onlineStatus, currentStatus, int32(status)) { // status changed! @@ -196,10 +197,10 @@ func updateOnlineStatus(status OnlineStatus, portalURL *url.URL, comment string) changed = true } - // captive portal + // Update captive portal. setCaptivePortal(portalURL) - // trigger event + // Trigger events. if changed { module.TriggerEvent(OnlineStatusChangedEvent, status) if status == StatusPortal { @@ -209,6 +210,9 @@ func updateOnlineStatus(status OnlineStatus, portalURL *url.URL, comment string) } triggerNetworkChangeCheck() + // Notify user. + notifyOnlineStatus(status) + // Trigger update check when coming (semi) online. if Online() { _ = updates.TriggerUpdate(false) @@ -216,11 +220,54 @@ func updateOnlineStatus(status OnlineStatus, portalURL *url.URL, comment string) } } +func notifyOnlineStatus(status OnlineStatus) { + var eventID, title, message string + + // Check if status is worth notifying. + switch status { + case StatusOffline: + eventID = "netenv:online-status:offline" + title = "Device is Offline" + message = "Portmaster did not detect any network connectivity." + case StatusLimited: + eventID = "netenv:online-status:limited" + title = "Limited network connectivity." + message = "Portmaster did detect local network connectivity, but could not detect connectivity to the Internet." + default: + // Delete notification, if present. + if onlineStatusNotification != nil { + onlineStatusNotification.Delete() + onlineStatusNotification = nil + } + return + } + + // Update notification if not present or online status changed. + switch { + case onlineStatusNotification == nil: + // Continue creating new notification. + case onlineStatusNotification.EventID == eventID: + // Notification stays the same, stick with the old one. + return + default: + // Delete old notification before triggering updated one. + onlineStatusNotification.Delete() + } + + // Create update status notification. + onlineStatusNotification = notifications.Notify(¬ifications.Notification{ + EventID: eventID, + Type: notifications.Info, + Title: title, + Message: message, + }) +} + func setCaptivePortal(portalURL *url.URL) { captivePortalLock.Lock() defer captivePortalLock.Unlock() - // delete + // Delete captive portal if no url is supplied. if portalURL == nil { captivePortal = &CaptivePortal{} if captivePortalNotification != nil { @@ -230,12 +277,12 @@ func setCaptivePortal(portalURL *url.URL) { return } - // return if unchanged - if portalURL.String() == captivePortal.URL { + // Only set captive portal once per detection. + if captivePortal.URL != "" { return } - // set + // Compile captive portal data. captivePortal = &CaptivePortal{ URL: portalURL.String(), } @@ -247,7 +294,7 @@ func setCaptivePortal(portalURL *url.URL) { captivePortal.Domain = portalURL.Hostname() } - // notify + // Notify user about portal. captivePortalNotification = notifications.Notify(¬ifications.Notification{ EventID: "netenv:captive-portal", Type: notifications.Info,