Merge pull request #1272 from safing/fix/connection-handling

Fix connection handlings
This commit is contained in:
Daniel Hovie 2023-08-04 22:22:08 +02:00 committed by GitHub
commit 93756e5fd8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 211 additions and 137 deletions

View file

@ -16,7 +16,7 @@ var (
module *modules.Module
selfcheckTask *modules.Task
selfcheckTaskRetryAfter = 5 * time.Second
selfcheckTaskRetryAfter = 15 * time.Second
// selfCheckIsFailing holds whether or not the self-check is currently
// failing. This helps other failure systems to not make noise when there is
@ -34,7 +34,7 @@ var (
// selfcheckFailThreshold holds the threshold of how many times the selfcheck
// must fail before it is reported.
const selfcheckFailThreshold = 5
const selfcheckFailThreshold = 10
func init() {
module = modules.Register("compat", prep, start, stop, "base", "network", "interception", "netenv", "notifications")
@ -60,7 +60,7 @@ func start() error {
Schedule(time.Now().Add(selfcheckTaskRetryAfter))
module.NewTask("clean notify thresholds", cleanNotifyThreshold).
Repeat(10 * time.Minute)
Repeat(1 * time.Hour)
return module.RegisterEventHook(
netenv.ModuleName,

View file

@ -236,7 +236,7 @@ func (issue *appIssue) notify(proc *process.Process) {
}
const (
notifyThresholdMinIncidents = 11
notifyThresholdMinIncidents = 10
notifyThresholdResetAfter = 2 * time.Minute
)

View file

@ -28,12 +28,12 @@ var (
systemIntegrationCheckDialNet = fmt.Sprintf("ip4:%d", uint8(SystemIntegrationCheckProtocol))
systemIntegrationCheckDialIP = SystemIntegrationCheckDstIP.String()
systemIntegrationCheckPackets = make(chan packet.Packet, 1)
systemIntegrationCheckWaitDuration = 40 * time.Second
systemIntegrationCheckWaitDuration = 45 * time.Second
// DNSCheckInternalDomainScope is the domain scope to use for dns checks.
DNSCheckInternalDomainScope = ".self-check." + resolver.InternalSpecialUseDomain
dnsCheckReceivedDomain = make(chan string, 1)
dnsCheckWaitDuration = 40 * time.Second
dnsCheckWaitDuration = 45 * time.Second
dnsCheckAnswerLock sync.Mutex
dnsCheckAnswer net.IP
)

View file

@ -224,7 +224,7 @@ func authorizeApp(ar *api.Request) (interface{}, error) {
Title: "An app requests access to the Portmaster",
Message: "Allow " + appName + " (" + proc.Profile().LocalProfile().Name + ") to query and modify the Portmaster?\n\nBinary: " + proc.Path,
ShowOnSystem: true,
Expires: time.Now().Add(time.Minute).UnixNano(),
Expires: time.Now().Add(time.Minute).Unix(),
AvailableActions: []*notifications.Action{
{
ID: "allow",

View file

@ -45,6 +45,7 @@ func filterDNSSection(
ip = v.AAAA
default:
// add non A/AAAA entries
// TODO: Add support for dns.SVCB and dns.HTTPS
goodEntries = append(goodEntries, rr)
continue
}
@ -257,6 +258,32 @@ func UpdateIPsAndCNAMEs(q *resolver.Query, rrCache *resolver.RRCache, conn *netw
case *dns.AAAA:
ips = append(ips, v.AAAA)
case *dns.SVCB:
if len(v.Target) >= 2 { // Ignore "" and ".".
cnames[v.Hdr.Name] = v.Target
}
for _, pair := range v.Value {
switch svcbParam := pair.(type) {
case *dns.SVCBIPv4Hint:
ips = append(ips, svcbParam.Hint...)
case *dns.SVCBIPv6Hint:
ips = append(ips, svcbParam.Hint...)
}
}
case *dns.HTTPS:
if len(v.Target) >= 2 { // Ignore "" and ".".
cnames[v.Hdr.Name] = v.Target
}
for _, pair := range v.Value {
switch svcbParam := pair.(type) {
case *dns.SVCBIPv4Hint:
ips = append(ips, svcbParam.Hint...)
case *dns.SVCBIPv6Hint:
ips = append(ips, svcbParam.Hint...)
}
}
}
}

View file

@ -102,8 +102,8 @@ int BPF_PROG(udp_v4_connect, struct sock *sk) {
return 0;
}
// Read PID
udp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid());
// Read PID (Careful: This is the Thread Group ID in kernel speak!)
udp_info->pid = __builtin_bswap32((u32)(bpf_get_current_pid_tgid() >> 32));
// Set src and dist ports
udp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num);
@ -151,8 +151,8 @@ int BPF_PROG(udp_v6_connect, struct sock *sk) {
return 0;
}
// Read PID
udp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid());
// Read PID (Careful: This is the Thread Group ID in kernel speak!)
udp_info->pid = __builtin_bswap32((u32)(bpf_get_current_pid_tgid() >> 32));
// Set src and dist ports
udp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num);

View file

@ -628,9 +628,9 @@ func bandwidthUpdateHandler(ctx context.Context) error {
return nil
case bwUpdate := <-interception.BandwidthUpdates:
if bwUpdate != nil {
updateBandwidth(ctx, bwUpdate)
// DEBUG:
// log.Debugf("filter: bandwidth update: %s", bwUpdate)
updateBandwidth(ctx, bwUpdate)
} else {
return errors.New("received nil bandwidth update from interception")
}
@ -653,6 +653,8 @@ func updateBandwidth(ctx context.Context, bwUpdate *packet.BandwidthUpdate) {
// Do not wait for connections that are locked.
// TODO: Use atomic operations for updating bandwidth stats.
if !conn.TryLock() {
// DEBUG:
// log.Warningf("filter: failed to lock connection for bandwidth update: %s", conn)
return
}
defer conn.Unlock()
@ -675,7 +677,7 @@ func updateBandwidth(ctx context.Context, bwUpdate *packet.BandwidthUpdate) {
if err := netquery.DefaultModule.Store.UpdateBandwidth(
ctx,
conn.HistoryEnabled,
conn.Process().GetID(),
conn.Process().GetKey(),
conn.ID,
conn.BytesReceived,
conn.BytesSent,

18
go.mod
View file

@ -1,11 +1,11 @@
module github.com/safing/portmaster
go 1.19
go 1.20
require (
github.com/agext/levenshtein v1.2.3
github.com/cilium/ebpf v0.11.0
github.com/coreos/go-iptables v0.6.0
github.com/coreos/go-iptables v0.7.0
github.com/florianl/go-conntrack v0.4.0
github.com/florianl/go-nfqueue v1.3.1
github.com/ghodss/yaml v1.0.0
@ -16,9 +16,9 @@ require (
github.com/hashicorp/go-version v1.6.0
github.com/jackc/puddle/v2 v2.2.1
github.com/miekg/dns v1.1.55
github.com/oschwald/maxminddb-golang v1.11.0
github.com/oschwald/maxminddb-golang v1.12.0
github.com/safing/jess v0.3.1
github.com/safing/portbase v0.17.0
github.com/safing/portbase v0.17.1
github.com/safing/portmaster-android/go v0.0.0-20230605085256-6abf4c495626
github.com/safing/spn v0.6.10
github.com/shirou/gopsutil v3.21.11+incompatible
@ -28,10 +28,10 @@ require (
github.com/tannerryan/ring v1.1.2
github.com/tevino/abool v1.2.0
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
golang.org/x/net v0.12.0
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b
golang.org/x/net v0.13.0
golang.org/x/sync v0.3.0
golang.org/x/sys v0.10.0
golang.org/x/sys v0.11.0
zombiezen.com/go/sqlite v0.13.0
)
@ -89,12 +89,12 @@ require (
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.11.0 // indirect
golang.org/x/tools v0.11.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5 // indirect
modernc.org/libc v1.24.1 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.6.0 // indirect
modernc.org/sqlite v1.24.0 // indirect
modernc.org/sqlite v1.25.0 // indirect
)

30
go.sum
View file

@ -36,8 +36,8 @@ github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@ -186,8 +186,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -208,6 +208,8 @@ github.com/safing/portbase v0.15.2/go.mod h1:5bHi99fz7Hh/wOsZUOI631WF9ePSHk57c4f
github.com/safing/portbase v0.16.2/go.mod h1:mzNCWqPbO7vIYbbK5PElGbudwd2vx4YPNawymL8Aro8=
github.com/safing/portbase v0.17.0 h1:RsDzbCGxRIbgaArri3y7MZskfxytEvvkzJpiboDUERQ=
github.com/safing/portbase v0.17.0/go.mod h1:eKCRqsfMFLVhNpd2sY/fKvnbuk+LrIYnQEZCg1i86Ho=
github.com/safing/portbase v0.17.1 h1:q2aNHjJw4aoqTqKOxZpxRhYCciHw1exZ7lfGuB78i1E=
github.com/safing/portbase v0.17.1/go.mod h1:1cVgDZIsPiqM5b+K88Kshir5PGIvsftYkx7y1x925+8=
github.com/safing/portmaster-android/go v0.0.0-20230605085256-6abf4c495626 h1:olc/REnUdpJN/Gmz8B030OxLpMYxyPDTrDILNEw0eKs=
github.com/safing/portmaster-android/go v0.0.0-20230605085256-6abf4c495626/go.mod h1:abwyAQrZGemWbSh/aCD9nnkp0SvFFf/mGWkAbOwPnFE=
github.com/safing/spn v0.6.10 h1:4fFBb7UvUzoCcOSd8immOz1Buiuasy5C1/lxfVFacBQ=
@ -307,8 +309,8 @@ golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI=
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -338,8 +340,8 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -391,8 +393,8 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -408,8 +410,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc=
golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -438,7 +440,7 @@ modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.24.0 h1:EsClRIWHGhLTCX44p+Ri/JLD+vFGo0QGjasg2/F9TlI=
modernc.org/sqlite v1.24.0/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
zombiezen.com/go/sqlite v0.13.0 h1:iEeyVqcm3fk5PCA8OQBhBxPnqrP4yYuVJBF+XZpSnOE=
zombiezen.com/go/sqlite v0.13.0/go.mod h1:Ht/5Rg3Ae2hoyh1I7gbWtWAl89CNocfqeb/aAMTkJr4=

View file

@ -60,6 +60,7 @@ type Entity struct { //nolint:maligned
IP net.IP
// IPScope holds the network scope of the IP.
// For DNS requests, this signifies in which scope the DNS request was resolved.
IPScope netutils.IPScope
// Country holds the country the IP address (ASN) is

View file

@ -22,7 +22,7 @@ import (
var hostname string
func handleRequestAsWorker(w dns.ResponseWriter, query *dns.Msg) {
err := module.RunWorker("dns request", func(ctx context.Context) error {
err := module.RunWorker("handle dns request", func(ctx context.Context) error {
return handleRequest(ctx, w, query)
})
if err != nil {
@ -187,6 +187,13 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
if rrCache != nil {
conn.DNSContext = rrCache.ToDNSRequestContext()
conn.Resolver = rrCache.Resolver
conn.Entity.IPScope = rrCache.Resolver.IPScope
} else {
// Get resolvers for this query to determine the resolving scope.
resolvers, _, _ := resolver.GetResolversInScope(ctx, q)
if len(resolvers) > 0 {
conn.Entity.IPScope = resolvers[0].Info.IPScope
}
}
switch conn.Verdict.Active {
@ -297,11 +304,14 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
return reply(nsutil.ServerFailure("internal error: empty reply"))
case rrCache.RCode == dns.RcodeNameError:
// Try alternatives domain names for unofficial domain spaces.
rrCache = checkAlternativeCaches(ctx, q)
if rrCache == nil {
altRRCache := checkAlternativeCaches(ctx, q)
if altRRCache != nil {
rrCache = altRRCache
} else {
// Return now if NXDomain.
return reply(nsutil.NxDomain("no answer found (NXDomain)"))
}
}
// Check with firewall again after resolving.

View file

@ -2,8 +2,6 @@ package netquery
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
@ -395,12 +393,8 @@ func (db *Database) MarkAllHistoryConnectionsEnded(ctx context.Context) error {
// UpdateBandwidth updates bandwidth data for the connection and optionally also writes
// the bandwidth data to the history database.
func (db *Database) UpdateBandwidth(ctx context.Context, enableHistory bool, processKey string, connID string, bytesReceived uint64, bytesSent uint64) error {
data := connID + "-" + processKey
hash := sha256.Sum256([]byte(data))
dbConnID := hex.EncodeToString(hash[:])
params := map[string]any{
":id": dbConnID,
":id": makeNqIDFromParts(processKey, connID),
}
parts := []string{}
@ -481,6 +475,12 @@ func (db *Database) Save(ctx context.Context, conn Conn, enableHistory bool) err
// and save some CPU cycles for the user
dbNames := []DatabaseName{LiveDatabase}
// TODO: Should we only add ended connection to the history database to save
// a couple INSERTs per connection?
// This means we need to write the current live DB to the history DB on
// shutdown in order to be able to pick the back up after a restart.
// Save to history DB if enabled.
if enableHistory {
dbNames = append(dbNames, HistoryDatabase)
}

View file

@ -100,24 +100,30 @@ func (mng *Manager) HandleFeed(ctx context.Context, feed <-chan *network.Connect
if !ok {
return
}
func() {
conn.Lock()
defer conn.Unlock()
if !conn.DataIsComplete() {
continue
return
}
model, err := convertConnection(conn)
if err != nil {
log.Errorf("netquery: failed to convert connection %s to sqlite model: %s", conn.ID, err)
continue
return
}
// DEBUG:
// log.Tracef("netquery: updating connection %s", conn.ID)
if err := mng.store.Save(ctx, *model, conn.HistoryEnabled); err != nil {
// Save to netquery database.
// Do not include internal connections in history.
if err := mng.store.Save(ctx, *model, conn.HistoryEnabled && !conn.Internal); err != nil {
log.Errorf("netquery: failed to save connection %s in sqlite database: %s", conn.ID, err)
continue
return
}
// we clone the record metadata from the connection
@ -129,6 +135,7 @@ func (mng *Manager) HandleFeed(ctx context.Context, feed <-chan *network.Connect
if err := mng.pushConnUpdate(ctx, *cloned, *model); err != nil {
log.Errorf("netquery: failed to push update for conn %s via database system: %s", conn.ID, err)
}
}()
}
}
}
@ -155,18 +162,16 @@ func (mng *Manager) pushConnUpdate(_ context.Context, meta record.Meta, conn Con
}
// convertConnection converts conn to the local representation used
// to persist the information in SQLite. convertConnection attempts
// to lock conn and may thus block for some time.
// to persist the information in SQLite.
// The caller must hold the lock to the given network.Connection.
func convertConnection(conn *network.Connection) (*Conn, error) {
conn.Lock()
defer conn.Unlock()
direction := "outbound"
if conn.Inbound {
direction = "inbound"
}
c := Conn{
ID: genConnID(conn),
ID: makeNqIDFromConn(conn),
External: conn.External,
IPVersion: conn.IPVersion,
IPProtocol: conn.IPProtocol,
@ -265,6 +270,13 @@ func convertConnection(conn *network.Connection) (*Conn, error) {
return &c, nil
}
func genConnID(conn *network.Connection) string {
return conn.ID + "-" + conn.Process().GetID()
// makeNqIDFromConn creates a netquery connection ID from the network connection.
func makeNqIDFromConn(conn *network.Connection) string {
return makeNqIDFromParts(conn.Process().GetKey(), conn.ID)
}
// makeNqIDFromParts creates a netquery connection ID from the given network
// connection ID and the process key.
func makeNqIDFromParts(processKey string, netConnID string) string {
return processKey + "-" + netConnID
}

View file

@ -89,8 +89,8 @@ func (m *module) prepare() error {
if err := api.RegisterEndpoint(api.Endpoint{
Path: "netquery/query",
MimeType: "application/json",
Read: api.PermitUser,
Write: api.PermitUser,
Read: api.PermitUser, // Needs read+write as the query is sent using POST data.
Write: api.PermitUser, // Needs read+write as the query is sent using POST data.
BelongsTo: m.Module,
HandlerFunc: queryHander.ServeHTTP,
Name: "Query Connections",
@ -102,7 +102,6 @@ func (m *module) prepare() error {
if err := api.RegisterEndpoint(api.Endpoint{
Path: "netquery/charts/connection-active",
MimeType: "application/json",
Read: api.PermitUser,
Write: api.PermitUser,
BelongsTo: m.Module,
HandlerFunc: chartHandler.ServeHTTP,
@ -116,7 +115,6 @@ func (m *module) prepare() error {
Path: "netquery/history/clear",
MimeType: "application/json",
Write: api.PermitUser,
Read: api.PermitUser,
BelongsTo: m.Module,
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
var body struct {

View file

@ -327,6 +327,7 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
Entity: &intel.Entity{
Domain: fqdn,
CNAME: cnames,
IPScope: netutils.Global, // Assign a global IP scope as default.
},
process: proc,
ProcessContext: getProcessContext(ctx, proc),
@ -369,6 +370,7 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname
Entity: &intel.Entity{
Domain: fqdn,
CNAME: cnames,
IPScope: netutils.Global, // Assign a global IP scope as default.
},
process: remoteHost,
ProcessContext: getProcessContext(ctx, remoteHost),
@ -782,15 +784,6 @@ func (conn *Connection) SetFirewallHandler(handler FirewallHandler) {
return
}
// Start packet handler worker when first handler is set.
if conn.firewallHandler == nil {
// start handling
module.StartWorker("packet handler", conn.packetHandlerWorker)
}
// Set new handler.
conn.firewallHandler = handler
// Initialize packet queue, if needed.
conn.pktQueueLock.Lock()
defer conn.pktQueueLock.Unlock()
@ -798,6 +791,14 @@ func (conn *Connection) SetFirewallHandler(handler FirewallHandler) {
conn.pktQueue = make(chan packet.Packet, 100)
conn.pktQueueActive = true
}
// Start packet handler worker when new handler is set.
if conn.firewallHandler == nil {
module.StartWorker("packet handler", conn.packetHandlerWorker)
}
// Set new handler.
conn.firewallHandler = handler
}
// UpdateFirewallHandler sets the firewall handler if it already set and the

View file

@ -8,6 +8,7 @@ import (
"time"
"github.com/miekg/dns"
"golang.org/x/exp/slices"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/nameserver/nsutil"
@ -18,6 +19,13 @@ import (
var (
openDNSRequests = make(map[string]*Connection) // key: <pid>/fqdn
openDNSRequestsLock sync.Mutex
supportedDomainToIPRecordTypes = []uint16{
dns.TypeA,
dns.TypeAAAA,
dns.TypeSVCB,
dns.TypeHTTPS,
}
)
const (
@ -30,6 +38,13 @@ const (
openDNSRequestLimit = 3 * time.Second
)
// IsSupportDNSRecordType returns whether the given DSN RR type is supported
// by the network package, as in the requests are specially handled and can be
// "merged" into the resulting connection.
func IsSupportDNSRecordType(rrType uint16) bool {
return slices.Contains[[]uint16, uint16](supportedDomainToIPRecordTypes, rrType)
}
func getDNSRequestCacheKey(pid int, fqdn string, qType uint16) string {
return strconv.Itoa(pid) + "/" + fqdn + dns.Type(qType).String()
}
@ -39,28 +54,28 @@ func removeOpenDNSRequest(pid int, fqdn string) {
defer openDNSRequestsLock.Unlock()
// Delete PID-specific requests.
delete(openDNSRequests, getDNSRequestCacheKey(pid, fqdn, dns.TypeA))
delete(openDNSRequests, getDNSRequestCacheKey(pid, fqdn, dns.TypeAAAA))
for _, dnsType := range supportedDomainToIPRecordTypes {
delete(openDNSRequests, getDNSRequestCacheKey(pid, fqdn, dnsType))
}
// If process is known, also check for non-attributed requests.
if pid != process.UnidentifiedProcessID {
delete(openDNSRequests, getDNSRequestCacheKey(process.UnidentifiedProcessID, fqdn, dns.TypeA))
delete(openDNSRequests, getDNSRequestCacheKey(process.UnidentifiedProcessID, fqdn, dns.TypeAAAA))
for _, dnsType := range supportedDomainToIPRecordTypes {
delete(openDNSRequests, getDNSRequestCacheKey(process.UnidentifiedProcessID, fqdn, dnsType))
}
}
}
// SaveOpenDNSRequest saves a dns request connection that was allowed to proceed.
func SaveOpenDNSRequest(q *resolver.Query, rrCache *resolver.RRCache, conn *Connection) {
// Only save requests that actually went out to reduce clutter.
if rrCache == nil || rrCache.ServedFromCache {
// Only save requests that actually went out (or triggered an async resolve) to reduce clutter.
if rrCache == nil || (rrCache.ServedFromCache && !rrCache.RequestingNew) {
return
}
// Try to "merge" A and AAAA requests into the resulting connection.
// Try to "merge" supported requests into the resulting connection.
// Save others immediately.
switch uint16(q.QType) {
case dns.TypeA, dns.TypeAAAA:
default:
if !IsSupportDNSRecordType(uint16(q.QType)) {
conn.Save()
return
}
@ -68,18 +83,12 @@ func SaveOpenDNSRequest(q *resolver.Query, rrCache *resolver.RRCache, conn *Conn
openDNSRequestsLock.Lock()
defer openDNSRequestsLock.Unlock()
// Check if there is an existing open DNS requests for the same domain/type.
// If so, save it now and replace it with the new request.
key := getDNSRequestCacheKey(conn.process.Pid, conn.Entity.Domain, uint16(q.QType))
if existingConn, ok := openDNSRequests[key]; ok {
// End previous request and save it.
existingConn.Lock()
existingConn.Ended = conn.Started
existingConn.Unlock()
existingConn.Save()
}
// Do not check for an existing open DNS request, as duplicates in such quick
// succession are not worth keeping.
// DNS queries are usually retried pretty quick.
// Save to open dns requests.
key := getDNSRequestCacheKey(conn.process.Pid, conn.Entity.Domain, uint16(q.QType))
openDNSRequests[key] = conn
}
@ -103,12 +112,15 @@ func writeOpenDNSRequestsToDB() {
threshold := time.Now().Add(-openDNSRequestLimit).Unix()
for id, conn := range openDNSRequests {
func() {
conn.Lock()
defer conn.Unlock()
if conn.Ended < threshold {
conn.Save()
delete(openDNSRequests, id)
}
conn.Unlock()
}()
}
}

View file

@ -314,10 +314,10 @@ func loadProcess(ctx context.Context, key string, pInfo *processInfo.Process) (*
return process, nil
}
// GetID returns the key that is used internally to identify the process.
// The ID consists of the PID and the start time of the process as reported by
// GetKey returns the key that is used internally to identify the process.
// The key consists of the PID and the start time of the process as reported by
// the system.
func (p *Process) GetID() string {
func (p *Process) GetKey() string {
return p.processKey
}

View file

@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"sync"
"time"
"github.com/safing/portbase/database"
"github.com/safing/portbase/database/record"
@ -170,7 +171,7 @@ func (info *IPInfo) Save() error {
}
// Calculate and set cache expiry.
var expires int64 = 86400 // Minimum TTL of one day.
expires := time.Now().Unix() + 86400 // Minimum TTL of one day.
for _, rd := range info.ResolvedDomains {
if rd.Expires > expires {
expires = rd.Expires

View file

@ -293,11 +293,19 @@ func startAsyncQuery(ctx context.Context, q *Query, currentRRCache *RRCache) {
// Set flag and log that we are refreshing this entry.
currentRRCache.RequestingNew = true
if currentRRCache.Expired() {
log.Tracer(ctx).Tracef(
"resolver: cache for %s has expired %s ago, refreshing async now",
q.ID(),
time.Since(time.Unix(currentRRCache.Expires, 0)).Round(time.Second),
)
} else {
log.Tracer(ctx).Tracef(
"resolver: cache for %s will expire in %s, refreshing async now",
q.ID(),
time.Until(time.Unix(currentRRCache.Expires, 0)).Round(time.Second),
)
}
// resolve async
module.StartWorker("resolve async", func(asyncCtx context.Context) error {

View file

@ -130,8 +130,8 @@ func upgradeSystemIntegrationFileContents(
}
// Check if we are allowed to upgrade from the existing file.
if !slices.Contains[string](permittedUpgradeHashes, existingHexSum) {
return fmt.Errorf("%s at %s %w, as it is not a previously published version and cannot be automatically upgraded - try installing again", name, filePath, ErrRequiresManualUpgrade)
if !slices.Contains[[]string, string](permittedUpgradeHashes, existingHexSum) {
return fmt.Errorf("%s at %s (sha256:%s) %w, as it is not a previously published version and cannot be automatically upgraded - try installing again", name, filePath, existingHexSum, ErrRequiresManualUpgrade)
}
// Start with upgrade!