mirror of
https://github.com/safing/portmaster
synced 2025-04-09 05:29:11 +00:00
Merge branch 'develop'
This commit is contained in:
commit
df4106fd70
66 changed files with 8188 additions and 6228 deletions
.github/workflows
.gitignoreEarthfilebase
cmds/portmaster-start
desktop
angular
tauri/src-tauri
service
compat
firewall
bypassing.godns.gomodule.gopacket_handler.go
instance.gointerception
dnsmonitor
etwlink_windows.goeventlistener.goeventlistener_linux.goeventlistener_windows.gomodule.govarlinktypes.go
nfq
windowskext
windowskext2
integration
network
resolver
updates
windows_core_dll
build.ps1dllmain.cppframework.hpch.cpppch.hwindows_core_dll.slnwindows_core_dll.vcxprojwindows_core_dll.vcxproj.filterswindows_core_dll.vcxproj.user
windows_kext
driver/src
kextinterface
wdk/src/filter_engine
41
.github/workflows/windows-dll.yml
vendored
Normal file
41
.github/workflows/windows-dll.yml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
name: Windows Portmaster Core DLL
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'windows_core_dll/**'
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- 'windows_core_dll/**'
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
- name: Build DLL
|
||||
run: msbuild windows_core_dll\windows_core_dll.sln -t:rebuild -property:Configuration=Release
|
||||
- name: Verify DLL
|
||||
shell: powershell
|
||||
run: |
|
||||
if (!(Test-Path "windows_core_dll/x64/Release/portmaster-core.dll")) {
|
||||
Write-Error "DLL build failed: portmaster-core.dll not found"
|
||||
exit 1
|
||||
}
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: portmaster-core-dll
|
||||
path: windows_core_dll/x64/Release/portmaster-core.dll
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -52,3 +52,4 @@ go.work.sum
|
|||
|
||||
# Kext releases
|
||||
windows_kext/release/kext_release_*.zip
|
||||
windows_core_dll/.vs/windows_core_dll
|
||||
|
|
|
@ -420,7 +420,7 @@ rust-base:
|
|||
DO rust+INIT --keep_fingerprints=true
|
||||
|
||||
# For now we need tauri-cli 2.0.0 for bulding
|
||||
DO rust+CARGO --args="install tauri-cli --version ^2.0.0-beta"
|
||||
DO rust+CARGO --args="install tauri-cli --version 2.1.0"
|
||||
|
||||
# Explicitly cache here.
|
||||
SAVE IMAGE --cache-hint
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hectane/go-acl"
|
||||
"github.com/safing/portmaster/base/database/iterator"
|
||||
"github.com/safing/portmaster/base/database/query"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
|
@ -288,10 +289,13 @@ func writeFile(filename string, data []byte, perm os.FileMode) error {
|
|||
defer t.Cleanup() //nolint:errcheck
|
||||
|
||||
// Set permissions before writing data, in case the data is sensitive.
|
||||
if !onWindows {
|
||||
if err := t.Chmod(perm); err != nil {
|
||||
return err
|
||||
}
|
||||
if onWindows {
|
||||
err = acl.Chmod(filename, perm)
|
||||
} else {
|
||||
err = t.Chmod(perm)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := t.Write(data); err != nil {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/hectane/go-acl"
|
||||
"github.com/safing/jess/filesig"
|
||||
"github.com/safing/jess/lhash"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
|
@ -136,7 +137,12 @@ func (reg *ResourceRegistry) fetchFile(ctx context.Context, client *http.Client,
|
|||
return fmt.Errorf("%s: failed to finalize file %s: %w", reg.Name, rv.storagePath(), err)
|
||||
}
|
||||
// set permissions
|
||||
if !onWindows {
|
||||
if onWindows {
|
||||
err = acl.Chmod(rv.storagePath(), 0o0755)
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to set permissions on downloaded file %s: %s", reg.Name, rv.storagePath(), err)
|
||||
}
|
||||
} else {
|
||||
// TODO: only set executable files to 0755, set other to 0644
|
||||
err = os.Chmod(rv.storagePath(), 0o0755) //nolint:gosec // See TODO above.
|
||||
if err != nil {
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"io/fs"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/hectane/go-acl"
|
||||
)
|
||||
|
||||
const isWindows = runtime.GOOS == "windows"
|
||||
|
@ -20,8 +22,9 @@ func EnsureDirectory(path string, perm os.FileMode) error {
|
|||
if f.IsDir() {
|
||||
// directory exists, check permissions
|
||||
if isWindows {
|
||||
// TODO: set correct permission on windows
|
||||
// acl.Chmod(path, perm)
|
||||
// Ignore windows permission error. For none admin users it will always fail.
|
||||
acl.Chmod(path, perm)
|
||||
return nil
|
||||
} else if f.Mode().Perm() != perm {
|
||||
return os.Chmod(path, perm)
|
||||
}
|
||||
|
@ -38,7 +41,13 @@ func EnsureDirectory(path string, perm os.FileMode) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("could not create dir %s: %w", path, err)
|
||||
}
|
||||
return os.Chmod(path, perm)
|
||||
if isWindows {
|
||||
// Ignore windows permission error. For none admin users it will always fail.
|
||||
acl.Chmod(path, perm)
|
||||
return nil
|
||||
} else {
|
||||
return os.Chmod(path, perm)
|
||||
}
|
||||
}
|
||||
// other error opening path
|
||||
return fmt.Errorf("failed to access %s: %w", path, err)
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package renameio
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/hectane/go-acl"
|
||||
)
|
||||
|
||||
// WriteFile mirrors os.WriteFile, replacing an existing file with the same
|
||||
// name atomically.
|
||||
|
@ -14,7 +19,12 @@ func WriteFile(filename string, data []byte, perm os.FileMode) error {
|
|||
}()
|
||||
|
||||
// Set permissions before writing data, in case the data is sensitive.
|
||||
if err := t.Chmod(perm); err != nil {
|
||||
if runtime.GOOS == "windows" {
|
||||
err = acl.Chmod(t.path, perm)
|
||||
} else {
|
||||
err = t.Chmod(perm)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -272,7 +272,7 @@ func fixExecPerm(path string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if err := os.Chmod(path, 0o0755); err != nil { //nolint:gosec // Set execution rights.
|
||||
if err := os.Chmod(path, 0o0755); err != nil { //nolint:gosec
|
||||
return fmt.Errorf("failed to chmod %s: %w", path, err)
|
||||
}
|
||||
|
||||
|
|
134
desktop/angular/package-lock.json
generated
134
desktop/angular/package-lock.json
generated
|
@ -23,13 +23,13 @@
|
|||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
"@tauri-apps/api": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-cli": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-clipboard-manager": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-dialog": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-notification": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-os": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0-rc",
|
||||
"@tauri-apps/api": ">=2.1.1",
|
||||
"@tauri-apps/plugin-cli": ">=2.0.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": ">=2.0.0",
|
||||
"@tauri-apps/plugin-dialog": ">=2.0.0",
|
||||
"@tauri-apps/plugin-notification": ">=2.0.0",
|
||||
"@tauri-apps/plugin-os": ">=2.0.0",
|
||||
"@tauri-apps/plugin-shell": "^2.0.1",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"d3": "^7.8.4",
|
||||
"data-urls": "^5.0.0",
|
||||
|
@ -4406,9 +4406,9 @@
|
|||
"peer": true
|
||||
},
|
||||
"node_modules/@tauri-apps/api": {
|
||||
"version": "2.0.0-rc.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.0.0-rc.4.tgz",
|
||||
"integrity": "sha512-UNiIhhKG08j4ooss2oEEVexffmWkgkYlC2M3GcX3VPtNsqFgVNL8Mcw/4Y7rO9M9S+ffAMnLOF5ypzyuyb8tyg==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.1.1.tgz",
|
||||
"integrity": "sha512-fzUfFFKo4lknXGJq8qrCidkUcKcH2UHhfaaCNt4GzgzGaW2iS26uFOg4tS3H4P8D6ZEeUxtiD5z0nwFF0UN30A==",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
|
@ -4416,57 +4416,57 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-cli": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-cli/-/plugin-cli-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-EcSTRfEU3zzlNbgwVtZVzqB19z3PNjyXD9H+YXuuLpV+Hwuh6Oi1fhUdCI0mp5zr9HSMWE+HzHkpBI7sVP1RyA==",
|
||||
"license": "MIT or APACHE-2.0",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-cli/-/plugin-cli-2.0.0.tgz",
|
||||
"integrity": "sha512-glQmlL1IiCGEa1FHYa/PTPSeYhfu56omLRgHXWlJECDt6DbJyRuJWVgtkQfUxtqnVdYnnU+DGIGeiInoEqtjLw==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-clipboard-manager": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-hFgUABMmQuVGKwHb8PR9fuqfk0WRkedbWUt/ZV5sL4Q6kLrsp3JYJvtzVPeMYdeBvMqHl8WXNxAc/zwSld2h9w==",
|
||||
"license": "MIT or APACHE-2.0",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.0.0.tgz",
|
||||
"integrity": "sha512-V1sXmbjnwfXt/r48RJMwfUmDMSaP/8/YbH4CLNxt+/sf1eHlIP8PRFdFDQwLN0cNQKu2rqQVbG/Wc/Ps6cDUhw==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-dialog": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-H28gh6BfZtjflHQ+HrmWwunDriBI3AQLAKnMs50GA6zeNUULqbQr7VXbAAKeJL/0CmWcecID4PKXVoSlaWRhEg==",
|
||||
"license": "MIT or APACHE-2.0",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.0.1.tgz",
|
||||
"integrity": "sha512-fnUrNr6EfvTqdls/ufusU7h6UbNFzLKvHk/zTuOiBq01R3dTODqwctZlzakdbfSp/7pNwTKvgKTAgl/NAP/Z0Q==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-notification": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-ddDj7xM8XR7Zv2vdpofNXlLjcp49p/VjlL0D+/eBcMuyooaLNMor3jz/+H6s23iHerdxMWA50mzy26BRN1BySA==",
|
||||
"license": "MIT or APACHE-2.0",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.0.0.tgz",
|
||||
"integrity": "sha512-6qEDYJS7mgXZWLXA0EFL+DVCJh8sJlzSoyw6B50pxhLPVFjc5Vr5DVzl5W3mUHaYhod5wsC984eQnlCCGqxYDA==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-os": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-PV8zlSTmYfiN2xzILUmlDSEycS7UYbH2yXk/ZqF+qQU6/s+OVQvmSth4EhllFjcpvPbtqELvpzfjw+2qEouchA==",
|
||||
"license": "MIT or APACHE-2.0",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.0.0.tgz",
|
||||
"integrity": "sha512-M7hG/nNyQYTJxVG/UhTKhp9mpXriwWzrs9mqDreB8mIgqA3ek5nHLdwRZJWhkKjZrnDT4v9CpA9BhYeplTlAiA==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-shell": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-JtNROc0rqEwN/g93ig5pK4cl1vUo2yn+osCpY9de64cy/d9hRzof7AuYOgvt/Xcd5VPQmlgo2AGvUh5sQRSR1A==",
|
||||
"license": "MIT or APACHE-2.0",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.0.1.tgz",
|
||||
"integrity": "sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tootallnate/once": {
|
||||
|
@ -21067,56 +21067,56 @@
|
|||
"peer": true
|
||||
},
|
||||
"@tauri-apps/api": {
|
||||
"version": "2.0.0-rc.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.0.0-rc.4.tgz",
|
||||
"integrity": "sha512-UNiIhhKG08j4ooss2oEEVexffmWkgkYlC2M3GcX3VPtNsqFgVNL8Mcw/4Y7rO9M9S+ffAMnLOF5ypzyuyb8tyg=="
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.1.1.tgz",
|
||||
"integrity": "sha512-fzUfFFKo4lknXGJq8qrCidkUcKcH2UHhfaaCNt4GzgzGaW2iS26uFOg4tS3H4P8D6ZEeUxtiD5z0nwFF0UN30A=="
|
||||
},
|
||||
"@tauri-apps/plugin-cli": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-cli/-/plugin-cli-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-EcSTRfEU3zzlNbgwVtZVzqB19z3PNjyXD9H+YXuuLpV+Hwuh6Oi1fhUdCI0mp5zr9HSMWE+HzHkpBI7sVP1RyA==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-cli/-/plugin-cli-2.0.0.tgz",
|
||||
"integrity": "sha512-glQmlL1IiCGEa1FHYa/PTPSeYhfu56omLRgHXWlJECDt6DbJyRuJWVgtkQfUxtqnVdYnnU+DGIGeiInoEqtjLw==",
|
||||
"requires": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@tauri-apps/plugin-clipboard-manager": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-hFgUABMmQuVGKwHb8PR9fuqfk0WRkedbWUt/ZV5sL4Q6kLrsp3JYJvtzVPeMYdeBvMqHl8WXNxAc/zwSld2h9w==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.0.0.tgz",
|
||||
"integrity": "sha512-V1sXmbjnwfXt/r48RJMwfUmDMSaP/8/YbH4CLNxt+/sf1eHlIP8PRFdFDQwLN0cNQKu2rqQVbG/Wc/Ps6cDUhw==",
|
||||
"requires": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@tauri-apps/plugin-dialog": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-H28gh6BfZtjflHQ+HrmWwunDriBI3AQLAKnMs50GA6zeNUULqbQr7VXbAAKeJL/0CmWcecID4PKXVoSlaWRhEg==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.0.1.tgz",
|
||||
"integrity": "sha512-fnUrNr6EfvTqdls/ufusU7h6UbNFzLKvHk/zTuOiBq01R3dTODqwctZlzakdbfSp/7pNwTKvgKTAgl/NAP/Z0Q==",
|
||||
"requires": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@tauri-apps/plugin-notification": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-ddDj7xM8XR7Zv2vdpofNXlLjcp49p/VjlL0D+/eBcMuyooaLNMor3jz/+H6s23iHerdxMWA50mzy26BRN1BySA==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-notification/-/plugin-notification-2.0.0.tgz",
|
||||
"integrity": "sha512-6qEDYJS7mgXZWLXA0EFL+DVCJh8sJlzSoyw6B50pxhLPVFjc5Vr5DVzl5W3mUHaYhod5wsC984eQnlCCGqxYDA==",
|
||||
"requires": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@tauri-apps/plugin-os": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-PV8zlSTmYfiN2xzILUmlDSEycS7UYbH2yXk/ZqF+qQU6/s+OVQvmSth4EhllFjcpvPbtqELvpzfjw+2qEouchA==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.0.0.tgz",
|
||||
"integrity": "sha512-M7hG/nNyQYTJxVG/UhTKhp9mpXriwWzrs9mqDreB8mIgqA3ek5nHLdwRZJWhkKjZrnDT4v9CpA9BhYeplTlAiA==",
|
||||
"requires": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@tauri-apps/plugin-shell": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-JtNROc0rqEwN/g93ig5pK4cl1vUo2yn+osCpY9de64cy/d9hRzof7AuYOgvt/Xcd5VPQmlgo2AGvUh5sQRSR1A==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.0.1.tgz",
|
||||
"integrity": "sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==",
|
||||
"requires": {
|
||||
"@tauri-apps/api": "^2.0.0-rc.4"
|
||||
"@tauri-apps/api": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@tootallnate/once": {
|
||||
|
|
|
@ -37,13 +37,13 @@
|
|||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
"@tauri-apps/api": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-cli": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-clipboard-manager": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-dialog": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-notification": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-os": ">=2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0-rc",
|
||||
"@tauri-apps/api": ">=2.1.1",
|
||||
"@tauri-apps/plugin-cli": ">=2.0.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": ">=2.0.0",
|
||||
"@tauri-apps/plugin-dialog": ">=2.0.0",
|
||||
"@tauri-apps/plugin-notification": ">=2.0.0",
|
||||
"@tauri-apps/plugin-os": ">=2.0.0",
|
||||
"@tauri-apps/plugin-shell": "^2.0.1",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"d3": "^7.8.4",
|
||||
"data-urls": "^5.0.0",
|
||||
|
|
4280
desktop/tauri/src-tauri/Cargo.lock
generated
4280
desktop/tauri/src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -12,21 +12,21 @@ rust-version = "1.60"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.0-rc.7", features = [] }
|
||||
tauri-build = { version = "2.0.3", features = [] }
|
||||
|
||||
[dependencies]
|
||||
# Tauri
|
||||
tauri = { version = "2.0.0-rc.8", features = ["tray-icon", "image-png", "config-json5", "devtools"] }
|
||||
tauri-plugin-shell = "2.0.0-rc"
|
||||
tauri-plugin-dialog = "2.0.0-rc"
|
||||
tauri-plugin-clipboard-manager = "2.0.0-rc"
|
||||
tauri-plugin-os = "2.0.0-rc"
|
||||
tauri-plugin-single-instance = "2.0.0-rc"
|
||||
tauri-plugin-notification = "2.0.0-rc"
|
||||
tauri-plugin-log = "2.0.0-rc"
|
||||
tauri-plugin-window-state = "2.0.0-rc"
|
||||
tauri = { version = "2.1.1", features = ["tray-icon", "image-png", "config-json5", "devtools"] }
|
||||
tauri-plugin-shell = "2.0.2"
|
||||
tauri-plugin-dialog = "2.0.3"
|
||||
tauri-plugin-clipboard-manager = "2.0.2"
|
||||
tauri-plugin-os = "2.0.1"
|
||||
tauri-plugin-single-instance = "2.0.1"
|
||||
tauri-plugin-notification = "2.0.1"
|
||||
tauri-plugin-log = "2.0.2"
|
||||
tauri-plugin-window-state = "2.0.2"
|
||||
|
||||
tauri-cli = "2.0.0-rc.8"
|
||||
tauri-cli = "2.1.0"
|
||||
clap = { version = "4" }
|
||||
|
||||
# General
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -29,7 +29,7 @@ use crate::{
|
|||
portmaster::PortmasterExt,
|
||||
window::{create_main_window, may_navigate_to_ui, open_window},
|
||||
};
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons};
|
||||
|
||||
pub type AppIcon = TrayIcon<Wry>;
|
||||
|
||||
|
@ -199,8 +199,10 @@ pub fn setup_tray_menu(
|
|||
app.dialog()
|
||||
.message("This does not stop the Portmaster system service")
|
||||
.title("Do you really want to quit the user interface?")
|
||||
.ok_button_label("Yes, exit")
|
||||
.cancel_button_label("No")
|
||||
.buttons(MessageDialogButtons::OkCancelCustom(
|
||||
"Yes, exit".to_owned(),
|
||||
"No".to_owned(),
|
||||
))
|
||||
.show(move |answer| {
|
||||
if answer {
|
||||
// let _ = handle.emit("exit-requested", "");
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
{{/each~}}
|
||||
</Component>
|
||||
<Component Id="Path" Guid="{{path_component_guid}}" Win64="$(var.Win64)">
|
||||
<File Id="Path" Source="{{app_exe_source}}" KeyPath="yes" Checksum="yes"/>
|
||||
<File Id="Path" Source="{{main_binary_path}}" KeyPath="yes" Checksum="yes"/>
|
||||
{{#each file_associations as |association| ~}}
|
||||
{{#each association.ext as |ext| ~}}
|
||||
<ProgId Id="{{../../product_name}}.{{ext}}" Advertise="yes" Description="{{association.description}}">
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
{{/each~}}
|
||||
</Component>
|
||||
<Component Id="Path" Guid="{{path_component_guid}}" Win64="$(var.Win64)">
|
||||
<File Id="Path" Source="{{app_exe_source}}" KeyPath="yes" Checksum="yes"/>
|
||||
<File Id="Path" Source="{{main_binary_path}}" KeyPath="yes" Checksum="yes"/>
|
||||
{{#each file_associations as |association| ~}}
|
||||
{{#each association.ext as |ext| ~}}
|
||||
<ProgId Id="{{../../product_name}}.{{ext}}" Advertise="yes" Description="{{association.description}}">
|
||||
|
|
6
go.mod
6
go.mod
|
@ -32,6 +32,7 @@ require (
|
|||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb
|
||||
github.com/jackc/puddle/v2 v2.2.2
|
||||
github.com/lmittmann/tint v1.0.5
|
||||
github.com/maruel/panicparse/v2 v2.3.1
|
||||
|
@ -58,6 +59,7 @@ require (
|
|||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/tidwall/sjson v1.2.5
|
||||
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
|
||||
github.com/varlink/go v0.4.0
|
||||
github.com/vincent-petithory/dataurl v1.0.0
|
||||
go.etcd.io/bbolt v1.3.11
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
|
||||
|
@ -120,8 +122,8 @@ require (
|
|||
golang.org/x/tools v0.27.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
modernc.org/libc v1.61.0 // indirect
|
||||
modernc.org/libc v1.61.2 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/sqlite v1.33.1 // indirect
|
||||
modernc.org/sqlite v1.34.1 // indirect
|
||||
)
|
||||
|
|
21
go.sum
21
go.sum
|
@ -137,6 +137,8 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe
|
|||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM=
|
||||
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E=
|
||||
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
|
@ -311,6 +313,8 @@ github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G
|
|||
github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
|
||||
github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ=
|
||||
github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY=
|
||||
github.com/varlink/go v0.4.0 h1:+/BQoUO9eJK/+MTSHwFcJch7TMsb6N6Dqp6g0qaXXRo=
|
||||
github.com/varlink/go v0.4.0/go.mod h1:DKg9Y2ctoNkesREGAEak58l+jOC6JU2aqZvUYs5DynU=
|
||||
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
|
||||
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||
|
@ -388,6 +392,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -460,16 +465,16 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
||||
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
||||
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
|
||||
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4=
|
||||
modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0=
|
||||
modernc.org/cc/v4 v4.23.1 h1:WqJoPL3x4cUufQVHkXpXX7ThFJ1C4ik80i2eXEXbhD8=
|
||||
modernc.org/cc/v4 v4.23.1/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.22.3 h1:C7AW89Zw3kygesTQWBzApwIn9ldM+cb/plrTIKq41Os=
|
||||
modernc.org/ccgo/v4 v4.22.3/go.mod h1:Dz7n0/UkBbH3pnYaxgi1mFSfF4REqUOZNziphZASx6k=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
|
||||
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/libc v1.61.0 h1:eGFcvWpqlnoGwzZeZe3PWJkkKbM/3SUGyk1DVZQ0TpE=
|
||||
modernc.org/libc v1.61.0/go.mod h1:DvxVX89wtGTu+r72MLGhygpfi3aUGgZRdAYGCAVVud0=
|
||||
modernc.org/libc v1.61.2 h1:dkO4DlowfClcJYsvf/RiK6fUwvzCQTmB34bJLt0CAGQ=
|
||||
modernc.org/libc v1.61.2/go.mod h1:4QGjNyX3h+rn7V5oHpJY2yH0QN6frt1X+5BkXzwLPCo=
|
||||
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.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
|
@ -478,8 +483,8 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
|||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM=
|
||||
modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
|
||||
modernc.org/sqlite v1.34.1 h1:u3Yi6M0N8t9yKRDwhXcyp1eS5/ErhPTBggxWFuR6Hfk=
|
||||
modernc.org/sqlite v1.34.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
|
|
@ -181,4 +181,5 @@ func New(instance instance) (*Compat, error) {
|
|||
|
||||
type instance interface {
|
||||
NetEnv() *netenv.NetEnv
|
||||
Resolver() *resolver.ResolverModule
|
||||
}
|
||||
|
|
|
@ -158,6 +158,12 @@ func selfcheck(ctx context.Context) (issue *systemIssue, err error) {
|
|||
|
||||
// Step 3: Have the nameserver respond with random data in the answer section.
|
||||
|
||||
// Check if the resolver is enabled
|
||||
if module.instance.Resolver().IsDisabled() {
|
||||
// There is no control over the response, there is nothing more that can be checked.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Wait for the reply from the resolver.
|
||||
select {
|
||||
case err := <-dnsCheckLookupError:
|
||||
|
|
|
@ -43,8 +43,24 @@ func PreventBypassing(ctx context.Context, conn *network.Connection) (endpoints.
|
|||
return endpoints.NoMatch, "", nil
|
||||
}
|
||||
|
||||
// If Portmaster resolver is disabled allow requests going to system dns resolver.
|
||||
// And allow all connections out of the System Resolver.
|
||||
if module.instance.Resolver().IsDisabled() {
|
||||
// TODO(vladimir): Is there a more specific check that can be done?
|
||||
if conn.Process().IsSystemResolver() {
|
||||
return endpoints.NoMatch, "", nil
|
||||
}
|
||||
if conn.Entity.Port == 53 && conn.Entity.IPScope.IsLocalhost() {
|
||||
return endpoints.NoMatch, "", nil
|
||||
}
|
||||
}
|
||||
|
||||
// Block bypass attempts using an (encrypted) DNS server.
|
||||
switch {
|
||||
case looksLikeOutgoingDNSRequest(conn) && module.instance.Resolver().IsDisabled():
|
||||
// Allow. Packet will be analyzed and blocked if its not a dns request, before sent.
|
||||
conn.Inspecting = true
|
||||
return endpoints.NoMatch, "", nil
|
||||
case conn.Entity.Port == 53:
|
||||
return endpoints.Denied,
|
||||
"blocked DNS query, manual dns setup required",
|
||||
|
@ -62,3 +78,17 @@ func PreventBypassing(ctx context.Context, conn *network.Connection) (endpoints.
|
|||
|
||||
return endpoints.NoMatch, "", nil
|
||||
}
|
||||
|
||||
func looksLikeOutgoingDNSRequest(conn *network.Connection) bool {
|
||||
// Outbound on remote port 53, UDP.
|
||||
if conn.Inbound {
|
||||
return false
|
||||
}
|
||||
if conn.Entity.Port != 53 {
|
||||
return false
|
||||
}
|
||||
if conn.IPProtocol != packet.UDP {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -287,6 +287,30 @@ func UpdateIPsAndCNAMEs(q *resolver.Query, rrCache *resolver.RRCache, conn *netw
|
|||
}
|
||||
}
|
||||
|
||||
// Create new record for this IP.
|
||||
record := resolver.ResolvedDomain{
|
||||
Domain: q.FQDN,
|
||||
Resolver: rrCache.Resolver,
|
||||
DNSRequestContext: rrCache.ToDNSRequestContext(),
|
||||
Expires: rrCache.Expires,
|
||||
}
|
||||
// Process CNAMEs
|
||||
record.AddCNAMEs(cnames)
|
||||
// Link connection with cnames.
|
||||
if conn.Type == network.DNSRequest {
|
||||
conn.Entity.CNAME = record.CNAMEs
|
||||
}
|
||||
|
||||
SaveIPsInCache(ips, profileID, record)
|
||||
}
|
||||
|
||||
// formatRR is a friendlier alternative to miekg/dns.RR.String().
|
||||
func formatRR(rr dns.RR) string {
|
||||
return strings.ReplaceAll(rr.String(), "\t", " ")
|
||||
}
|
||||
|
||||
// SaveIPsInCache saves the provided ips in the dns cashe assoseted with the record Domain and CNAMEs.
|
||||
func SaveIPsInCache(ips []net.IP, profileID string, record resolver.ResolvedDomain) {
|
||||
// Package IPs and CNAMEs into IPInfo structs.
|
||||
for _, ip := range ips {
|
||||
// Never save domain attributions for localhost IPs.
|
||||
|
@ -294,31 +318,6 @@ func UpdateIPsAndCNAMEs(q *resolver.Query, rrCache *resolver.RRCache, conn *netw
|
|||
continue
|
||||
}
|
||||
|
||||
// Create new record for this IP.
|
||||
record := resolver.ResolvedDomain{
|
||||
Domain: q.FQDN,
|
||||
Resolver: rrCache.Resolver,
|
||||
DNSRequestContext: rrCache.ToDNSRequestContext(),
|
||||
Expires: rrCache.Expires,
|
||||
}
|
||||
|
||||
// Resolve all CNAMEs in the correct order and add the to the record - up to max 50 layers.
|
||||
domain := q.FQDN
|
||||
for range 50 {
|
||||
nextDomain, isCNAME := cnames[domain]
|
||||
if !isCNAME || nextDomain == domain {
|
||||
break
|
||||
}
|
||||
|
||||
record.CNAMEs = append(record.CNAMEs, nextDomain)
|
||||
domain = nextDomain
|
||||
}
|
||||
|
||||
// Update the entity to include the CNAMEs of the query response.
|
||||
conn.Entity.CNAME = record.CNAMEs
|
||||
|
||||
// Check if there is an existing record for this DNS response.
|
||||
// Else create a new one.
|
||||
ipString := ip.String()
|
||||
info, err := resolver.GetIPInfo(profileID, ipString)
|
||||
if err != nil {
|
||||
|
@ -341,8 +340,3 @@ func UpdateIPsAndCNAMEs(q *resolver.Query, rrCache *resolver.RRCache, conn *netw
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// formatRR is a friendlier alternative to miekg/dns.RR.String().
|
||||
func formatRR(rr dns.RR) string {
|
||||
return strings.ReplaceAll(rr.String(), "\t", " ")
|
||||
}
|
||||
|
|
99
service/firewall/interception/dnsmonitor/etwlink_windows.go
Normal file
99
service/firewall/interception/dnsmonitor/etwlink_windows.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package dnsmonitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/safing/portmaster/service/integration"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type ETWSession struct {
|
||||
i integration.ETWFunctions
|
||||
|
||||
shutdownGuard atomic.Bool
|
||||
shutdownMutex sync.Mutex
|
||||
|
||||
state uintptr
|
||||
}
|
||||
|
||||
// NewSession creates new ETW event listener and initilizes it. This is a low level interface, make sure to call DestorySession when you are done using it.
|
||||
func NewSession(etwInterface integration.ETWFunctions, callback func(domain string, result string)) (*ETWSession, error) {
|
||||
etwSession := &ETWSession{
|
||||
i: etwInterface,
|
||||
}
|
||||
|
||||
// Make sure session from previous instances are not running.
|
||||
_ = etwSession.i.StopOldSession()
|
||||
|
||||
// Initialize notification activated callback
|
||||
win32Callback := windows.NewCallback(func(domain *uint16, result *uint16) uintptr {
|
||||
callback(windows.UTF16PtrToString(domain), windows.UTF16PtrToString(result))
|
||||
return 0
|
||||
})
|
||||
// The function only allocates memory it will not fail.
|
||||
etwSession.state = etwSession.i.CreateState(win32Callback)
|
||||
|
||||
// Make sure DestroySession is called even if caller forgets to call it.
|
||||
runtime.SetFinalizer(etwSession, func(s *ETWSession) {
|
||||
_ = s.i.DestroySession(s.state)
|
||||
})
|
||||
|
||||
// Initialize session.
|
||||
err := etwSession.i.InitializeSession(etwSession.state)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialzie session: %q", err)
|
||||
}
|
||||
|
||||
return etwSession, nil
|
||||
}
|
||||
|
||||
// StartTrace starts the tracing session of dns events. This is a blocking call. It will not return until the trace is stopped.
|
||||
func (l *ETWSession) StartTrace() error {
|
||||
return l.i.StartTrace(l.state)
|
||||
}
|
||||
|
||||
// IsRunning returns true if DestroySession has NOT been called.
|
||||
func (l *ETWSession) IsRunning() bool {
|
||||
return !l.shutdownGuard.Load()
|
||||
}
|
||||
|
||||
// FlushTrace flushes the trace buffer.
|
||||
func (l *ETWSession) FlushTrace() error {
|
||||
l.shutdownMutex.Lock()
|
||||
defer l.shutdownMutex.Unlock()
|
||||
|
||||
// Make sure session is still running.
|
||||
if l.shutdownGuard.Load() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return l.i.FlushTrace(l.state)
|
||||
}
|
||||
|
||||
// StopTrace stopes the trace. This will cause StartTrace to return.
|
||||
func (l *ETWSession) StopTrace() error {
|
||||
return l.i.StopTrace(l.state)
|
||||
}
|
||||
|
||||
// DestroySession closes the session and frees the allocated memory. Listener cannot be used after this function is called.
|
||||
func (l *ETWSession) DestroySession() error {
|
||||
l.shutdownMutex.Lock()
|
||||
defer l.shutdownMutex.Unlock()
|
||||
|
||||
if l.shutdownGuard.Swap(true) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := l.i.DestroySession(l.state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.state = 0
|
||||
return nil
|
||||
}
|
19
service/firewall/interception/dnsmonitor/eventlistener.go
Normal file
19
service/firewall/interception/dnsmonitor/eventlistener.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
//go:build !linux && !windows
|
||||
// +build !linux,!windows
|
||||
|
||||
package dnsmonitor
|
||||
|
||||
type Listener struct{}
|
||||
|
||||
func newListener(_ *DNSMonitor) (*Listener, error) {
|
||||
return &Listener{}, nil
|
||||
}
|
||||
|
||||
func (l *Listener) flush() error {
|
||||
// Nothing to flush
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) stop() error {
|
||||
return nil
|
||||
}
|
145
service/firewall/interception/dnsmonitor/eventlistener_linux.go
Normal file
145
service/firewall/interception/dnsmonitor/eventlistener_linux.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package dnsmonitor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/varlink/go/varlink"
|
||||
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/resolver"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
varlinkConn *varlink.Connection
|
||||
}
|
||||
|
||||
func newListener(module *DNSMonitor) (*Listener, error) {
|
||||
// Set source of the resolver.
|
||||
ResolverInfo.Source = resolver.ServerSourceSystemd
|
||||
|
||||
// Check if the system has systemd-resolver.
|
||||
_, err := os.Stat("/run/systemd/resolve/io.systemd.Resolve.Monitor")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("system does not support systemd resolver monitor")
|
||||
}
|
||||
|
||||
listener := &Listener{}
|
||||
|
||||
restartAttempts := 0
|
||||
|
||||
module.mgr.Go("systemd-resolver-event-listener", func(w *mgr.WorkerCtx) error {
|
||||
// Abort initialization if the connection failed after too many tries.
|
||||
if restartAttempts > 10 {
|
||||
return nil
|
||||
}
|
||||
restartAttempts += 1
|
||||
|
||||
// Initialize varlink connection
|
||||
varlinkConn, err := varlink.NewConnection(module.mgr.Ctx(), "unix:/run/systemd/resolve/io.systemd.Resolve.Monitor")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to systemd-resolver varlink service: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if varlinkConn != nil {
|
||||
err = varlinkConn.Close()
|
||||
if err != nil {
|
||||
log.Errorf("dnsmonitor: failed to close varlink connection: %s", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
listener.varlinkConn = varlinkConn
|
||||
// Subscribe to the dns query events
|
||||
receive, err := listener.varlinkConn.Send(w.Ctx(), "io.systemd.Resolve.Monitor.SubscribeQueryResults", nil, varlink.More)
|
||||
if err != nil {
|
||||
var varlinkErr *varlink.Error
|
||||
if errors.As(err, &varlinkErr) {
|
||||
return fmt.Errorf("failed to issue Varlink call: %+v", varlinkErr.Parameters)
|
||||
} else {
|
||||
return fmt.Errorf("failed to issue Varlink call: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
queryResult := QueryResult{}
|
||||
// Receive the next event from the resolver.
|
||||
flags, err := receive(w.Ctx(), &queryResult)
|
||||
if err != nil {
|
||||
var varlinkErr *varlink.Error
|
||||
if errors.As(err, &varlinkErr) {
|
||||
return fmt.Errorf("failed to receive Varlink reply: %+v", varlinkErr.Parameters)
|
||||
} else {
|
||||
return fmt.Errorf("failed to receive Varlink reply: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the reply indicates the end of the stream
|
||||
if flags&varlink.Continues == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Ignore if there is no question.
|
||||
if queryResult.Question == nil || len(*queryResult.Question) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Protmaster self check
|
||||
domain := (*queryResult.Question)[0].Name
|
||||
if processIfSelfCheckDomain(dns.Fqdn(domain)) {
|
||||
// Not need to process result.
|
||||
continue
|
||||
}
|
||||
|
||||
if queryResult.Rcode != nil {
|
||||
continue // Ignore DNS errors
|
||||
}
|
||||
|
||||
listener.processAnswer(domain, &queryResult)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
func (l *Listener) flush() error {
|
||||
// Nothing to flush
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) processAnswer(domain string, queryResult *QueryResult) {
|
||||
// Allocated data struct for the parsed result.
|
||||
cnames := make(map[string]string)
|
||||
ips := make([]net.IP, 0, 5)
|
||||
|
||||
// Check if the query is valid
|
||||
if queryResult.Answer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Go trough each answer entry.
|
||||
for _, a := range *queryResult.Answer {
|
||||
if a.RR.Address != nil {
|
||||
ip := net.IP(*a.RR.Address)
|
||||
// Answer contains ip address.
|
||||
ips = append(ips, ip)
|
||||
|
||||
} else if a.RR.Name != nil {
|
||||
// Answer is a CNAME.
|
||||
cnames[domain] = *a.RR.Name
|
||||
}
|
||||
}
|
||||
|
||||
saveDomain(domain, ips, cnames)
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package dnsmonitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/resolver"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
etw *ETWSession
|
||||
}
|
||||
|
||||
func newListener(module *DNSMonitor) (*Listener, error) {
|
||||
// Set source of the resolver.
|
||||
ResolverInfo.Source = resolver.ServerSourceETW
|
||||
|
||||
listener := &Listener{}
|
||||
var err error
|
||||
// Initialize new dns event session.
|
||||
listener.etw, err = NewSession(module.instance.OSIntegration().GetETWInterface(), listener.processEvent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Start listening for events.
|
||||
module.mgr.Go("etw-dns-event-listener", func(w *mgr.WorkerCtx) error {
|
||||
return listener.etw.StartTrace()
|
||||
})
|
||||
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
func (l *Listener) flush() error {
|
||||
return l.etw.FlushTrace()
|
||||
}
|
||||
|
||||
func (l *Listener) stop() error {
|
||||
if l == nil {
|
||||
return fmt.Errorf("listener is nil")
|
||||
}
|
||||
if l.etw == nil {
|
||||
return fmt.Errorf("invalid etw session")
|
||||
}
|
||||
// Stop and destroy trace. Destroy should be called even if stop fails for some reason.
|
||||
err := l.etw.StopTrace()
|
||||
err2 := l.etw.DestroySession()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("StopTrace failed: %w", err)
|
||||
}
|
||||
|
||||
if err2 != nil {
|
||||
return fmt.Errorf("DestroySession failed: %w", err2)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) processEvent(domain string, result string) {
|
||||
if processIfSelfCheckDomain(dns.Fqdn(domain)) {
|
||||
// Not need to process result.
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore empty results
|
||||
if len(result) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
cnames := make(map[string]string)
|
||||
ips := []net.IP{}
|
||||
|
||||
resultArray := strings.Split(result, ";")
|
||||
for _, r := range resultArray {
|
||||
// For results other than IP addresses, the string starts with "type:"
|
||||
if strings.HasPrefix(r, "type:") {
|
||||
dnsValueArray := strings.Split(r, " ")
|
||||
if len(dnsValueArray) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Ignore everything except CNAME records
|
||||
if value, err := strconv.ParseInt(dnsValueArray[1], 10, 16); err == nil && value == int64(dns.TypeCNAME) {
|
||||
cnames[domain] = dnsValueArray[2]
|
||||
}
|
||||
|
||||
} else {
|
||||
// If the event doesn't start with "type:", it's an IP address
|
||||
ip := net.ParseIP(r)
|
||||
if ip != nil {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
saveDomain(domain, ips, cnames)
|
||||
}
|
139
service/firewall/interception/dnsmonitor/module.go
Normal file
139
service/firewall/interception/dnsmonitor/module.go
Normal file
|
@ -0,0 +1,139 @@
|
|||
package dnsmonitor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/compat"
|
||||
"github.com/safing/portmaster/service/integration"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/network/netutils"
|
||||
"github.com/safing/portmaster/service/resolver"
|
||||
)
|
||||
|
||||
var ResolverInfo = resolver.ResolverInfo{
|
||||
Name: "SystemResolver",
|
||||
Type: resolver.ServerTypeMonitor,
|
||||
}
|
||||
|
||||
type DNSMonitor struct {
|
||||
instance instance
|
||||
mgr *mgr.Manager
|
||||
|
||||
listener *Listener
|
||||
}
|
||||
|
||||
// Manager returns the module manager.
|
||||
func (dl *DNSMonitor) Manager() *mgr.Manager {
|
||||
return dl.mgr
|
||||
}
|
||||
|
||||
// Start starts the module.
|
||||
func (dl *DNSMonitor) Start() error {
|
||||
// Initialize dns event listener
|
||||
var err error
|
||||
dl.listener, err = newListener(dl)
|
||||
if err != nil {
|
||||
log.Warningf("dnsmonitor: failed to start dns listener: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the module.
|
||||
func (dl *DNSMonitor) Stop() error {
|
||||
if dl.listener != nil {
|
||||
err := dl.listener.stop()
|
||||
if err != nil {
|
||||
log.Errorf("dnsmonitor: failed to close listener: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush flushes the buffer forcing all events to be processed.
|
||||
func (dl *DNSMonitor) Flush() error {
|
||||
return dl.listener.flush()
|
||||
}
|
||||
|
||||
func saveDomain(domain string, ips []net.IP, cnames map[string]string) {
|
||||
fqdn := dns.Fqdn(domain)
|
||||
// Create new record for this IP.
|
||||
record := resolver.ResolvedDomain{
|
||||
Domain: fqdn,
|
||||
Resolver: &ResolverInfo,
|
||||
DNSRequestContext: &resolver.DNSRequestContext{},
|
||||
Expires: 0,
|
||||
}
|
||||
|
||||
// Process cnames
|
||||
record.AddCNAMEs(cnames)
|
||||
|
||||
// Add to cache
|
||||
saveIPsInCache(ips, resolver.IPInfoProfileScopeGlobal, record)
|
||||
}
|
||||
|
||||
func New(instance instance) (*DNSMonitor, error) {
|
||||
// Initialize module
|
||||
m := mgr.New("DNSMonitor")
|
||||
module := &DNSMonitor{
|
||||
mgr: m,
|
||||
instance: instance,
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
||||
|
||||
type instance interface {
|
||||
OSIntegration() *integration.OSIntegration
|
||||
}
|
||||
|
||||
func processIfSelfCheckDomain(fqdn string) bool {
|
||||
// Check for compat check dns request.
|
||||
if strings.HasSuffix(fqdn, compat.DNSCheckInternalDomainScope) {
|
||||
subdomain := strings.TrimSuffix(fqdn, compat.DNSCheckInternalDomainScope)
|
||||
_ = compat.SubmitDNSCheckDomain(subdomain)
|
||||
log.Infof("dnsmonitor: self-check domain received")
|
||||
// No need to parse the answer.
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// saveIPsInCache saves the provided ips in the dns cashe assoseted with the record Domain and CNAMEs.
|
||||
func saveIPsInCache(ips []net.IP, profileID string, record resolver.ResolvedDomain) {
|
||||
// Package IPs and CNAMEs into IPInfo structs.
|
||||
for _, ip := range ips {
|
||||
// Never save domain attributions for localhost IPs.
|
||||
if netutils.GetIPScope(ip) == netutils.HostLocal {
|
||||
continue
|
||||
}
|
||||
|
||||
ipString := ip.String()
|
||||
info, err := resolver.GetIPInfo(profileID, ipString)
|
||||
if err != nil {
|
||||
if !errors.Is(err, database.ErrNotFound) {
|
||||
log.Errorf("dnsmonitor: failed to search for IP info record: %s", err)
|
||||
}
|
||||
|
||||
info = &resolver.IPInfo{
|
||||
IP: ipString,
|
||||
ProfileID: profileID,
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new record to the resolved domains for this IP and scope.
|
||||
info.AddDomain(record)
|
||||
|
||||
// Save if the record is new or has been updated.
|
||||
if err := info.Save(); err != nil {
|
||||
log.Errorf("dnsmonitor: failed to save IP info record: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
83
service/firewall/interception/dnsmonitor/varlinktypes.go
Normal file
83
service/firewall/interception/dnsmonitor/varlinktypes.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package dnsmonitor
|
||||
|
||||
// List of struct that define the systemd-resolver varlink dns event protocol.
|
||||
// Source: `sudo varlinkctl introspect /run/systemd/resolve/io.systemd.Resolve.Monitor io.systemd.Resolve.Monitor`
|
||||
|
||||
type ResourceKey struct {
|
||||
Class int `json:"class"`
|
||||
Type int `json:"type"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ResourceRecord struct {
|
||||
Key ResourceKey `json:"key"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Address *[]byte `json:"address,omitempty"`
|
||||
// Rest of the fields are not used.
|
||||
// Priority *int `json:"priority,omitempty"`
|
||||
// Weight *int `json:"weight,omitempty"`
|
||||
// Port *int `json:"port,omitempty"`
|
||||
// CPU *string `json:"cpu,omitempty"`
|
||||
// OS *string `json:"os,omitempty"`
|
||||
// Items *[]string `json:"items,omitempty"`
|
||||
// MName *string `json:"mname,omitempty"`
|
||||
// RName *string `json:"rname,omitempty"`
|
||||
// Serial *int `json:"serial,omitempty"`
|
||||
// Refresh *int `json:"refresh,omitempty"`
|
||||
// Expire *int `json:"expire,omitempty"`
|
||||
// Minimum *int `json:"minimum,omitempty"`
|
||||
// Exchange *string `json:"exchange,omitempty"`
|
||||
// Version *int `json:"version,omitempty"`
|
||||
// Size *int `json:"size,omitempty"`
|
||||
// HorizPre *int `json:"horiz_pre,omitempty"`
|
||||
// VertPre *int `json:"vert_pre,omitempty"`
|
||||
// Latitude *int `json:"latitude,omitempty"`
|
||||
// Longitude *int `json:"longitude,omitempty"`
|
||||
// Altitude *int `json:"altitude,omitempty"`
|
||||
// KeyTag *int `json:"key_tag,omitempty"`
|
||||
// Algorithm *int `json:"algorithm,omitempty"`
|
||||
// DigestType *int `json:"digest_type,omitempty"`
|
||||
// Digest *string `json:"digest,omitempty"`
|
||||
// FPType *int `json:"fptype,omitempty"`
|
||||
// Fingerprint *string `json:"fingerprint,omitempty"`
|
||||
// Flags *int `json:"flags,omitempty"`
|
||||
// Protocol *int `json:"protocol,omitempty"`
|
||||
// DNSKey *string `json:"dnskey,omitempty"`
|
||||
// Signer *string `json:"signer,omitempty"`
|
||||
// TypeCovered *int `json:"type_covered,omitempty"`
|
||||
// Labels *int `json:"labels,omitempty"`
|
||||
// OriginalTTL *int `json:"original_ttl,omitempty"`
|
||||
// Expiration *int `json:"expiration,omitempty"`
|
||||
// Inception *int `json:"inception,omitempty"`
|
||||
// Signature *string `json:"signature,omitempty"`
|
||||
// NextDomain *string `json:"next_domain,omitempty"`
|
||||
// Types *[]int `json:"types,omitempty"`
|
||||
// Iterations *int `json:"iterations,omitempty"`
|
||||
// Salt *string `json:"salt,omitempty"`
|
||||
// Hash *string `json:"hash,omitempty"`
|
||||
// CertUsage *int `json:"cert_usage,omitempty"`
|
||||
// Selector *int `json:"selector,omitempty"`
|
||||
// MatchingType *int `json:"matching_type,omitempty"`
|
||||
// Data *string `json:"data,omitempty"`
|
||||
// Tag *string `json:"tag,omitempty"`
|
||||
// Value *string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
type Answer struct {
|
||||
RR *ResourceRecord `json:"rr,omitempty"`
|
||||
Raw string `json:"raw"`
|
||||
IfIndex *int `json:"ifindex,omitempty"`
|
||||
}
|
||||
|
||||
type QueryResult struct {
|
||||
Ready *bool `json:"ready,omitempty"`
|
||||
State *string `json:"state,omitempty"`
|
||||
Rcode *int `json:"rcode,omitempty"`
|
||||
Errno *int `json:"errno,omitempty"`
|
||||
Question *[]ResourceKey `json:"question,omitempty"`
|
||||
CollectedQuestions *[]ResourceKey `json:"collectedQuestions,omitempty"`
|
||||
Answer *[]Answer `json:"answer,omitempty"`
|
||||
}
|
|
@ -188,7 +188,7 @@ func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
if err := pmpacket.Parse(*attrs.Payload, &pkt.Base); err != nil {
|
||||
if err := pmpacket.ParseLayer3(*attrs.Payload, &pkt.Base); err != nil {
|
||||
log.Warningf("nfqueue: failed to parse payload: %s", err)
|
||||
_ = pkt.Drop()
|
||||
return 0
|
||||
|
|
|
@ -59,7 +59,7 @@ func (pkt *Packet) LoadPacketData() error {
|
|||
return packet.ErrFailedToLoadPayload
|
||||
}
|
||||
|
||||
err = packet.Parse(payload, &pkt.Base)
|
||||
err = packet.ParseLayer3(payload, &pkt.Base)
|
||||
if err != nil {
|
||||
log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to parse payload: %s", err)
|
||||
return packet.ErrFailedToLoadPayload
|
||||
|
|
|
@ -55,6 +55,7 @@ func Handler(ctx context.Context, packets chan packet.Packet, bandwidthUpdate ch
|
|||
newPacket := &Packet{
|
||||
verdictRequest: conn.ID,
|
||||
payload: conn.Payload,
|
||||
payloadLayer: conn.PayloadLayer,
|
||||
verdictSet: abool.NewBool(false),
|
||||
}
|
||||
info := newPacket.Info()
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package windowskext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
@ -19,6 +20,7 @@ type Packet struct {
|
|||
|
||||
verdictRequest uint64
|
||||
payload []byte
|
||||
payloadLayer uint8
|
||||
verdictSet *abool.AtomicBool
|
||||
|
||||
payloadLoaded bool
|
||||
|
@ -51,7 +53,15 @@ func (pkt *Packet) LoadPacketData() error {
|
|||
pkt.payloadLoaded = true
|
||||
|
||||
if len(pkt.payload) > 0 {
|
||||
err := packet.Parse(pkt.payload, &pkt.Base)
|
||||
var err error
|
||||
switch pkt.payloadLayer {
|
||||
case 3:
|
||||
err = packet.ParseLayer3(pkt.payload, &pkt.Base)
|
||||
case 4:
|
||||
err = packet.ParseLayer4(pkt.payload, &pkt.Base)
|
||||
default:
|
||||
err = fmt.Errorf("unsupported payload layer: %d", pkt.payloadLayer)
|
||||
}
|
||||
if err != nil {
|
||||
log.Tracef("payload: %#v", pkt.payload)
|
||||
log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to parse payload: %s", err)
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/safing/portmaster/service/netquery"
|
||||
"github.com/safing/portmaster/service/network"
|
||||
"github.com/safing/portmaster/service/profile"
|
||||
"github.com/safing/portmaster/service/resolver"
|
||||
"github.com/safing/portmaster/spn/access"
|
||||
"github.com/safing/portmaster/spn/captain"
|
||||
)
|
||||
|
@ -34,8 +35,7 @@ func (ss *stringSliceFlag) Set(value string) error {
|
|||
var allowedClients stringSliceFlag
|
||||
|
||||
type Firewall struct {
|
||||
mgr *mgr.Manager
|
||||
|
||||
mgr *mgr.Manager
|
||||
instance instance
|
||||
}
|
||||
|
||||
|
@ -165,4 +165,5 @@ type instance interface {
|
|||
Access() *access.Access
|
||||
Network() *network.Network
|
||||
NetQuery() *netquery.NetQuery
|
||||
Resolver() *resolver.ResolverModule
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portmaster/base/log"
|
||||
|
@ -23,6 +25,7 @@ import (
|
|||
"github.com/safing/portmaster/service/network/netutils"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
"github.com/safing/portmaster/service/process"
|
||||
"github.com/safing/portmaster/service/resolver"
|
||||
"github.com/safing/portmaster/spn/access"
|
||||
)
|
||||
|
||||
|
@ -444,8 +447,9 @@ func filterHandler(conn *network.Connection, pkt packet.Packet) {
|
|||
filterConnection = false
|
||||
log.Tracer(pkt.Ctx()).Infof("filter: granting own pre-authenticated connection %s", conn)
|
||||
|
||||
// Redirect outbound DNS packets if enabled,
|
||||
// Redirect outbound DNS packets if enabled,
|
||||
case dnsQueryInterception() &&
|
||||
!module.instance.Resolver().IsDisabled() &&
|
||||
pkt.IsOutbound() &&
|
||||
pkt.Info().DstPort == 53 &&
|
||||
// that don't match the address of our nameserver,
|
||||
|
@ -478,11 +482,13 @@ func filterHandler(conn *network.Connection, pkt packet.Packet) {
|
|||
|
||||
// Decide how to continue handling connection.
|
||||
switch {
|
||||
case conn.Inspecting && looksLikeOutgoingDNSRequest(conn):
|
||||
inspectDNSPacket(conn, pkt)
|
||||
conn.UpdateFirewallHandler(inspectDNSPacket)
|
||||
case conn.Inspecting:
|
||||
log.Tracer(pkt.Ctx()).Trace("filter: start inspecting")
|
||||
conn.UpdateFirewallHandler(inspectAndVerdictHandler)
|
||||
inspectAndVerdictHandler(conn, pkt)
|
||||
|
||||
default:
|
||||
conn.StopFirewallHandler()
|
||||
verdictHandler(conn, pkt)
|
||||
|
@ -506,7 +512,7 @@ func FilterConnection(ctx context.Context, conn *network.Connection, pkt packet.
|
|||
}
|
||||
|
||||
// TODO: Enable inspection framework again.
|
||||
conn.Inspecting = false
|
||||
// conn.Inspecting = false
|
||||
|
||||
// TODO: Quick fix for the SPN.
|
||||
// Use inspection framework for proper encryption detection.
|
||||
|
@ -580,6 +586,98 @@ func inspectAndVerdictHandler(conn *network.Connection, pkt packet.Packet) {
|
|||
issueVerdict(conn, pkt, 0, true)
|
||||
}
|
||||
|
||||
func inspectDNSPacket(conn *network.Connection, pkt packet.Packet) {
|
||||
// Ignore info-only packets in this handler.
|
||||
if pkt.InfoOnly() {
|
||||
return
|
||||
}
|
||||
|
||||
dnsPacket := new(dns.Msg)
|
||||
err := pkt.LoadPacketData()
|
||||
if err != nil {
|
||||
_ = pkt.Block()
|
||||
log.Errorf("filter: failed to load packet payload: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse and block invalid packets.
|
||||
err = dnsPacket.Unpack(pkt.Payload())
|
||||
if err != nil {
|
||||
err = pkt.PermanentBlock()
|
||||
if err != nil {
|
||||
log.Errorf("filter: failed to block packet: %s", err)
|
||||
}
|
||||
_ = conn.SetVerdict(network.VerdictBlock, "none DNS data on DNS port", "", nil)
|
||||
conn.VerdictPermanent = true
|
||||
conn.Save()
|
||||
return
|
||||
}
|
||||
|
||||
// Packet was parsed.
|
||||
// Allow it but only after the answer was added to the cache.
|
||||
defer func() {
|
||||
err = pkt.Accept()
|
||||
if err != nil {
|
||||
log.Errorf("filter: failed to accept dns packet: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Check if packet has a question.
|
||||
if len(dnsPacket.Question) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Read create structs with the needed data.
|
||||
question := dnsPacket.Question[0]
|
||||
fqdn := dns.Fqdn(question.Name)
|
||||
|
||||
// Check for compat check dns request.
|
||||
if strings.HasSuffix(fqdn, compat.DNSCheckInternalDomainScope) {
|
||||
subdomain := strings.TrimSuffix(fqdn, compat.DNSCheckInternalDomainScope)
|
||||
_ = compat.SubmitDNSCheckDomain(subdomain)
|
||||
log.Infof("packet_handler: self-check domain received")
|
||||
// No need to parse the answer.
|
||||
return
|
||||
}
|
||||
|
||||
// Check if there is an answer.
|
||||
if len(dnsPacket.Answer) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
resolverInfo := &resolver.ResolverInfo{
|
||||
Name: "DNSRequestObserver",
|
||||
Type: resolver.ServerTypeFirewall,
|
||||
Source: resolver.ServerSourceFirewall,
|
||||
IP: conn.Entity.IP,
|
||||
Domain: conn.Entity.Domain,
|
||||
IPScope: conn.Entity.IPScope,
|
||||
}
|
||||
|
||||
rrCache := &resolver.RRCache{
|
||||
Domain: fqdn,
|
||||
Question: dns.Type(question.Qtype),
|
||||
RCode: dnsPacket.Rcode,
|
||||
Answer: dnsPacket.Answer,
|
||||
Ns: dnsPacket.Ns,
|
||||
Extra: dnsPacket.Extra,
|
||||
Resolver: resolverInfo,
|
||||
}
|
||||
|
||||
query := &resolver.Query{
|
||||
FQDN: fqdn,
|
||||
QType: dns.Type(question.Qtype),
|
||||
NoCaching: false,
|
||||
IgnoreFailing: false,
|
||||
LocalResolversOnly: false,
|
||||
ICANNSpace: false,
|
||||
DomainRoot: "",
|
||||
}
|
||||
|
||||
// Save to cache
|
||||
UpdateIPsAndCNAMEs(query, rrCache, conn)
|
||||
}
|
||||
|
||||
func icmpFilterHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
// Load packet data.
|
||||
err := pkt.LoadPacketData()
|
||||
|
|
|
@ -19,6 +19,8 @@ import (
|
|||
"github.com/safing/portmaster/service/core/base"
|
||||
"github.com/safing/portmaster/service/firewall"
|
||||
"github.com/safing/portmaster/service/firewall/interception"
|
||||
"github.com/safing/portmaster/service/firewall/interception/dnsmonitor"
|
||||
"github.com/safing/portmaster/service/integration"
|
||||
"github.com/safing/portmaster/service/intel/customlists"
|
||||
"github.com/safing/portmaster/service/intel/filterlists"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
|
@ -65,6 +67,7 @@ type Instance struct {
|
|||
|
||||
core *core.Core
|
||||
updates *updates.Updates
|
||||
integration *integration.OSIntegration
|
||||
geoip *geoip.GeoIP
|
||||
netenv *netenv.NetEnv
|
||||
ui *ui.UI
|
||||
|
@ -74,6 +77,7 @@ type Instance struct {
|
|||
firewall *firewall.Firewall
|
||||
filterLists *filterlists.FilterLists
|
||||
interception *interception.Interception
|
||||
dnsmonitor *dnsmonitor.DNSMonitor
|
||||
customlist *customlists.CustomList
|
||||
status *status.Status
|
||||
broadcasts *broadcasts.Broadcasts
|
||||
|
@ -107,7 +111,6 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
instance.ctx, instance.cancelCtx = context.WithCancel(context.Background())
|
||||
|
||||
var err error
|
||||
|
||||
// Base modules
|
||||
instance.base, err = base.New(instance)
|
||||
if err != nil {
|
||||
|
@ -151,6 +154,10 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
if err != nil {
|
||||
return instance, fmt.Errorf("create updates module: %w", err)
|
||||
}
|
||||
instance.integration, err = integration.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create integration module: %w", err)
|
||||
}
|
||||
instance.geoip, err = geoip.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create customlist module: %w", err)
|
||||
|
@ -187,6 +194,10 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
if err != nil {
|
||||
return instance, fmt.Errorf("create interception module: %w", err)
|
||||
}
|
||||
instance.dnsmonitor, err = dnsmonitor.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create dns-listener module: %w", err)
|
||||
}
|
||||
instance.customlist, err = customlists.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create customlist module: %w", err)
|
||||
|
@ -275,6 +286,7 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
|
||||
instance.core,
|
||||
instance.updates,
|
||||
instance.integration,
|
||||
instance.geoip,
|
||||
instance.netenv,
|
||||
|
||||
|
@ -288,6 +300,7 @@ func New(svcCfg *ServiceConfig) (*Instance, error) { //nolint:maintidx
|
|||
instance.filterLists,
|
||||
instance.customlist,
|
||||
instance.interception,
|
||||
instance.dnsmonitor,
|
||||
|
||||
instance.compat,
|
||||
instance.status,
|
||||
|
@ -378,6 +391,11 @@ func (i *Instance) Updates() *updates.Updates {
|
|||
return i.updates
|
||||
}
|
||||
|
||||
// OSIntegration returns the integration module.
|
||||
func (i *Instance) OSIntegration() *integration.OSIntegration {
|
||||
return i.integration
|
||||
}
|
||||
|
||||
// GeoIP returns the geoip module.
|
||||
func (i *Instance) GeoIP() *geoip.GeoIP {
|
||||
return i.geoip
|
||||
|
@ -463,6 +481,11 @@ func (i *Instance) Interception() *interception.Interception {
|
|||
return i.interception
|
||||
}
|
||||
|
||||
// DNSMonitor returns the dns-listener module.
|
||||
func (i *Instance) DNSMonitor() *dnsmonitor.DNSMonitor {
|
||||
return i.dnsmonitor
|
||||
}
|
||||
|
||||
// CustomList returns the customlist module.
|
||||
func (i *Instance) CustomList() *customlists.CustomList {
|
||||
return i.customlist
|
||||
|
|
114
service/integration/etw_windows.go
Normal file
114
service/integration/etw_windows.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type ETWFunctions struct {
|
||||
createState *windows.Proc
|
||||
initializeSession *windows.Proc
|
||||
startTrace *windows.Proc
|
||||
flushTrace *windows.Proc
|
||||
stopTrace *windows.Proc
|
||||
destroySession *windows.Proc
|
||||
stopOldSession *windows.Proc
|
||||
}
|
||||
|
||||
func initializeETW(dll *windows.DLL) (ETWFunctions, error) {
|
||||
var functions ETWFunctions
|
||||
var err error
|
||||
functions.createState, err = dll.FindProc("PM_ETWCreateState")
|
||||
if err != nil {
|
||||
return functions, fmt.Errorf("failed to load function PM_ETWCreateState: %q", err)
|
||||
}
|
||||
functions.initializeSession, err = dll.FindProc("PM_ETWInitializeSession")
|
||||
if err != nil {
|
||||
return functions, fmt.Errorf("failed to load function PM_ETWInitializeSession: %q", err)
|
||||
}
|
||||
functions.startTrace, err = dll.FindProc("PM_ETWStartTrace")
|
||||
if err != nil {
|
||||
return functions, fmt.Errorf("failed to load function PM_ETWStartTrace: %q", err)
|
||||
}
|
||||
functions.flushTrace, err = dll.FindProc("PM_ETWFlushTrace")
|
||||
if err != nil {
|
||||
return functions, fmt.Errorf("failed to load function PM_ETWFlushTrace: %q", err)
|
||||
}
|
||||
functions.stopTrace, err = dll.FindProc("PM_ETWStopTrace")
|
||||
if err != nil {
|
||||
return functions, fmt.Errorf("failed to load function PM_ETWStopTrace: %q", err)
|
||||
}
|
||||
functions.destroySession, err = dll.FindProc("PM_ETWDestroySession")
|
||||
if err != nil {
|
||||
return functions, fmt.Errorf("failed to load function PM_ETWDestroySession: %q", err)
|
||||
}
|
||||
functions.stopOldSession, err = dll.FindProc("PM_ETWStopOldSession")
|
||||
if err != nil {
|
||||
return functions, fmt.Errorf("failed to load function PM_ETWDestroySession: %q", err)
|
||||
}
|
||||
return functions, nil
|
||||
}
|
||||
|
||||
// CreateState calls the dll createState C function.
|
||||
func (etw ETWFunctions) CreateState(callback uintptr) uintptr {
|
||||
state, _, _ := etw.createState.Call(callback)
|
||||
return state
|
||||
}
|
||||
|
||||
// InitializeSession calls the dll initializeSession C function.
|
||||
func (etw ETWFunctions) InitializeSession(state uintptr) error {
|
||||
rc, _, _ := etw.initializeSession.Call(state)
|
||||
if rc != 0 {
|
||||
return fmt.Errorf("failed with status code: %d", rc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartTrace calls the dll startTrace C function.
|
||||
func (etw ETWFunctions) StartTrace(state uintptr) error {
|
||||
rc, _, _ := etw.startTrace.Call(state)
|
||||
if rc != 0 {
|
||||
return fmt.Errorf("failed with status code: %d", rc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FlushTrace calls the dll flushTrace C function.
|
||||
func (etw ETWFunctions) FlushTrace(state uintptr) error {
|
||||
rc, _, _ := etw.flushTrace.Call(state)
|
||||
if rc != 0 {
|
||||
return fmt.Errorf("failed with status code: %d", rc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopTrace calls the dll stopTrace C function.
|
||||
func (etw ETWFunctions) StopTrace(state uintptr) error {
|
||||
rc, _, _ := etw.stopTrace.Call(state)
|
||||
if rc != 0 {
|
||||
return fmt.Errorf("failed with status code: %d", rc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestroySession calls the dll destroySession C function.
|
||||
func (etw ETWFunctions) DestroySession(state uintptr) error {
|
||||
rc, _, _ := etw.destroySession.Call(state)
|
||||
if rc != 0 {
|
||||
return fmt.Errorf("failed with status code: %d", rc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopOldSession calls the dll stopOldSession C function.
|
||||
func (etw ETWFunctions) StopOldSession() error {
|
||||
rc, _, _ := etw.stopOldSession.Call()
|
||||
if rc != 0 {
|
||||
return fmt.Errorf("failed with status code: %d", rc)
|
||||
}
|
||||
return nil
|
||||
}
|
16
service/integration/integration.go
Normal file
16
service/integration/integration.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package integration
|
||||
|
||||
type OSSpecific struct{}
|
||||
|
||||
// Initialize is empty on any OS different then Windows.
|
||||
func (i *OSIntegration) Initialize() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp releases any resourses allocated during initializaion.
|
||||
func (i *OSIntegration) CleanUp() error {
|
||||
return nil
|
||||
}
|
52
service/integration/integration_windows.go
Normal file
52
service/integration/integration_windows.go
Normal file
|
@ -0,0 +1,52 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type OSSpecific struct {
|
||||
dll *windows.DLL
|
||||
etwFunctions ETWFunctions
|
||||
}
|
||||
|
||||
// Initialize loads the dll and finds all the needed functions from it.
|
||||
func (i *OSIntegration) Initialize() error {
|
||||
// Find path to the dll.
|
||||
file, err := updates.GetFile("portmaster-core.dll")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the DLL.
|
||||
i.os.dll, err = windows.LoadDLL(file.Path())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load dll: %q", err)
|
||||
}
|
||||
|
||||
// Enumerate all needed dll functions.
|
||||
i.os.etwFunctions, err = initializeETW(i.os.dll)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp releases any resourses allocated during initializaion.
|
||||
func (i *OSIntegration) CleanUp() error {
|
||||
if i.os.dll != nil {
|
||||
return i.os.dll.Release()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetETWInterface return struct containing all the ETW related functions.
|
||||
func (i *OSIntegration) GetETWInterface() ETWFunctions {
|
||||
return i.os.etwFunctions
|
||||
}
|
49
service/integration/module.go
Normal file
49
service/integration/module.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
)
|
||||
|
||||
// OSIntegration module provides special integration with the OS.
|
||||
type OSIntegration struct {
|
||||
m *mgr.Manager
|
||||
states *mgr.StateMgr
|
||||
|
||||
//nolint:unused
|
||||
os OSSpecific
|
||||
|
||||
instance instance
|
||||
}
|
||||
|
||||
// New returns a new OSIntegration module.
|
||||
func New(instance instance) (*OSIntegration, error) {
|
||||
m := mgr.New("OSIntegration")
|
||||
module := &OSIntegration{
|
||||
m: m,
|
||||
states: m.NewStateMgr(),
|
||||
|
||||
instance: instance,
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
||||
|
||||
// Manager returns the module manager.
|
||||
func (i *OSIntegration) Manager() *mgr.Manager {
|
||||
return i.m
|
||||
}
|
||||
|
||||
// Start starts the module.
|
||||
func (i *OSIntegration) Start() error {
|
||||
return i.Initialize()
|
||||
}
|
||||
|
||||
// Stop stops the module.
|
||||
func (i *OSIntegration) Stop() error {
|
||||
return i.CleanUp()
|
||||
}
|
||||
|
||||
type instance interface {
|
||||
Updates() *updates.Updates
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
@ -18,6 +19,7 @@ import (
|
|||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/netutils"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
"github.com/safing/portmaster/service/network/reference"
|
||||
"github.com/safing/portmaster/service/process"
|
||||
_ "github.com/safing/portmaster/service/process/tags"
|
||||
"github.com/safing/portmaster/service/resolver"
|
||||
|
@ -542,6 +544,23 @@ func (conn *Connection) GatherConnectionInfo(pkt packet.Packet) (err error) {
|
|||
// Try again with the global scope, in case DNS went through the system resolver.
|
||||
ipinfo, err = resolver.GetIPInfo(resolver.IPInfoProfileScopeGlobal, pkt.Info().RemoteIP().String())
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" && err != nil {
|
||||
// On windows domains may come with delay.
|
||||
if module.instance.Resolver().IsDisabled() && conn.shouldWaitForDomain() {
|
||||
// Flush the dns listener buffer and try again.
|
||||
for i := range 4 {
|
||||
_ = module.instance.DNSMonitor().Flush()
|
||||
ipinfo, err = resolver.GetIPInfo(resolver.IPInfoProfileScopeGlobal, pkt.Info().RemoteIP().String())
|
||||
if err == nil {
|
||||
log.Tracer(pkt.Ctx()).Debugf("network: found domain from dnsmonitor after %d tries", i+1)
|
||||
break
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
lastResolvedDomain := ipinfo.MostRecentDomain()
|
||||
if lastResolvedDomain != nil {
|
||||
|
@ -869,3 +888,17 @@ func (conn *Connection) String() string {
|
|||
return fmt.Sprintf("%s -> %s", conn.process, conn.Entity.IP)
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Connection) shouldWaitForDomain() bool {
|
||||
// Should wait for Global Unicast, outgoing and not ICMP connections
|
||||
switch {
|
||||
case conn.Entity.IPScope != netutils.Global:
|
||||
return false
|
||||
case conn.Inbound:
|
||||
return false
|
||||
case reference.IsICMP(conn.Entity.Protocol):
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -9,10 +9,12 @@ import (
|
|||
"sync/atomic"
|
||||
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/firewall/interception/dnsmonitor"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/state"
|
||||
"github.com/safing/portmaster/service/profile"
|
||||
"github.com/safing/portmaster/service/resolver"
|
||||
)
|
||||
|
||||
// Events.
|
||||
|
@ -188,4 +190,6 @@ func New(instance instance) (*Network, error) {
|
|||
|
||||
type instance interface {
|
||||
Profile() *profile.ProfileModule
|
||||
Resolver() *resolver.ResolverModule
|
||||
DNSMonitor() *dnsmonitor.DNSMonitor
|
||||
}
|
||||
|
|
|
@ -106,11 +106,12 @@ func checkError(packet gopacket.Packet, info *Info) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Parse parses an IP packet and saves the information in the given packet object.
|
||||
func Parse(packetData []byte, pktBase *Base) (err error) {
|
||||
// ParseLayer3 parses an IP packet and saves the information in the given packet object.
|
||||
func ParseLayer3(packetData []byte, pktBase *Base) (err error) {
|
||||
if len(packetData) == 0 {
|
||||
return errors.New("empty packet")
|
||||
}
|
||||
|
||||
pktBase.layer3Data = packetData
|
||||
|
||||
ipVersion := packetData[0] >> 4
|
||||
|
@ -155,6 +156,62 @@ func Parse(packetData []byte, pktBase *Base) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ParseLayer4 parses an layer 4 packet and saves the information in the given packet object.
|
||||
func ParseLayer4(packetData []byte, pktBase *Base) (err error) {
|
||||
if len(packetData) == 0 {
|
||||
return errors.New("empty packet")
|
||||
}
|
||||
|
||||
var layer gopacket.LayerType
|
||||
switch pktBase.info.Protocol {
|
||||
case ICMP:
|
||||
layer = layers.LayerTypeICMPv4
|
||||
case IGMP:
|
||||
layer = layers.LayerTypeIGMP
|
||||
case TCP:
|
||||
layer = layers.LayerTypeTCP
|
||||
case UDP:
|
||||
layer = layers.LayerTypeUDP
|
||||
case ICMPv6:
|
||||
layer = layers.LayerTypeICMPv6
|
||||
case UDPLite:
|
||||
return fmt.Errorf("UDPLite not supported")
|
||||
case RAW:
|
||||
return fmt.Errorf("RAW protocol not supported")
|
||||
case AnyHostInternalProtocol61:
|
||||
return fmt.Errorf("AnyHostInternalProtocol61 protocol not supported")
|
||||
default:
|
||||
return fmt.Errorf("protocol not supported")
|
||||
}
|
||||
|
||||
packet := gopacket.NewPacket(packetData, layer, gopacket.DecodeOptions{
|
||||
Lazy: true,
|
||||
NoCopy: true,
|
||||
})
|
||||
|
||||
availableDecoders := []func(gopacket.Packet, *Info) error{
|
||||
parseTCP,
|
||||
parseUDP,
|
||||
// parseUDPLite, // We don't yet support udplite.
|
||||
parseICMPv4,
|
||||
parseICMPv6,
|
||||
parseIGMP,
|
||||
checkError,
|
||||
}
|
||||
|
||||
for _, dec := range availableDecoders {
|
||||
if err := dec(packet, pktBase.Info()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pktBase.layers = packet
|
||||
if transport := packet.TransportLayer(); transport != nil {
|
||||
pktBase.layer5Data = transport.LayerPayload()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
genIPProtocolFromLayerType()
|
||||
}
|
||||
|
|
|
@ -52,6 +52,27 @@ type ResolvedDomain struct {
|
|||
Expires int64
|
||||
}
|
||||
|
||||
// AddCNAMEs adds all cnames from the map related to its set Domain.
|
||||
func (resolved *ResolvedDomain) AddCNAMEs(cnames map[string]string) {
|
||||
// Resolve all CNAMEs in the correct order and add the to the record - up to max 50 layers.
|
||||
domain := resolved.Domain
|
||||
domainLoop:
|
||||
for range 50 {
|
||||
nextDomain, isCNAME := cnames[domain]
|
||||
switch {
|
||||
case !isCNAME:
|
||||
break domainLoop
|
||||
case nextDomain == resolved.Domain:
|
||||
break domainLoop
|
||||
case nextDomain == domain:
|
||||
break domainLoop
|
||||
}
|
||||
|
||||
resolved.CNAMEs = append(resolved.CNAMEs, nextDomain)
|
||||
domain = nextDomain
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of ResolvedDomain including
|
||||
// the CNAME chain. It implements fmt.Stringer.
|
||||
func (resolved *ResolvedDomain) String() string {
|
||||
|
|
|
@ -29,6 +29,8 @@ type ResolverModule struct { //nolint
|
|||
failingResolverWorkerMgr *mgr.WorkerMgr
|
||||
suggestUsingStaleCacheTask *mgr.WorkerMgr
|
||||
|
||||
isDisabled atomic.Bool
|
||||
|
||||
states *mgr.StateMgr
|
||||
}
|
||||
|
||||
|
@ -52,6 +54,10 @@ func (rm *ResolverModule) Stop() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (rm *ResolverModule) IsDisabled() bool {
|
||||
return rm.isDisabled.Load()
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
// Set DNS test connectivity function for the online status check
|
||||
netenv.DNSTestQueryFunc = func(ctx context.Context, fdqn string) (ips []net.IP, ok bool, err error) {
|
||||
|
|
|
@ -17,17 +17,22 @@ import (
|
|||
|
||||
// DNS Resolver Attributes.
|
||||
const (
|
||||
ServerTypeDNS = "dns"
|
||||
ServerTypeTCP = "tcp"
|
||||
ServerTypeDoT = "dot"
|
||||
ServerTypeDoH = "doh"
|
||||
ServerTypeMDNS = "mdns"
|
||||
ServerTypeEnv = "env"
|
||||
ServerTypeDNS = "dns"
|
||||
ServerTypeTCP = "tcp"
|
||||
ServerTypeDoT = "dot"
|
||||
ServerTypeDoH = "doh"
|
||||
ServerTypeMDNS = "mdns"
|
||||
ServerTypeEnv = "env"
|
||||
ServerTypeMonitor = "monitor"
|
||||
ServerTypeFirewall = "firewall"
|
||||
|
||||
ServerSourceConfigured = "config"
|
||||
ServerSourceOperatingSystem = "system"
|
||||
ServerSourceMDNS = "mdns"
|
||||
ServerSourceEnv = "env"
|
||||
ServerSourceETW = "etw"
|
||||
ServerSourceSystemd = "systemd"
|
||||
ServerSourceFirewall = "firewall"
|
||||
)
|
||||
|
||||
// DNS resolver scheme aliases.
|
||||
|
@ -82,11 +87,11 @@ type ResolverInfo struct { //nolint:golint,maligned // TODO
|
|||
Name string
|
||||
|
||||
// Type describes the type of the resolver.
|
||||
// Possible values include dns, tcp, dot, doh, mdns, env.
|
||||
// Possible values include dns, tcp, dot, doh, mdns, env, monitor, firewall.
|
||||
Type string
|
||||
|
||||
// Source describes where the resolver configuration came from.
|
||||
// Possible values include config, system, mdns, env.
|
||||
// Possible values include config, system, mdns, env, etw, systemd, firewall.
|
||||
Source string
|
||||
|
||||
// IP is the IP address of the resolver
|
||||
|
|
|
@ -388,7 +388,6 @@ func loadResolvers() {
|
|||
|
||||
// Resolve module error about missing resolvers.
|
||||
module.states.Remove(missingResolversErrorID)
|
||||
|
||||
// Check if settings were changed and clear name cache when they did.
|
||||
newResolverConfig := configuredNameServers()
|
||||
if len(currentResolverConfig) > 0 &&
|
||||
|
@ -399,6 +398,14 @@ func loadResolvers() {
|
|||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// If no resolvers are configure set the disabled state. So other modules knows that the users does not want to use Portmaster resolver.
|
||||
if len(newResolverConfig) == 0 {
|
||||
module.isDisabled.Store(true)
|
||||
} else {
|
||||
module.isDisabled.Store(false)
|
||||
}
|
||||
|
||||
currentResolverConfig = newResolverConfig
|
||||
|
||||
newResolvers := append(
|
||||
|
@ -431,7 +438,7 @@ func loadResolvers() {
|
|||
// save resolvers
|
||||
globalResolvers = newResolvers
|
||||
|
||||
// assing resolvers to scopes
|
||||
// assign resolvers to scopes
|
||||
setScopedResolvers(globalResolvers)
|
||||
|
||||
// set active resolvers (for cache validation)
|
||||
|
|
|
@ -48,9 +48,9 @@ func EnsureChromeSandboxPermissions(reg *updater.ResourceRegistry) error {
|
|||
sandboxFile := filepath.Join(unpackedPath, "chrome-sandbox")
|
||||
if err := os.Chmod(sandboxFile, 0o0755|os.ModeSetuid); err != nil {
|
||||
log.Errorf(suidBitWarning, 0o0755|os.ModeSetuid, sandboxFile)
|
||||
|
||||
return fmt.Errorf("failed to chmod: %w", err)
|
||||
}
|
||||
|
||||
log.Debugf("updates: fixed SUID permission for chrome-sandbox")
|
||||
|
||||
return nil
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hectane/go-acl"
|
||||
processInfo "github.com/shirou/gopsutil/process"
|
||||
"github.com/tevino/abool"
|
||||
|
||||
|
@ -349,7 +350,12 @@ func upgradeBinary(fileToUpgrade string, file *updater.File) error {
|
|||
}
|
||||
|
||||
// check permissions
|
||||
if !onWindows {
|
||||
if onWindows {
|
||||
err = acl.Chmod(fileToUpgrade, 0o0755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set permissions on %s: %w", fileToUpgrade, err)
|
||||
}
|
||||
} else {
|
||||
info, err := os.Stat(fileToUpgrade)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get file info on %s: %w", fileToUpgrade, err)
|
||||
|
|
2
windows_core_dll/build.ps1
Normal file
2
windows_core_dll/build.ps1
Normal file
|
@ -0,0 +1,2 @@
|
|||
msbuild .\windows_core_dll.sln /p:Configuration=Release
|
||||
ls .\x64\Release\portmaster-core.dll
|
197
windows_core_dll/dllmain.cpp
Normal file
197
windows_core_dll/dllmain.cpp
Normal file
|
@ -0,0 +1,197 @@
|
|||
// dllmain.cpp : Defines the entry point for the DLL application.
|
||||
#include "pch.h"
|
||||
|
||||
#pragma comment(lib, "tdh.lib")
|
||||
|
||||
// GUID of the DNS log provider
|
||||
static const GUID DNS_CLIENT_PROVIDER_GUID = {
|
||||
0x1C95126E,
|
||||
0x7EEA,
|
||||
0x49A9,
|
||||
{0xA3, 0xFE, 0xA3, 0x78, 0xB0, 0x3D, 0xDB, 0x4D} };
|
||||
|
||||
// GUID of the event session. This should be unique for the application.
|
||||
static const GUID PORTMASTER_ETW_SESSION_GUID = {
|
||||
0x0211d070,
|
||||
0xc3b2,
|
||||
0x4609,
|
||||
{0x92, 0xf5, 0x28, 0xe7, 0x18, 0xb2, 0x3b, 0x18} };
|
||||
|
||||
// Name of the session. This is visble when user queries all ETW sessions.
|
||||
// (example `logman query -ets`)
|
||||
#define LOGSESSION_NAME L"PortmasterDNSEventListener"
|
||||
|
||||
// Fuction type of the callback that will be called on each event.
|
||||
typedef uint64_t(*GoEventRecordCallback)(wchar_t* domain, wchar_t* result);
|
||||
|
||||
// Holds the state of the ETW Session.
|
||||
struct ETWSessionState {
|
||||
TRACEHANDLE SessionTraceHandle;
|
||||
EVENT_TRACE_PROPERTIES* SessionProperties;
|
||||
TRACEHANDLE sessionHandle;
|
||||
GoEventRecordCallback callback;
|
||||
};
|
||||
|
||||
// getPropertyValue reads a property from the event.
|
||||
static bool getPropertyValue(PEVENT_RECORD evt, LPWSTR prop, PBYTE* pData) {
|
||||
// Describe the data that needs to be retrieved from the event.
|
||||
PROPERTY_DATA_DESCRIPTOR DataDescriptor;
|
||||
ZeroMemory(&DataDescriptor, sizeof(DataDescriptor));
|
||||
DataDescriptor.PropertyName = (ULONGLONG)(prop);
|
||||
DataDescriptor.ArrayIndex = 0;
|
||||
|
||||
DWORD PropertySize = 0;
|
||||
// Check if the data is avaliable and what is the size of it.
|
||||
DWORD status =
|
||||
TdhGetPropertySize(evt, 0, NULL, 1, &DataDescriptor, &PropertySize);
|
||||
if (ERROR_SUCCESS != status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate memory for the data.
|
||||
*pData = (PBYTE)malloc(PropertySize);
|
||||
if (NULL == *pData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the data.
|
||||
status =
|
||||
TdhGetProperty(evt, 0, NULL, 1, &DataDescriptor, PropertySize, *pData);
|
||||
if (ERROR_SUCCESS != status) {
|
||||
if (*pData) {
|
||||
free(*pData);
|
||||
*pData = NULL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// EventRecordCallback is a callback called on each event.
|
||||
static void WINAPI EventRecordCallback(PEVENT_RECORD eventRecord) {
|
||||
PBYTE resultValue = NULL;
|
||||
PBYTE domainValue = NULL;
|
||||
|
||||
getPropertyValue(eventRecord, (LPWSTR)L"QueryResults", &resultValue);
|
||||
getPropertyValue(eventRecord, (LPWSTR)L"QueryName", &domainValue);
|
||||
|
||||
ETWSessionState* state = (ETWSessionState*)eventRecord->UserContext;
|
||||
|
||||
if (resultValue != NULL && domainValue != NULL) {
|
||||
state->callback((wchar_t*)domainValue, (wchar_t*)resultValue);
|
||||
}
|
||||
|
||||
free(resultValue);
|
||||
free(domainValue);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// PM_ETWCreateState allocates memory for the state and initializes the config for the session. PM_ETWDestroySession must be called to avoid leaks.
|
||||
// callback must be set to a valid function pointer.
|
||||
__declspec(dllexport) ETWSessionState* PM_ETWCreateState(GoEventRecordCallback callback) {
|
||||
// Create trace session properties.
|
||||
ULONG BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME);
|
||||
EVENT_TRACE_PROPERTIES* SessionProperties =
|
||||
(EVENT_TRACE_PROPERTIES*)calloc(1, BufferSize);
|
||||
SessionProperties->Wnode.BufferSize = BufferSize;
|
||||
SessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
SessionProperties->Wnode.ClientContext = 1; // QPC clock resolution
|
||||
SessionProperties->Wnode.Guid = PORTMASTER_ETW_SESSION_GUID;
|
||||
SessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
|
||||
SessionProperties->MaximumFileSize = 1; // MB
|
||||
SessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
|
||||
|
||||
// Create state
|
||||
ETWSessionState* state = (ETWSessionState*)calloc(1, sizeof(ETWSessionState));
|
||||
state->SessionProperties = SessionProperties;
|
||||
state->callback = callback;
|
||||
return state;
|
||||
}
|
||||
|
||||
// PM_ETWInitializeSession initializes the session.
|
||||
__declspec(dllexport) uint32_t PM_ETWInitializeSession(ETWSessionState* state) {
|
||||
return StartTrace(&state->SessionTraceHandle, LOGSESSION_NAME,
|
||||
state->SessionProperties);
|
||||
}
|
||||
|
||||
// PM_ETWStartTrace subscribes to the dns events and start listening. The function blocks while the listener is running.
|
||||
// Call PM_ETWStopTrace to stop the listener.
|
||||
__declspec(dllexport) uint32_t PM_ETWStartTrace(ETWSessionState* state) {
|
||||
ULONG status =
|
||||
EnableTraceEx2(state->SessionTraceHandle, (LPCGUID)&DNS_CLIENT_PROVIDER_GUID,
|
||||
EVENT_CONTROL_CODE_ENABLE_PROVIDER,
|
||||
TRACE_LEVEL_INFORMATION, 0, 0, 0, NULL);
|
||||
|
||||
if (status != ERROR_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
EVENT_TRACE_LOGFILE trace = { 0 };
|
||||
|
||||
trace.LoggerName = (LPWSTR)(LOGSESSION_NAME);
|
||||
trace.ProcessTraceMode =
|
||||
PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
|
||||
trace.EventRecordCallback = EventRecordCallback;
|
||||
trace.Context = state;
|
||||
|
||||
state->sessionHandle = OpenTrace(&trace);
|
||||
if (state->sessionHandle == INVALID_PROCESSTRACE_HANDLE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = ProcessTrace(&state->sessionHandle, 1, NULL, NULL);
|
||||
if (status != ERROR_SUCCESS) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// PM_ETWFlushTrace flushes the event buffer.
|
||||
__declspec(dllexport) uint32_t PM_ETWFlushTrace(ETWSessionState* state) {
|
||||
return ControlTrace(state->SessionTraceHandle, LOGSESSION_NAME,
|
||||
state->SessionProperties, EVENT_TRACE_CONTROL_FLUSH);
|
||||
}
|
||||
|
||||
// PM_ETWFlushTrace stops the listener.
|
||||
__declspec(dllexport) uint32_t PM_ETWStopTrace(ETWSessionState* state) {
|
||||
return ControlTrace(state->SessionTraceHandle, LOGSESSION_NAME, state->SessionProperties,
|
||||
EVENT_TRACE_CONTROL_STOP);
|
||||
}
|
||||
|
||||
// PM_ETWFlushTrace Closes the session and frees resourses.
|
||||
__declspec(dllexport) uint32_t PM_ETWDestroySession(ETWSessionState* state) {
|
||||
if (state == NULL) {
|
||||
return 1;
|
||||
}
|
||||
uint32_t status = CloseTrace(state->sessionHandle);
|
||||
|
||||
// Free memory.
|
||||
free(state->SessionProperties);
|
||||
free(state);
|
||||
return status;
|
||||
}
|
||||
|
||||
// PM_ETWStopOldSession removes old session with the same name if they exist.
|
||||
// It returns success(0) only if its able to delete the old session.
|
||||
__declspec(dllexport) ULONG PM_ETWStopOldSession() {
|
||||
ULONG status = ERROR_SUCCESS;
|
||||
TRACEHANDLE sessionHandle = 0;
|
||||
|
||||
// Create trace session properties
|
||||
size_t bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME);
|
||||
EVENT_TRACE_PROPERTIES* sessionProperties = (EVENT_TRACE_PROPERTIES*)calloc(1, bufferSize);
|
||||
sessionProperties->Wnode.BufferSize = (ULONG)bufferSize;
|
||||
sessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
sessionProperties->Wnode.ClientContext = 1; // QPC clock resolution
|
||||
sessionProperties->Wnode.Guid = PORTMASTER_ETW_SESSION_GUID;
|
||||
sessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
|
||||
|
||||
// Use Control trace will stop the session which will trigger a delete.
|
||||
status = ControlTrace(NULL, LOGSESSION_NAME, sessionProperties, EVENT_TRACE_CONTROL_STOP);
|
||||
|
||||
free(sessionProperties);
|
||||
return status;
|
||||
}
|
||||
}
|
5
windows_core_dll/framework.h
Normal file
5
windows_core_dll/framework.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
// Windows Header Files
|
||||
#include <windows.h>
|
5
windows_core_dll/pch.cpp
Normal file
5
windows_core_dll/pch.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
// pch.cpp: source file corresponding to the pre-compiled header
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
|
22
windows_core_dll/pch.h
Normal file
22
windows_core_dll/pch.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
// pch.h: This is a precompiled header file.
|
||||
// Files listed below are compiled only once, improving build performance for future builds.
|
||||
// This also affects IntelliSense performance, including code completion and many code browsing features.
|
||||
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
|
||||
// Do not add files here that you will be updating frequently as this negates the performance advantage.
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
// add headers that you want to pre-compile here
|
||||
#include "framework.h"
|
||||
|
||||
#include <evntrace.h>
|
||||
#include <tdh.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <evntcons.h>
|
||||
#include <codecvt>
|
||||
#include <thread>
|
||||
|
||||
|
||||
#endif //PCH_H
|
31
windows_core_dll/windows_core_dll.sln
Normal file
31
windows_core_dll/windows_core_dll.sln
Normal file
|
@ -0,0 +1,31 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.11.35222.181
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "windows_core_dll", "windows_core_dll.vcxproj", "{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Debug|x64.Build.0 = Debug|x64
|
||||
{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Debug|x86.Build.0 = Debug|Win32
|
||||
{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Release|x64.ActiveCfg = Release|x64
|
||||
{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Release|x64.Build.0 = Release|x64
|
||||
{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Release|x86.ActiveCfg = Release|Win32
|
||||
{6F3C7EAF-8511-4822-AAF0-1086D27E4DA9}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {8E60106D-49DF-49C7-AC08-02775342FEAE}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
158
windows_core_dll/windows_core_dll.vcxproj
Normal file
158
windows_core_dll/windows_core_dll.vcxproj
Normal file
|
@ -0,0 +1,158 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{6f3c7eaf-8511-4822-aaf0-1086d27e4da9}</ProjectGuid>
|
||||
<RootNamespace>windowscoredll</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>portmaster-core</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;WINDOWSCOREDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;WINDOWSCOREDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;WINDOWSCOREDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;WINDOWSCOREDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
33
windows_core_dll/windows_core_dll.vcxproj.filters
Normal file
33
windows_core_dll/windows_core_dll.vcxproj.filters
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
4
windows_core_dll/windows_core_dll.vcxproj.user
Normal file
4
windows_core_dll/windows_core_dll.vcxproj.user
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
|
@ -105,6 +105,9 @@ pub fn ale_layer_connect_v6(data: CalloutData) {
|
|||
}
|
||||
|
||||
fn ale_layer_auth(mut data: CalloutData, ale_data: AleLayerData) {
|
||||
// Make the default path as drop.
|
||||
data.block_and_absorb();
|
||||
|
||||
let Some(device) = crate::entry::get_device() else {
|
||||
return;
|
||||
};
|
||||
|
|
|
@ -110,9 +110,12 @@ fn ip_packet_layer(
|
|||
interface_index: u32,
|
||||
sub_interface_index: u32,
|
||||
) {
|
||||
// Make the default path as drop.
|
||||
data.block_and_absorb();
|
||||
|
||||
// Block all fragment data. No easy way to keep track of the origin and they are rarely used.
|
||||
if data.is_fragment_data() {
|
||||
data.action_block();
|
||||
data.block_and_absorb();
|
||||
crate::err!("blocked fragment packet");
|
||||
return;
|
||||
}
|
||||
|
@ -147,7 +150,7 @@ fn ip_packet_layer(
|
|||
} {
|
||||
Ok(key) => key,
|
||||
Err(err) => {
|
||||
crate::dbg!("failed to get key from nbl: {}", err);
|
||||
crate::err!("failed to get key from nbl: {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1 +1 @@
|
|||
[2, 0, 5, 0]
|
||||
[2, 0, 6, 0]
|
||||
|
|
|
@ -161,24 +161,28 @@ impl<'a> CalloutData<'a> {
|
|||
pub fn action_permit(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).action_permit();
|
||||
(*self.classify_out).clear_absorb_flag();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action_continue(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).action_continue();
|
||||
(*self.classify_out).clear_absorb_flag();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action_block(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).action_block();
|
||||
(*self.classify_out).clear_absorb_flag();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action_none(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).set_none();
|
||||
(*self.classify_out).clear_absorb_flag();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,13 +202,6 @@ impl<'a> CalloutData<'a> {
|
|||
self.get_value_u32(flags_index) & FWP_CONDITION_FLAG_IS_REAUTHORIZE > 0
|
||||
}
|
||||
|
||||
pub fn parmit_and_absorb(&mut self) {
|
||||
unsafe {
|
||||
(*self.classify_out).action_permit();
|
||||
(*self.classify_out).set_absorb();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_callout_id(&self) -> usize {
|
||||
self.callout_id
|
||||
}
|
||||
|
|
|
@ -80,6 +80,11 @@ impl ClassifyOut {
|
|||
self.flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
|
||||
}
|
||||
|
||||
// Removes the absorb flag.
|
||||
pub fn clear_absorb_flag(&mut self) {
|
||||
self.flags &= !FWPS_CLASSIFY_OUT_FLAG_ABSORB;
|
||||
}
|
||||
|
||||
// Clear the write flag permission. Next filter in the chain will not change the action.
|
||||
pub fn clear_write_flag(&mut self) {
|
||||
self.rights &= !FWPS_RIGHT_ACTION_WRITE;
|
||||
|
|
|
@ -62,7 +62,7 @@ pub(crate) fn register_sublayer(
|
|||
sublayer.displayData.name = name.as_ptr() as _;
|
||||
sublayer.displayData.description = description.as_ptr() as _;
|
||||
sublayer.flags = 0;
|
||||
sublayer.weight = 0xFFFF;
|
||||
sublayer.weight = 0xFFFF; // Set to Max value. Weight compared to other sublayers.
|
||||
|
||||
let status = FwpmSubLayerAdd0(filter_engine_handle, &sublayer, core::ptr::null_mut());
|
||||
check_ntstatus(status as i32)?;
|
||||
|
|
Loading…
Add table
Reference in a new issue