Merge branch 'develop' into feature/new-installer

This commit is contained in:
Vladimir Stoilov 2025-01-14 15:30:57 +02:00
commit 5039e9efca
No known key found for this signature in database
GPG key ID: 2F190B67A43A81AF
40 changed files with 929 additions and 163 deletions

3
.gitignore vendored
View file

@ -12,8 +12,7 @@ go.mod.*
vendor vendor
# testing # testing
testing testdata
spn/testing/simple/testdata
# Compiled Object files, Static and Dynamic libs (Shared Objects) # Compiled Object files, Static and Dynamic libs (Shared Objects)
*.a *.a

View file

@ -70,6 +70,11 @@ build:
# ./dist/all/assets.zip # ./dist/all/assets.zip
BUILD +assets BUILD +assets
build-spn:
BUILD +go-build --CMDS="hub" --GOOS="linux" --GOARCH="amd64"
BUILD +go-build --CMDS="hub" --GOOS="linux" --GOARCH="arm64"
# TODO: Add other platforms
go-ci: go-ci:
BUILD +go-build --GOOS="linux" --GOARCH="amd64" BUILD +go-build --GOOS="linux" --GOARCH="amd64"
BUILD +go-build --GOOS="linux" --GOARCH="arm64" BUILD +go-build --GOOS="linux" --GOARCH="arm64"

View file

@ -6,6 +6,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/safing/portmaster/base/utils"
"github.com/tevino/abool" "github.com/tevino/abool"
) )
@ -23,10 +24,14 @@ func Initialize(databasesRootDir string) error {
if initialized.SetToIf(false, true) { if initialized.SetToIf(false, true) {
rootDir = databasesRootDir rootDir = databasesRootDir
// Ensure database root dir exists.
err := os.MkdirAll(rootDir, 0o0700) err := os.MkdirAll(rootDir, 0o0700)
if err != nil { if err != nil {
return fmt.Errorf("could not create/open database directory (%s): %w", rootDir, err) return fmt.Errorf("failed to create/check database dir %q: %w", rootDir, err)
}
// ensure root and databases dirs
err = utils.EnsureDirectory(rootDir, utils.AdminOnlyPermission)
if err != nil {
return fmt.Errorf("could not set permissions to database directory (%s): %w", rootDir, err)
} }
return nil return nil
@ -63,5 +68,9 @@ func getLocation(name, storageType string) (string, error) {
if err != nil { if err != nil {
return "", fmt.Errorf("failed to create/check database dir %q: %w", location, err) return "", fmt.Errorf("failed to create/check database dir %q: %w", location, err)
} }
err = utils.EnsureDirectory(location, utils.AdminOnlyPermission)
if err != nil {
return "", fmt.Errorf("could not set permissions to directory (%s): %w", location, err)
}
return location, nil return location, nil
} }

View file

@ -15,7 +15,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/hectane/go-acl"
"github.com/safing/portmaster/base/database/iterator" "github.com/safing/portmaster/base/database/iterator"
"github.com/safing/portmaster/base/database/query" "github.com/safing/portmaster/base/database/query"
"github.com/safing/portmaster/base/database/record" "github.com/safing/portmaster/base/database/record"
@ -289,11 +288,8 @@ func writeFile(filename string, data []byte, perm os.FileMode) error {
defer t.Cleanup() //nolint:errcheck defer t.Cleanup() //nolint:errcheck
// Set permissions before writing data, in case the data is sensitive. // Set permissions before writing data, in case the data is sensitive.
if onWindows { // TODO(vladimir): to set permissions on windows we need the full path of the file.
err = acl.Chmod(filename, perm) err = t.Chmod(perm)
} else {
err = t.Chmod(perm)
}
if err != nil { if err != nil {
return err return err
} }

View file

@ -74,6 +74,7 @@ func Set(setName string, setVersion string, setLicenseName string) {
if setVersion != "" { if setVersion != "" {
version = setVersion version = setVersion
versionNumber = setVersion
} }
} }

View file

@ -6,15 +6,13 @@ import (
"io/fs" "io/fs"
"os" "os"
"runtime" "runtime"
"github.com/hectane/go-acl"
) )
const isWindows = runtime.GOOS == "windows" const isWindows = runtime.GOOS == "windows"
// EnsureDirectory ensures that the given directory exists and that is has the given permissions set. // EnsureDirectory ensures that the given directory exists and that is has the given permissions set.
// If path is a file, it is deleted and a directory created. // If path is a file, it is deleted and a directory created.
func EnsureDirectory(path string, perm os.FileMode) error { func EnsureDirectory(path string, perm FSPermission) error {
// open path // open path
f, err := os.Stat(path) f, err := os.Stat(path)
if err == nil { if err == nil {
@ -23,10 +21,10 @@ func EnsureDirectory(path string, perm os.FileMode) error {
// directory exists, check permissions // directory exists, check permissions
if isWindows { if isWindows {
// Ignore windows permission error. For none admin users it will always fail. // Ignore windows permission error. For none admin users it will always fail.
acl.Chmod(path, perm) _ = SetDirPermission(path, perm)
return nil return nil
} else if f.Mode().Perm() != perm { } else if f.Mode().Perm() != perm.AsUnixDirExecPermission() {
return os.Chmod(path, perm) return SetDirPermission(path, perm)
} }
return nil return nil
} }
@ -37,17 +35,17 @@ func EnsureDirectory(path string, perm os.FileMode) error {
} }
// file does not exist (or has been deleted) // file does not exist (or has been deleted)
if err == nil || errors.Is(err, fs.ErrNotExist) { if err == nil || errors.Is(err, fs.ErrNotExist) {
err = os.Mkdir(path, perm) err = os.MkdirAll(path, perm.AsUnixDirExecPermission())
if err != nil { if err != nil {
return fmt.Errorf("could not create dir %s: %w", path, err) return fmt.Errorf("could not create dir %s: %w", path, err)
} }
if isWindows { // Set permissions.
// Ignore windows permission error. For none admin users it will always fail. err = SetDirPermission(path, perm)
acl.Chmod(path, perm) // Ignore windows permission error. For none admin users it will always fail.
return nil if !isWindows {
} else { return err
return os.Chmod(path, perm)
} }
return nil
} }
// other error opening path // other error opening path
return fmt.Errorf("failed to access %s: %w", path, err) return fmt.Errorf("failed to access %s: %w", path, err)

20
base/utils/permissions.go Normal file
View file

@ -0,0 +1,20 @@
//go:build !windows
package utils
import "os"
// SetDirPermission sets the permission of a directory.
func SetDirPermission(path string, perm FSPermission) error {
return os.Chmod(path, perm.AsUnixDirExecPermission())
}
// SetExecPermission sets the permission of an executable file.
func SetExecPermission(path string, perm FSPermission) error {
return SetDirPermission(path, perm)
}
// SetFilePermission sets the permission of a non executable file.
func SetFilePermission(path string, perm FSPermission) error {
return os.Chmod(path, perm.AsUnixFilePermission())
}

View file

@ -0,0 +1,79 @@
//go:build windows
package utils
import (
"github.com/hectane/go-acl"
"golang.org/x/sys/windows"
)
var (
systemSID *windows.SID
adminsSID *windows.SID
usersSID *windows.SID
)
func init() {
// Initialize Security ID for all need groups.
// Reference: https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers
var err error
systemSID, err = windows.StringToSid("S-1-5-18") // SYSTEM (Local System)
if err != nil {
panic(err)
}
adminsSID, err = windows.StringToSid("S-1-5-32-544") // Administrators
if err != nil {
panic(err)
}
usersSID, err = windows.StringToSid("S-1-5-32-545") // Users
if err != nil {
panic(err)
}
}
// SetDirPermission sets the permission of a directory.
func SetDirPermission(path string, perm FSPermission) error {
SetFilePermission(path, perm)
return nil
}
// SetExecPermission sets the permission of an executable file.
func SetExecPermission(path string, perm FSPermission) error {
SetFilePermission(path, perm)
return nil
}
// SetFilePermission sets the permission of a non executable file.
func SetFilePermission(path string, perm FSPermission) {
switch perm {
case AdminOnlyPermission:
// Set only admin rights, remove all others.
acl.Apply(
path,
true,
false,
acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID),
acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID),
)
case PublicReadPermission:
// Set admin rights and read/execute rights for users, remove all others.
acl.Apply(
path,
true,
false,
acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID),
acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID),
acl.GrantSid(windows.GENERIC_READ|windows.GENERIC_EXECUTE, usersSID),
)
case PublicWritePermission:
// Set full control to admin and regular users. Guest users will not have access.
acl.Apply(
path,
true,
false,
acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID),
acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID),
acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, usersSID),
)
}
}

