diff --git a/compat/module.go b/compat/module.go index a6e9da05..fb1b7d1f 100644 --- a/compat/module.go +++ b/compat/module.go @@ -7,6 +7,7 @@ import ( "github.com/safing/portbase/log" "github.com/safing/portbase/modules" "github.com/safing/portmaster/netenv" + "github.com/safing/portmaster/resolver" "github.com/tevino/abool" ) @@ -15,11 +16,25 @@ var ( selfcheckTask *modules.Task selfcheckTaskRetryAfter = 10 * time.Second - selfCheckIsFailing = abool.New() + + // selfCheckIsFailing holds whether or not the self-check is currently + // failing. This helps other failure systems to not make noise when there is + // an underlying failure. + selfCheckIsFailing = abool.New() + + // selfcheckFails counts how often the self check failed successively. + // selfcheckFails is not locked as it is only accessed by the self-check task. + selfcheckFails int ) func init() { module = modules.Register("compat", prep, start, stop, "base", "network", "interception", "netenv", "notifications") + + // Workaround resolver integration. + // See resolver/compat.go for details. + resolver.CompatDNSCheckInternalDomainScope = DNSCheckInternalDomainScope + resolver.CompatSelfCheckIsFailing = SelfCheckIsFailing + resolver.CompatSubmitDNSCheckDomain = SubmitDNSCheckDomain } func prep() error { @@ -55,6 +70,7 @@ func selfcheckTaskFunc(ctx context.Context, task *modules.Task) error { issue, err := selfcheck(ctx) if err == nil { selfCheckIsFailing.UnSet() + selfcheckFails = 0 resetSystemIssue() return nil } @@ -62,14 +78,18 @@ func selfcheckTaskFunc(ctx context.Context, task *modules.Task) error { // Log result. if issue != nil { selfCheckIsFailing.Set() + selfcheckFails++ log.Errorf("compat: %s", err) - issue.notify(err) + if selfcheckFails >= 3 { + issue.notify(err) + } // Retry quicker when failed. task.Schedule(time.Now().Add(selfcheckTaskRetryAfter)) } else { selfCheckIsFailing.UnSet() + selfcheckFails = 0 // Only log internal errors, but don't notify. log.Warningf("compat: %s", err) diff --git a/compat/selfcheck.go b/compat/selfcheck.go index 71fec997..407ccdb2 100644 --- a/compat/selfcheck.go +++ b/compat/selfcheck.go @@ -12,6 +12,7 @@ import ( "github.com/safing/portbase/log" "github.com/safing/portbase/rng" "github.com/safing/portmaster/network/packet" + "github.com/safing/portmaster/resolver" ) var ( @@ -25,7 +26,7 @@ var ( systemIntegrationCheckPackets = make(chan packet.Packet, 1) systemIntegrationCheckWaitDuration = 3 * time.Second - DNSCheckInternalDomainScope string + DNSCheckInternalDomainScope = ".self-check." + resolver.InternalSpecialUseDomain dnsCheckReceivedDomain = make(chan string, 1) dnsCheckWaitDuration = 3 * time.Second dnsCheckAnswerLock sync.Mutex diff --git a/firewall/interception.go b/firewall/interception.go index b90f697f..4691d447 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -376,26 +376,32 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) { conn.Process().Profile() != nil && conn.Process().Profile().UseSPN() { - // Exclude requests of the SPN itself. - if !captain.IsExcepted(conn.Entity.IP) { - conn.Tunneled = true + switch { + case captain.ClientBootstrapping() && + conn.Process().Pid == ownPID: + // Exclude the Portmaster during SPN bootstrapping. - // Check if client is ready. - if captain.ClientReady() { - // Queue request in sluice. - err := sluice.AwaitRequest(conn, crew.HandleSluiceRequest) - if err != nil { - log.Tracer(pkt.Ctx()).Warningf("failed to rqeuest tunneling: %s", err) - conn.Failed("failed to request tunneling", "") - } else { - log.Tracer(pkt.Ctx()).Trace("filter: tunneling requested") - conn.Verdict = network.VerdictRerouteToTunnel - } + case captain.IsExcepted(conn.Entity.IP) && + conn.Process().Pid == ownPID: + // Exclude requests of the SPN itself. + + case captain.ClientReady(): + // Queue request in sluice. + err := sluice.AwaitRequest(conn, crew.HandleSluiceRequest) + if err != nil { + log.Tracer(pkt.Ctx()).Warningf("failed to rqeuest tunneling: %s", err) + conn.Failed("failed to request tunneling", "") } else { - // Block connection as SPN is not ready yet. - log.Tracer(pkt.Ctx()).Trace("SPN not ready for tunneling") - conn.Failed("SPN not ready for tunneling", "") + log.Tracer(pkt.Ctx()).Trace("filter: tunneling requested") + conn.Verdict = network.VerdictRerouteToTunnel + conn.Tunneled = true } + + default: + // Block connection as SPN is not ready yet. + log.Tracer(pkt.Ctx()).Trace("SPN not ready for tunneling") + conn.Failed("SPN not ready for tunneling", "") + } } diff --git a/netenv/online-status.go b/netenv/online-status.go index a1e452a3..c3f77219 100644 --- a/netenv/online-status.go +++ b/netenv/online-status.go @@ -404,6 +404,7 @@ func checkOnlineStatus(ctx context.Context) { if ConnectedToSPN.IsSet() { updateOnlineStatus(StatusOnline, nil, "connected to SPN") + return } // 1) check for addresses diff --git a/network/state/lookup.go b/network/state/lookup.go index fdd8c3d1..86bf76c1 100644 --- a/network/state/lookup.go +++ b/network/state/lookup.go @@ -125,7 +125,7 @@ func (table *tcpTable) findSocket(pktInfo *packet.Info) ( for _, socketInfo := range table.listeners { if localPort == socketInfo.Local.Port && (socketInfo.ListensAny || localIP.Equal(socketInfo.Local.IP)) { - return socketInfo, false + return socketInfo, true } } diff --git a/resolver/compat.go b/resolver/compat.go new file mode 100644 index 00000000..3397667e --- /dev/null +++ b/resolver/compat.go @@ -0,0 +1,12 @@ +package resolver + +import "net" + +// This is a workaround for enabling the resolver to work with the compat +// module without importing it. Long-term, the network module should not import +// the resolver package, as this breaks the SPN hub. +var ( + CompatDNSCheckInternalDomainScope string + CompatSelfCheckIsFailing func() bool + CompatSubmitDNSCheckDomain func(subdomain string) (respondWith net.IP) +) diff --git a/resolver/resolve.go b/resolver/resolve.go index 9e9644a7..0206e09d 100644 --- a/resolver/resolve.go +++ b/resolver/resolve.go @@ -11,7 +11,6 @@ import ( "github.com/safing/portbase/database" "github.com/safing/portbase/log" - "github.com/safing/portmaster/compat" "github.com/safing/portmaster/netenv" ) @@ -407,7 +406,7 @@ resolveLoop: err = fmt.Errorf("all %d query-compliant resolvers failed, last error: %s", len(resolvers), err) if primarySource == ServerSourceConfigured && - netenv.Online() && compat.SelfCheckIsFailing() { + netenv.Online() && CompatSelfCheckIsFailing() { notifyAboutFailingResolvers(err) } else { resetFailingResolversNotification() diff --git a/resolver/resolver-env.go b/resolver/resolver-env.go index 87d47d6e..79142de2 100644 --- a/resolver/resolver-env.go +++ b/resolver/resolver-env.go @@ -8,16 +8,15 @@ import ( "github.com/miekg/dns" "github.com/safing/portbase/log" - "github.com/safing/portmaster/compat" "github.com/safing/portmaster/netenv" "github.com/safing/portmaster/network/netutils" ) const ( - internalSpecialUseDomain = "portmaster.home.arpa." + InternalSpecialUseDomain = "portmaster.home.arpa." - routerDomain = "router.local." + internalSpecialUseDomain - captivePortalDomain = "captiveportal.local." + internalSpecialUseDomain + routerDomain = "router.local." + InternalSpecialUseDomain + captivePortalDomain = "captiveportal.local." + InternalSpecialUseDomain ) var ( @@ -38,11 +37,10 @@ var ( func prepEnvResolver() (err error) { netenv.SpecialCaptivePortalDomain = captivePortalDomain - compat.DNSCheckInternalDomainScope = ".self-check." + internalSpecialUseDomain internalSpecialUseSOA, err = dns.NewRR(fmt.Sprintf( "%s 17 IN SOA localhost. none.localhost. 0 0 0 0 0", - internalSpecialUseDomain, + InternalSpecialUseDomain, )) if err != nil { return err @@ -50,7 +48,7 @@ func prepEnvResolver() (err error) { internalSpecialUseComment, err = dns.NewRR(fmt.Sprintf( `%s 17 IN TXT "This is a special use TLD of the Portmaster."`, - internalSpecialUseDomain, + InternalSpecialUseDomain, )) return err } @@ -94,9 +92,9 @@ func (er *envResolverConn) Query(ctx context.Context, q *Query) (*RRCache, error // Check for suffix matches. switch { - case strings.HasSuffix(q.FQDN, compat.DNSCheckInternalDomainScope): - subdomain := strings.TrimSuffix(q.FQDN, compat.DNSCheckInternalDomainScope) - respondWith := compat.SubmitDNSCheckDomain(subdomain) + case strings.HasSuffix(q.FQDN, CompatDNSCheckInternalDomainScope): + subdomain := strings.TrimSuffix(q.FQDN, CompatDNSCheckInternalDomainScope) + respondWith := CompatSubmitDNSCheckDomain(subdomain) // We'll get an A record. Only respond if it's an A question. if respondWith != nil && uint16(q.QType) == dns.TypeA { @@ -110,7 +108,7 @@ func (er *envResolverConn) Query(ctx context.Context, q *Query) (*RRCache, error } case dns.TypeSOA: // Direct query for the SOA record. - if q.FQDN == internalSpecialUseDomain { + if q.FQDN == InternalSpecialUseDomain { return er.makeRRCache(q, []dns.RR{internalSpecialUseSOA}), nil } } diff --git a/resolver/scopes.go b/resolver/scopes.go index a291db22..1412f47c 100644 --- a/resolver/scopes.go +++ b/resolver/scopes.go @@ -26,7 +26,7 @@ var ( // Internal Special-Use Domain // Used by Portmaster for special addressing. internalSpecialUseDomains = []string{ - "." + internalSpecialUseDomain, + "." + InternalSpecialUseDomain, } // Multicast DNS