mirror of
https://github.com/safing/portmaster
synced 2025-09-02 18:49:14 +00:00
Calm down and relax dns query check
This commit is contained in:
parent
ea1b189330
commit
e178b732bc
4 changed files with 94 additions and 44 deletions
|
@ -34,7 +34,7 @@ func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Pa
|
||||||
case conn.Process().Pid == ownPID:
|
case conn.Process().Pid == ownPID:
|
||||||
// Bypass tunneling for certain own connections.
|
// Bypass tunneling for certain own connections.
|
||||||
switch {
|
switch {
|
||||||
case captain.ClientBootstrapping():
|
case !captain.ClientReady():
|
||||||
return
|
return
|
||||||
case captain.IsExcepted(conn.Entity.IP):
|
case captain.IsExcepted(conn.Entity.IP):
|
||||||
return
|
return
|
||||||
|
|
|
@ -37,13 +37,12 @@ var (
|
||||||
PortalTestIP = net.IPv4(192, 0, 2, 1)
|
PortalTestIP = net.IPv4(192, 0, 2, 1)
|
||||||
PortalTestURL = fmt.Sprintf("http://%s/", PortalTestIP)
|
PortalTestURL = fmt.Sprintf("http://%s/", PortalTestIP)
|
||||||
|
|
||||||
DNSTestDomain = "one.one.one.one."
|
DNSTestDomain = "online-check.safing.io."
|
||||||
DNSTestExpectedIP = net.IPv4(1, 1, 1, 1)
|
DNSTestExpectedIP = net.IPv4(0, 65, 67, 75) // Ascii: \0ACK
|
||||||
|
DNSTestQueryFunc func(ctx context.Context, fdqn string) (ips []net.IP, ok bool, err error)
|
||||||
DNSFallbackTestDomain = "dns-check.safing.io."
|
|
||||||
DNSFallbackTestExpectedIP = net.IPv4(0, 65, 67, 75) // Ascii: \0ACK
|
|
||||||
|
|
||||||
ConnectedToSPN = abool.New()
|
ConnectedToSPN = abool.New()
|
||||||
|
ConnectedToDNS = abool.New()
|
||||||
|
|
||||||
// SpecialCaptivePortalDomain is the domain name used to point to the detected captive portal IP
|
// SpecialCaptivePortalDomain is the domain name used to point to the detected captive portal IP
|
||||||
// or the captive portal test IP. The default value should be overridden by the resolver package,
|
// or the captive portal test IP. The default value should be overridden by the resolver package,
|
||||||
|
@ -53,8 +52,6 @@ var (
|
||||||
// ConnectivityDomains holds all connectivity domains. This slice must not be modified.
|
// ConnectivityDomains holds all connectivity domains. This slice must not be modified.
|
||||||
ConnectivityDomains = []string{
|
ConnectivityDomains = []string{
|
||||||
SpecialCaptivePortalDomain,
|
SpecialCaptivePortalDomain,
|
||||||
DNSTestDomain, // Internal DNS Check
|
|
||||||
DNSFallbackTestDomain, // Internal DNS Check
|
|
||||||
|
|
||||||
// Windows
|
// Windows
|
||||||
"dns.msftncsi.com.", // DNS Check
|
"dns.msftncsi.com.", // DNS Check
|
||||||
|
@ -380,20 +377,20 @@ func monitorOnlineStatus(ctx context.Context) error {
|
||||||
func getDynamicStatusTrigger() <-chan time.Time {
|
func getDynamicStatusTrigger() <-chan time.Time {
|
||||||
switch GetOnlineStatus() {
|
switch GetOnlineStatus() {
|
||||||
case StatusOffline:
|
case StatusOffline:
|
||||||
// Will be triggered by network change anyway.
|
// Will also be triggered by network change.
|
||||||
return time.After(20 * time.Second)
|
return time.After(10 * time.Second)
|
||||||
case StatusLimited, StatusPortal:
|
case StatusLimited, StatusPortal:
|
||||||
// Change will not be detected otherwise, but impact is minor.
|
// Change will not be detected otherwise, but impact is minor.
|
||||||
return time.After(5 * time.Second)
|
return time.After(5 * time.Second)
|
||||||
case StatusSemiOnline:
|
case StatusSemiOnline:
|
||||||
// Very small impact.
|
// Very small impact.
|
||||||
return time.After(20 * time.Second)
|
return time.After(60 * time.Second)
|
||||||
case StatusOnline:
|
case StatusOnline:
|
||||||
// Don't check until resolver reports problems.
|
// Don't check until resolver reports problems.
|
||||||
return nil
|
return nil
|
||||||
case StatusUnknown:
|
case StatusUnknown:
|
||||||
return time.After(5 * time.Second)
|
fallthrough
|
||||||
default: // other unknown status
|
default:
|
||||||
return time.After(5 * time.Minute)
|
return time.After(5 * time.Minute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -407,13 +404,18 @@ func checkOnlineStatus(ctx context.Context) {
|
||||||
return StatusUnknown
|
return StatusUnknown
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// 0) check if connected to SPN
|
// 0) check if connected to SPN and/or DNS.
|
||||||
|
|
||||||
if ConnectedToSPN.IsSet() {
|
if ConnectedToSPN.IsSet() {
|
||||||
updateOnlineStatus(StatusOnline, nil, "connected to SPN")
|
updateOnlineStatus(StatusOnline, nil, "connected to SPN")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ConnectedToDNS.IsSet() {
|
||||||
|
updateOnlineStatus(StatusOnline, nil, "connected to DNS")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 1) check for addresses
|
// 1) check for addresses
|
||||||
|
|
||||||
ipv4, ipv6, err := GetAssignedAddresses()
|
ipv4, ipv6, err := GetAssignedAddresses()
|
||||||
|
@ -508,34 +510,28 @@ func checkOnlineStatus(ctx context.Context) {
|
||||||
|
|
||||||
// 3) resolve a query
|
// 3) resolve a query
|
||||||
|
|
||||||
// Check with primary dns check domain.
|
// Check if we can resolve the dns check domain.
|
||||||
ips, err := net.LookupIP(DNSTestDomain)
|
if DNSTestQueryFunc == nil {
|
||||||
if err != nil {
|
updateOnlineStatus(StatusOnline, nil, "all checks passed, dns query check disabled")
|
||||||
log.Warningf("netenv: dns check query failed: %s", err)
|
return
|
||||||
} else {
|
}
|
||||||
// check for expected response
|
ips, ok, err := DNSTestQueryFunc(ctx, DNSTestDomain)
|
||||||
for _, ip := range ips {
|
switch {
|
||||||
if ip.Equal(DNSTestExpectedIP) {
|
case ok && err != nil:
|
||||||
|
updateOnlineStatus(StatusOnline, nil, fmt.Sprintf(
|
||||||
|
"all checks passed, acceptable result for dns query check: %s",
|
||||||
|
err,
|
||||||
|
))
|
||||||
|
case ok && len(ips) >= 1 && ips[0].Equal(DNSTestExpectedIP):
|
||||||
updateOnlineStatus(StatusOnline, nil, "all checks passed")
|
updateOnlineStatus(StatusOnline, nil, "all checks passed")
|
||||||
return
|
case ok && len(ips) >= 1:
|
||||||
|
log.Warningf("netenv: dns query check response mismatched: got %s", ips[0])
|
||||||
|
updateOnlineStatus(StatusOnline, nil, "all checks passed, dns query check response mismatched")
|
||||||
|
case ok:
|
||||||
|
log.Warningf("netenv: dns query check response mismatched: empty response")
|
||||||
|
updateOnlineStatus(StatusOnline, nil, "all checks passed, dns query check response was empty")
|
||||||
|
default:
|
||||||
|
log.Warningf("netenv: dns query check failed: %s", err)
|
||||||
|
updateOnlineStatus(StatusOffline, nil, "dns query check failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If that did not work, check with fallback dns check domain.
|
|
||||||
ips, err = net.LookupIP(DNSFallbackTestDomain)
|
|
||||||
if err != nil {
|
|
||||||
log.Warningf("netenv: dns fallback check query failed: %s", err)
|
|
||||||
updateOnlineStatus(StatusLimited, nil, "dns fallback check query failed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// check for expected response
|
|
||||||
for _, ip := range ips {
|
|
||||||
if ip.Equal(DNSFallbackTestExpectedIP) {
|
|
||||||
updateOnlineStatus(StatusOnline, nil, "all checks passed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// unexpected response
|
|
||||||
updateOnlineStatus(StatusSemiOnline, nil, "dns check query response mismatched")
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -318,8 +319,8 @@ func resolveAndCache(ctx context.Context, q *Query, oldCache *RRCache) (rrCache
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we are online
|
// check if we are online
|
||||||
if primarySource != ServerSourceEnv && netenv.GetOnlineStatus() == netenv.StatusOffline {
|
if netenv.GetOnlineStatus() == netenv.StatusOffline && primarySource != ServerSourceEnv {
|
||||||
if !netenv.IsConnectivityDomain(q.FQDN) {
|
if q.FQDN != netenv.DNSTestDomain && !netenv.IsConnectivityDomain(q.FQDN) {
|
||||||
// we are offline and this is not an online check query
|
// we are offline and this is not an online check query
|
||||||
return oldCache, ErrOffline
|
return oldCache, ErrOffline
|
||||||
}
|
}
|
||||||
|
@ -358,6 +359,7 @@ resolveLoop:
|
||||||
// some resolvers might also block
|
// some resolvers might also block
|
||||||
return nil, err
|
return nil, err
|
||||||
case netenv.GetOnlineStatus() == netenv.StatusOffline &&
|
case netenv.GetOnlineStatus() == netenv.StatusOffline &&
|
||||||
|
q.FQDN != netenv.DNSTestDomain &&
|
||||||
!netenv.IsConnectivityDomain(q.FQDN):
|
!netenv.IsConnectivityDomain(q.FQDN):
|
||||||
// we are offline and this is not an online check query
|
// we are offline and this is not an online check query
|
||||||
return oldCache, ErrOffline
|
return oldCache, ErrOffline
|
||||||
|
@ -478,3 +480,45 @@ func shouldResetCache(q *Query) (reset bool) {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
netenv.DNSTestQueryFunc = testConnectivity
|
||||||
|
}
|
||||||
|
|
||||||
|
// testConnectivity test if resolving a query succeeds and returns whether the
|
||||||
|
// query itself succeeded, separate from interpreting the result.
|
||||||
|
func testConnectivity(ctx context.Context, fdqn string) (ips []net.IP, ok bool, err error) {
|
||||||
|
q := &Query{
|
||||||
|
FQDN: fdqn,
|
||||||
|
QType: dns.Type(dns.TypeA),
|
||||||
|
NoCaching: true,
|
||||||
|
}
|
||||||
|
if !q.check() {
|
||||||
|
return nil, false, ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
rrCache, err := resolveAndCache(ctx, q, nil)
|
||||||
|
switch {
|
||||||
|
case err == nil:
|
||||||
|
switch rrCache.RCode {
|
||||||
|
case dns.RcodeNameError:
|
||||||
|
return nil, true, ErrNotFound
|
||||||
|
case dns.RcodeRefused:
|
||||||
|
return nil, true, errors.New("refused")
|
||||||
|
default:
|
||||||
|
ips := rrCache.ExportAllARecords()
|
||||||
|
if len(ips) > 0 {
|
||||||
|
return ips, true, nil
|
||||||
|
}
|
||||||
|
return nil, true, ErrNotFound
|
||||||
|
}
|
||||||
|
case errors.Is(err, ErrNotFound):
|
||||||
|
return nil, true, err
|
||||||
|
case errors.Is(err, ErrBlocked):
|
||||||
|
return nil, true, err
|
||||||
|
case errors.Is(err, ErrNoCompliance):
|
||||||
|
return nil, true, err
|
||||||
|
default:
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -206,8 +206,8 @@ func (brc *BasicResolverConn) init() {
|
||||||
|
|
||||||
// ReportFailure reports that an error occurred with this resolver.
|
// ReportFailure reports that an error occurred with this resolver.
|
||||||
func (brc *BasicResolverConn) ReportFailure() {
|
func (brc *BasicResolverConn) ReportFailure() {
|
||||||
|
// Don't mark resolver as failed if we are offline.
|
||||||
if !netenv.Online() {
|
if !netenv.Online() {
|
||||||
// don't mark failed if we are offline
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,6 +224,11 @@ func (brc *BasicResolverConn) ReportFailure() {
|
||||||
// the fail.
|
// the fail.
|
||||||
brc.networkChangedFlag.Refresh()
|
brc.networkChangedFlag.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Report to netenv that a configured server failed.
|
||||||
|
if brc.resolver.Info.Source == ServerSourceConfigured {
|
||||||
|
netenv.ConnectedToDNS.UnSet()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsFailing returns if this resolver is currently failing.
|
// IsFailing returns if this resolver is currently failing.
|
||||||
|
@ -255,4 +260,9 @@ func (brc *BasicResolverConn) ResetFailure() {
|
||||||
defer brc.failLock.Unlock()
|
defer brc.failLock.Unlock()
|
||||||
brc.fails = 0
|
brc.fails = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Report to netenv that a configured server succeeded.
|
||||||
|
if brc.resolver.Info.Source == ServerSourceConfigured {
|
||||||
|
netenv.ConnectedToDNS.Set()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue