mirror of
https://github.com/safing/portmaster
synced 2025-04-25 13:29:10 +00:00
Add compatibility assistant module
This commit is contained in:
parent
3193cd35b9
commit
113f37dcab
15 changed files with 557 additions and 6 deletions
29
compat/api.go
Normal file
29
compat/api.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package compat
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/api"
|
||||
)
|
||||
|
||||
func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: "compat/self-check",
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
ActionFunc: selfcheckViaAPI,
|
||||
Name: "Run Integration Self-Check",
|
||||
Description: "Runs a couple integration self-checks in order to see if the system integration works.",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func selfcheckViaAPI(ar *api.Request) (msg string, err error) {
|
||||
_, err = selfcheck(ar.Context())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "self-check successful", nil
|
||||
}
|
36
compat/callbacks.go
Normal file
36
compat/callbacks.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package compat
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
"github.com/safing/portmaster/process"
|
||||
)
|
||||
|
||||
func SubmitSystemIntegrationCheckPacket(p packet.Packet) {
|
||||
select {
|
||||
case systemIntegrationCheckPackets <- p:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func SubmitDNSCheckDomain(subdomain string) (respondWith net.IP) {
|
||||
// Submit queried domain.
|
||||
select {
|
||||
case dnsCheckReceivedDomain <- subdomain:
|
||||
default:
|
||||
}
|
||||
|
||||
// Return the answer.
|
||||
dnsCheckAnswerLock.Lock()
|
||||
defer dnsCheckAnswerLock.Unlock()
|
||||
return dnsCheckAnswer
|
||||
}
|
||||
|
||||
func ReportSecureDNSBypassIssue(p *process.Process) {
|
||||
secureDNSBypassIssue.notify(p)
|
||||
}
|
||||
|
||||
func ReportMultiPeerUDPTunnelIssue(p *process.Process) {
|
||||
multiPeerUDPTunnelIssue.notify(p)
|
||||
}
|
82
compat/module.go
Normal file
82
compat/module.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package compat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/netenv"
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
var (
|
||||
module *modules.Module
|
||||
|
||||
selfcheckTask *modules.Task
|
||||
selfcheckTaskRetryAfter = 10 * time.Second
|
||||
selfCheckIsFailing = abool.New()
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("compat", prep, start, stop, "base", "network", "interception", "netenv", "notifications")
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
return registerAPIEndpoints()
|
||||
}
|
||||
|
||||
func start() error {
|
||||
selfcheckTask = module.NewTask("compatibility self-check", selfcheckTaskFunc).
|
||||
Repeat(1 * time.Minute).
|
||||
StartASAP()
|
||||
|
||||
return module.RegisterEventHook(
|
||||
netenv.ModuleName,
|
||||
netenv.NetworkChangedEvent,
|
||||
"trigger compat self-check",
|
||||
func(_ context.Context, _ interface{}) error {
|
||||
selfcheckTask.StartASAP()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func stop() error {
|
||||
selfcheckTask.Cancel()
|
||||
selfcheckTask = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func selfcheckTaskFunc(ctx context.Context, task *modules.Task) error {
|
||||
// Run selfcheck and return if successful.
|
||||
issue, err := selfcheck(ctx)
|
||||
if err == nil {
|
||||
selfCheckIsFailing.UnSet()
|
||||
resetSystemIssue()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Log result.
|
||||
if issue != nil {
|
||||
selfCheckIsFailing.Set()
|
||||
|
||||
log.Errorf("compat: %s", err)
|
||||
issue.notify(err)
|
||||
|
||||
// Retry quicker when failed.
|
||||
task.Schedule(time.Now().Add(selfcheckTaskRetryAfter))
|
||||
} else {
|
||||
selfCheckIsFailing.UnSet()
|
||||
|
||||
// Only log internal errors, but don't notify.
|
||||
log.Warningf("compat: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SelfCheckIsFailing() bool {
|
||||
return selfCheckIsFailing.IsSet()
|
||||
}
|
159
compat/notify.go
Normal file
159
compat/notify.go
Normal file
|
@ -0,0 +1,159 @@
|
|||
package compat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/notifications"
|
||||
"github.com/safing/portmaster/process"
|
||||
)
|
||||
|
||||
type baseIssue struct {
|
||||
id string
|
||||
title string
|
||||
message string
|
||||
level notifications.Type
|
||||
}
|
||||
|
||||
type systemIssue baseIssue
|
||||
|
||||
type appIssue baseIssue
|
||||
|
||||
var (
|
||||
systemIssueNotification *notifications.Notification
|
||||
systemIssueNotificationLock sync.Mutex
|
||||
|
||||
systemIntegrationIssue = &systemIssue{
|
||||
id: "compat:system-integration-issue",
|
||||
title: "Detected System Integration Issue",
|
||||
message: "Portmaster detected a problem with its system integration. You can try to restart or reinstall the Portmaster. If that does not help, please report the issue via [GitHub](https://github.com/safing/portmaster/issues) or send a mail to [support@safing.io](mailto:support@safing.io) so we can help you out.",
|
||||
level: notifications.Error,
|
||||
}
|
||||
systemCompatibilityIssue = &systemIssue{
|
||||
id: "compat:compatibility-issue",
|
||||
title: "Detected Compatibility Issue",
|
||||
message: "Portmaster detected that something is interfering with its operation. This could be a VPN, an Anti-Virus or another network protection software. Please check if you are running an incompatible [VPN client](https://docs.safing.io/portmaster/install/status/vpn-compatibility) or [software](https://docs.safing.io/portmaster/install/status/software-compatibility). Otherwise, please report the issue via [GitHub](https://github.com/safing/portmaster/issues) or send a mail to [support@safing.io](mailto:support@safing.io) so we can help you out.",
|
||||
level: notifications.Error,
|
||||
}
|
||||
|
||||
secureDNSBypassIssue = &appIssue{
|
||||
id: "compat:secure-dns-bypass-%s",
|
||||
title: "Detected %s Bypass Attempt",
|
||||
message: "Portmaster detected that %s is trying to use a secure DNS resolver. While this is a good thing, the Portmaster already handles secure DNS for your whole device. Please disable the secure DNS resolver within the app.",
|
||||
// TODO: Add this when the new docs page is finished:
|
||||
// , or [find out about other options](link to new docs page)
|
||||
level: notifications.Warning,
|
||||
}
|
||||
multiPeerUDPTunnelIssue = &appIssue{
|
||||
id: "compat:multi-peer-udp-tunnel-%s",
|
||||
title: "Detected SPN Incompatibility in %s",
|
||||
message: "Portmaster detected that %s is trying to connect to multiple servers via the SPN using a single UDP connection. This is common for technologies such as torrents. Unfortunately, the SPN does not support this feature currently. You can try to change this behavior within the affected app or you could exempt it from using the SPN.",
|
||||
level: notifications.Warning,
|
||||
}
|
||||
)
|
||||
|
||||
func (issue *systemIssue) notify(err error) {
|
||||
systemIssueNotificationLock.Lock()
|
||||
defer systemIssueNotificationLock.Unlock()
|
||||
|
||||
if systemIssueNotification != nil {
|
||||
// Ignore duplicate notification.
|
||||
if issue.id == systemIssueNotification.EventID {
|
||||
return
|
||||
}
|
||||
|
||||
// Remove old notification.
|
||||
systemIssueNotification.Delete()
|
||||
}
|
||||
|
||||
// Create new notification.
|
||||
n := ¬ifications.Notification{
|
||||
EventID: issue.id,
|
||||
Type: issue.level,
|
||||
Title: issue.title,
|
||||
Message: issue.message,
|
||||
ShowOnSystem: true,
|
||||
}
|
||||
notifications.Notify(n)
|
||||
|
||||
systemIssueNotification = n
|
||||
n.AttachToModule(module)
|
||||
|
||||
// Report the raw error as module error.
|
||||
module.NewErrorMessage("selfcheck", err).Report()
|
||||
}
|
||||
|
||||
func resetSystemIssue() {
|
||||
systemIssueNotificationLock.Lock()
|
||||
defer systemIssueNotificationLock.Unlock()
|
||||
|
||||
if systemIssueNotification != nil {
|
||||
systemIssueNotification.Delete()
|
||||
}
|
||||
systemIssueNotification = nil
|
||||
}
|
||||
|
||||
func (issue *appIssue) notify(p *process.Process) {
|
||||
// Get profile from process.
|
||||
profile := p.Profile().LocalProfile()
|
||||
if profile == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Log warning.
|
||||
log.Warningf(
|
||||
"compat: detected %s issue with %s",
|
||||
strings.ReplaceAll(
|
||||
strings.TrimPrefix(
|
||||
strings.TrimSuffix(issue.id, "-%d"),
|
||||
"compat:",
|
||||
),
|
||||
"-", " ",
|
||||
),
|
||||
p.Path,
|
||||
)
|
||||
|
||||
// Check if we already have this notification.
|
||||
eventID := fmt.Sprintf(issue.id, profile.ID)
|
||||
n := notifications.Get(eventID)
|
||||
if n != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, create a new one.
|
||||
n = ¬ifications.Notification{
|
||||
EventID: eventID,
|
||||
Type: issue.level,
|
||||
Title: fmt.Sprintf(issue.title, profile.Name),
|
||||
Message: fmt.Sprintf(issue.message, profile.Name),
|
||||
ShowOnSystem: true,
|
||||
AvailableActions: []*notifications.Action{
|
||||
{
|
||||
ID: "ack",
|
||||
Text: "OK",
|
||||
},
|
||||
},
|
||||
}
|
||||
notifications.Notify(n)
|
||||
|
||||
// Set warning on profile.
|
||||
module.StartWorker("set app compat warning", func(ctx context.Context) error {
|
||||
func() {
|
||||
profile.Lock()
|
||||
defer profile.Unlock()
|
||||
|
||||
profile.Warning = fmt.Sprintf(
|
||||
"%s \nThis was last detected at %s.",
|
||||
fmt.Sprintf(issue.message, p.Name),
|
||||
time.Now().Format("15:04 on 2.1.2006"),
|
||||
)
|
||||
profile.WarningLastUpdated = time.Now()
|
||||
}()
|
||||
|
||||
return profile.Save()
|
||||
})
|
||||
}
|
193
compat/selfcheck.go
Normal file
193
compat/selfcheck.go
Normal file
|
@ -0,0 +1,193 @@
|
|||
package compat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
)
|
||||
|
||||
var (
|
||||
selfcheckLock sync.Mutex
|
||||
|
||||
SystemIntegrationCheckDstIP = net.IPv4(127, 65, 67, 75)
|
||||
SystemIntegrationCheckProtocol = packet.AnyHostInternalProtocol61
|
||||
|
||||
systemIntegrationCheckDialNet = fmt.Sprintf("ip4:%d", uint8(SystemIntegrationCheckProtocol))
|
||||
systemIntegrationCheckDialIP = SystemIntegrationCheckDstIP.String()
|
||||
systemIntegrationCheckPackets = make(chan packet.Packet, 1)
|
||||
systemIntegrationCheckWaitDuration = 3 * time.Second
|
||||
|
||||
DNSCheckInternalDomainScope string
|
||||
dnsCheckReceivedDomain = make(chan string, 1)
|
||||
dnsCheckWaitDuration = 3 * time.Second
|
||||
dnsCheckAnswerLock sync.Mutex
|
||||
dnsCheckAnswer net.IP
|
||||
|
||||
DNSTestDomain = "one.one.one.one."
|
||||
DNSTestExpectedIP = net.IPv4(1, 1, 1, 1)
|
||||
)
|
||||
|
||||
func selfcheck(ctx context.Context) (issue *systemIssue, err error) {
|
||||
selfcheckLock.Lock()
|
||||
defer selfcheckLock.Unlock()
|
||||
|
||||
// Step 1: Check if the system integration sees a packet.
|
||||
|
||||
// Empty recv channel.
|
||||
select {
|
||||
case <-systemIntegrationCheckPackets:
|
||||
case <-ctx.Done():
|
||||
return nil, context.Canceled
|
||||
default:
|
||||
}
|
||||
|
||||
// Send packet.
|
||||
conn, err := net.DialTimeout(
|
||||
systemIntegrationCheckDialNet,
|
||||
systemIntegrationCheckDialIP,
|
||||
time.Second,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create system integration conn: %w", err)
|
||||
}
|
||||
_, err = conn.Write([]byte("SELF-CHECK"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send system integration packet: %w", err)
|
||||
}
|
||||
|
||||
// Wait for packet.
|
||||
select {
|
||||
case <-systemIntegrationCheckPackets:
|
||||
// Check passed!
|
||||
log.Tracef("compat: self-check #1: system integration check passed")
|
||||
case <-time.After(systemIntegrationCheckWaitDuration):
|
||||
return systemIntegrationIssue, fmt.Errorf("self-check #1: system integration check failed: did not receive test packet after %s", systemIntegrationCheckWaitDuration)
|
||||
case <-ctx.Done():
|
||||
return nil, context.Canceled
|
||||
}
|
||||
|
||||
// Step 2: Check if a DNS request arrives at the nameserver
|
||||
// This step necessary also includes some setup for step 3.
|
||||
|
||||
// Generate random subdomain.
|
||||
randomSubdomainBytes, err := rng.Bytes(16)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("self-check #2: failed to get random bytes for subdomain check: %w", err)
|
||||
}
|
||||
randomSubdomain := "a" + strings.ToLower(hex.EncodeToString(randomSubdomainBytes)) + "b"
|
||||
|
||||
// Generate random answer.
|
||||
var B, C, D uint64
|
||||
B, err = rng.Number(255)
|
||||
if err == nil {
|
||||
C, err = rng.Number(255)
|
||||
}
|
||||
if err == nil {
|
||||
D, err = rng.Number(255)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("self-check #2: failed to get random number for subdomain check response: %w", err)
|
||||
}
|
||||
randomAnswer := net.IPv4(127, byte(B), byte(C), byte(D))
|
||||
func() {
|
||||
dnsCheckAnswerLock.Lock()
|
||||
defer dnsCheckAnswerLock.Unlock()
|
||||
dnsCheckAnswer = randomAnswer
|
||||
}()
|
||||
|
||||
// Setup variables for lookup worker.
|
||||
var (
|
||||
dnsCheckReturnedIP net.IP
|
||||
dnsCheckLookupError = make(chan error)
|
||||
)
|
||||
|
||||
// Empty recv channel.
|
||||
select {
|
||||
case <-dnsCheckReceivedDomain:
|
||||
case <-ctx.Done():
|
||||
return nil, context.Canceled
|
||||
default:
|
||||
}
|
||||
|
||||
// Start worker for the DNS lookup.
|
||||
module.StartWorker("dns check lookup", func(_ context.Context) error {
|
||||
ips, err := net.LookupIP(randomSubdomain + DNSCheckInternalDomainScope)
|
||||
if err == nil && len(ips) > 0 {
|
||||
dnsCheckReturnedIP = ips[0]
|
||||
}
|
||||
select {
|
||||
case dnsCheckLookupError <- err:
|
||||
case <-time.After(dnsCheckWaitDuration * 2):
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
// Wait for the resolver to receive the query.
|
||||
select {
|
||||
case receivedTestDomain := <-dnsCheckReceivedDomain:
|
||||
if receivedTestDomain != randomSubdomain {
|
||||
return systemCompatibilityIssue, fmt.Errorf("self-check #2: dns integration check failed: received unmatching subdomain %q", receivedTestDomain)
|
||||
}
|
||||
case <-time.After(dnsCheckWaitDuration):
|
||||
return systemCompatibilityIssue, fmt.Errorf("self-check #2: dns integration check failed: did not receive test query after %s", dnsCheckWaitDuration)
|
||||
}
|
||||
log.Tracef("compat: self-check #2: dns integration query check passed")
|
||||
|
||||
// Step 3: Have the nameserver respond with random data in the answer section.
|
||||
|
||||
// Wait for the reply from the resolver.
|
||||
select {
|
||||
case err := <-dnsCheckLookupError:
|
||||
if err != nil {
|
||||
return systemCompatibilityIssue, fmt.Errorf("self-check #3: dns integration check failed: failed to receive test response: %w", err)
|
||||
}
|
||||
case <-time.After(dnsCheckWaitDuration):
|
||||
return systemCompatibilityIssue, fmt.Errorf("self-check #3: dns integration check failed: did not receive test response after %s", dnsCheckWaitDuration)
|
||||
case <-ctx.Done():
|
||||
return nil, context.Canceled
|
||||
}
|
||||
|
||||
// Check response.
|
||||
if !dnsCheckReturnedIP.Equal(randomAnswer) {
|
||||
return systemCompatibilityIssue, fmt.Errorf("self-check #3: dns integration check failed: received unmatching response %q", dnsCheckReturnedIP)
|
||||
}
|
||||
log.Tracef("compat: self-check #3: dns integration response check passed")
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
* Check if the system integration sees a packet:
|
||||
* Send raw IP packet with random content and protocol, report finding to compat module.
|
||||
* use `Dial("ip4:61", "127.65.67.75")`.
|
||||
* Firewall reports back the data seen on `ip4:61` to IP `127.65.67.75`.
|
||||
* If this fails, the system integration is broken. -> Integration Issue
|
||||
* Check if a DNS request arrives at the nameserver:
|
||||
* Send A question for `[random-subdomain].self-check.portmaster.home.arpa.`.
|
||||
* Nameserver reports back the data seen.
|
||||
* If this fails, redirection to the nameserver fails.
|
||||
* This means there is another software interfering with DNS. -> Compatibility Issue
|
||||
* Have the nameserver respond with random data in the answer section.
|
||||
* Compat provides nameserver with random response data.
|
||||
* Compat module checks if the received data matches.
|
||||
* If this fails, redirection to the nameserver fails.
|
||||
* This means there is another software interfering with DNS on the return path. -> Compatibility Issue
|
||||
* DROPPED: If resolvers are reported failing, but we are online:
|
||||
* Send out plain DNS requests to one.one.one.one. and dns.quad9.net via the Go standard lookup and check if the responses are correct.
|
||||
* If not, something is blocking the Portmaster -> Secure DNS Issue
|
||||
* Discuss if this is necessary:
|
||||
* Does this improve from only having a failed TCP connection to the resolver?
|
||||
* Could another program block port 853, but fully leave requests for one.one.one.one. to port 53 alone?
|
||||
|
||||
*/
|
|
@ -27,7 +27,7 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("core", prep, start, nil, "base", "subsystems", "status", "updates", "api", "notifications", "ui", "netenv", "network", "interception")
|
||||
module = modules.Register("core", prep, start, nil, "base", "subsystems", "status", "updates", "api", "notifications", "ui", "netenv", "network", "interception", "compat")
|
||||
subsystems.Register(
|
||||
"core",
|
||||
"Core",
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/safing/portmaster/compat"
|
||||
|
||||
"github.com/safing/portmaster/nameserver/nsutil"
|
||||
"github.com/safing/portmaster/network"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
|
@ -30,6 +32,7 @@ func PreventBypassing(ctx context.Context, conn *network.Connection) (endpoints.
|
|||
// Make an exception for ICMP, as these IPs are also often used for debugging.
|
||||
default:
|
||||
if conn.Entity.MatchLists(resolverFilterLists) {
|
||||
compat.ReportSecureDNSBypassIssue(conn.Process())
|
||||
return endpoints.Denied,
|
||||
"blocked rogue connection to DNS resolver",
|
||||
nsutil.BlockIP()
|
||||
|
|
|
@ -170,6 +170,11 @@ func FilterResolvedDNS(
|
|||
return nil
|
||||
}
|
||||
|
||||
// Don't filter env responses.
|
||||
if rrCache.Resolver.Type == resolver.ServerTypeEnv {
|
||||
return rrCache
|
||||
}
|
||||
|
||||
// special grant for connectivity domains
|
||||
if checkConnectivityDomain(ctx, conn, layeredProfile, nil) {
|
||||
// returns true if check triggered
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portmaster/compat"
|
||||
|
||||
"github.com/safing/spn/captain"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
|
@ -314,6 +316,13 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
|||
_ = pkt.PermanentAccept()
|
||||
return true
|
||||
}
|
||||
|
||||
case compat.SystemIntegrationCheckProtocol:
|
||||
if pkt.Info().Dst.Equal(compat.SystemIntegrationCheckDstIP) {
|
||||
compat.SubmitSystemIntegrationCheckPacket(pkt)
|
||||
_ = pkt.Drop()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
1
go.mod
1
go.mod
|
@ -18,6 +18,7 @@ require (
|
|||
github.com/klauspost/reedsolomon v1.9.13 // indirect
|
||||
github.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267 // indirect
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/mr-tron/base58 v1.2.0
|
||||
github.com/oschwald/maxminddb-golang v1.8.0
|
||||
github.com/safing/portbase v0.12.3
|
||||
github.com/safing/spn v0.3.6
|
||||
|
|
|
@ -30,6 +30,8 @@ const (
|
|||
ICMPv6 = IPProtocol(58)
|
||||
UDPLite = IPProtocol(136)
|
||||
RAW = IPProtocol(255)
|
||||
|
||||
AnyHostInternalProtocol61 = IPProtocol(61)
|
||||
)
|
||||
|
||||
// Verdicts
|
||||
|
|
|
@ -94,7 +94,12 @@ func parseIGMP(packet gopacket.Packet, info *Info) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func checkError(packet gopacket.Packet, _ *Info) error {
|
||||
func checkError(packet gopacket.Packet, info *Info) error {
|
||||
// Check for known unparseable before checking the error layer.
|
||||
if info.Protocol == AnyHostInternalProtocol61 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := packet.ErrorLayer(); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
|
|
@ -68,9 +68,16 @@ type Profile struct { //nolint:maligned // not worth the effort
|
|||
// Name is a human readable name of the profile. It
|
||||
// defaults to the basename of the application.
|
||||
Name string
|
||||
// Description may holds an optional description of the
|
||||
// Description may hold an optional description of the
|
||||
// profile or the purpose of the application.
|
||||
Description string
|
||||
// Warning may hold an optional warning about this application.
|
||||
// It may be static or be added later on when the Portmaster detected an
|
||||
// issue with the application.
|
||||
Warning string
|
||||
// WarningLastUpdated holds the timestamp when the Warning field was last
|
||||
// updated.
|
||||
WarningLastUpdated time.Time
|
||||
// Homepage may refer the the website of the application
|
||||
// vendor.
|
||||
Homepage string
|
||||
|
|
|
@ -112,7 +112,7 @@ func getSpecialProfile(profileID, linkedPath string) *Profile {
|
|||
},
|
||||
)
|
||||
// Add description to tell users about the quirks of this profile.
|
||||
systemResolverProfile.Description = `The System DNS Client is a system service that requires special handling. For regular network connections, the configured settings will apply as usual, but DNS requests coming from the System DNS Client are handled in a special way, as they could actually be coming from any other application on the system.
|
||||
systemResolverProfile.Warning = `The System DNS Client is a system service that requires special handling. For regular network connections, the configured settings will apply as usual, but DNS requests coming from the System DNS Client are handled in a special way, as they could actually be coming from any other application on the system.
|
||||
|
||||
In order to respect the app settings of the actual application, DNS requests from the System DNS Client are only subject to the following settings:
|
||||
|
||||
|
@ -179,7 +179,7 @@ func specialProfileNeedsReset(profile *Profile) bool {
|
|||
|
||||
switch profile.ID {
|
||||
case SystemResolverProfileID:
|
||||
return canBeUpgraded(profile, "1.6.2021")
|
||||
return canBeUpgraded(profile, "18.11.2021")
|
||||
case PortmasterAppProfileID:
|
||||
return canBeUpgraded(profile, "8.9.2021")
|
||||
default:
|
||||
|
|
|
@ -4,15 +4,17 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"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 = "17.home.arpa."
|
||||
internalSpecialUseDomain = "portmaster.home.arpa."
|
||||
|
||||
routerDomain = "router.local." + internalSpecialUseDomain
|
||||
captivePortalDomain = "captiveportal.local." + internalSpecialUseDomain
|
||||
|
@ -36,6 +38,7 @@ 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",
|
||||
|
@ -57,6 +60,7 @@ type envResolverConn struct{}
|
|||
func (er *envResolverConn) Query(ctx context.Context, q *Query) (*RRCache, error) {
|
||||
switch uint16(q.QType) {
|
||||
case dns.TypeA, dns.TypeAAAA: // We respond with all IPv4/6 addresses we can find.
|
||||
// Check for exact matches.
|
||||
switch q.FQDN {
|
||||
case captivePortalDomain:
|
||||
// Get IP address of the captive portal.
|
||||
|
@ -86,7 +90,23 @@ func (er *envResolverConn) Query(ctx context.Context, q *Query) (*RRCache, error
|
|||
return er.nxDomain(q), nil
|
||||
}
|
||||
return er.makeRRCache(q, records), nil
|
||||
}
|
||||
|
||||
// Check for suffix matches.
|
||||
switch {
|
||||
case strings.HasSuffix(q.FQDN, compat.DNSCheckInternalDomainScope):
|
||||
subdomain := strings.TrimSuffix(q.FQDN, compat.DNSCheckInternalDomainScope)
|
||||
respondWith := compat.SubmitDNSCheckDomain(subdomain)
|
||||
|
||||
// We'll get an A record. Only respond if it's an A question.
|
||||
if respondWith != nil && uint16(q.QType) == dns.TypeA {
|
||||
records, err := netutils.IPsToRRs(q.FQDN, []net.IP{respondWith})
|
||||
if err != nil {
|
||||
log.Warningf("nameserver: failed to create dns check response to %s: %s", q.FQDN, err)
|
||||
return er.nxDomain(q), nil
|
||||
}
|
||||
return er.makeRRCache(q, records), nil
|
||||
}
|
||||
}
|
||||
case dns.TypeSOA:
|
||||
// Direct query for the SOA record.
|
||||
|
|
Loading…
Add table
Reference in a new issue