View file

@ -2,25 +2,61 @@ package utils
import ( import (
"fmt" "fmt"
"os" "io/fs"
"path/filepath" "path/filepath"
"strings" "strings"
"sync" "sync"
) )
type FSPermission uint8
const (
AdminOnlyPermission FSPermission = iota
PublicReadPermission
PublicWritePermission
)
// AsUnixDirExecPermission return the corresponding unix permission for a directory or executable.
func (perm FSPermission) AsUnixDirExecPermission() fs.FileMode {
switch perm {
case AdminOnlyPermission:
return 0o700
case PublicReadPermission:
return 0o755
case PublicWritePermission:
return 0o777
}
return 0
}
// AsUnixFilePermission return the corresponding unix permission for a regular file.
func (perm FSPermission) AsUnixFilePermission() fs.FileMode {
switch perm {
case AdminOnlyPermission:
return 0o600
case PublicReadPermission:
return 0o644
case PublicWritePermission:
return 0o666
}
return 0
}
// DirStructure represents a directory structure with permissions that should be enforced. // DirStructure represents a directory structure with permissions that should be enforced.
type DirStructure struct { type DirStructure struct {
sync.Mutex sync.Mutex
Path string Path string
Dir string Dir string
Perm os.FileMode Perm FSPermission
Parent *DirStructure Parent *DirStructure
Children map[string]*DirStructure Children map[string]*DirStructure
} }
// NewDirStructure returns a new DirStructure. // NewDirStructure returns a new DirStructure.
func NewDirStructure(path string, perm os.FileMode) *DirStructure { func NewDirStructure(path string, perm FSPermission) *DirStructure {
return &DirStructure{ return &DirStructure{
Path: path, Path: path,
Perm: perm, Perm: perm,
@ -29,7 +65,7 @@ func NewDirStructure(path string, perm os.FileMode) *DirStructure {
} }
// ChildDir adds a new child DirStructure and returns it. Should the child already exist, the existing child is returned and the permissions are updated. // ChildDir adds a new child DirStructure and returns it. Should the child already exist, the existing child is returned and the permissions are updated.
func (ds *DirStructure) ChildDir(dirName string, perm os.FileMode) (child *DirStructure) { func (ds *DirStructure) ChildDir(dirName string, perm FSPermission) (child *DirStructure) {
ds.Lock() ds.Lock()
defer ds.Unlock() defer ds.Unlock()

View file

@ -13,13 +13,13 @@ func ExampleDirStructure() {
// output: // output:
// / [755] // / [755]
// /repo [777] // /repo [777]
// /repo/b [707] // /repo/b [755]
// /repo/b/c [750] // /repo/b/c [777]
// /repo/b/d [707] // /repo/b/d [755]
// /repo/b/d/e [707] // /repo/b/d/e [755]
// /repo/b/d/f [707] // /repo/b/d/f [755]
// /repo/b/d/f/g [707] // /repo/b/d/f/g [755]
// /repo/b/d/f/g/h [707] // /repo/b/d/f/g/h [755]
// /secret [700] // /secret [700]
basePath, err := os.MkdirTemp("", "") basePath, err := os.MkdirTemp("", "")
@ -28,12 +28,12 @@ func ExampleDirStructure() {
return return
} }
ds := NewDirStructure(basePath, 0o0755) ds := NewDirStructure(basePath, PublicReadPermission)
secret := ds.ChildDir("secret", 0o0700) secret := ds.ChildDir("secret", AdminOnlyPermission)
repo := ds.ChildDir("repo", 0o0777) repo := ds.ChildDir("repo", PublicWritePermission)
_ = repo.ChildDir("a", 0o0700) _ = repo.ChildDir("a", AdminOnlyPermission)
b := repo.ChildDir("b", 0o0707) b := repo.ChildDir("b", PublicReadPermission)
c := b.ChildDir("c", 0o0750) c := b.ChildDir("c", PublicWritePermission)
err = ds.Ensure() err = ds.Ensure()
if err != nil { if err != nil {

View file

@ -33,7 +33,7 @@ var (
) )
func init() { func init() {
// Add persisent flags for all commands. // Add persistent flags for all commands.
rootCmd.PersistentFlags().StringVar(&binDir, "bin-dir", "", "set directory for executable binaries (rw/ro)") rootCmd.PersistentFlags().StringVar(&binDir, "bin-dir", "", "set directory for executable binaries (rw/ro)")
rootCmd.PersistentFlags().StringVar(&dataDir, "data-dir", "", "set directory for variable data (rw)") rootCmd.PersistentFlags().StringVar(&dataDir, "data-dir", "", "set directory for variable data (rw)")
@ -61,8 +61,8 @@ func main() {
} }
func initializeGlobals(cmd *cobra.Command, args []string) { func initializeGlobals(cmd *cobra.Command, args []string) {
// Set version info. // Set name and license.
info.Set("SPN Hub", "", "GPLv3") info.Set("SPN Hub", "0.7.8", "GPLv3")
// Configure metrics. // Configure metrics.
_ = metrics.SetNamespace("hub") _ = metrics.SetNamespace("hub")
@ -78,6 +78,7 @@ func initializeGlobals(cmd *cobra.Command, args []string) {
svc, err := service.New(svcCfg) svc, err := service.New(svcCfg)
return svc, err return svc, err
} }
cmdbase.SvcConfig = &service.ServiceConfig{ cmdbase.SvcConfig = &service.ServiceConfig{
BinDir: binDir, BinDir: binDir,
DataDir: dataDir, DataDir: dataDir,

View file

@ -35,7 +35,7 @@ var (
) )
func init() { func init() {
// Add persisent flags for all commands. // Add persistent flags for all commands.
rootCmd.PersistentFlags().StringVar(&binDir, "bin-dir", "", "set directory for executable binaries (rw/ro)") rootCmd.PersistentFlags().StringVar(&binDir, "bin-dir", "", "set directory for executable binaries (rw/ro)")
rootCmd.PersistentFlags().StringVar(&dataDir, "data-dir", "", "set directory for variable data (rw)") rootCmd.PersistentFlags().StringVar(&dataDir, "data-dir", "", "set directory for variable data (rw)")

43
go.mod
View file

@ -14,7 +14,7 @@ require (
github.com/awalterschulze/gographviz v2.0.3+incompatible github.com/awalterschulze/gographviz v2.0.3+incompatible
github.com/bluele/gcache v0.0.2 github.com/bluele/gcache v0.0.2
github.com/brianvoe/gofakeit v3.18.0+incompatible github.com/brianvoe/gofakeit v3.18.0+incompatible
github.com/cilium/ebpf v0.16.0 github.com/cilium/ebpf v0.17.1
github.com/coreos/go-iptables v0.8.0 github.com/coreos/go-iptables v0.8.0
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/dgraph-io/badger v1.6.2 github.com/dgraph-io/badger v1.6.2
@ -33,10 +33,10 @@ require (
github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/go-version v1.7.0
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb
github.com/jackc/puddle/v2 v2.2.2 github.com/jackc/puddle/v2 v2.2.2
github.com/lmittmann/tint v1.0.5 github.com/lmittmann/tint v1.0.6
github.com/maruel/panicparse/v2 v2.3.1 github.com/maruel/panicparse/v2 v2.4.0
github.com/mat/besticon v3.12.0+incompatible github.com/mat/besticon v3.12.0+incompatible
github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-colorable v0.1.14
github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-isatty v0.0.20
github.com/miekg/dns v1.1.62 github.com/miekg/dns v1.1.62
github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/copystructure v1.2.0
@ -61,29 +61,28 @@ require (
github.com/varlink/go v0.4.0 github.com/varlink/go v0.4.0
github.com/vincent-petithory/dataurl v1.0.0 github.com/vincent-petithory/dataurl v1.0.0
go.etcd.io/bbolt v1.3.11 go.etcd.io/bbolt v1.3.11
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
golang.org/x/image v0.22.0 golang.org/x/image v0.23.0
golang.org/x/net v0.31.0 golang.org/x/net v0.34.0
golang.org/x/sync v0.9.0 golang.org/x/sync v0.10.0
golang.org/x/sys v0.27.0 golang.org/x/sys v0.29.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
zombiezen.com/go/sqlite v1.4.0 zombiezen.com/go/sqlite v1.4.0
) )
require ( require (
al.essio.dev/pkg/shellescape v1.5.1 // indirect
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
github.com/aead/ecdh v0.2.0 // indirect github.com/aead/ecdh v0.2.0 // indirect
github.com/alessio/shellescape v1.4.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/danieljoos/wincred v1.2.1 // indirect github.com/danieljoos/wincred v1.2.2 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgraph-io/ristretto v0.2.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f // indirect github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f // indirect
github.com/golang/glog v1.2.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
@ -112,16 +111,16 @@ require (
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zalando/go-keyring v0.2.5 // indirect github.com/zalando/go-keyring v0.2.6 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect github.com/zeebo/blake3 v0.2.4 // indirect
golang.org/x/crypto v0.29.0 // indirect golang.org/x/crypto v0.32.0 // indirect
golang.org/x/mod v0.22.0 // indirect golang.org/x/mod v0.22.0 // indirect
golang.org/x/text v0.20.0 // indirect golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.27.0 // indirect golang.org/x/tools v0.29.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect google.golang.org/protobuf v1.36.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
modernc.org/libc v1.61.0 // indirect modernc.org/libc v1.61.7 // indirect
modernc.org/mathutil v1.6.0 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.0 // indirect modernc.org/memory v1.8.1 // indirect
modernc.org/sqlite v1.33.1 // indirect modernc.org/sqlite v1.34.4 // indirect
) )

123
go.sum
View file

@ -1,3 +1,5 @@
al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
@ -14,8 +16,6 @@ github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 h1:5L8Mj9Co9sJVgW3TpY
github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6/go.mod h1:3HgLJ9d18kXMLQlJvIY3+FszZYMxCz8WfE2MQ7hDY0w= github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6/go.mod h1:3HgLJ9d18kXMLQlJvIY3+FszZYMxCz8WfE2MQ7hDY0w=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
@ -27,13 +27,12 @@ github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6
github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8= github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8=
github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= github.com/cilium/ebpf v0.17.1 h1:G8mzU81R2JA1nE5/8SRubzqvBMmAmri2VL8BIZPWvV0=
github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= github.com/cilium/ebpf v0.17.1/go.mod h1:vay2FaYSmIlv3r8dNACd4mW/OCaZLJKJOo+IHBvCIO8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc= github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc=
@ -41,18 +40,19 @@ github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFE
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs= github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dhaavi/winres v0.2.2 h1:SUago7FwhgLSMyDdeuV6enBZ+ZQSl0KwcnbWzvlfBls= github.com/dhaavi/winres v0.2.2 h1:SUago7FwhgLSMyDdeuV6enBZ+ZQSl0KwcnbWzvlfBls=
github.com/dhaavi/winres v0.2.2/go.mod h1:1NTs+/DtKP1BplIL1+XQSoq4X1PUfLczexS7gf3x9T4= github.com/dhaavi/winres v0.2.2/go.mod h1:1NTs+/DtKP1BplIL1+XQSoq4X1PUfLczexS7gf3x9T4=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@ -92,9 +92,6 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
github.com/golang/gddo v0.0.0-20180823221919-9d8ff1c67be5/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/gddo v0.0.0-20180823221919-9d8ff1c67be5/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg= github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg=
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4= github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@ -167,21 +164,18 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lmittmann/tint v1.0.5 h1:NQclAutOfYsqs2F1Lenue6OoWCajs5wJcP3DfWVpePw= github.com/lmittmann/tint v1.0.6 h1:vkkuDAZXc0EFGNzYjWcV0h7eEX+uujH48f/ifSkJWgc=
github.com/lmittmann/tint v1.0.5/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/lmittmann/tint v1.0.6/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/maruel/panicparse/v2 v2.3.1 h1:NtJavmbMn0DyzmmSStE8yUsmPZrZmudPH7kplxBinOA= github.com/maruel/panicparse/v2 v2.4.0 h1:yQKMIbQ0DKfinzVkTkcUzQyQ60UCiNnYfR7PWwTs2VI=
github.com/maruel/panicparse/v2 v2.3.1/go.mod h1:s3UmQB9Fm/n7n/prcD2xBGDkwXD6y2LeZnhbEXvs9Dg= github.com/maruel/panicparse/v2 v2.4.0/go.mod h1:nOY2OKe8csO3F3SA5+hsxot05JLgukrF54B9x88fVp4=
github.com/mat/besticon v3.12.0+incompatible h1:1KTD6wisfjfnX+fk9Kx/6VEZL+MAW1LhCkL9Q47H9Bg= github.com/mat/besticon v3.12.0+incompatible h1:1KTD6wisfjfnX+fk9Kx/6VEZL+MAW1LhCkL9Q47H9Bg=
github.com/mat/besticon v3.12.0+incompatible/go.mod h1:mA1auQYHt6CW5e7L9HJLmqVQC8SzNk2gVwouO0AbiEU= github.com/mat/besticon v3.12.0+incompatible/go.mod h1:mA1auQYHt6CW5e7L9HJLmqVQC8SzNk2gVwouO0AbiEU=
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
@ -207,7 +201,6 @@ github.com/mdlayher/socket v0.1.0/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5A
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
@ -325,8 +318,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8= github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
@ -340,12 +333,12 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -374,15 +367,15 @@ golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -408,7 +401,6 @@ golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -416,13 +408,10 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -430,16 +419,16 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -448,8 +437,8 @@ google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -461,28 +450,28 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
honnef.co/go/tools v0.2.2/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.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4= modernc.org/ccgo/v4 v4.23.10 h1:DnDZT/H6TtoJvQmVf7d8W+lVqEZpIJY/+0ENFh1LIHE=
modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0= modernc.org/ccgo/v4 v4.23.10/go.mod h1:vdN4h2WR5aEoNondUx26K7G8X+nuBscYnAEWSRmN2/0=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= 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.6.1 h1:+Qf6xdG8l7B27TQ8D8lw/iFMUj1RXRBOuMUWziJOsk8=
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= modernc.org/gc/v2 v2.6.1/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.61.0 h1:eGFcvWpqlnoGwzZeZe3PWJkkKbM/3SUGyk1DVZQ0TpE= modernc.org/libc v1.61.7 h1:exz8rasFniviSgh3dH7QBnQHqYh9lolA5hVYfsiwkfo=
modernc.org/libc v1.61.0/go.mod h1:DvxVX89wtGTu+r72MLGhygpfi3aUGgZRdAYGCAVVud0= modernc.org/libc v1.61.7/go.mod h1:xspSrXRNVSfWfcfqgvZDVe/Hw5kv4FVC6IRfoms5v/0=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= modernc.org/memory v1.8.1 h1:HS1HRg1jEohnuONobEq2WrLEhLyw8+J42yLFTnllm2A=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= modernc.org/memory v1.8.1/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM= modernc.org/sqlite v1.34.4 h1:sjdARozcL5KJBvYQvLlZEmctRgW9xqIZc2ncN7PU0P8=
modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= modernc.org/sqlite v1.34.4/go.mod h1:3QQFCG2SEMtc2nv+Wq4cQCH7Hjcg+p/RMlS1XK+zwbk=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
zombiezen.com/go/sqlite v1.4.0 h1:N1s3RIljwtp4541Y8rM880qgGIgq3fTD2yks1xftnKU= zombiezen.com/go/sqlite v1.4.0 h1:N1s3RIljwtp4541Y8rM880qgGIgq3fTD2yks1xftnKU=

View file

@ -14,7 +14,7 @@ import (
) )
type ETWSession struct { type ETWSession struct {
i integration.ETWFunctions i *integration.ETWFunctions
shutdownGuard atomic.Bool shutdownGuard atomic.Bool
shutdownMutex sync.Mutex shutdownMutex sync.Mutex
@ -23,7 +23,10 @@ type ETWSession struct {
} }
// 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. // 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) { func NewSession(etwInterface *integration.ETWFunctions, callback func(domain string, result string)) (*ETWSession, error) {
if etwInterface == nil {
return nil, fmt.Errorf("etw interface was nil")
}
etwSession := &ETWSession{ etwSession := &ETWSession{
i: etwInterface, i: etwInterface,
} }
@ -47,7 +50,7 @@ func NewSession(etwInterface integration.ETWFunctions, callback func(domain stri
// Initialize session. // Initialize session.
err := etwSession.i.InitializeSession(etwSession.state) err := etwSession.i.InitializeSession(etwSession.state)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to initialzie session: %q", err) return nil, fmt.Errorf("failed to initialize session: %q", err)
} }
return etwSession, nil return etwSession, nil
@ -65,6 +68,10 @@ func (l *ETWSession) IsRunning() bool {
// FlushTrace flushes the trace buffer. // FlushTrace flushes the trace buffer.
func (l *ETWSession) FlushTrace() error { func (l *ETWSession) FlushTrace() error {
if l.i == nil {
return fmt.Errorf("session not initialized")
}
l.shutdownMutex.Lock() l.shutdownMutex.Lock()
defer l.shutdownMutex.Unlock() defer l.shutdownMutex.Unlock()
@ -83,6 +90,9 @@ func (l *ETWSession) StopTrace() error {
// DestroySession closes the session and frees the allocated memory. Listener cannot be used after this function is called. // DestroySession closes the session and frees the allocated memory. Listener cannot be used after this function is called.
func (l *ETWSession) DestroySession() error { func (l *ETWSession) DestroySession() error {
if l.i == nil {
return fmt.Errorf("session not initialized")
}
l.shutdownMutex.Lock() l.shutdownMutex.Lock()
defer l.shutdownMutex.Unlock() defer l.shutdownMutex.Unlock()

View file

@ -23,22 +23,38 @@ func newListener(module *DNSMonitor) (*Listener, error) {
ResolverInfo.Source = resolver.ServerSourceETW ResolverInfo.Source = resolver.ServerSourceETW
listener := &Listener{} listener := &Listener{}
var err error
// Initialize new dns event session. // Initialize new dns event session.
listener.etw, err = NewSession(module.instance.OSIntegration().GetETWInterface(), listener.processEvent) err := initializeSessions(module, listener)
if err != nil { if err != nil {
return nil, err // Listen for event if the dll has been loaded
module.instance.OSIntegration().OnInitializedEvent.AddCallback("loader-listener", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) {
err = initializeSessions(module, listener)
if err != nil {
return false, err
}
return true, nil
})
} }
// Start listening for events.
module.mgr.Go("etw-dns-event-listener", func(w *mgr.WorkerCtx) error {
return listener.etw.StartTrace()
})
return listener, nil return listener, nil
} }
func initializeSessions(module *DNSMonitor, listener *Listener) error {
var err error
listener.etw, err = NewSession(module.instance.OSIntegration().GetETWInterface(), listener.processEvent)
if err != nil {
return err
}
// Start listener
module.mgr.Go("etw-dns-event-listener", func(w *mgr.WorkerCtx) error {
return listener.etw.StartTrace()
})
return nil
}
func (l *Listener) flush() error { func (l *Listener) flush() error {
if l.etw == nil {
return fmt.Errorf("etw not initialized")
}
return l.etw.FlushTrace() return l.etw.FlushTrace()
} }

View file

@ -19,8 +19,8 @@ type ETWFunctions struct {
stopOldSession *windows.Proc stopOldSession *windows.Proc
} }
func initializeETW(dll *windows.DLL) (ETWFunctions, error) { func initializeETW(dll *windows.DLL) (*ETWFunctions, error) {
var functions ETWFunctions functions := &ETWFunctions{}
var err error var err error
functions.createState, err = dll.FindProc("PM_ETWCreateState") functions.createState, err = dll.FindProc("PM_ETWCreateState")
if err != nil { if err != nil {

View file

@ -5,23 +5,54 @@ package integration
import ( import (
"fmt" "fmt"
"sync"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/service/mgr"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
type OSSpecific struct { type OSSpecific struct {
dll *windows.DLL dll *windows.DLL
etwFunctions ETWFunctions etwFunctions *ETWFunctions
} }
// Initialize loads the dll and finds all the needed functions from it. // Initialize loads the dll and finds all the needed functions from it.
func (i *OSIntegration) Initialize() error { func (i *OSIntegration) Initialize() error {
// Try to load dll
err := i.loadDLL()
if err != nil {
log.Errorf("integration: failed to load dll: %s", err)
callbackLock := sync.Mutex{}
// listen for event from the updater and try to load again if any.
i.instance.BinaryUpdates().EventResourcesUpdated.AddCallback("core-dll-loader", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) {
// Make sure no multiple callas are executed at the same time.
callbackLock.Lock()
defer callbackLock.Unlock()
// Try to load again.
err = i.loadDLL()
if err != nil {
log.Errorf("integration: failed to load dll: %s", err)
} else {
log.Info("integration: initialize successful after updater event")
}
return false, nil
})
} else {
log.Info("integration: initialize successful")
}
return nil
}
func (i *OSIntegration) loadDLL() error {
// Find path to the dll. // Find path to the dll.
file, err := i.instance.BinaryUpdates().GetFile("portmaster-core.dll") file, err := i.instance.BinaryUpdates().GetFile("portmaster-core.dll")
if err != nil { if err != nil {
return err return err
} }
// Load the DLL. // Load the DLL.
i.os.dll, err = windows.LoadDLL(file.Path()) i.os.dll, err = windows.LoadDLL(file.Path())
if err != nil { if err != nil {
@ -34,6 +65,9 @@ func (i *OSIntegration) Initialize() error {
return err return err
} }
// Notify listeners
i.OnInitializedEvent.Submit(struct{}{})
return nil return nil
} }
@ -45,7 +79,7 @@ func (i *OSIntegration) CleanUp() error {
return nil return nil
} }
// GetETWInterface return struct containing all the ETW related functions. // GetETWInterface return struct containing all the ETW related functions, and nil if it was not loaded yet
func (i *OSIntegration) GetETWInterface() ETWFunctions { func (i *OSIntegration) GetETWInterface() *ETWFunctions {
return i.os.etwFunctions return i.os.etwFunctions
} }

View file

@ -7,8 +7,9 @@ import (
// OSIntegration module provides special integration with the OS. // OSIntegration module provides special integration with the OS.
type OSIntegration struct { type OSIntegration struct {
m *mgr.Manager m *mgr.Manager
states *mgr.StateMgr
OnInitializedEvent *mgr.EventMgr[struct{}]
//nolint:unused //nolint:unused
os OSSpecific os OSSpecific
@ -20,10 +21,9 @@ type OSIntegration struct {
func New(instance instance) (*OSIntegration, error) { func New(instance instance) (*OSIntegration, error) {
m := mgr.New("OSIntegration") m := mgr.New("OSIntegration")
module := &OSIntegration{ module := &OSIntegration{
m: m, m: m,
states: m.NewStateMgr(), OnInitializedEvent: mgr.NewEventMgr[struct{}]("on-initialized", m),
instance: instance,
instance: instance,
} }
return module, nil return module, nil

View file

@ -49,7 +49,7 @@ var (
var cache = database.NewInterface(&database.Options{ var cache = database.NewInterface(&database.Options{
Local: true, Local: true,
Internal: true, Internal: true,
CacheSize: 2 ^ 8, CacheSize: 256,
}) })
// getFileFunc is the function used to get a file from // getFileFunc is the function used to get a file from

View file

@ -4,7 +4,7 @@ import "time"
// SleepyTicker is wrapper over time.Ticker that respects the sleep mode of the module. // SleepyTicker is wrapper over time.Ticker that respects the sleep mode of the module.
type SleepyTicker struct { type SleepyTicker struct {
ticker time.Ticker ticker *time.Ticker
normalDuration time.Duration normalDuration time.Duration
sleepDuration time.Duration sleepDuration time.Duration
sleepMode bool sleepMode bool
@ -16,7 +16,7 @@ type SleepyTicker struct {
// If sleepDuration is set to 0 ticker will not tick during sleep. // If sleepDuration is set to 0 ticker will not tick during sleep.
func NewSleepyTicker(normalDuration time.Duration, sleepDuration time.Duration) *SleepyTicker { func NewSleepyTicker(normalDuration time.Duration, sleepDuration time.Duration) *SleepyTicker {
st := &SleepyTicker{ st := &SleepyTicker{
ticker: *time.NewTicker(normalDuration), ticker: time.NewTicker(normalDuration),
normalDuration: normalDuration, normalDuration: normalDuration,
sleepDuration: sleepDuration, sleepDuration: sleepDuration,
sleepMode: false, sleepMode: false,

View file

@ -0,0 +1,57 @@
package mgr
import (
"testing"
"time"
)
func TestSleepyTickerStop(t *testing.T) {
normalDuration := 100 * time.Millisecond
sleepDuration := 200 * time.Millisecond
st := NewSleepyTicker(normalDuration, sleepDuration)
st.Stop() // no panic expected here
}
func TestSleepyTicker(t *testing.T) {
normalDuration := 100 * time.Millisecond
sleepDuration := 200 * time.Millisecond
st := NewSleepyTicker(normalDuration, sleepDuration)
// Test normal mode
select {
case <-st.Wait():
// Expected tick
case <-time.After(normalDuration + 50*time.Millisecond):
t.Error("expected tick in normal mode")
}
// Test sleep mode
st.SetSleep(true)
select {
case <-st.Wait():
// Expected tick
case <-time.After(sleepDuration + 50*time.Millisecond):
t.Error("expected tick in sleep mode")
}
// Test sleep mode with sleepDuration == 0
st = NewSleepyTicker(normalDuration, 0)
st.SetSleep(true)
select {
case <-st.Wait():
t.Error("did not expect tick when sleepDuration is 0")
case <-time.After(normalDuration):
// Expected no tick
}
// Test stopping the ticker
st.Stop()
select {
case <-st.Wait():
t.Error("did not expect tick after stopping the ticker")
case <-time.After(normalDuration):
// Expected no tick
}
}

View file

@ -19,6 +19,7 @@ import (
"github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/config"
"github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/utils"
"github.com/safing/portmaster/service/netquery/orm" "github.com/safing/portmaster/service/netquery/orm"
"github.com/safing/portmaster/service/network" "github.com/safing/portmaster/service/network"
"github.com/safing/portmaster/service/network/netutils" "github.com/safing/portmaster/service/network/netutils"
@ -131,6 +132,10 @@ func New(dbPath string) (*Database, error) {
if err := os.MkdirAll(historyParentDir, 0o0700); err != nil { if err := os.MkdirAll(historyParentDir, 0o0700); err != nil {
return nil, fmt.Errorf("failed to ensure database directory exists: %w", err) return nil, fmt.Errorf("failed to ensure database directory exists: %w", err)
} }
err := utils.EnsureDirectory(historyParentDir, utils.AdminOnlyPermission)
if err != nil {
return nil, fmt.Errorf("failed to set permission to folder %s: %w", historyParentDir, err)
}
// Get file location of history database. // Get file location of history database.
historyFile := filepath.Join(historyParentDir, "history.db") historyFile := filepath.Join(historyParentDir, "history.db")
@ -229,6 +234,10 @@ func VacuumHistory(ctx context.Context) (err error) {
if err := os.MkdirAll(historyParentDir, 0o0700); err != nil { if err := os.MkdirAll(historyParentDir, 0o0700); err != nil {
return fmt.Errorf("failed to ensure database directory exists: %w", err) return fmt.Errorf("failed to ensure database directory exists: %w", err)
} }
err = utils.EnsureDirectory(historyParentDir, utils.AdminOnlyPermission)
if err != nil {
return fmt.Errorf("failed to set permission to folder %s: %w", historyParentDir, err)
}
// Get file location of history database. // Get file location of history database.
historyFile := filepath.Join(historyParentDir, "history.db") historyFile := filepath.Join(historyParentDir, "history.db")

View file

@ -550,7 +550,11 @@ func (conn *Connection) GatherConnectionInfo(pkt packet.Packet) (err error) {
if module.instance.Resolver().IsDisabled() && conn.shouldWaitForDomain() { if module.instance.Resolver().IsDisabled() && conn.shouldWaitForDomain() {
// Flush the dns listener buffer and try again. // Flush the dns listener buffer and try again.
for i := range 4 { for i := range 4 {
_ = module.instance.DNSMonitor().Flush() err = module.instance.DNSMonitor().Flush()
if err != nil {
// Error flushing, dont try again.
break
}
ipinfo, err = resolver.GetIPInfo(resolver.IPInfoProfileScopeGlobal, pkt.Info().RemoteIP().String()) ipinfo, err = resolver.GetIPInfo(resolver.IPInfoProfileScopeGlobal, pkt.Info().RemoteIP().String())
if err == nil { if err == nil {
log.Tracer(pkt.Ctx()).Debugf("network: found domain from dnsmonitor after %d tries", i+1) log.Tracer(pkt.Ctx()).Debugf("network: found domain from dnsmonitor after %d tries", i+1)

View file

@ -11,6 +11,7 @@ import (
"github.com/safing/portmaster/base/database" "github.com/safing/portmaster/base/database"
"github.com/safing/portmaster/base/database/migration" "github.com/safing/portmaster/base/database/migration"
"github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/utils"
_ "github.com/safing/portmaster/service/core/base" _ "github.com/safing/portmaster/service/core/base"
"github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/mgr"
"github.com/safing/portmaster/service/profile/binmeta" "github.com/safing/portmaster/service/profile/binmeta"
@ -66,10 +67,22 @@ func prep() error {
} }
// Setup icon storage location. // Setup icon storage location.
iconsDir := filepath.Join(module.instance.DataDir(), "databases", "icons") databaseDir := filepath.Join(module.instance.DataDir(), "databases")
iconsDir := filepath.Join(databaseDir, "icons")
if err := os.MkdirAll(iconsDir, 0o0700); err != nil { if err := os.MkdirAll(iconsDir, 0o0700); err != nil {
return fmt.Errorf("failed to create/check icons directory: %w", err) return fmt.Errorf("failed to create/check icons directory: %w", err)
} }
// Ensure folder permissions
err := utils.EnsureDirectory(databaseDir, utils.AdminOnlyPermission)
if err != nil {
return fmt.Errorf("failed to set permission to folder %s: %w", databaseDir, err)
}
err = utils.EnsureDirectory(iconsDir, utils.AdminOnlyPermission)
if err != nil {
return fmt.Errorf("failed to set permission to folder %s: %w", iconsDir, err)
}
binmeta.ProfileIconStoragePath = iconsDir binmeta.ProfileIconStoragePath = iconsDir
return nil return nil

View file

@ -510,7 +510,7 @@ func setScopedResolvers(resolvers []*Resolver) {
for _, resolver := range resolvers { for _, resolver := range resolvers {
if resolver.Info.IPScope.IsLAN() { if resolver.Info.IPScope.IsLAN() {
localResolvers = append(localResolvers, resolver) localResolvers = append(localResolvers, resolver)
} else if _, err := netenv.GetLocalNetwork(resolver.Info.IP); err != nil { } else if net, _ := netenv.GetLocalNetwork(resolver.Info.IP); net != nil {
localResolvers = append(localResolvers, resolver) localResolvers = append(localResolvers, resolver)
} }

View file

@ -8,6 +8,7 @@ import (
"github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/api"
"github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/utils"
"github.com/safing/portmaster/service/mgr" "github.com/safing/portmaster/service/mgr"
"github.com/safing/portmaster/service/updates" "github.com/safing/portmaster/service/updates"
) )
@ -35,6 +36,12 @@ func start() error {
log.Warningf("ui: failed to create safe exec dir: %s", err) log.Warningf("ui: failed to create safe exec dir: %s", err)
} }
// Ensure directory permission
err = utils.EnsureDirectory(execDir, utils.PublicWritePermission)
if err != nil {
log.Warningf("ui: failed to set permissions to directory %s: %s", execDir, err)
}
return nil return nil
} }

25
spn/testing/README.md Normal file
View file

@ -0,0 +1,25 @@
# Testing Port17
## Simple Docker Setup
Run `run.sh` to start the docker compose test network.
Then, connect to the test network, by starting the core with the "test" spn map and the correct bootstrap file.
Run `stop.sh` to remove all docker resources again.
Setup Guide can be found in the directory.
## Advanced Setup with Shadow
For advanced testing we use [shadow](https://github.com/shadow/shadow).
The following section will help you set up shadow and will guide you how to test Port17 in a local Shadow environment.
### Setting up
Download the docker version from here: [https://security.cs.georgetown.edu/shadow-docker-images/shadow-standalone.tar.gz](https://security.cs.georgetown.edu/shadow-docker-images/shadow-standalone.tar.gz)
Then import the image into docker with `gunzip -c shadow-standalone.tar.gz | sudo docker load`.
### Running
Execute `sudo docker run -t -i -u shadow shadow-standalone /bin/bash` to start an interactive container with shadow.

View file

@ -0,0 +1,50 @@
# Setup Guide
1. Build SPN Hub
```
cd ../../../cmds/hub/
./build
```
2. Reset any previous state (for a fresh test)
```
./reset-databases.sh
```
3. Change compose file and config template as required
Files:
- `docker-compose.yml`
- `config-template.json`
4. Start test network
```
./run.sh
```
5. Option 1: Join as Hub
For testing just one Hub with a different build or config, you can simply use `./join.sh` to join the network with the most recently build hub binary.
6. Option 2: Join as Portmaster
For connecting to the SPN test network with Portmaster, execute portmaster like this:
```
sudo ../../../cmds/portmaster-core/portmaster-core --disable-shutdown-event --devmode --log debug --data /opt/safing/portmaster --spn-map test --bootstrap-file ./testdata/shared/bootstrap.dsd
```
Note:
This uses the same portmaster data and config as your installed version.
As the SPN Test net operates under a different ID ("test" instead of "main"), this will not pollute the SPN state of your installed Portmaster.
7. Stop the test net
This is important, as just stopping the `./run.sh` script will leave you with interfaces with public IPs!
```
./stop.sh
```

41
spn/testing/simple/clientsim.sh Executable file
View file

@ -0,0 +1,41 @@
#!/bin/bash
cd "$( dirname "${BASH_SOURCE[0]}" )"
realpath() {
path=`eval echo "$1"`
folder=$(dirname "$path")
echo $(cd "$folder"; pwd)/$(basename "$path");
}
if [[ ! -f "../../client" ]]; then
echo "please compile client.go in main directory (output: client)"
exit 1
fi
bin_path="$(realpath ../../client)"
data_path="$(realpath ./testdata)"
if [[ ! -d "$data_path" ]]; then
mkdir "$data_path"
fi
shared_path="$(realpath ./testdata/shared)"
if [[ ! -d "$shared_path" ]]; then
mkdir "$shared_path"
fi
docker network ls | grep spn-simpletest-network >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
docker network create spn-simpletest-network --subnet 6.0.0.0/24
fi
docker run -ti --rm \
--name spn-simpletest-clientsim \
--network spn-simpletest-network \
-v $bin_path:/opt/client:ro \
-v $data_path/clientsim:/opt/data \
-v $shared_path:/opt/shared \
--entrypoint /opt/client \
toolset.safing.network/dev \
--data /opt/data \
--bootstrap-file /opt/shared/bootstrap.dsd \
--log trace $*

View file

@ -0,0 +1,19 @@
{
"core": {
"log": {
"level": "trace"
},
"metrics": {
"instance": "test_$HUBNAME",
"push": ""
}
},
"spn": {
"publicHub": {
"name": "test-$HUBNAME",
"transports": ["http:80", "http:8080", "tcp:17"],
"allowUnencrypted": true,
"bindToAdvertised": true
}
}
}

View file

@ -0,0 +1,139 @@
version: "2.4"
networks:
default:
ipam:
driver: default
config:
- subnet: 6.0.0.0/24
services:
hub1:
container_name: spn-test-simple-hub1
hostname: hub1
image: toolset.safing.network/dev
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_BIN}:/opt/hub1:ro
- ${SPN_TEST_DATA_DIR}/hub1:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.11
hub2:
container_name: spn-test-simple-hub2
hostname: hub2
image: alpine
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_BIN}:/opt/hub2:ro
- ${SPN_TEST_DATA_DIR}/hub2:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.12
hub3:
container_name: spn-test-simple-hub3
hostname: hub3
image: toolset.safing.network/dev
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_BIN}:/opt/hub3:ro
- ${SPN_TEST_DATA_DIR}/hub3:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.13
hub4:
container_name: spn-test-simple-hub4
hostname: hub4
image: toolset.safing.network/dev
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_BIN}:/opt/hub4:ro
- ${SPN_TEST_DATA_DIR}/hub4:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.14
hub5:
container_name: spn-test-simple-hub5
hostname: hub5
image: toolset.safing.network/dev
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_BIN}:/opt/hub5:ro
- ${SPN_TEST_DATA_DIR}/hub5:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.15
hub6:
container_name: spn-test-simple-hub6
hostname: hub6
image: toolset.safing.network/dev
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_OLD_BIN}:/opt/hub6:ro
- ${SPN_TEST_DATA_DIR}/hub6:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.16
hub7:
container_name: spn-test-simple-hub7
hostname: hub7
image: toolset.safing.network/dev
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_OLD_BIN}:/opt/hub7:ro
- ${SPN_TEST_DATA_DIR}/hub7:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.17
hub8:
container_name: spn-test-simple-hub8
hostname: hub8
image: toolset.safing.network/dev
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_OLD_BIN}:/opt/hub8:ro
- ${SPN_TEST_DATA_DIR}/hub8:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.18
hub9:
container_name: spn-test-simple-hub9
hostname: hub9
image: toolset.safing.network/dev
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_OLD_BIN}:/opt/hub9:ro
- ${SPN_TEST_DATA_DIR}/hub9:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.19
hub10:
container_name: spn-test-simple-hub10
hostname: hub10
image: toolset.safing.network/dev
entrypoint: "/opt/shared/entrypoint.sh"
volumes:
- ${SPN_TEST_OLD_BIN}:/opt/hub10:ro
- ${SPN_TEST_DATA_DIR}/hub10:/opt/data
- ${SPN_TEST_SHARED_DATA_DIR}:/opt/shared
networks:
default:
ipv4_address: 6.0.0.20

View file

@ -0,0 +1,17 @@
#!/bin/sh
# Get hostname.
HUBNAME=$HOSTNAME
if [ "$HUBNAME" = "" ]; then
HUBNAME=$(cat /etc/hostname)
fi
export HUBNAME
# Read, process and write config.
cat /opt/shared/config-template.json | sed "s/\$HUBNAME/$HUBNAME/g" > /opt/data/config.json
# Get binary to start.
BIN=$(ls /opt/ | grep hub)
# Start Hub.
/opt/$BIN --data /opt/data --log trace --spn-map test --bootstrap-file /opt/shared/bootstrap.dsd --api-address 0.0.0.0:817 --devmode

View file

@ -0,0 +1,35 @@
#!/bin/bash
cd "$( dirname "${BASH_SOURCE[0]}" )"
MAIN_INTEL_FILE="intel-testnet.json"
if [[ ! -f $MAIN_INTEL_FILE ]]; then
echo "missing $MAIN_INTEL_FILE"
exit 1
fi
echo "if the containing directory cannot be created, you might need to adjust permissions, as nodes are run with root in test containers..."
echo "$ sudo chmod -R 777 data/hub*/updates"
echo "starting to update..."
for hubDir in data/hub*; do
# Build destination path
hubIntelFile="${hubDir}/updates/all/intel/spn/main-intel_v0-0-0.dsd"
# Copy file
mkdir -p "${hubDir}/updates/all/intel/spn"
echo -n "J" > "$hubIntelFile"
cat $MAIN_INTEL_FILE >> "$hubIntelFile"
echo "updated $hubIntelFile"
done
if [[ -d /var/lib/portmaster ]]; then
echo "updating intel for local portmaster installation..."
portmasterSPNIntelFile="/var/lib/portmaster/updates/all/intel/spn/main-intel_v0-0-0.dsd"
echo -n "J" > "$portmasterSPNIntelFile"
cat $MAIN_INTEL_FILE >> "$portmasterSPNIntelFile"
echo "updated $portmasterSPNIntelFile"
fi

View file

@ -0,0 +1,25 @@
# Get current list of IDs from test net:
# curl http://127.0.0.1:817/api/v1/spn/map/test/pins | jq ".[] | .ID"
# Then import into test client with:
# curl -X POST --upload-file intel-client.yaml http://127.0.0.1:817/api/v1/spn/map/test/intel/update
Hubs:
Zwm48YWWFGdwXjhE1MyEkWfqxPr9DiUBoXpusTZ1FMQnuK:
Trusted: true
Zwu5LkkbfCbAcYxWG3vtWF1VvWjgWpc1GJfkwRdLFNtytV:
Trusted: true
ZwuQpz5CqYmYoLnt9KXQ8oxnmosBzfrCYwCGhxT4fsG1Dz:
Trusted: true
ZwwmC3dHzr7J6XW9mc2KD6FDNuXwPVJUFi9dLnDSNMyjLk:
Trusted: true
ZwxSBdvqtJyz8zRWKZe6QyK51KH9av6VFay2GQvpFrWKHq:
Trusted: true
ZwxnuL6zMLj4AxJX8Bj369w2tNrVtYxzffVcXZuMxdxbGj:
Trusted: true
ZwyXdnC8JkC7m796skGD7QWGoYycByR3KVntkXMY8CxRZQ:
Trusted: true
Zwz7AHiH1EevD9eYFqvQQPbVWyBBcksTRxxafbRx5Cvc4F:
Trusted: true
ZwzMtc65t9XBMwmLm2xNSL69FvqHGPLiqeNBZ3eEN5a9sS:
Trusted: true
ZwzjnCUNGsuWnkYmN3QEj8JPLxU6V1QQFk9b47AigmPKiH:
Trusted: true

View file

@ -0,0 +1,17 @@
{
"BootstrapHubs": [
],
"TrustedHubs": [
"ZwrY9G9HDo1J3qQrrQs8VF2KD99bj7KyWesJ5kWFUDBU6r",
"Zwj56ZFXrsud8gc1Rw3zuxRwMLhGkwvtvnTxCVtJ8EWLhQ",
"ZwpdW87ityD9i3N9x8oweCJnbZEqo346VBg4mCsCvTr1Zo",
"ZwpJ6ebddk1sccUVpo92JUqicBfKzBN2w4pEGoEY7UsNhX",
"Zwte3Jffp9PWmeWfrn8RyGuvZZFCg3v7XR3tpQjdo9TpVt",
"ZwrTcdiPF5zR5h9q9EdjHCrrXzYVBdQe5HmEYUWXdLkke3",
"Zwv7tSn5iU6bYKn53NaGCxPtL8vSxSK7F9VdQezDaDCLBt",
"Zwvtdq3K9knP9iNaRS1Ju8CETWTqy7oRwbScjBtJGBpqhB"
],
"AdviseOnlyTrustedHubs": true,
"AdviseOnlyTrustedHomeHubs": true,
"AdviseOnlyTrustedDestinationHubs": true
}

42
spn/testing/simple/join.sh Executable file
View file

@ -0,0 +1,42 @@
#!/bin/bash
cd "$( dirname "${BASH_SOURCE[0]}" )"
realpath() {
path=`eval echo "$1"`
folder=$(dirname "$path")
echo $(cd "$folder"; pwd)/$(basename "$path");
}
leftover=$(docker ps -a | grep spn-test-simple-me | cut -d" " -f1)
if [[ $leftover != "" ]]; then
docker stop $leftover
docker rm $leftover
fi
if [[ ! -f "../../../cmds/hub/hub" ]]; then
echo "please build the hub cmd using cmds/hub/build"
exit 1
fi
SPN_TEST_BIN="$(realpath ../../../cmds/hub/hub)"
SPN_TEST_DATA_DIR="$(realpath ./testdata)"
if [[ ! -d "$SPN_TEST_DATA_DIR" ]]; then
mkdir "$SPN_TEST_DATA_DIR"
fi
SPN_TEST_SHARED_DATA_DIR="$(realpath ./testdata/shared)"
if [[ ! -d "$SPN_TEST_SHARED_DATA_DIR" ]]; then
mkdir "$SPN_TEST_SHARED_DATA_DIR"
fi
docker run -ti \
--name spn-test-simple-me \
--hostname me \
--network spn-test-simple_default \
-v $SPN_TEST_BIN:/opt/hub_me:ro \
-v $SPN_TEST_DATA_DIR/me:/opt/data \
-v $SPN_TEST_SHARED_DATA_DIR:/opt/shared \
--entrypoint /opt/hub_me \
toolset.safing.network/dev \
--devmode --api-address 0.0.0.0:8081 \
--data /opt/data -log trace --spn-map test --bootstrap-file /opt/shared/bootstrap.dsd

View file

@ -0,0 +1,7 @@
#!/bin/bash
cd "$( dirname "${BASH_SOURCE[0]}" )"
rm -rf testdata/me/*
rm -rf testdata/shared/*
rm -rf testdata/hub*/databases

52
spn/testing/simple/run.sh Executable file
View file

@ -0,0 +1,52 @@
#!/bin/bash
cd "$( dirname "${BASH_SOURCE[0]}" )"
realpath() {
path=`eval echo "$1"`
folder=$(dirname "$path")
echo $(cd "$folder"; pwd)/$(basename "$path");
}
leftovers=$(docker ps -a | grep spn-test-simple | cut -d" " -f1)
if [[ $leftovers != "" ]]; then
docker stop $leftovers
docker rm $leftovers
fi
if [[ ! -f "../../../cmds/hub/hub" ]]; then
echo "please build the hub cmd using cmds/hub/build"
exit 1
fi
# Create variables.
SPN_TEST_BIN="$(realpath ../../../cmds/hub/hub)"
SPN_TEST_DATA_DIR="$(realpath ./testdata)"
if [[ ! -d "$SPN_TEST_DATA_DIR" ]]; then
mkdir "$SPN_TEST_DATA_DIR"
fi
SPN_TEST_SHARED_DATA_DIR="$(realpath ./testdata/shared)"
if [[ ! -d "$SPN_TEST_SHARED_DATA_DIR" ]]; then
mkdir "$SPN_TEST_SHARED_DATA_DIR"
fi
# Check if there is an old binary for testing.
SPN_TEST_OLD_BIN=$SPN_TEST_BIN
if [[ -f "./testdata/old-hub" ]]; then
SPN_TEST_OLD_BIN="$(realpath ./testdata/old-hub)"
echo "WARNING: running in hybrid mode with old version at $SPN_TEST_OLD_BIN"
fi
# Export variables
export SPN_TEST_BIN
export SPN_TEST_OLD_BIN
export SPN_TEST_DATA_DIR
export SPN_TEST_SHARED_DATA_DIR
# Copy files.
cp config-template.json ./testdata/shared/config-template.json
cp entrypoint.sh ./testdata/shared/entrypoint.sh
chmod 555 ./testdata/shared/entrypoint.sh
# Run!
docker compose -p spn-test-simple up --remove-orphans

15
spn/testing/simple/stop.sh Executable file
View file

@ -0,0 +1,15 @@
#!/bin/bash
cd "$( dirname "${BASH_SOURCE[0]}" )"
docker compose -p spn-test-simple stop
docker compose -p spn-test-simple rm
oldnet=$(docker network ls | grep spn-test-simple | cut -d" " -f1)
if [[ $oldnet != "" ]]; then
docker network rm $oldnet
fi
if [[ -d "data/shared" ]]; then
rm -r "data/shared"
fi