Restructure modules (#1572)
* Move portbase into monorepo * Add new simple module mgr * [WIP] Switch to new simple module mgr * Add StateMgr and more worker variants * [WIP] Switch more modules * [WIP] Switch more modules * [WIP] swtich more modules * [WIP] switch all SPN modules * [WIP] switch all service modules * [WIP] Convert all workers to the new module system * [WIP] add new task system to module manager * [WIP] Add second take for scheduling workers * [WIP] Add FIXME for bugs in new scheduler * [WIP] Add minor improvements to scheduler * [WIP] Add new worker scheduler * [WIP] Fix more bug related to new module system * [WIP] Fix start handing of the new module system * [WIP] Improve startup process * [WIP] Fix minor issues * [WIP] Fix missing subsystem in settings * [WIP] Initialize managers in constructor * [WIP] Move module event initialization to constrictors * [WIP] Fix setting for enabling and disabling the SPN module * [WIP] Move API registeration into module construction * [WIP] Update states mgr for all modules * [WIP] Add CmdLine operation support * Add state helper methods to module group and instance * Add notification and module status handling to status package * Fix starting issues * Remove pilot widget and update security lock to new status data * Remove debug logs * Improve http server shutdown * Add workaround for cleanly shutting down firewall+netquery * Improve logging * Add syncing states with notifications for new module system * Improve starting, stopping, shutdown; resolve FIXMEs/TODOs * [WIP] Fix most unit tests * Review new module system and fix minor issues * Push shutdown and restart events again via API * Set sleep mode via interface * Update example/template module * [WIP] Fix spn/cabin unit test * Remove deprecated UI elements * Make log output more similar for the logging transition phase * Switch spn hub and observer cmds to new module system * Fix log sources * Make worker mgr less error prone * Fix tests and minor issues * Fix observation hub * Improve shutdown and restart handling * Split up big connection.go source file * Move varint and dsd packages to structures repo * Improve expansion test * Fix linter warnings * Fix interception module on windows * Fix linter errors --------- Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
This commit is contained in:
@@ -5,9 +5,9 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/access/account"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/access/account"
|
||||
"github.com/safing/portmaster/spn/access/token"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
// Client URLs.
|
||||
@@ -57,15 +57,8 @@ func makeClientRequest(opts *clientRequestOptions) (resp *http.Response, err err
|
||||
// Get context for request.
|
||||
var ctx context.Context
|
||||
var cancel context.CancelFunc
|
||||
if module.Online() {
|
||||
// Only use module context if online.
|
||||
ctx, cancel = context.WithTimeout(module.Ctx, opts.requestTimeout)
|
||||
defer cancel()
|
||||
} else {
|
||||
// Otherwise, use the background context.
|
||||
ctx, cancel = context.WithTimeout(context.Background(), opts.requestTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
ctx, cancel = context.WithTimeout(module.mgr.Ctx(), opts.requestTimeout)
|
||||
defer cancel()
|
||||
|
||||
// Create new request.
|
||||
request, err := http.NewRequestWithContext(ctx, opts.method, opts.url, nil)
|
||||
@@ -214,7 +207,7 @@ func Login(username, password string) (user *UserRecord, code int, err error) {
|
||||
defer clientRequestLock.Unlock()
|
||||
|
||||
// Trigger account update when done.
|
||||
defer module.TriggerEvent(AccountUpdateEvent, nil)
|
||||
defer module.EventAccountUpdate.Submit(struct{}{})
|
||||
|
||||
// Get previous user.
|
||||
previousUser, err := GetUser()
|
||||
@@ -300,7 +293,7 @@ func Logout(shallow, purge bool) error {
|
||||
defer clientRequestLock.Unlock()
|
||||
|
||||
// Trigger account update when done.
|
||||
defer module.TriggerEvent(AccountUpdateEvent, nil)
|
||||
defer module.EventAccountUpdate.Submit(struct{}{})
|
||||
|
||||
// Clear caches.
|
||||
clearUserCaches()
|
||||
@@ -382,7 +375,7 @@ func UpdateUser() (user *UserRecord, statusCode int, err error) {
|
||||
defer clientRequestLock.Unlock()
|
||||
|
||||
// Trigger account update when done.
|
||||
defer module.TriggerEvent(AccountUpdateEvent, nil)
|
||||
defer module.EventAccountUpdate.Submit(struct{}{})
|
||||
|
||||
// Create request options.
|
||||
userData := &account.User{}
|
||||
|
||||
@@ -63,7 +63,7 @@ func loginAndRefresh(t *testing.T, doLogin bool, refreshTimes int) {
|
||||
t.Logf("auth token: %+v", authToken.Token)
|
||||
}
|
||||
|
||||
for i := 0; i < refreshTimes; i++ {
|
||||
for range refreshTimes {
|
||||
user, _, err := UpdateUser()
|
||||
if err != nil {
|
||||
t.Fatalf("getting profile failed: %s", err)
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/spn/access/account"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,25 +1,45 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/access/account"
|
||||
"github.com/safing/portmaster/spn/access/token"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
)
|
||||
|
||||
var (
|
||||
module *modules.Module
|
||||
type Access struct {
|
||||
mgr *mgr.Manager
|
||||
instance instance
|
||||
|
||||
accountUpdateTask *modules.Task
|
||||
updateAccountWorkerMgr *mgr.WorkerMgr
|
||||
|
||||
EventAccountUpdate *mgr.EventMgr[struct{}]
|
||||
}
|
||||
|
||||
func (a *Access) Manager() *mgr.Manager {
|
||||
return a.mgr
|
||||
}
|
||||
|
||||
func (a *Access) Start() error {
|
||||
return start()
|
||||
}
|
||||
|
||||
func (a *Access) Stop() error {
|
||||
return stop()
|
||||
}
|
||||
|
||||
var (
|
||||
module *Access
|
||||
shimLoaded atomic.Bool
|
||||
|
||||
tokenIssuerIsFailing = abool.New()
|
||||
tokenIssuerRetryDuration = 10 * time.Minute
|
||||
@@ -38,15 +58,9 @@ var (
|
||||
ErrNotLoggedIn = errors.New("not logged in")
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("access", prep, start, stop, "terminal")
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
module.RegisterEvent(AccountUpdateEvent, true)
|
||||
|
||||
// Register API handlers.
|
||||
if conf.Client() {
|
||||
if conf.Integrated() {
|
||||
err := registerAPIEndpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -62,25 +76,46 @@ func start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if conf.Client() {
|
||||
if conf.Integrated() {
|
||||
// Add config listener to enable/disable SPN.
|
||||
module.instance.Config().EventConfigChange.AddCallback("spn enable check", func(wc *mgr.WorkerCtx, s struct{}) (bool, error) {
|
||||
// Do not do anything when we are shutting down.
|
||||
if module.instance.Stopping() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
enabled := config.GetAsBool("spn/enable", false)
|
||||
if enabled() {
|
||||
module.mgr.Go("ensure SPN is started", module.instance.SPNGroup().EnsureStartedWorker)
|
||||
} else {
|
||||
module.mgr.Go("ensure SPN is stopped", module.instance.SPNGroup().EnsureStoppedWorker)
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
|
||||
// Check if we need to enable SPN now.
|
||||
enabled := config.GetAsBool("spn/enable", false)
|
||||
if enabled() {
|
||||
module.mgr.Go("ensure SPN is started", module.instance.SPNGroup().EnsureStartedWorker)
|
||||
}
|
||||
|
||||
// Load tokens from database.
|
||||
loadTokens()
|
||||
|
||||
// Register new task.
|
||||
accountUpdateTask = module.NewTask(
|
||||
"update account",
|
||||
UpdateAccount,
|
||||
).Repeat(24 * time.Hour).Schedule(time.Now().Add(1 * time.Minute))
|
||||
module.updateAccountWorkerMgr.Delay(1 * time.Minute)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func stop() error {
|
||||
if conf.Client() {
|
||||
// Stop account update task.
|
||||
accountUpdateTask.Cancel()
|
||||
accountUpdateTask = nil
|
||||
if conf.Integrated() {
|
||||
// Make sure SPN is stopped before we proceed.
|
||||
err := module.mgr.Do("ensure SPN is shut down", module.instance.SPNGroup().EnsureStoppedWorker)
|
||||
if err != nil {
|
||||
log.Errorf("access: stop SPN: %s", err)
|
||||
}
|
||||
|
||||
// Store tokens to database.
|
||||
storeTokens()
|
||||
@@ -93,11 +128,14 @@ func stop() error {
|
||||
}
|
||||
|
||||
// UpdateAccount updates the user account and fetches new tokens, if needed.
|
||||
func UpdateAccount(_ context.Context, task *modules.Task) error {
|
||||
func UpdateAccount(_ *mgr.WorkerCtx) error {
|
||||
// Schedule next call - this will change if other conditions are met bellow.
|
||||
module.updateAccountWorkerMgr.Delay(24 * time.Hour)
|
||||
|
||||
// Retry sooner if the token issuer is failing.
|
||||
defer func() {
|
||||
if tokenIssuerIsFailing.IsSet() && task != nil {
|
||||
task.Schedule(time.Now().Add(tokenIssuerRetryDuration))
|
||||
if tokenIssuerIsFailing.IsSet() {
|
||||
module.updateAccountWorkerMgr.Delay(tokenIssuerRetryDuration)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -128,15 +166,15 @@ func UpdateAccount(_ context.Context, task *modules.Task) error {
|
||||
|
||||
case time.Until(*u.Subscription.EndsAt) < 24*time.Hour &&
|
||||
time.Since(*u.Subscription.EndsAt) < 24*time.Hour:
|
||||
// Update account every hour 24h hours before and after the subscription ends.
|
||||
task.Schedule(time.Now().Add(time.Hour))
|
||||
// Update account every hour for 24h hours before and after the subscription ends.
|
||||
module.updateAccountWorkerMgr.Delay(1 * time.Hour)
|
||||
|
||||
case u.Subscription.NextBillingDate == nil: // No auto-subscription.
|
||||
|
||||
case time.Until(*u.Subscription.NextBillingDate) < 24*time.Hour &&
|
||||
time.Since(*u.Subscription.NextBillingDate) < 24*time.Hour:
|
||||
// Update account every hour 24h hours before and after the next billing date.
|
||||
task.Schedule(time.Now().Add(time.Hour))
|
||||
module.updateAccountWorkerMgr.Delay(1 * time.Hour)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -165,11 +203,8 @@ func tokenIssuerFailed() {
|
||||
if !tokenIssuerIsFailing.SetToIf(false, true) {
|
||||
return
|
||||
}
|
||||
if !module.Online() {
|
||||
return
|
||||
}
|
||||
|
||||
accountUpdateTask.Schedule(time.Now().Add(tokenIssuerRetryDuration))
|
||||
module.updateAccountWorkerMgr.Delay(tokenIssuerRetryDuration)
|
||||
}
|
||||
|
||||
// IsLoggedIn returns whether a User is currently logged in.
|
||||
@@ -192,3 +227,31 @@ func (user *UserRecord) MayUseTheSPN() bool {
|
||||
|
||||
return user.User.MayUseSPN()
|
||||
}
|
||||
|
||||
// New returns a new Access module.
|
||||
func New(instance instance) (*Access, error) {
|
||||
if !shimLoaded.CompareAndSwap(false, true) {
|
||||
return nil, errors.New("only one instance allowed")
|
||||
}
|
||||
|
||||
m := mgr.New("Access")
|
||||
module = &Access{
|
||||
mgr: m,
|
||||
instance: instance,
|
||||
|
||||
EventAccountUpdate: mgr.NewEventMgr[struct{}](AccountUpdateEvent, m),
|
||||
updateAccountWorkerMgr: m.NewWorkerMgr("update account", UpdateAccount, nil),
|
||||
}
|
||||
|
||||
if err := prep(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
||||
|
||||
type instance interface {
|
||||
Config() *config.Config
|
||||
SPNGroup() *mgr.ExtendedGroup
|
||||
Stopping() bool
|
||||
}
|
||||
|
||||
@@ -1,13 +1,57 @@
|
||||
package access
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portmaster/service/core/pmtesting"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
conf.EnableClient(true)
|
||||
pmtesting.TestMain(m, module)
|
||||
type testInstance struct {
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
func (stub *testInstance) Config() *config.Config {
|
||||
return stub.config
|
||||
}
|
||||
|
||||
func (stub *testInstance) SPNGroup() *mgr.ExtendedGroup {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stub *testInstance) Stopping() bool {
|
||||
return false
|
||||
}
|
||||
func (stub *testInstance) SetCmdLineOperation(f func() error) {}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
instance := &testInstance{}
|
||||
var err error
|
||||
instance.config, err = config.New(instance)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create config module: %s", err)
|
||||
os.Exit(0)
|
||||
}
|
||||
module, err = New(instance)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create access module: %s", err)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
err = instance.config.Start()
|
||||
if err != nil {
|
||||
fmt.Printf("failed to start config module: %s", err)
|
||||
os.Exit(0)
|
||||
}
|
||||
err = module.Start()
|
||||
if err != nil {
|
||||
fmt.Printf("failed to start access module: %s", err)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
conf.EnableClient(true)
|
||||
m.Run()
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/notifications"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/notifications"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -3,10 +3,10 @@ package access
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/access/token"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
)
|
||||
|
||||
// OpTypeAccessCodeAuth is the type ID of the auth operation.
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/database/query"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/access/token"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
func loadTokens() {
|
||||
@@ -120,7 +120,7 @@ func clearTokens() {
|
||||
}
|
||||
|
||||
// Purge database storage prefix.
|
||||
ctx, cancel := context.WithTimeout(module.Ctx, 10*time.Second)
|
||||
ctx, cancel := context.WithTimeout(module.mgr.Ctx(), 10*time.Second)
|
||||
defer cancel()
|
||||
n, err := db.Purge(ctx, query.New(fmt.Sprintf(tokenStorageKeyTemplate, "")))
|
||||
if err != nil {
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/service/core/pmtesting"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
)
|
||||
|
||||
type testInstance struct{}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
module := modules.Register("token", nil, nil, nil, "rng")
|
||||
pmtesting.TestMain(m, module)
|
||||
rng, err := rng.New(testInstance{})
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create RNG module: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = rng.Start()
|
||||
if err != nil {
|
||||
fmt.Printf("failed to start RNG module: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
m.Run()
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/mr-tron/base58"
|
||||
"github.com/rot256/pblind"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
const pblindSecretSize = 32
|
||||
@@ -217,7 +217,7 @@ func (pbh *PBlindHandler) CreateSetup() (state *PBlindSignerState, setupResponse
|
||||
}
|
||||
|
||||
// Go through the batch.
|
||||
for i := 0; i < pbh.opts.BatchSize; i++ {
|
||||
for i := range pbh.opts.BatchSize {
|
||||
info, err := pbh.makeInfo(i + 1)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create info #%d: %w", i, err)
|
||||
@@ -257,7 +257,7 @@ func (pbh *PBlindHandler) CreateTokenRequest(requestSetup *PBlindSetupResponse)
|
||||
}
|
||||
|
||||
// Go through the batch.
|
||||
for i := 0; i < pbh.opts.BatchSize; i++ {
|
||||
for i := range pbh.opts.BatchSize {
|
||||
// Check if we have setup data.
|
||||
if requestSetup.Msgs[i] == nil {
|
||||
return nil, fmt.Errorf("missing setup data #%d", i)
|
||||
@@ -319,7 +319,7 @@ func (pbh *PBlindHandler) IssueTokens(state *PBlindSignerState, request *PBlindT
|
||||
}
|
||||
|
||||
// Go through the batch.
|
||||
for i := 0; i < pbh.opts.BatchSize; i++ {
|
||||
for i := range pbh.opts.BatchSize {
|
||||
// Check if we have request data.
|
||||
if request.Msgs[i] == nil {
|
||||
return nil, fmt.Errorf("missing request data #%d", i)
|
||||
@@ -360,7 +360,7 @@ func (pbh *PBlindHandler) ProcessIssuedTokens(issuedTokens *IssuedPBlindTokens)
|
||||
finalizedTokens := make([]*PBlindToken, pbh.opts.BatchSize)
|
||||
|
||||
// Go through the batch.
|
||||
for i := 0; i < pbh.opts.BatchSize; i++ {
|
||||
for i := range pbh.opts.BatchSize {
|
||||
// Finalize token.
|
||||
err := pbh.requestState[i].State.ProcessMessage3(*issuedTokens.Msgs[i])
|
||||
if err != nil {
|
||||
|
||||
@@ -124,7 +124,7 @@ func TestPBlindLibrary(t *testing.T) {
|
||||
|
||||
// Create signers and prep requests.
|
||||
start := time.Now()
|
||||
for i := 0; i < batchSize; i++ {
|
||||
for i := range batchSize {
|
||||
signer, err := pblind.CreateSigner(sk, info)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -146,7 +146,7 @@ func TestPBlindLibrary(t *testing.T) {
|
||||
|
||||
// Create requesters and create requests.
|
||||
start = time.Now()
|
||||
for i := 0; i < batchSize; i++ {
|
||||
for i := range batchSize {
|
||||
requester, err := pblind.CreateRequester(pk, info, msgStr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -178,7 +178,7 @@ func TestPBlindLibrary(t *testing.T) {
|
||||
|
||||
// Sign requests
|
||||
start = time.Now()
|
||||
for i := 0; i < batchSize; i++ {
|
||||
for i := range batchSize {
|
||||
var msg2S pblind.Message2
|
||||
_, err = asn1.Unmarshal(toServer[i], &msg2S)
|
||||
if err != nil {
|
||||
@@ -204,7 +204,7 @@ func TestPBlindLibrary(t *testing.T) {
|
||||
|
||||
// Verify signed requests
|
||||
start = time.Now()
|
||||
for i := 0; i < batchSize; i++ {
|
||||
for i := range batchSize {
|
||||
var msg3R pblind.Message3
|
||||
_, err := asn1.Unmarshal(toClient[i], &msg3R)
|
||||
if err != nil {
|
||||
@@ -234,7 +234,7 @@ func TestPBlindLibrary(t *testing.T) {
|
||||
|
||||
// Verify on server
|
||||
start = time.Now()
|
||||
for i := 0; i < batchSize; i++ {
|
||||
for i := range batchSize {
|
||||
var sig pblind.Signature
|
||||
_, err := asn1.Unmarshal(toServer[i], &sig)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
func TestFull(t *testing.T) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/mr-tron/base58"
|
||||
|
||||
"github.com/safing/jess/lhash"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/structures/container"
|
||||
)
|
||||
|
||||
// Token represents a token, consisting of a zone (name) and some data.
|
||||
|
||||
@@ -3,7 +3,7 @@ package token
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
)
|
||||
|
||||
func TestToken(t *testing.T) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/jess/lhash"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/access/token"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
@@ -48,7 +48,7 @@ func InitializeZones() error {
|
||||
|
||||
// Special client zone config.
|
||||
var requestSignalHandler func(token.Handler)
|
||||
if conf.Client() {
|
||||
if conf.Integrated() {
|
||||
requestSignalHandler = shouldRequestTokensHandler
|
||||
}
|
||||
|
||||
@@ -139,14 +139,8 @@ func initializeTestZone() error {
|
||||
}
|
||||
|
||||
func shouldRequestTokensHandler(_ token.Handler) {
|
||||
// accountUpdateTask is always set in client mode and when the module is online.
|
||||
// Check if it's set in case this gets executed in other circumstances.
|
||||
if accountUpdateTask == nil {
|
||||
log.Warningf("spn/access: trying to trigger account update, but the task is not available")
|
||||
return
|
||||
}
|
||||
|
||||
accountUpdateTask.StartASAP()
|
||||
// Run the account update task as now.
|
||||
module.updateAccountWorkerMgr.Go()
|
||||
}
|
||||
|
||||
// GetTokenAmount returns the amount of tokens for the given zones.
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/profile/endpoints"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
"github.com/safing/jess"
|
||||
"github.com/safing/jess/tools"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/info"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/base/info"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
)
|
||||
|
||||
@@ -19,8 +19,8 @@ func TestIdentity(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create new identity.
|
||||
identityTestKey := "core:spn/public/identity"
|
||||
id, err := CreateIdentity(module.Ctx, conf.MainMapName)
|
||||
identityTestKey := "core:spn/public/identity-test"
|
||||
id, err := CreateIdentity(module.m.Ctx(), conf.MainMapName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -111,19 +111,37 @@ func TestIdentity(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Ensure the Measurements reset the values.
|
||||
measurements := id.Hub.GetMeasurements()
|
||||
measurements.SetLatency(0)
|
||||
measurements.SetCapacity(0)
|
||||
measurements.SetCalculatedCost(hub.MaxCalculatedCost)
|
||||
|
||||
// Save to and load from database.
|
||||
err = id.Save()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
id2, changed, err := LoadIdentity(identityTestKey)
|
||||
id2, _, err := LoadIdentity(identityTestKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if changed {
|
||||
t.Error("unexpected change")
|
||||
}
|
||||
|
||||
// Check if they match
|
||||
assert.Equal(t, id, id2, "identities should be equal")
|
||||
// Reset everything that should not be compared.
|
||||
id.infoExportCache = nil
|
||||
id2.infoExportCache = nil
|
||||
id.statusExportCache = nil
|
||||
id2.statusExportCache = nil
|
||||
id.ExchKeys = nil
|
||||
id2.ExchKeys = nil
|
||||
id.Hub.Status = nil
|
||||
id2.Hub.Status = nil
|
||||
id.Hub.PublicKey = nil
|
||||
id2.Hub.PublicKey = nil
|
||||
|
||||
// Check important aspects of the identities.
|
||||
assert.Equal(t, id.ID, id2.ID, "identity IDs must be equal")
|
||||
assert.Equal(t, id.Map, id2.Map, "identity Maps should be equal")
|
||||
assert.Equal(t, id.Hub, id2.Hub, "identity Hubs should be equal")
|
||||
assert.Equal(t, id.Signet, id2.Signet, "identity Signets should be equal")
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/safing/jess"
|
||||
"github.com/safing/jess/tools"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
)
|
||||
|
||||
@@ -144,7 +144,7 @@ func (id *Identity) MaintainExchKeys(newStatus *hub.Status, now time.Time) (chan
|
||||
func (id *Identity) createExchKey(eks *providedExchKeyScheme, now time.Time) error {
|
||||
// get ID
|
||||
var keyID string
|
||||
for i := 0; i < 1000000; i++ { // not forever
|
||||
for range 1000000 { // not forever
|
||||
// generate new ID
|
||||
b, err := rng.Bytes(3)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func TestKeyMaintenance(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
id, err := CreateIdentity(module.Ctx, conf.MainMapName)
|
||||
id, err := CreateIdentity(module.m.Ctx(), conf.MainMapName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func TestKeyMaintenance(t *testing.T) {
|
||||
changeCnt := 0
|
||||
|
||||
now := time.Now()
|
||||
for i := 0; i < iterations; i++ {
|
||||
for range iterations {
|
||||
changed, err := id.MaintainExchKeys(id.Hub.Status, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -1,16 +1,35 @@
|
||||
package cabin
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/modules"
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
)
|
||||
|
||||
var module *modules.Module
|
||||
|
||||
func init() {
|
||||
module = modules.Register("cabin", prep, nil, nil, "base", "rng")
|
||||
type Cabin struct {
|
||||
m *mgr.Manager
|
||||
instance instance
|
||||
}
|
||||
|
||||
func (c *Cabin) Manager() *mgr.Manager {
|
||||
return c.m
|
||||
}
|
||||
|
||||
func (c *Cabin) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cabin) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
module *Cabin
|
||||
shimLoaded atomic.Bool
|
||||
)
|
||||
|
||||
func prep() error {
|
||||
if err := initProvidedExchKeySchemes(); err != nil {
|
||||
return err
|
||||
@@ -24,3 +43,24 @@ func prep() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New returns a new Cabin module.
|
||||
func New(instance instance) (*Cabin, error) {
|
||||
if !shimLoaded.CompareAndSwap(false, true) {
|
||||
return nil, errors.New("only one instance allowed")
|
||||
}
|
||||
|
||||
m := mgr.New("Cabin")
|
||||
module = &Cabin{
|
||||
m: m,
|
||||
instance: instance,
|
||||
}
|
||||
|
||||
if err := prep(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
||||
|
||||
type instance interface{}
|
||||
|
||||
@@ -1,13 +1,113 @@
|
||||
package cabin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portmaster/service/core/pmtesting"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database/dbmodule"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/service/core/base"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
conf.EnablePublicHub(true)
|
||||
pmtesting.TestMain(m, module)
|
||||
type testInstance struct {
|
||||
db *dbmodule.DBModule
|
||||
api *api.API
|
||||
config *config.Config
|
||||
rng *rng.Rng
|
||||
base *base.Base
|
||||
}
|
||||
|
||||
func (stub *testInstance) Config() *config.Config {
|
||||
return stub.config
|
||||
}
|
||||
|
||||
func (stub *testInstance) SPNGroup() *mgr.ExtendedGroup {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stub *testInstance) Stopping() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (stub *testInstance) Ready() bool {
|
||||
return true
|
||||
}
|
||||
func (stub *testInstance) SetCmdLineOperation(f func() error) {}
|
||||
|
||||
func runTest(m *testing.M) error {
|
||||
api.SetDefaultAPIListenAddress("0.0.0.0:8080")
|
||||
// Initialize dataroot
|
||||
ds, err := config.InitializeUnitTestDataroot("test-cabin")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize dataroot: %w", err)
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(ds) }()
|
||||
|
||||
// Init
|
||||
instance := &testInstance{}
|
||||
instance.db, err = dbmodule.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create database: %w", err)
|
||||
}
|
||||
instance.api, err = api.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create api: %w", err)
|
||||
}
|
||||
instance.config, err = config.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config module: %w", err)
|
||||
}
|
||||
instance.rng, err = rng.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create rng module: %w", err)
|
||||
}
|
||||
instance.base, err = base.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create base module: %w", err)
|
||||
}
|
||||
module, err = New(struct{}{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create cabin module: %w", err)
|
||||
}
|
||||
// Start
|
||||
err = instance.db.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start database: %w", err)
|
||||
}
|
||||
err = instance.api.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start api: %w", err)
|
||||
}
|
||||
err = instance.config.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start config module: %w", err)
|
||||
}
|
||||
err = instance.rng.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start rng module: %w", err)
|
||||
}
|
||||
err = instance.base.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start base module: %w", err)
|
||||
}
|
||||
err = module.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start cabin module: %w", err)
|
||||
}
|
||||
conf.EnablePublicHub(true)
|
||||
|
||||
m.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if err := runTest(m); err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/safing/jess"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
func TestVerification(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
id, err := CreateIdentity(module.Ctx, "test")
|
||||
id, err := CreateIdentity(module.m.Ctx(), "test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package captain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/database/query"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -16,9 +16,8 @@ const (
|
||||
|
||||
func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: apiPathForSPNReInit,
|
||||
Write: api.PermitAdmin,
|
||||
// BelongsTo: module, // Do not attach to module, as this must run outside of the module.
|
||||
Path: apiPathForSPNReInit,
|
||||
Write: api.PermitAdmin,
|
||||
ActionFunc: handleReInit,
|
||||
Name: "Re-initialize SPN",
|
||||
Description: "Stops the SPN, resets all caches and starts it again. The SPN account and settings are not changed.",
|
||||
@@ -30,16 +29,14 @@ func registerAPIEndpoints() error {
|
||||
}
|
||||
|
||||
func handleReInit(ar *api.Request) (msg string, err error) {
|
||||
// Disable module and check
|
||||
changed := module.Disable()
|
||||
if !changed {
|
||||
return "", errors.New("can only re-initialize when the SPN is enabled")
|
||||
if !conf.Client() && !conf.Integrated() {
|
||||
return "", fmt.Errorf("re-initialization only possible on integrated clients")
|
||||
}
|
||||
|
||||
// Run module manager.
|
||||
err = modules.ManageModules()
|
||||
// Make sure SPN is stopped and wait for it to complete.
|
||||
err = module.mgr.Do("stop SPN for re-init", module.instance.SPNGroup().EnsureStoppedWorker)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to stop SPN: %w", err)
|
||||
return "", fmt.Errorf("failed to stop SPN for re-init: %w", err)
|
||||
}
|
||||
|
||||
// Delete SPN cache.
|
||||
@@ -52,13 +49,10 @@ func handleReInit(ar *api.Request) (msg string, err error) {
|
||||
return "", fmt.Errorf("failed to delete SPN cache: %w", err)
|
||||
}
|
||||
|
||||
// Enable module.
|
||||
module.Enable()
|
||||
|
||||
// Run module manager.
|
||||
err = modules.ManageModules()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to start SPN after cache reset: %w", err)
|
||||
// Start SPN if it is enabled.
|
||||
enabled := config.GetAsBool("spn/enable", false)
|
||||
if enabled() {
|
||||
module.mgr.Go("ensure SPN is started", module.instance.SPNGroup().EnsureStartedWorker)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
"github.com/safing/portmaster/spn/navigator"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
// BootstrapFile is used for sideloading bootstrap data.
|
||||
@@ -74,7 +74,7 @@ func bootstrapWithUpdates() error {
|
||||
return errors.New("using the bootstrap-file argument disables bootstrapping via the update system")
|
||||
}
|
||||
|
||||
return updateSPNIntel(module.Ctx, nil)
|
||||
return updateSPNIntel(module.mgr.Ctx(), nil)
|
||||
}
|
||||
|
||||
// loadBootstrapFile loads a file with bootstrap hub entries and imports them.
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
package captain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/notifications"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/notifications"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/netutils"
|
||||
"github.com/safing/portmaster/spn/access"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/crew"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
"github.com/safing/portmaster/spn/navigator"
|
||||
@@ -40,7 +41,7 @@ func ClientReady() bool {
|
||||
}
|
||||
|
||||
type (
|
||||
clientComponentFunc func(ctx context.Context) clientComponentResult
|
||||
clientComponentFunc func(ctx *mgr.WorkerCtx) clientComponentResult
|
||||
clientComponentResult uint8
|
||||
)
|
||||
|
||||
@@ -71,20 +72,21 @@ func triggerClientHealthCheck() {
|
||||
}
|
||||
}
|
||||
|
||||
func clientManager(ctx context.Context) error {
|
||||
func clientManager(ctx *mgr.WorkerCtx) error {
|
||||
defer func() {
|
||||
ready.UnSet()
|
||||
netenv.ConnectedToSPN.UnSet()
|
||||
resetSPNStatus(StatusDisabled, true)
|
||||
module.Resolve("")
|
||||
module.states.Clear()
|
||||
clientStopHomeHub(ctx)
|
||||
}()
|
||||
|
||||
module.Hint(
|
||||
"spn:establishing-home-hub",
|
||||
"Connecting to SPN...",
|
||||
"Connecting to the SPN network is in progress.",
|
||||
)
|
||||
module.states.Add(mgr.State{
|
||||
ID: "spn:establishing-home-hub",
|
||||
Name: "Connecting to SPN...",
|
||||
Message: "Connecting to the SPN network is in progress.",
|
||||
Type: mgr.StateTypeHint,
|
||||
})
|
||||
|
||||
// TODO: When we are starting and the SPN module is faster online than the
|
||||
// nameserver, then updating the account will fail as the DNS query is
|
||||
@@ -97,15 +99,14 @@ func clientManager(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
healthCheckTicker := module.NewSleepyTicker(clientHealthCheckTickDuration, clientHealthCheckTickDurationSleepMode)
|
||||
module.healthCheckTicker = mgr.NewSleepyTicker(clientHealthCheckTickDuration, clientHealthCheckTickDurationSleepMode)
|
||||
defer module.healthCheckTicker.Stop()
|
||||
|
||||
reconnect:
|
||||
for {
|
||||
// Check if we are shutting down.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if ctx.IsDone() {
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
// Reset SPN status.
|
||||
@@ -143,8 +144,10 @@ reconnect:
|
||||
ready.Set()
|
||||
netenv.ConnectedToSPN.Set()
|
||||
|
||||
module.TriggerEvent(SPNConnectedEvent, nil)
|
||||
module.StartWorker("update quick setting countries", navigator.Main.UpdateConfigQuickSettings)
|
||||
module.EventSPNConnected.Submit(struct{}{})
|
||||
if conf.Integrated() {
|
||||
module.mgr.Go("update quick setting countries", navigator.Main.UpdateConfigQuickSettings)
|
||||
}
|
||||
|
||||
// Reset last health check value, as we have just connected.
|
||||
lastHealthCheck = time.Now()
|
||||
@@ -179,7 +182,7 @@ reconnect:
|
||||
|
||||
// Wait for signal to run maintenance again.
|
||||
select {
|
||||
case <-healthCheckTicker.Wait():
|
||||
case <-module.healthCheckTicker.Wait():
|
||||
case <-clientHealthCheckTrigger:
|
||||
case <-crew.ConnectErrors():
|
||||
case <-clientNetworkChangedFlag.Signal():
|
||||
@@ -191,7 +194,7 @@ reconnect:
|
||||
}
|
||||
}
|
||||
|
||||
func clientCheckNetworkReady(ctx context.Context) clientComponentResult {
|
||||
func clientCheckNetworkReady(ctx *mgr.WorkerCtx) clientComponentResult {
|
||||
// Check if we are online enough for connecting.
|
||||
switch netenv.GetOnlineStatus() { //nolint:exhaustive
|
||||
case netenv.StatusOffline,
|
||||
@@ -211,7 +214,7 @@ func clientCheckNetworkReady(ctx context.Context) clientComponentResult {
|
||||
// Attempts to use the same will result in errors.
|
||||
var DisableAccount bool
|
||||
|
||||
func clientCheckAccountAndTokens(ctx context.Context) clientComponentResult {
|
||||
func clientCheckAccountAndTokens(ctx *mgr.WorkerCtx) clientComponentResult {
|
||||
if DisableAccount {
|
||||
return clientResultOk
|
||||
}
|
||||
@@ -225,7 +228,7 @@ func clientCheckAccountAndTokens(ctx context.Context) clientComponentResult {
|
||||
`Please restart Portmaster.`,
|
||||
// TODO: Add restart button.
|
||||
// TODO: Use special UI restart action in order to reload UI on restart.
|
||||
).AttachToModule(module)
|
||||
).SyncWithState(module.states)
|
||||
resetSPNStatus(StatusFailed, true)
|
||||
log.Errorf("spn/captain: client internal error: %s", err)
|
||||
return clientResultReconnect
|
||||
@@ -238,7 +241,7 @@ func clientCheckAccountAndTokens(ctx context.Context) clientComponentResult {
|
||||
"SPN Login Required",
|
||||
`Please log in to access the SPN.`,
|
||||
spnLoginButton,
|
||||
).AttachToModule(module)
|
||||
).SyncWithState(module.states)
|
||||
resetSPNStatus(StatusFailed, true)
|
||||
log.Warningf("spn/captain: enabled but not logged in")
|
||||
return clientResultReconnect
|
||||
@@ -256,7 +259,7 @@ func clientCheckAccountAndTokens(ctx context.Context) clientComponentResult {
|
||||
"spn:failed-to-update-user",
|
||||
"SPN Account Server Error",
|
||||
fmt.Sprintf(`The status of your SPN account could not be updated: %s`, err),
|
||||
).AttachToModule(module)
|
||||
).SyncWithState(module.states)
|
||||
resetSPNStatus(StatusFailed, true)
|
||||
log.Errorf("spn/captain: failed to update ineligible account: %s", err)
|
||||
return clientResultReconnect
|
||||
@@ -273,7 +276,7 @@ func clientCheckAccountAndTokens(ctx context.Context) clientComponentResult {
|
||||
"SPN Not Included In Package",
|
||||
"Your current Portmaster Package does not include access to the SPN. Please upgrade your package on the Account Page.",
|
||||
spnOpenAccountPage,
|
||||
).AttachToModule(module)
|
||||
).SyncWithState(module.states)
|
||||
resetSPNStatus(StatusFailed, true)
|
||||
return clientResultReconnect
|
||||
}
|
||||
@@ -288,7 +291,7 @@ func clientCheckAccountAndTokens(ctx context.Context) clientComponentResult {
|
||||
"Portmaster Package Issue",
|
||||
"Cannot enable SPN: "+message,
|
||||
spnOpenAccountPage,
|
||||
).AttachToModule(module)
|
||||
).SyncWithState(module.states)
|
||||
resetSPNStatus(StatusFailed, true)
|
||||
return clientResultReconnect
|
||||
}
|
||||
@@ -308,7 +311,7 @@ func clientCheckAccountAndTokens(ctx context.Context) clientComponentResult {
|
||||
"spn:tokens-exhausted",
|
||||
"SPN Access Tokens Exhausted",
|
||||
`The Portmaster failed to get new access tokens to access the SPN. The Portmaster will automatically retry to get new access tokens.`,
|
||||
).AttachToModule(module)
|
||||
).SyncWithState(module.states)
|
||||
resetSPNStatus(StatusFailed, false)
|
||||
}
|
||||
return clientResultRetry
|
||||
@@ -318,7 +321,7 @@ func clientCheckAccountAndTokens(ctx context.Context) clientComponentResult {
|
||||
return clientResultOk
|
||||
}
|
||||
|
||||
func clientStopHomeHub(ctx context.Context) clientComponentResult {
|
||||
func clientStopHomeHub(ctx *mgr.WorkerCtx) clientComponentResult {
|
||||
// Don't use the context in this function, as it will likely be canceled
|
||||
// already and would disrupt any context usage in here.
|
||||
|
||||
@@ -337,9 +340,13 @@ func clientStopHomeHub(ctx context.Context) clientComponentResult {
|
||||
return clientResultOk
|
||||
}
|
||||
|
||||
func clientConnectToHomeHub(ctx context.Context) clientComponentResult {
|
||||
func clientConnectToHomeHub(ctx *mgr.WorkerCtx) clientComponentResult {
|
||||
err := establishHomeHub(ctx)
|
||||
if err != nil {
|
||||
if ctx.IsDone() {
|
||||
return clientResultShutdown
|
||||
}
|
||||
|
||||
log.Errorf("spn/captain: failed to establish connection to home hub: %s", err)
|
||||
resetSPNStatus(StatusFailed, true)
|
||||
|
||||
@@ -356,7 +363,7 @@ func clientConnectToHomeHub(ctx context.Context) clientComponentResult {
|
||||
Key: CfgOptionHomeHubPolicyKey,
|
||||
},
|
||||
},
|
||||
).AttachToModule(module)
|
||||
).SyncWithState(module.states)
|
||||
|
||||
case errors.Is(err, ErrReInitSPNSuggested):
|
||||
notifications.NotifyError(
|
||||
@@ -372,14 +379,14 @@ func clientConnectToHomeHub(ctx context.Context) clientComponentResult {
|
||||
ResultAction: "display",
|
||||
},
|
||||
},
|
||||
).AttachToModule(module)
|
||||
).SyncWithState(module.states)
|
||||
|
||||
default:
|
||||
notifications.NotifyWarn(
|
||||
"spn:home-hub-failure",
|
||||
"SPN Failed to Connect",
|
||||
fmt.Sprintf("Failed to connect to a home hub: %s. The Portmaster will retry to connect automatically.", err),
|
||||
).AttachToModule(module)
|
||||
).SyncWithState(module.states)
|
||||
}
|
||||
|
||||
return clientResultReconnect
|
||||
@@ -394,7 +401,7 @@ func clientConnectToHomeHub(ctx context.Context) clientComponentResult {
|
||||
return clientResultOk
|
||||
}
|
||||
|
||||
func clientSetActiveConnectionStatus(ctx context.Context) clientComponentResult {
|
||||
func clientSetActiveConnectionStatus(ctx *mgr.WorkerCtx) clientComponentResult {
|
||||
// Get current home.
|
||||
home, homeTerminal := navigator.Main.GetHome()
|
||||
if home == nil || homeTerminal == nil {
|
||||
@@ -402,7 +409,7 @@ func clientSetActiveConnectionStatus(ctx context.Context) clientComponentResult
|
||||
}
|
||||
|
||||
// Resolve any connection error.
|
||||
module.Resolve("")
|
||||
module.states.Clear()
|
||||
|
||||
// Update SPN Status with connection information, if not already correctly set.
|
||||
spnStatus.Lock()
|
||||
@@ -437,7 +444,7 @@ func clientSetActiveConnectionStatus(ctx context.Context) clientComponentResult
|
||||
return clientResultOk
|
||||
}
|
||||
|
||||
func clientCheckHomeHubConnection(ctx context.Context) clientComponentResult {
|
||||
func clientCheckHomeHubConnection(ctx *mgr.WorkerCtx) clientComponentResult {
|
||||
// Check the status of the Home Hub.
|
||||
home, homeTerminal := navigator.Main.GetHome()
|
||||
if home == nil || homeTerminal == nil || homeTerminal.IsBeingAbandoned() {
|
||||
@@ -459,7 +466,7 @@ func clientCheckHomeHubConnection(ctx context.Context) clientComponentResult {
|
||||
// Prepare to reconnect to the network.
|
||||
|
||||
// Reset all failing states, as these might have been caused by the failing home hub.
|
||||
navigator.Main.ResetFailingStates(ctx)
|
||||
navigator.Main.ResetFailingStates()
|
||||
|
||||
// If the last health check is clearly too long ago, assume that the device was sleeping and do not set the home node to failing yet.
|
||||
if time.Since(lastHealthCheck) > clientHealthCheckTickDuration+
|
||||
@@ -479,7 +486,7 @@ func clientCheckHomeHubConnection(ctx context.Context) clientComponentResult {
|
||||
return clientResultOk
|
||||
}
|
||||
|
||||
func pingHome(ctx context.Context, t terminal.Terminal, timeout time.Duration) (latency time.Duration, err *terminal.Error) {
|
||||
func pingHome(ctx *mgr.WorkerCtx, t terminal.Terminal, timeout time.Duration) (latency time.Duration, err *terminal.Error) {
|
||||
started := time.Now()
|
||||
|
||||
// Start ping operation.
|
||||
|
||||
@@ -3,7 +3,7 @@ package captain
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/service/profile"
|
||||
"github.com/safing/portmaster/service/profile/endpoints"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
@@ -56,8 +56,24 @@ var (
|
||||
)
|
||||
|
||||
func prepConfig() error {
|
||||
// Home Node Rules
|
||||
// Register spn module setting.
|
||||
err := config.Register(&config.Option{
|
||||
Name: "SPN Module",
|
||||
Key: CfgOptionEnableSPNKey,
|
||||
Description: "Start the Safing Privacy Network module. If turned off, the SPN is fully disabled on this device.",
|
||||
OptType: config.OptTypeBool,
|
||||
DefaultValue: false,
|
||||
Annotations: config.Annotations{
|
||||
config.DisplayOrderAnnotation: cfgOptionEnableSPNOrder,
|
||||
config.CategoryAnnotation: "General",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Home Node Rules
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Home Node Rules",
|
||||
Key: CfgOptionHomeHubPolicyKey,
|
||||
Description: `Customize which countries should or should not be used for your Home Node. The Home Node is your entry into the SPN. You connect directly to it and all your connections are routed through it.
|
||||
@@ -190,7 +206,11 @@ This setting mainly exists for when you need to simulate your presence in anothe
|
||||
}
|
||||
|
||||
// Config options for use.
|
||||
cfgOptionRoutingAlgorithm = config.Concurrent.GetAsString(profile.CfgOptionRoutingAlgorithmKey, navigator.DefaultRoutingProfileID)
|
||||
if conf.Integrated() {
|
||||
cfgOptionRoutingAlgorithm = config.Concurrent.GetAsString(profile.CfgOptionRoutingAlgorithmKey, navigator.DefaultRoutingProfileID)
|
||||
} else {
|
||||
cfgOptionRoutingAlgorithm = func() string { return navigator.DefaultRoutingProfileID }
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
@@ -28,8 +28,8 @@ func EstablishCrane(callerCtx context.Context, dst *hub.Hub) (*docks.Crane, erro
|
||||
return nil, fmt.Errorf("failed to launch ship: %w", err)
|
||||
}
|
||||
|
||||
// On pure clients, mark all ships as public in order to show unmasked data in logs.
|
||||
if conf.Client() && !conf.PublicHub() {
|
||||
// If not a public hub, mark all ships as public in order to show unmasked data in logs.
|
||||
if !conf.PublicHub() {
|
||||
ship.MarkPublic()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package captain
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
@@ -34,7 +32,7 @@ func handleCraneUpdate(crane *docks.Crane) {
|
||||
|
||||
func updateConnectionStatus() {
|
||||
// Delay updating status for a better chance to combine multiple changes.
|
||||
statusUpdateTask.Schedule(time.Now().Add(maintainStatusUpdateDelay))
|
||||
module.statusUpdater.Delay(maintainStatusUpdateDelay)
|
||||
|
||||
// Check if we lost all connections and trigger a pending restart if we did.
|
||||
for _, crane := range docks.GetAllAssignedCranes() {
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/updater"
|
||||
"github.com/safing/portmaster/base/updater"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
@@ -23,28 +23,18 @@ var (
|
||||
)
|
||||
|
||||
func registerIntelUpdateHook() error {
|
||||
if err := module.RegisterEventHook(
|
||||
updates.ModuleName,
|
||||
updates.ResourceUpdateEvent,
|
||||
"update SPN intel",
|
||||
updateSPNIntel,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
module.instance.Updates().EventResourcesUpdated.AddCallback("update SPN intel", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) {
|
||||
return false, updateSPNIntel(wc.Ctx(), nil)
|
||||
})
|
||||
|
||||
if err := module.RegisterEventHook(
|
||||
"config",
|
||||
config.ChangeEvent,
|
||||
"update SPN intel",
|
||||
updateSPNIntel,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
module.instance.Config().EventConfigChange.AddCallback("update SPN intel", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) {
|
||||
return false, updateSPNIntel(wc.Ctx(), nil)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateSPNIntel(ctx context.Context, _ interface{}) (err error) {
|
||||
func updateSPNIntel(_ context.Context, _ interface{}) (err error) {
|
||||
intelResourceUpdateLock.Lock()
|
||||
defer intelResourceUpdateLock.Unlock()
|
||||
|
||||
|
||||
@@ -1,59 +1,73 @@
|
||||
package captain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/modules/subsystems"
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/network/netutils"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/crew"
|
||||
"github.com/safing/portmaster/spn/navigator"
|
||||
"github.com/safing/portmaster/spn/patrol"
|
||||
"github.com/safing/portmaster/spn/ships"
|
||||
_ "github.com/safing/portmaster/spn/sluice"
|
||||
)
|
||||
|
||||
const controlledFailureExitCode = 24
|
||||
|
||||
var module *modules.Module
|
||||
|
||||
// SPNConnectedEvent is the name of the event that is fired when the SPN has connected and is ready.
|
||||
const SPNConnectedEvent = "spn connect"
|
||||
|
||||
func init() {
|
||||
module = modules.Register("captain", prep, start, stop, "base", "terminal", "cabin", "ships", "docks", "crew", "navigator", "sluice", "patrol", "netenv")
|
||||
module.RegisterEvent(SPNConnectedEvent, false)
|
||||
subsystems.Register(
|
||||
"spn",
|
||||
"SPN",
|
||||
"Safing Privacy Network",
|
||||
module,
|
||||
"config:spn/",
|
||||
&config.Option{
|
||||
Name: "SPN Module",
|
||||
Key: CfgOptionEnableSPNKey,
|
||||
Description: "Start the Safing Privacy Network module. If turned off, the SPN is fully disabled on this device.",
|
||||
OptType: config.OptTypeBool,
|
||||
DefaultValue: false,
|
||||
Annotations: config.Annotations{
|
||||
config.DisplayOrderAnnotation: cfgOptionEnableSPNOrder,
|
||||
config.CategoryAnnotation: "General",
|
||||
},
|
||||
},
|
||||
)
|
||||
// Captain is the main module of the SPN.
|
||||
type Captain struct {
|
||||
mgr *mgr.Manager
|
||||
instance instance
|
||||
|
||||
healthCheckTicker *mgr.SleepyTicker
|
||||
|
||||
publicIdentityUpdater *mgr.WorkerMgr
|
||||
statusUpdater *mgr.WorkerMgr
|
||||
|
||||
states *mgr.StateMgr
|
||||
EventSPNConnected *mgr.EventMgr[struct{}]
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
// Manager returns the module manager.
|
||||
func (c *Captain) Manager() *mgr.Manager {
|
||||
return c.mgr
|
||||
}
|
||||
|
||||
// States returns the module states.
|
||||
func (c *Captain) States() *mgr.StateMgr {
|
||||
return c.states
|
||||
}
|
||||
|
||||
// Start starts the module.
|
||||
func (c *Captain) Start() error {
|
||||
return start()
|
||||
}
|
||||
|
||||
// Stop stops the module.
|
||||
func (c *Captain) Stop() error {
|
||||
return stop()
|
||||
}
|
||||
|
||||
// SetSleep sets the sleep mode of the module.
|
||||
func (c *Captain) SetSleep(enabled bool) {
|
||||
if c.healthCheckTicker != nil {
|
||||
c.healthCheckTicker.SetSleep(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Captain) prep() error {
|
||||
// Check if we can parse the bootstrap hub flag.
|
||||
if err := prepBootstrapHubFlag(); err != nil {
|
||||
return err
|
||||
@@ -75,17 +89,13 @@ func prep() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := module.RegisterEventHook(
|
||||
"patrol",
|
||||
patrol.ChangeSignalEventName,
|
||||
c.instance.Patrol().EventChangeSignal.AddCallback(
|
||||
"trigger hub status maintenance",
|
||||
func(_ context.Context, _ any) error {
|
||||
func(_ *mgr.WorkerCtx, _ struct{}) (bool, error) {
|
||||
TriggerHubStatusMaintenance()
|
||||
return nil
|
||||
return false, nil
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return prepConfig()
|
||||
@@ -98,29 +108,15 @@ func start() error {
|
||||
}
|
||||
ships.EnableMasking(maskingBytes)
|
||||
|
||||
// Initialize intel.
|
||||
if err := registerIntelUpdateHook(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateSPNIntel(module.Ctx, nil); err != nil {
|
||||
log.Errorf("spn/captain: failed to update SPN intel: %s", err)
|
||||
}
|
||||
|
||||
// Initialize identity and piers.
|
||||
if conf.PublicHub() {
|
||||
// Load identity.
|
||||
if err := loadPublicIdentity(); err != nil {
|
||||
// We cannot recover from this, set controlled failure (do not retry).
|
||||
modules.SetExitStatusCode(controlledFailureExitCode)
|
||||
|
||||
return err
|
||||
return fmt.Errorf("load public identity: %w", err)
|
||||
}
|
||||
|
||||
// Check if any networks are configured.
|
||||
if !conf.HubHasIPv4() && !conf.HubHasIPv6() {
|
||||
// We cannot recover from this, set controlled failure (do not retry).
|
||||
modules.SetExitStatusCode(controlledFailureExitCode)
|
||||
|
||||
return errors.New("no IP addresses for Hub configured (or detected)")
|
||||
}
|
||||
|
||||
@@ -139,6 +135,17 @@ func start() error {
|
||||
crew.EnableConnecting(publicIdentity.Hub)
|
||||
}
|
||||
|
||||
// Initialize intel.
|
||||
module.mgr.Go("start", func(wc *mgr.WorkerCtx) error {
|
||||
if err := registerIntelUpdateHook(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateSPNIntel(module.mgr.Ctx(), nil); err != nil {
|
||||
log.Errorf("spn/captain: failed to update SPN intel: %s", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Subscribe to updates of cranes.
|
||||
startDockHooks()
|
||||
|
||||
@@ -152,29 +159,20 @@ func start() error {
|
||||
|
||||
// network optimizer
|
||||
if conf.PublicHub() {
|
||||
module.NewTask("optimize network", optimizeNetwork).
|
||||
Repeat(1 * time.Minute).
|
||||
Schedule(time.Now().Add(15 * time.Second))
|
||||
module.mgr.Delay("optimize network delay", 15*time.Second, optimizeNetwork).Repeat(1 * time.Minute)
|
||||
}
|
||||
|
||||
// client + home hub manager
|
||||
if conf.Client() {
|
||||
module.StartServiceWorker("client manager", 0, clientManager)
|
||||
module.mgr.Go("client manager", clientManager)
|
||||
|
||||
// Reset failing hubs when the network changes while not connected.
|
||||
if err := module.RegisterEventHook(
|
||||
"netenv",
|
||||
"network changed",
|
||||
"reset failing hubs",
|
||||
func(_ context.Context, _ interface{}) error {
|
||||
if ready.IsNotSet() {
|
||||
navigator.Main.ResetFailingStates(module.Ctx)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
module.instance.NetEnv().EventNetworkChange.AddCallback("reset failing hubs", func(_ *mgr.WorkerCtx, _ struct{}) (bool, error) {
|
||||
if ready.IsNotSet() {
|
||||
navigator.Main.ResetFailingStates()
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -217,3 +215,40 @@ func apiAuthenticator(r *http.Request, s *http.Server) (*api.AuthToken, error) {
|
||||
Write: api.PermitUser,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
module *Captain
|
||||
shimLoaded atomic.Bool
|
||||
)
|
||||
|
||||
// New returns a new Captain module.
|
||||
func New(instance instance) (*Captain, error) {
|
||||
if !shimLoaded.CompareAndSwap(false, true) {
|
||||
return nil, errors.New("only one instance allowed")
|
||||
}
|
||||
m := mgr.New("Captain")
|
||||
module = &Captain{
|
||||
mgr: m,
|
||||
instance: instance,
|
||||
|
||||
states: mgr.NewStateMgr(m),
|
||||
EventSPNConnected: mgr.NewEventMgr[struct{}](SPNConnectedEvent, m),
|
||||
|
||||
publicIdentityUpdater: m.NewWorkerMgr("maintain public identity", maintainPublicIdentity, nil),
|
||||
statusUpdater: m.NewWorkerMgr("maintain public status", maintainPublicStatus, nil),
|
||||
}
|
||||
|
||||
if err := module.prep(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
||||
|
||||
type instance interface {
|
||||
NetEnv() *netenv.NetEnv
|
||||
Patrol() *patrol.Patrol
|
||||
Config() *config.Config
|
||||
Updates() *updates.Updates
|
||||
SPNGroup() *mgr.ExtendedGroup
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/intel"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/profile/endpoints"
|
||||
"github.com/safing/portmaster/spn/access"
|
||||
@@ -28,7 +28,7 @@ var (
|
||||
ErrReInitSPNSuggested = errors.New("SPN re-init suggested")
|
||||
)
|
||||
|
||||
func establishHomeHub(ctx context.Context) error {
|
||||
func establishHomeHub(ctx *mgr.WorkerCtx) error {
|
||||
// Get own IP.
|
||||
locations, ok := netenv.GetInternetLocation()
|
||||
if !ok || len(locations.All) == 0 {
|
||||
@@ -46,10 +46,10 @@ func establishHomeHub(ctx context.Context) error {
|
||||
var myEntity *intel.Entity
|
||||
if dl := locations.BestV4(); dl != nil && dl.IP != nil {
|
||||
myEntity = (&intel.Entity{IP: dl.IP}).Init(0)
|
||||
myEntity.FetchData(ctx)
|
||||
myEntity.FetchData(ctx.Ctx())
|
||||
} else if dl := locations.BestV6(); dl != nil && dl.IP != nil {
|
||||
myEntity = (&intel.Entity{IP: dl.IP}).Init(0)
|
||||
myEntity.FetchData(ctx)
|
||||
myEntity.FetchData(ctx.Ctx())
|
||||
}
|
||||
|
||||
// Get home hub policy for selecting the home hub.
|
||||
@@ -112,8 +112,8 @@ findCandidates:
|
||||
err = connectToHomeHub(ctx, candidate)
|
||||
if err != nil {
|
||||
// Check if context is canceled.
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
if ctx.IsDone() {
|
||||
return ctx.Ctx().Err()
|
||||
}
|
||||
// Check if the SPN protocol is stopping again.
|
||||
if errors.Is(err, terminal.ErrStopping) {
|
||||
@@ -131,12 +131,12 @@ findCandidates:
|
||||
return errors.New("no home hub candidates available")
|
||||
}
|
||||
|
||||
func connectToHomeHub(ctx context.Context, dst *hub.Hub) error {
|
||||
func connectToHomeHub(wCtx *mgr.WorkerCtx, dst *hub.Hub) error {
|
||||
// Create new context with timeout.
|
||||
// The maximum timeout is a worst case safeguard.
|
||||
// Keep in mind that multiple IPs and protocols may be tried in all configurations.
|
||||
// Some servers will be (possibly on purpose) hard to reach.
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
|
||||
ctx, cancel := context.WithTimeout(wCtx.Ctx(), 5*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// Set and clean up exceptions.
|
||||
@@ -210,7 +210,7 @@ func connectToHomeHub(ctx context.Context, dst *hub.Hub) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func optimizeNetwork(ctx context.Context, task *modules.Task) error {
|
||||
func optimizeNetwork(ctx *mgr.WorkerCtx) error {
|
||||
if publicIdentity == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -254,7 +254,7 @@ optimize:
|
||||
} else if createdConnections < result.MaxConnect {
|
||||
attemptedConnections++
|
||||
|
||||
crane, tErr := EstablishPublicLane(ctx, connectTo.Hub)
|
||||
crane, tErr := EstablishPublicLane(ctx.Ctx(), connectTo.Hub)
|
||||
if !tErr.IsOK() {
|
||||
log.Warningf("spn/captain: failed to establish lane to %s: %s", connectTo.Hub, tErr)
|
||||
} else {
|
||||
|
||||
@@ -3,13 +3,13 @@ package captain
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/varint"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/varint"
|
||||
)
|
||||
|
||||
// GossipOpType is the type ID of the gossip operation.
|
||||
@@ -128,7 +128,7 @@ func (op *GossipOp) Deliver(msg *terminal.Msg) *terminal.Error {
|
||||
}
|
||||
|
||||
// Import and verify.
|
||||
h, forward, tErr := docks.ImportAndVerifyHubInfo(module.Ctx, "", announcementData, statusData, conf.MainMapName, conf.MainMapScope)
|
||||
h, forward, tErr := docks.ImportAndVerifyHubInfo(module.mgr.Ctx(), "", announcementData, statusData, conf.MainMapName, conf.MainMapScope)
|
||||
if tErr != nil {
|
||||
if tErr.Is(hub.ErrOldData) {
|
||||
log.Debugf("spn/captain: ignoring old %s from %s", gossipMsgType, op.craneID)
|
||||
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/varint"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/varint"
|
||||
)
|
||||
|
||||
// GossipQueryOpType is the type ID of the gossip query operation.
|
||||
@@ -63,12 +64,12 @@ func runGossipQueryOp(t terminal.Terminal, opID uint32, data *container.Containe
|
||||
op.ctx, op.cancelCtx = context.WithCancel(t.Ctx())
|
||||
op.InitOperationBase(t, opID)
|
||||
|
||||
module.StartWorker("gossip query handler", op.handler)
|
||||
module.mgr.Go("gossip query handler", op.handler)
|
||||
|
||||
return op, nil
|
||||
}
|
||||
|
||||
func (op *GossipQueryOp) handler(_ context.Context) error {
|
||||
func (op *GossipQueryOp) handler(_ *mgr.WorkerCtx) error {
|
||||
tErr := op.sendMsgs(hub.MsgTypeAnnouncement)
|
||||
if tErr != nil {
|
||||
op.Stop(op, tErr)
|
||||
@@ -166,7 +167,7 @@ func (op *GossipQueryOp) Deliver(msg *terminal.Msg) *terminal.Error {
|
||||
}
|
||||
|
||||
// Import and verify.
|
||||
h, forward, tErr := docks.ImportAndVerifyHubInfo(module.Ctx, "", announcementData, statusData, conf.MainMapName, conf.MainMapScope)
|
||||
h, forward, tErr := docks.ImportAndVerifyHubInfo(module.mgr.Ctx(), "", announcementData, statusData, conf.MainMapName, conf.MainMapScope)
|
||||
if tErr != nil {
|
||||
log.Warningf("spn/captain: failed to import %s from gossip query: %s", gossipMsgType, tErr)
|
||||
} else {
|
||||
|
||||
@@ -3,12 +3,12 @@ package captain
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portmaster/spn/cabin"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
)
|
||||
|
||||
// PublishOpType is the type ID of the publish operation.
|
||||
@@ -85,7 +85,7 @@ func runPublishOp(t terminal.Terminal, opID uint32, data *container.Container) (
|
||||
if err != nil {
|
||||
return nil, terminal.ErrMalformedData.With("failed to get status: %w", err)
|
||||
}
|
||||
h, forward, tErr := docks.ImportAndVerifyHubInfo(module.Ctx, "", announcementData, statusData, conf.MainMapName, conf.MainMapScope)
|
||||
h, forward, tErr := docks.ImportAndVerifyHubInfo(module.mgr.Ctx(), "", announcementData, statusData, conf.MainMapName, conf.MainMapScope)
|
||||
if tErr != nil {
|
||||
return nil, tErr.Wrap("failed to import and verify hub")
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/intel"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/network/netutils"
|
||||
"github.com/safing/portmaster/service/profile/endpoints"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
@@ -45,7 +46,7 @@ func startPiers() error {
|
||||
}
|
||||
|
||||
// Start worker to handle docking requests.
|
||||
module.StartServiceWorker("docking request handler", 0, dockingRequestHandler)
|
||||
module.mgr.Go("docking request handler", dockingRequestHandler)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -56,7 +57,7 @@ func stopPiers() {
|
||||
}
|
||||
}
|
||||
|
||||
func dockingRequestHandler(ctx context.Context) error {
|
||||
func dockingRequestHandler(wc *mgr.WorkerCtx) error {
|
||||
// Sink all waiting ships when this worker ends.
|
||||
// But don't be destructive so the service worker could recover.
|
||||
defer func() {
|
||||
@@ -74,7 +75,7 @@ func dockingRequestHandler(ctx context.Context) error {
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-wc.Done():
|
||||
return nil
|
||||
case ship := <-dockingRequests:
|
||||
// Ignore nil ships.
|
||||
@@ -82,7 +83,7 @@ func dockingRequestHandler(ctx context.Context) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := checkDockingPermission(ctx, ship); err != nil {
|
||||
if err := checkDockingPermission(wc.Ctx(), ship); err != nil {
|
||||
log.Warningf("spn/captain: denied ship from %s to dock at pier %s: %s", ship.RemoteAddr(), ship.Transport().String(), err)
|
||||
} else {
|
||||
handleDockingRequest(ship)
|
||||
@@ -123,8 +124,8 @@ func handleDockingRequest(ship ships.Ship) {
|
||||
return
|
||||
}
|
||||
|
||||
module.StartWorker("start crane", func(ctx context.Context) error {
|
||||
_ = crane.Start(ctx)
|
||||
module.mgr.Go("start crane", func(wc *mgr.WorkerCtx) error {
|
||||
_ = crane.Start(wc.Ctx())
|
||||
// Crane handles errors internally.
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
package captain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/metrics"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/metrics"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/cabin"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
@@ -27,9 +26,6 @@ const (
|
||||
var (
|
||||
publicIdentity *cabin.Identity
|
||||
publicIdentityKey = "core:spn/public/identity"
|
||||
|
||||
publicIdentityUpdateTask *modules.Task
|
||||
statusUpdateTask *modules.Task
|
||||
)
|
||||
|
||||
func loadPublicIdentity() (err error) {
|
||||
@@ -42,7 +38,7 @@ func loadPublicIdentity() (err error) {
|
||||
log.Infof("spn/captain: loaded public hub identity %s", publicIdentity.Hub.ID)
|
||||
case errors.Is(err, database.ErrNotFound):
|
||||
// does not exist, create new
|
||||
publicIdentity, err = cabin.CreateIdentity(module.Ctx, conf.MainMapName)
|
||||
publicIdentity, err = cabin.CreateIdentity(module.mgr.Ctx(), conf.MainMapName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create new identity: %w", err)
|
||||
}
|
||||
@@ -91,36 +87,22 @@ func loadPublicIdentity() (err error) {
|
||||
}
|
||||
|
||||
func prepPublicIdentityMgmt() error {
|
||||
publicIdentityUpdateTask = module.NewTask(
|
||||
"maintain public identity",
|
||||
maintainPublicIdentity,
|
||||
)
|
||||
module.statusUpdater.Repeat(maintainStatusInterval)
|
||||
|
||||
statusUpdateTask = module.NewTask(
|
||||
"maintain public status",
|
||||
maintainPublicStatus,
|
||||
).Repeat(maintainStatusInterval)
|
||||
|
||||
return module.RegisterEventHook(
|
||||
"config",
|
||||
"config change",
|
||||
"update public identity from config",
|
||||
func(_ context.Context, _ interface{}) error {
|
||||
// trigger update in 5 minutes
|
||||
publicIdentityUpdateTask.Schedule(time.Now().Add(5 * time.Minute))
|
||||
return nil
|
||||
},
|
||||
)
|
||||
module.instance.Config().EventConfigChange.AddCallback("update public identity from config",
|
||||
func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) {
|
||||
module.publicIdentityUpdater.Delay(5 * time.Minute)
|
||||
return false, nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerHubStatusMaintenance queues the Hub status update task to be executed.
|
||||
func TriggerHubStatusMaintenance() {
|
||||
if statusUpdateTask != nil {
|
||||
statusUpdateTask.Queue()
|
||||
}
|
||||
module.statusUpdater.Go()
|
||||
}
|
||||
|
||||
func maintainPublicIdentity(ctx context.Context, task *modules.Task) error {
|
||||
func maintainPublicIdentity(_ *mgr.WorkerCtx) error {
|
||||
changed, err := publicIdentity.MaintainAnnouncement(nil, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to maintain announcement: %w", err)
|
||||
@@ -146,7 +128,7 @@ func maintainPublicIdentity(ctx context.Context, task *modules.Task) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func maintainPublicStatus(ctx context.Context, task *modules.Task) error {
|
||||
func maintainPublicStatus(ctx *mgr.WorkerCtx) error {
|
||||
// Get current lanes.
|
||||
cranes := docks.GetAllAssignedCranes()
|
||||
lanes := make([]*hub.Lane, 0, len(cranes))
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/runtime"
|
||||
"github.com/safing/portbase/utils/debug"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/base/runtime"
|
||||
"github.com/safing/portmaster/base/utils/debug"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/navigator"
|
||||
|
||||
@@ -1,30 +1,41 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"github.com/tevino/abool"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
publicHub = abool.New()
|
||||
client = abool.New()
|
||||
publicHub atomic.Bool
|
||||
client atomic.Bool
|
||||
integrated atomic.Bool
|
||||
)
|
||||
|
||||
// PublicHub returns whether this is a public Hub.
|
||||
func PublicHub() bool {
|
||||
return publicHub.IsSet()
|
||||
return publicHub.Load()
|
||||
}
|
||||
|
||||
// EnablePublicHub enables the public hub mode.
|
||||
func EnablePublicHub(enable bool) {
|
||||
publicHub.SetTo(enable)
|
||||
publicHub.Store(enable)
|
||||
}
|
||||
|
||||
// Client returns whether this is a client.
|
||||
func Client() bool {
|
||||
return client.IsSet()
|
||||
return client.Load()
|
||||
}
|
||||
|
||||
// EnableClient enables the client mode.
|
||||
func EnableClient(enable bool) {
|
||||
client.SetTo(enable)
|
||||
client.Store(enable)
|
||||
}
|
||||
|
||||
// Integrated returns whether SPN is running integrated into Portmaster.
|
||||
func Integrated() bool {
|
||||
return integrated.Load()
|
||||
}
|
||||
|
||||
// EnableIntegration enables the integrated mode.
|
||||
func EnableIntegration(enable bool) {
|
||||
integrated.Store(enable)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/network"
|
||||
"github.com/safing/portmaster/service/profile/endpoints"
|
||||
"github.com/safing/portmaster/spn/access"
|
||||
@@ -37,7 +38,7 @@ func HandleSluiceRequest(connInfo *network.Connection, conn net.Conn) {
|
||||
connInfo: connInfo,
|
||||
conn: conn,
|
||||
}
|
||||
module.StartWorker("tunnel handler", t.connectWorker)
|
||||
module.mgr.Go("tunnel handler", t.connectWorker)
|
||||
}
|
||||
|
||||
// Tunnel represents the local information and endpoint of a data tunnel.
|
||||
@@ -52,9 +53,9 @@ type Tunnel struct {
|
||||
stickied bool
|
||||
}
|
||||
|
||||
func (t *Tunnel) connectWorker(ctx context.Context) (err error) {
|
||||
func (t *Tunnel) connectWorker(wc *mgr.WorkerCtx) (err error) {
|
||||
// Get tracing logger.
|
||||
ctx, tracer := log.AddTracer(ctx)
|
||||
ctx, tracer := log.AddTracer(wc.Ctx())
|
||||
defer tracer.Submit()
|
||||
|
||||
// Save start time.
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/metrics"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
package crew
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
)
|
||||
|
||||
var module *modules.Module
|
||||
type Crew struct {
|
||||
mgr *mgr.Manager
|
||||
instance instance
|
||||
}
|
||||
|
||||
func init() {
|
||||
module = modules.Register("crew", nil, start, stop, "terminal", "docks", "navigator", "intel", "cabin")
|
||||
func (c *Crew) Manager() *mgr.Manager {
|
||||
return c.mgr
|
||||
}
|
||||
|
||||
func (c *Crew) Start() error {
|
||||
return start()
|
||||
}
|
||||
|
||||
func (c *Crew) Stop() error {
|
||||
return stop()
|
||||
}
|
||||
|
||||
func start() error {
|
||||
module.NewTask("sticky cleaner", cleanStickyHubs).
|
||||
Repeat(10 * time.Minute)
|
||||
|
||||
_ = module.mgr.Repeat("sticky cleaner", 10*time.Minute, cleanStickyHubs)
|
||||
return registerMetrics()
|
||||
}
|
||||
|
||||
@@ -42,3 +53,23 @@ func reportConnectError(tErr *terminal.Error) {
|
||||
func ConnectErrors() <-chan *terminal.Error {
|
||||
return connectErrors
|
||||
}
|
||||
|
||||
var (
|
||||
module *Crew
|
||||
shimLoaded atomic.Bool
|
||||
)
|
||||
|
||||
// New returns a new Config module.
|
||||
func New(instance instance) (*Crew, error) {
|
||||
if !shimLoaded.CompareAndSwap(false, true) {
|
||||
return nil, errors.New("only one instance allowed")
|
||||
}
|
||||
m := mgr.New("Crew")
|
||||
module = &Crew{
|
||||
mgr: m,
|
||||
instance: instance,
|
||||
}
|
||||
return module, nil
|
||||
}
|
||||
|
||||
type instance interface{}
|
||||
|
||||
@@ -1,13 +1,134 @@
|
||||
package crew
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portmaster/service/core/pmtesting"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database/dbmodule"
|
||||
"github.com/safing/portmaster/base/metrics"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/service/core/base"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/cabin"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
conf.EnablePublicHub(true)
|
||||
pmtesting.TestMain(m, module)
|
||||
type testInstance struct {
|
||||
db *dbmodule.DBModule
|
||||
config *config.Config
|
||||
metrics *metrics.Metrics
|
||||
rng *rng.Rng
|
||||
base *base.Base
|
||||
terminal *terminal.TerminalModule
|
||||
cabin *cabin.Cabin
|
||||
}
|
||||
|
||||
func (stub *testInstance) Config() *config.Config {
|
||||
return stub.config
|
||||
}
|
||||
|
||||
func (stub *testInstance) Metrics() *metrics.Metrics {
|
||||
return stub.metrics
|
||||
}
|
||||
|
||||
func (stub *testInstance) SPNGroup() *mgr.ExtendedGroup {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stub *testInstance) Stopping() bool {
|
||||
return false
|
||||
}
|
||||
func (stub *testInstance) SetCmdLineOperation(f func() error) {}
|
||||
|
||||
func runTest(m *testing.M) error {
|
||||
conf.EnablePublicHub(true) // Make hub config available.
|
||||
ds, err := config.InitializeUnitTestDataroot("test-crew")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize dataroot: %w", err)
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(ds) }()
|
||||
|
||||
instance := &testInstance{}
|
||||
// Init
|
||||
instance.db, err = dbmodule.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create database module: %w", err)
|
||||
}
|
||||
instance.config, err = config.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config module: %w", err)
|
||||
}
|
||||
instance.metrics, err = metrics.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create metrics module: %w", err)
|
||||
}
|
||||
instance.rng, err = rng.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create rng module: %w", err)
|
||||
}
|
||||
instance.base, err = base.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create base module: %w", err)
|
||||
}
|
||||
instance.terminal, err = terminal.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create terminal module: %w", err)
|
||||
}
|
||||
instance.cabin, err = cabin.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create cabin module: %w", err)
|
||||
}
|
||||
module, err = New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create crew module: %w", err)
|
||||
}
|
||||
|
||||
// Start
|
||||
err = instance.db.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start db module: %w", err)
|
||||
}
|
||||
err = instance.config.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start config module: %w", err)
|
||||
}
|
||||
err = instance.metrics.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start metrics module: %w", err)
|
||||
}
|
||||
err = instance.rng.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start rng module: %w", err)
|
||||
}
|
||||
err = instance.base.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start base module: %w", err)
|
||||
}
|
||||
err = instance.terminal.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start terminal module: %w", err)
|
||||
}
|
||||
err = instance.cabin.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start cabin module: %w", err)
|
||||
}
|
||||
err = module.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start crew module: %w", err)
|
||||
}
|
||||
|
||||
conf.EnablePublicHub(true)
|
||||
m.Run()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if err := runTest(m); err != nil {
|
||||
fmt.Printf("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/network/netutils"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
// ConnectOpType is the type ID for the connection operation.
|
||||
@@ -141,7 +142,7 @@ func NewConnectOp(tunnel *Tunnel) (*ConnectOp, *terminal.Error) {
|
||||
entry: true,
|
||||
tunnel: tunnel,
|
||||
}
|
||||
op.ctx, op.cancelCtx = context.WithCancel(module.Ctx)
|
||||
op.ctx, op.cancelCtx = context.WithCancel(module.mgr.Ctx())
|
||||
op.dfq = terminal.NewDuplexFlowQueue(op.Ctx(), request.QueueSize, op.submitUpstream)
|
||||
|
||||
// Prepare init msg.
|
||||
@@ -159,9 +160,9 @@ func NewConnectOp(tunnel *Tunnel) (*ConnectOp, *terminal.Error) {
|
||||
// Setup metrics.
|
||||
op.started = time.Now()
|
||||
|
||||
module.StartWorker("connect op conn reader", op.connReader)
|
||||
module.StartWorker("connect op conn writer", op.connWriter)
|
||||
module.StartWorker("connect op flow handler", op.dfq.FlowHandler)
|
||||
module.mgr.Go("connect op conn reader", op.connReader)
|
||||
module.mgr.Go("connect op conn writer", op.connWriter)
|
||||
module.mgr.Go("connect op flow handler", op.dfq.FlowHandler)
|
||||
|
||||
log.Infof("spn/crew: connected to %s via %s", request, tunnel.dstPin.Hub)
|
||||
return op, nil
|
||||
@@ -202,12 +203,12 @@ func startConnectOp(t terminal.Terminal, opID uint32, data *container.Container)
|
||||
op.dfq = terminal.NewDuplexFlowQueue(op.Ctx(), request.QueueSize, op.submitUpstream)
|
||||
|
||||
// Start worker to complete setting up the connection.
|
||||
module.StartWorker("connect op setup", op.handleSetup)
|
||||
module.mgr.Go("connect op setup", op.handleSetup)
|
||||
|
||||
return op, nil
|
||||
}
|
||||
|
||||
func (op *ConnectOp) handleSetup(_ context.Context) error {
|
||||
func (op *ConnectOp) handleSetup(_ *mgr.WorkerCtx) error {
|
||||
// Get terminal session for rate limiting.
|
||||
var session *terminal.Session
|
||||
if sessionTerm, ok := op.t.(terminal.SessionTerminal); ok {
|
||||
@@ -309,9 +310,9 @@ func (op *ConnectOp) setup(session *terminal.Session) {
|
||||
op.conn = conn
|
||||
|
||||
// Start worker.
|
||||
module.StartWorker("connect op conn reader", op.connReader)
|
||||
module.StartWorker("connect op conn writer", op.connWriter)
|
||||
module.StartWorker("connect op flow handler", op.dfq.FlowHandler)
|
||||
module.mgr.Go("connect op conn reader", op.connReader)
|
||||
module.mgr.Go("connect op conn writer", op.connWriter)
|
||||
module.mgr.Go("connect op flow handler", op.dfq.FlowHandler)
|
||||
|
||||
connectOpCntConnected.Inc()
|
||||
log.Infof("spn/crew: connected op %s#%d to %s", op.t.FmtID(), op.ID(), op.request)
|
||||
@@ -337,7 +338,7 @@ const (
|
||||
rateLimitMaxMbit = 128
|
||||
)
|
||||
|
||||
func (op *ConnectOp) connReader(_ context.Context) error {
|
||||
func (op *ConnectOp) connReader(_ *mgr.WorkerCtx) error {
|
||||
// Metrics setup and submitting.
|
||||
atomic.AddInt64(activeConnectOps, 1)
|
||||
defer func() {
|
||||
@@ -403,7 +404,7 @@ func (op *ConnectOp) Deliver(msg *terminal.Msg) *terminal.Error {
|
||||
return op.dfq.Deliver(msg)
|
||||
}
|
||||
|
||||
func (op *ConnectOp) connWriter(_ context.Context) error {
|
||||
func (op *ConnectOp) connWriter(_ *mgr.WorkerCtx) error {
|
||||
// Metrics submitting.
|
||||
defer func() {
|
||||
connectOpOutgoingDataHistogram.Update(float64(op.outgoingTraffic.Load()))
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestConnectOp(t *testing.T) {
|
||||
// Set up connect op.
|
||||
b.GrantPermission(terminal.MayConnect)
|
||||
conf.EnablePublicHub(true)
|
||||
identity, err := cabin.CreateIdentity(module.Ctx, "test")
|
||||
identity, err := cabin.CreateIdentity(module.mgr.Ctx(), "test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create identity: %s", err)
|
||||
}
|
||||
@@ -61,8 +61,7 @@ func TestConnectOp(t *testing.T) {
|
||||
t.Fatalf("failed to update identity: %s", err)
|
||||
}
|
||||
EnableConnecting(identity.Hub)
|
||||
|
||||
for i := 0; i < 1; i++ {
|
||||
{
|
||||
appConn, sluiceConn := net.Pipe()
|
||||
_, tErr := NewConnectOp(&Tunnel{
|
||||
connInfo: &network.Connection{
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"crypto/subtle"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package crew
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/network"
|
||||
"github.com/safing/portmaster/service/network/packet"
|
||||
"github.com/safing/portmaster/spn/navigator"
|
||||
@@ -149,7 +148,7 @@ func (t *Tunnel) avoidDestinationHub() {
|
||||
log.Warningf("spn/crew: avoiding %s for %s", t.dstPin.Hub, ipKey)
|
||||
}
|
||||
|
||||
func cleanStickyHubs(ctx context.Context, task *modules.Task) error {
|
||||
func cleanStickyHubs(ctx *mgr.WorkerCtx) error {
|
||||
stickyLock.Lock()
|
||||
defer stickyLock.Unlock()
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
func TestEffectiveBandwidth(t *testing.T) { //nolint:paralleltest // Run alone.
|
||||
@@ -66,7 +66,7 @@ func TestEffectiveBandwidth(t *testing.T) { //nolint:paralleltest // Run alone.
|
||||
t.Fatal(tErr)
|
||||
}
|
||||
// Start handler.
|
||||
module.StartWorker("op capacity handler", op.handler)
|
||||
module.mgr.Go("op capacity handler", op.handler)
|
||||
|
||||
// Wait for result and check error.
|
||||
tErr = <-op.Result()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package docks
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
)
|
||||
|
||||
// CraneControllerTerminal is a terminal for the crane itself.
|
||||
@@ -80,7 +80,7 @@ func initCraneController(
|
||||
t.GrantPermission(terminal.IsCraneController)
|
||||
|
||||
// Start workers.
|
||||
t.StartWorkers(module, "crane controller terminal")
|
||||
t.StartWorkers(module.mgr, "crane controller terminal")
|
||||
|
||||
return cct
|
||||
}
|
||||
|
||||
@@ -12,14 +12,15 @@ import (
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/jess"
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/varint"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/cabin"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
"github.com/safing/portmaster/spn/ships"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/varint"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -110,7 +111,7 @@ type Crane struct {
|
||||
// NewCrane returns a new crane.
|
||||
func NewCrane(ship ships.Ship, connectedHub *hub.Hub, id *cabin.Identity) (*Crane, error) {
|
||||
// Cranes always run in module context.
|
||||
ctx, cancelCtx := context.WithCancel(module.Ctx)
|
||||
ctx, cancelCtx := context.WithCancel(module.mgr.Ctx())
|
||||
|
||||
newCrane := &Crane{
|
||||
ctx: ctx,
|
||||
@@ -351,7 +352,7 @@ func (crane *Crane) AbandonTerminal(id uint32, err *terminal.Error) {
|
||||
if crane.stopping.IsSet() &&
|
||||
crane.terminalCount() <= 1 {
|
||||
// Stop the crane in worker, so the caller can do some work.
|
||||
module.StartWorker("retire crane", func(_ context.Context) error {
|
||||
module.mgr.Go("retire crane", func(_ *mgr.WorkerCtx) error {
|
||||
// Let enough time for the last errors to be sent, as terminals are abandoned in a goroutine.
|
||||
time.Sleep(3 * time.Second)
|
||||
crane.Stop(nil)
|
||||
@@ -426,7 +427,7 @@ func (crane *Crane) decrypt(shipment *container.Container) (decrypted *container
|
||||
return container.New(decryptedData), nil
|
||||
}
|
||||
|
||||
func (crane *Crane) unloader(workerCtx context.Context) error {
|
||||
func (crane *Crane) unloader(workerCtx *mgr.WorkerCtx) error {
|
||||
// Unclean shutdown safeguard.
|
||||
defer crane.Stop(terminal.ErrUnknownError.With("unloader died"))
|
||||
|
||||
@@ -516,7 +517,7 @@ func (crane *Crane) unloadUntilFull(buf []byte) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (crane *Crane) handler(workerCtx context.Context) error {
|
||||
func (crane *Crane) handler(workerCtx *mgr.WorkerCtx) error {
|
||||
var partialShipment *container.Container
|
||||
var segmentLength uint32
|
||||
|
||||
@@ -618,7 +619,7 @@ handling:
|
||||
if deliveryErr != nil {
|
||||
msg.Finish()
|
||||
// This is a hot path. Start a worker for abandoning the terminal.
|
||||
module.StartWorker("end terminal", func(_ context.Context) error {
|
||||
module.mgr.Go("end terminal", func(_ *mgr.WorkerCtx) error {
|
||||
crane.AbandonTerminal(t.ID(), deliveryErr.Wrap("failed to deliver data"))
|
||||
return nil
|
||||
})
|
||||
@@ -635,7 +636,7 @@ handling:
|
||||
receivedErr = terminal.ErrUnknownError.AsExternal()
|
||||
}
|
||||
// This is a hot path. Start a worker for abandoning the terminal.
|
||||
module.StartWorker("end terminal", func(_ context.Context) error {
|
||||
module.mgr.Go("end terminal", func(_ *mgr.WorkerCtx) error {
|
||||
crane.AbandonTerminal(terminalID, receivedErr)
|
||||
return nil
|
||||
})
|
||||
@@ -645,7 +646,7 @@ handling:
|
||||
}
|
||||
}
|
||||
|
||||
func (crane *Crane) loader(workerCtx context.Context) (err error) {
|
||||
func (crane *Crane) loader(workerCtx *mgr.WorkerCtx) (err error) {
|
||||
shipment := container.New()
|
||||
var partialShipment *container.Container
|
||||
var loadingTimer *time.Timer
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package docks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -70,7 +70,7 @@ func (crane *Crane) establishTerminal(id uint32, initData *container.Container)
|
||||
case crane.terminalMsgs <- msg:
|
||||
default:
|
||||
// Send error async.
|
||||
module.StartWorker("abandon terminal", func(ctx context.Context) error {
|
||||
module.mgr.Go("abandon terminal", func(ctx *mgr.WorkerCtx) error {
|
||||
select {
|
||||
case crane.terminalMsgs <- msg:
|
||||
case <-ctx.Done():
|
||||
|
||||
@@ -5,13 +5,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/safing/jess"
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portbase/formats/varint"
|
||||
"github.com/safing/portbase/info"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/info"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/dsd"
|
||||
"github.com/safing/structures/varint"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -71,7 +71,7 @@ func (crane *Crane) Start(callerCtx context.Context) error {
|
||||
}
|
||||
|
||||
func (crane *Crane) startLocal(callerCtx context.Context) *terminal.Error {
|
||||
module.StartWorker("crane unloader", crane.unloader)
|
||||
module.mgr.Go("crane unloader", crane.unloader)
|
||||
|
||||
if !crane.ship.IsSecure() {
|
||||
// Start encrypted channel.
|
||||
@@ -171,8 +171,8 @@ func (crane *Crane) startLocal(callerCtx context.Context) *terminal.Error {
|
||||
}
|
||||
|
||||
// Start remaining workers.
|
||||
module.StartWorker("crane loader", crane.loader)
|
||||
module.StartWorker("crane handler", crane.handler)
|
||||
module.mgr.Go("crane loader", crane.loader)
|
||||
module.mgr.Go("crane handler", crane.handler)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -180,7 +180,7 @@ func (crane *Crane) startLocal(callerCtx context.Context) *terminal.Error {
|
||||
func (crane *Crane) startRemote(callerCtx context.Context) *terminal.Error {
|
||||
var initMsg *container.Container
|
||||
|
||||
module.StartWorker("crane unloader", crane.unloader)
|
||||
module.mgr.Go("crane unloader", crane.unloader)
|
||||
|
||||
handling:
|
||||
for {
|
||||
@@ -270,8 +270,8 @@ handling:
|
||||
}
|
||||
|
||||
// Start remaining workers.
|
||||
module.StartWorker("crane loader", crane.loader)
|
||||
module.StartWorker("crane handler", crane.handler)
|
||||
module.mgr.Go("crane loader", crane.loader)
|
||||
module.mgr.Go("crane handler", crane.handler)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package docks
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
)
|
||||
|
||||
// CraneTerminal is a terminal started by a crane.
|
||||
@@ -74,7 +74,7 @@ func initCraneTerminal(
|
||||
t.SetTerminalExtension(ct)
|
||||
|
||||
// Start workers.
|
||||
t.StartWorkers(module, "crane terminal")
|
||||
t.StartWorkers(module.mgr, "crane terminal")
|
||||
|
||||
return ct
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func testCraneWithCounter(t *testing.T, testID string, encrypting bool, loadSize
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crane test %s could not create crane1: %s", testID, err))
|
||||
}
|
||||
err = crane1.Start(module.Ctx)
|
||||
err = crane1.Start(module.mgr.Ctx())
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crane test %s could not start crane1: %s", testID, err))
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func testCraneWithCounter(t *testing.T, testID string, encrypting bool, loadSize
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crane test %s could not create crane2: %s", testID, err))
|
||||
}
|
||||
err = crane2.Start(module.Ctx)
|
||||
err = crane2.Start(module.mgr.Ctx())
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crane test %s could not start crane2: %s", testID, err))
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func (t *StreamingTerminal) ID() uint32 {
|
||||
}
|
||||
|
||||
func (t *StreamingTerminal) Ctx() context.Context {
|
||||
return module.Ctx
|
||||
return module.mgr.Ctx()
|
||||
}
|
||||
|
||||
func (t *StreamingTerminal) Deliver(msg *terminal.Msg) *terminal.Error {
|
||||
@@ -170,7 +170,7 @@ func testCraneWithStreaming(t *testing.T, testID string, encrypting bool, loadSi
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crane test %s could not create crane1: %s", testID, err))
|
||||
}
|
||||
err = crane1.Start(module.Ctx)
|
||||
err = crane1.Start(module.mgr.Ctx())
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crane test %s could not start crane1: %s", testID, err))
|
||||
}
|
||||
@@ -182,7 +182,7 @@ func testCraneWithStreaming(t *testing.T, testID string, encrypting bool, loadSi
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crane test %s could not create crane2: %s", testID, err))
|
||||
}
|
||||
err = crane2.Start(module.Ctx)
|
||||
err = crane2.Start(module.mgr.Ctx())
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crane test %s could not start crane2: %s", testID, err))
|
||||
}
|
||||
@@ -257,7 +257,7 @@ func getTestIdentity(t *testing.T) (*cabin.Identity, *hub.Hub) {
|
||||
|
||||
if testIdentity == nil {
|
||||
var err error
|
||||
testIdentity, err = cabin.CreateIdentity(module.Ctx, "test")
|
||||
testIdentity, err = cabin.CreateIdentity(module.mgr.Ctx(), "test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create identity: %s", err)
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/varint"
|
||||
"github.com/safing/portmaster/spn/cabin"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/varint"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -3,7 +3,7 @@ package docks
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
"github.com/safing/portmaster/spn/ships"
|
||||
@@ -33,7 +33,7 @@ func ImportAndVerifyHubInfo(ctx context.Context, hubID string, announcementData,
|
||||
var hubKnown, hubChanged bool
|
||||
if announcementData != nil {
|
||||
hubFromMsg, known, changed, err := hub.ApplyAnnouncement(nil, announcementData, mapName, scope, false)
|
||||
if err != nil && firstErr == nil {
|
||||
if err != nil {
|
||||
firstErr = terminal.ErrInternalError.With("failed to apply announcement: %w", err)
|
||||
}
|
||||
if known {
|
||||
@@ -170,7 +170,7 @@ func verifyHubIP(ctx context.Context, h *hub.Hub, ip net.IP) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create crane: %w", err)
|
||||
}
|
||||
module.StartWorker("crane unloader", crane.unloader)
|
||||
module.mgr.Go("crane unloader", crane.unloader)
|
||||
defer crane.Stop(nil)
|
||||
|
||||
// Verify Hub.
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/metrics"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -5,15 +5,35 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
_ "github.com/safing/portmaster/spn/access"
|
||||
)
|
||||
|
||||
var (
|
||||
module *modules.Module
|
||||
// Docks handles connections to other network participants.
|
||||
type Docks struct {
|
||||
mgr *mgr.Manager
|
||||
instance instance
|
||||
}
|
||||
|
||||
// Manager returns the module manager.
|
||||
func (d *Docks) Manager() *mgr.Manager {
|
||||
return d.mgr
|
||||
}
|
||||
|
||||
// Start starts the module.
|
||||
func (d *Docks) Start() error {
|
||||
return start()
|
||||
}
|
||||
|
||||
// Stop stops the module.
|
||||
func (d *Docks) Stop() error {
|
||||
return stopAllCranes()
|
||||
}
|
||||
|
||||
var (
|
||||
allCranes = make(map[string]*Crane) // ID = Crane ID
|
||||
assignedCranes = make(map[string]*Crane) // ID = connected Hub ID
|
||||
cranesLock sync.RWMutex
|
||||
@@ -21,10 +41,6 @@ var (
|
||||
runningTests bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("docks", nil, start, stopAllCranes, "terminal", "cabin", "access")
|
||||
}
|
||||
|
||||
func start() error {
|
||||
return registerMetrics()
|
||||
}
|
||||
@@ -34,7 +50,7 @@ func registerCrane(crane *Crane) error {
|
||||
defer cranesLock.Unlock()
|
||||
|
||||
// Generate new IDs until a unique one is found.
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
// Generate random ID.
|
||||
randomID, err := rng.Bytes(3)
|
||||
if err != nil {
|
||||
@@ -115,3 +131,23 @@ func GetAllAssignedCranes() map[string]*Crane {
|
||||
}
|
||||
return copiedCranes
|
||||
}
|
||||
|
||||
var (
|
||||
module *Docks
|
||||
shimLoaded atomic.Bool
|
||||
)
|
||||
|
||||
// New returns a new Docks module.
|
||||
func New(instance instance) (*Docks, error) {
|
||||
if !shimLoaded.CompareAndSwap(false, true) {
|
||||
return nil, errors.New("only one instance allowed")
|
||||
}
|
||||
m := mgr.New("Docks")
|
||||
module = &Docks{
|
||||
mgr: m,
|
||||
instance: instance,
|
||||
}
|
||||
return module, nil
|
||||
}
|
||||
|
||||
type instance interface{}
|
||||
|
||||
@@ -1,16 +1,148 @@
|
||||
package docks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portmaster/service/core/pmtesting"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database/dbmodule"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/metrics"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/service/core/base"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/access"
|
||||
"github.com/safing/portmaster/spn/cabin"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
type testInstance struct {
|
||||
db *dbmodule.DBModule
|
||||
config *config.Config
|
||||
metrics *metrics.Metrics
|
||||
rng *rng.Rng
|
||||
base *base.Base
|
||||
access *access.Access
|
||||
terminal *terminal.TerminalModule
|
||||
cabin *cabin.Cabin
|
||||
}
|
||||
|
||||
func (stub *testInstance) Config() *config.Config {
|
||||
return stub.config
|
||||
}
|
||||
|
||||
func (stub *testInstance) Metrics() *metrics.Metrics {
|
||||
return stub.metrics
|
||||
}
|
||||
|
||||
func (stub *testInstance) SPNGroup() *mgr.ExtendedGroup {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stub *testInstance) Stopping() bool {
|
||||
return false
|
||||
}
|
||||
func (stub *testInstance) SetCmdLineOperation(f func() error) {}
|
||||
|
||||
func runTest(m *testing.M) error {
|
||||
_ = log.Start()
|
||||
|
||||
ds, err := config.InitializeUnitTestDataroot("test-docks")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize dataroot: %w", err)
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(ds) }()
|
||||
|
||||
instance := &testInstance{}
|
||||
runningTests = true
|
||||
conf.EnablePublicHub(true) // Make hub config available.
|
||||
access.EnableTestMode() // Register test zone instead of real ones.
|
||||
pmtesting.TestMain(m, module)
|
||||
|
||||
// Init
|
||||
instance.db, err = dbmodule.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create database module: %w", err)
|
||||
}
|
||||
instance.config, err = config.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config module: %w", err)
|
||||
}
|
||||
instance.metrics, err = metrics.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create metrics module: %w", err)
|
||||
}
|
||||
instance.rng, err = rng.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create rng module: %w", err)
|
||||
}
|
||||
instance.base, err = base.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create base module: %w", err)
|
||||
}
|
||||
instance.access, err = access.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create access module: %w", err)
|
||||
}
|
||||
instance.terminal, err = terminal.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create terminal module: %w", err)
|
||||
}
|
||||
instance.cabin, err = cabin.New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create cabin module: %w", err)
|
||||
}
|
||||
module, err = New(instance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create docks module: %w", err)
|
||||
}
|
||||
|
||||
// Start
|
||||
err = instance.db.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start db module: %w", err)
|
||||
}
|
||||
err = instance.config.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start config module: %w", err)
|
||||
}
|
||||
err = instance.metrics.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start metrics module: %w", err)
|
||||
}
|
||||
err = instance.rng.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start rng module: %w", err)
|
||||
}
|
||||
err = instance.base.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start base module: %w", err)
|
||||
}
|
||||
err = instance.access.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start access module: %w", err)
|
||||
}
|
||||
err = instance.terminal.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start terminal module: %w", err)
|
||||
}
|
||||
err = instance.cabin.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start cabin module: %w", err)
|
||||
}
|
||||
err = module.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start docks module: %w", err)
|
||||
}
|
||||
|
||||
m.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if err := runTest(m); err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@ package docks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -116,7 +116,7 @@ func NewCapacityTestOp(t terminal.Terminal, opts *CapacityTestOptions) (*Capacit
|
||||
}
|
||||
|
||||
// Start handler.
|
||||
module.StartWorker("op capacity handler", op.handler)
|
||||
module.mgr.Go("op capacity handler", op.handler)
|
||||
|
||||
return op, nil
|
||||
}
|
||||
@@ -157,13 +157,13 @@ func startCapacityTestOp(t terminal.Terminal, opID uint32, data *container.Conta
|
||||
|
||||
// Start handler and sender.
|
||||
op.senderStarted = true
|
||||
module.StartWorker("op capacity handler", op.handler)
|
||||
module.StartWorker("op capacity sender", op.sender)
|
||||
module.mgr.Go("op capacity handler", op.handler)
|
||||
module.mgr.Go("op capacity sender", op.sender)
|
||||
|
||||
return op, nil
|
||||
}
|
||||
|
||||
func (op *CapacityTestOp) handler(ctx context.Context) error {
|
||||
func (op *CapacityTestOp) handler(ctx *mgr.WorkerCtx) error {
|
||||
defer capacityTestRunning.UnSet()
|
||||
|
||||
returnErr := terminal.ErrStopping
|
||||
@@ -204,7 +204,7 @@ func (op *CapacityTestOp) handler(ctx context.Context) error {
|
||||
maxTestTimeReached = time.After(op.opts.MaxTime)
|
||||
if !op.senderStarted {
|
||||
op.senderStarted = true
|
||||
module.StartWorker("op capacity sender", op.sender)
|
||||
module.mgr.Go("op capacity sender", op.sender)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ func (op *CapacityTestOp) handler(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (op *CapacityTestOp) sender(ctx context.Context) error {
|
||||
func (op *CapacityTestOp) sender(ctx *mgr.WorkerCtx) error {
|
||||
for {
|
||||
// Send next chunk.
|
||||
msg := op.NewMsg(capacityTestSendData)
|
||||
|
||||
@@ -60,7 +60,7 @@ func testCapacityOp(t *testing.T, opts *CapacityTestOptions) {
|
||||
b.GrantPermission(terminal.IsCraneController)
|
||||
op, tErr := NewCapacityTestOp(a, opts)
|
||||
if tErr != nil {
|
||||
t.Fatalf("failed to start op: %s", err)
|
||||
t.Fatalf("failed to start op: %s", tErr)
|
||||
}
|
||||
|
||||
// Wait for result and check error.
|
||||
|
||||
@@ -8,9 +8,10 @@ import (
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
)
|
||||
|
||||
// ExpandOpType is the type ID of the expand operation.
|
||||
@@ -201,13 +202,13 @@ func expand(t terminal.Terminal, opID uint32, data *container.Container) (termin
|
||||
}
|
||||
|
||||
// Start workers.
|
||||
module.StartWorker("expand op forward relay", op.forwardHandler)
|
||||
module.StartWorker("expand op backward relay", op.backwardHandler)
|
||||
module.mgr.Go("expand op forward relay", op.forwardHandler)
|
||||
module.mgr.Go("expand op backward relay", op.backwardHandler)
|
||||
if op.flowControl != nil {
|
||||
op.flowControl.StartWorkers(module, "expand op")
|
||||
op.flowControl.StartWorkers(module.mgr, "expand op")
|
||||
}
|
||||
if op.relayTerminal.flowControl != nil {
|
||||
op.relayTerminal.flowControl.StartWorkers(module, "expand op terminal")
|
||||
op.relayTerminal.flowControl.StartWorkers(module.mgr, "expand op terminal")
|
||||
}
|
||||
|
||||
return op, nil
|
||||
@@ -259,7 +260,7 @@ func (op *ExpandOp) submitBackwardUpstream(msg *terminal.Msg, timeout time.Durat
|
||||
}
|
||||
}
|
||||
|
||||
func (op *ExpandOp) forwardHandler(_ context.Context) error {
|
||||
func (op *ExpandOp) forwardHandler(_ *mgr.WorkerCtx) error {
|
||||
// Metrics setup and submitting.
|
||||
atomic.AddInt64(activeExpandOps, 1)
|
||||
started := time.Now()
|
||||
@@ -290,7 +291,7 @@ func (op *ExpandOp) forwardHandler(_ context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (op *ExpandOp) backwardHandler(_ context.Context) error {
|
||||
func (op *ExpandOp) backwardHandler(_ *mgr.WorkerCtx) error {
|
||||
for {
|
||||
select {
|
||||
case msg := <-op.relayTerminal.recvProxy():
|
||||
@@ -336,7 +337,7 @@ func (op *ExpandOp) HandleStop(err *terminal.Error) (errorToSend *terminal.Error
|
||||
// Abandon shuts down the terminal unregistering it from upstream and calling HandleAbandon().
|
||||
func (t *ExpansionRelayTerminal) Abandon(err *terminal.Error) {
|
||||
if t.abandoning.SetToIf(false, true) {
|
||||
module.StartWorker("terminal abandon procedure", func(_ context.Context) error {
|
||||
module.mgr.Go("terminal abandon procedure", func(_ *mgr.WorkerCtx) error {
|
||||
t.handleAbandonProcedure(err)
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -2,15 +2,15 @@ package docks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/varint"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/rng"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/varint"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -82,12 +82,12 @@ func NewLatencyTestOp(t terminal.Terminal) (*LatencyTestClientOp, *terminal.Erro
|
||||
}
|
||||
|
||||
// Start handler.
|
||||
module.StartWorker("op latency handler", op.handler)
|
||||
module.mgr.Go("op latency handler", op.handler)
|
||||
|
||||
return op, nil
|
||||
}
|
||||
|
||||
func (op *LatencyTestClientOp) handler(ctx context.Context) error {
|
||||
func (op *LatencyTestClientOp) handler(ctx *mgr.WorkerCtx) error {
|
||||
returnErr := terminal.ErrStopping
|
||||
defer func() {
|
||||
// Linters don't get that returnErr is used when directly used as defer.
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestLatencyOp(t *testing.T) {
|
||||
b.GrantPermission(terminal.IsCraneController)
|
||||
op, tErr := NewLatencyTestOp(a)
|
||||
if tErr != nil {
|
||||
t.Fatalf("failed to start op: %s", err)
|
||||
t.Fatalf("failed to start op: %s", tErr)
|
||||
}
|
||||
|
||||
// Wait for result and check error.
|
||||
|
||||
@@ -4,10 +4,11 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
// SyncStateOpType is the type ID of the sync state operation.
|
||||
@@ -39,8 +40,8 @@ func init() {
|
||||
|
||||
// startSyncStateOp starts a worker that runs the sync state operation.
|
||||
func (crane *Crane) startSyncStateOp() {
|
||||
module.StartWorker("sync crane state", func(ctx context.Context) error {
|
||||
tErr := crane.Controller.SyncState(ctx)
|
||||
module.mgr.Go("sync crane state", func(wc *mgr.WorkerCtx) error {
|
||||
tErr := crane.Controller.SyncState(wc.Ctx())
|
||||
if tErr != nil {
|
||||
return tErr
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package docks
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
"github.com/safing/structures/container"
|
||||
)
|
||||
|
||||
// ExpansionTerminal is used for expanding to another Hub.
|
||||
@@ -53,7 +53,7 @@ func ExpandTo(from terminal.Terminal, routeTo string, encryptFor *hub.Hub) (*Exp
|
||||
|
||||
// Create base terminal for expansion.
|
||||
base, initData, tErr := terminal.NewLocalBaseTerminal(
|
||||
module.Ctx,
|
||||
module.mgr.Ctx(),
|
||||
0, // Ignore; The ID of the operation is used for communication.
|
||||
from.FmtID(),
|
||||
encryptFor,
|
||||
@@ -81,7 +81,7 @@ func ExpandTo(from terminal.Terminal, routeTo string, encryptFor *hub.Hub) (*Exp
|
||||
}
|
||||
|
||||
// Start Workers.
|
||||
base.StartWorkers(module, "expansion terminal")
|
||||
base.StartWorkers(module.mgr, "expansion terminal")
|
||||
|
||||
return expansion, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package docks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
@@ -75,7 +76,7 @@ func testExpansion( //nolint:maintidx,thelper
|
||||
serverCountTo uint64,
|
||||
inParallel bool,
|
||||
) {
|
||||
testID += fmt.Sprintf(":encrypt=%v,flowType=%d,parallel=%v", terminalOpts.Encrypt, terminalOpts.FlowControl, inParallel)
|
||||
testID += fmt.Sprintf(":encrypt=%v,flowCtrl=%d,parallel=%v", terminalOpts.Encrypt, terminalOpts.FlowControl, inParallel)
|
||||
|
||||
var identity2, identity3, identity4 *cabin.Identity
|
||||
var connectedHub2, connectedHub3, connectedHub4 *hub.Hub
|
||||
@@ -95,6 +96,9 @@ func testExpansion( //nolint:maintidx,thelper
|
||||
var craneWg sync.WaitGroup
|
||||
craneWg.Add(6)
|
||||
|
||||
craneCtx, cancelCraneCtx := context.WithCancel(context.Background())
|
||||
defer cancelCraneCtx()
|
||||
|
||||
go func() {
|
||||
var err error
|
||||
crane1, err = NewCrane(ship1to2, connectedHub2, nil)
|
||||
@@ -102,7 +106,7 @@ func testExpansion( //nolint:maintidx,thelper
|
||||
panic(fmt.Sprintf("expansion test %s could not create crane1: %s", testID, err))
|
||||
}
|
||||
crane1.ID = "c1"
|
||||
err = crane1.Start(module.Ctx)
|
||||
err = crane1.Start(craneCtx)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("expansion test %s could not start crane1: %s", testID, err))
|
||||
}
|
||||
@@ -116,7 +120,7 @@ func testExpansion( //nolint:maintidx,thelper
|
||||
panic(fmt.Sprintf("expansion test %s could not create crane2to1: %s", testID, err))
|
||||
}
|
||||
crane2to1.ID = "c2to1"
|
||||
err = crane2to1.Start(module.Ctx)
|
||||
err = crane2to1.Start(craneCtx)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("expansion test %s could not start crane2to1: %s", testID, err))
|
||||
}
|
||||
@@ -130,7 +134,7 @@ func testExpansion( //nolint:maintidx,thelper
|
||||
panic(fmt.Sprintf("expansion test %s could not create crane2to3: %s", testID, err))
|
||||
}
|
||||
crane2to3.ID = "c2to3"
|
||||
err = crane2to3.Start(module.Ctx)
|
||||
err = crane2to3.Start(craneCtx)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("expansion test %s could not start crane2to3: %s", testID, err))
|
||||
}
|
||||
@@ -144,7 +148,7 @@ func testExpansion( //nolint:maintidx,thelper
|
||||
panic(fmt.Sprintf("expansion test %s could not create crane3to2: %s", testID, err))
|
||||
}
|
||||
crane3to2.ID = "c3to2"
|
||||
err = crane3to2.Start(module.Ctx)
|
||||
err = crane3to2.Start(craneCtx)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("expansion test %s could not start crane3to2: %s", testID, err))
|
||||
}
|
||||
@@ -158,7 +162,7 @@ func testExpansion( //nolint:maintidx,thelper
|
||||
panic(fmt.Sprintf("expansion test %s could not create crane3to4: %s", testID, err))
|
||||
}
|
||||
crane3to4.ID = "c3to4"
|
||||
err = crane3to4.Start(module.Ctx)
|
||||
err = crane3to4.Start(craneCtx)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("expansion test %s could not start crane3to4: %s", testID, err))
|
||||
}
|
||||
@@ -172,7 +176,7 @@ func testExpansion( //nolint:maintidx,thelper
|
||||
panic(fmt.Sprintf("expansion test %s could not create crane4: %s", testID, err))
|
||||
}
|
||||
crane4.ID = "c4"
|
||||
err = crane4.Start(module.Ctx)
|
||||
err = crane4.Start(craneCtx)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("expansion test %s could not start crane4: %s", testID, err))
|
||||
}
|
||||
@@ -288,7 +292,7 @@ func testExpansion( //nolint:maintidx,thelper
|
||||
op1.Wait()
|
||||
}
|
||||
|
||||
// Wait for completion.
|
||||
// Signal completion.
|
||||
close(finished)
|
||||
|
||||
// Wait a little so that all errors can be propagated, so we can truly see
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/iterator"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/database/iterator"
|
||||
"github.com/safing/portmaster/base/database/query"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/safing/jess"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/profile/endpoints"
|
||||
)
|
||||
|
||||
@@ -425,7 +425,7 @@ func equalStringSlice(a, b []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
for i := range len(a) {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,20 +1,121 @@
|
||||
package hub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
_ "github.com/safing/portmaster/service/core/base"
|
||||
"github.com/safing/portmaster/service/core/pmtesting"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database/dbmodule"
|
||||
"github.com/safing/portmaster/base/notifications"
|
||||
"github.com/safing/portmaster/service/core/base"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
)
|
||||
|
||||
type testInstance struct {
|
||||
db *dbmodule.DBModule
|
||||
api *api.API
|
||||
config *config.Config
|
||||
updates *updates.Updates
|
||||
base *base.Base
|
||||
}
|
||||
|
||||
func (stub *testInstance) Updates() *updates.Updates {
|
||||
return stub.updates
|
||||
}
|
||||
|
||||
func (stub *testInstance) API() *api.API {
|
||||
return stub.api
|
||||
}
|
||||
|
||||
func (stub *testInstance) Config() *config.Config {
|
||||
return stub.config
|
||||
}
|
||||
|
||||
func (stub *testInstance) Notifications() *notifications.Notifications {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stub *testInstance) Base() *base.Base {
|
||||
return stub.base
|
||||
}
|
||||
|
||||
func (stub *testInstance) Ready() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (stub *testInstance) Restart() {}
|
||||
|
||||
func (stub *testInstance) Shutdown() {}
|
||||
|
||||
func (stub *testInstance) SetCmdLineOperation(f func() error) {}
|
||||
|
||||
func runTest(m *testing.M) error {
|
||||
api.SetDefaultAPIListenAddress("0.0.0.0:8080")
|
||||
ds, err := config.InitializeUnitTestDataroot("test-hub")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize dataroot: %w", err)
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(ds) }()
|
||||
|
||||
stub := &testInstance{}
|
||||
// Init
|
||||
stub.db, err = dbmodule.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create database: %w", err)
|
||||
}
|
||||
stub.api, err = api.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create api: %w", err)
|
||||
}
|
||||
stub.config, err = config.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config: %w", err)
|
||||
}
|
||||
stub.updates, err = updates.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create updates: %w", err)
|
||||
}
|
||||
stub.base, err = base.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to base updates: %w", err)
|
||||
}
|
||||
|
||||
// Start
|
||||
err = stub.db.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start database: %w", err)
|
||||
}
|
||||
err = stub.api.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start api: %w", err)
|
||||
}
|
||||
err = stub.config.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start config: %w", err)
|
||||
}
|
||||
err = stub.updates.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start updates: %w", err)
|
||||
}
|
||||
err = stub.base.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start base: %w", err)
|
||||
}
|
||||
|
||||
m.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// TODO: We need the database module, so maybe set up a module for this package.
|
||||
module := modules.Register("hub", nil, nil, nil, "base")
|
||||
pmtesting.TestMain(m, module)
|
||||
if err := runTest(m); err != nil {
|
||||
fmt.Printf("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEquality(t *testing.T) {
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
|
||||
"github.com/safing/jess"
|
||||
"github.com/safing/jess/lhash"
|
||||
"github.com/safing/portbase/container"
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/network/netutils"
|
||||
"github.com/safing/structures/container"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/safing/jess"
|
||||
"github.com/safing/portbase/formats/dsd"
|
||||
"github.com/safing/structures/dsd"
|
||||
)
|
||||
|
||||
func TestHubUpdate(t *testing.T) {
|
||||
|
||||
443
spn/instance.go
Normal file
443
spn/instance.go
Normal file
@@ -0,0 +1,443 @@
|
||||
package spn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database/dbmodule"
|
||||
"github.com/safing/portmaster/base/metrics"
|
||||
"github.com/safing/portmaster/base/notifications"
|
||||
"github.com/safing/portmaster/base/rng"
|
||||
"github.com/safing/portmaster/base/runtime"
|
||||
"github.com/safing/portmaster/service/core"
|
||||
"github.com/safing/portmaster/service/core/base"
|
||||
"github.com/safing/portmaster/service/intel/filterlists"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
"github.com/safing/portmaster/spn/access"
|
||||
"github.com/safing/portmaster/spn/cabin"
|
||||
"github.com/safing/portmaster/spn/captain"
|
||||
"github.com/safing/portmaster/spn/crew"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
"github.com/safing/portmaster/spn/navigator"
|
||||
"github.com/safing/portmaster/spn/patrol"
|
||||
"github.com/safing/portmaster/spn/ships"
|
||||
"github.com/safing/portmaster/spn/sluice"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
)
|
||||
|
||||
// Instance is an instance of a Portmaster service.
|
||||
type Instance struct {
|
||||
ctx context.Context
|
||||
cancelCtx context.CancelFunc
|
||||
serviceGroup *mgr.Group
|
||||
|
||||
exitCode atomic.Int32
|
||||
|
||||
base *base.Base
|
||||
database *dbmodule.DBModule
|
||||
config *config.Config
|
||||
api *api.API
|
||||
metrics *metrics.Metrics
|
||||
runtime *runtime.Runtime
|
||||
rng *rng.Rng
|
||||
|
||||
core *core.Core
|
||||
updates *updates.Updates
|
||||
geoip *geoip.GeoIP
|
||||
netenv *netenv.NetEnv
|
||||
filterLists *filterlists.FilterLists
|
||||
|
||||
access *access.Access
|
||||
cabin *cabin.Cabin
|
||||
navigator *navigator.Navigator
|
||||
captain *captain.Captain
|
||||
crew *crew.Crew
|
||||
docks *docks.Docks
|
||||
patrol *patrol.Patrol
|
||||
ships *ships.Ships
|
||||
sluice *sluice.SluiceModule
|
||||
terminal *terminal.TerminalModule
|
||||
|
||||
CommandLineOperation func() error
|
||||
}
|
||||
|
||||
// New returns a new Portmaster service instance.
|
||||
func New() (*Instance, error) {
|
||||
// Create instance to pass it to modules.
|
||||
instance := &Instance{}
|
||||
instance.ctx, instance.cancelCtx = context.WithCancel(context.Background())
|
||||
|
||||
var err error
|
||||
|
||||
// Base modules
|
||||
instance.base, err = base.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create base module: %w", err)
|
||||
}
|
||||
instance.database, err = dbmodule.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create database module: %w", err)
|
||||
}
|
||||
instance.config, err = config.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create config module: %w", err)
|
||||
}
|
||||
instance.api, err = api.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create api module: %w", err)
|
||||
}
|
||||
instance.metrics, err = metrics.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create metrics module: %w", err)
|
||||
}
|
||||
instance.runtime, err = runtime.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create runtime module: %w", err)
|
||||
}
|
||||
instance.rng, err = rng.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create rng module: %w", err)
|
||||
}
|
||||
|
||||
// Service modules
|
||||
instance.core, err = core.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create core module: %w", err)
|
||||
}
|
||||
instance.updates, err = updates.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create updates module: %w", err)
|
||||
}
|
||||
instance.geoip, err = geoip.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create customlist module: %w", err)
|
||||
}
|
||||
instance.netenv, err = netenv.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create netenv module: %w", err)
|
||||
}
|
||||
instance.filterLists, err = filterlists.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create filterLists module: %w", err)
|
||||
}
|
||||
|
||||
// SPN modules
|
||||
instance.access, err = access.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create access module: %w", err)
|
||||
}
|
||||
instance.cabin, err = cabin.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create cabin module: %w", err)
|
||||
}
|
||||
instance.navigator, err = navigator.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create navigator module: %w", err)
|
||||
}
|
||||
instance.crew, err = crew.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create crew module: %w", err)
|
||||
}
|
||||
instance.docks, err = docks.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create docks module: %w", err)
|
||||
}
|
||||
instance.patrol, err = patrol.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create patrol module: %w", err)
|
||||
}
|
||||
instance.ships, err = ships.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create ships module: %w", err)
|
||||
}
|
||||
instance.sluice, err = sluice.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create sluice module: %w", err)
|
||||
}
|
||||
instance.terminal, err = terminal.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create terminal module: %w", err)
|
||||
}
|
||||
instance.captain, err = captain.New(instance)
|
||||
if err != nil {
|
||||
return instance, fmt.Errorf("create captain module: %w", err)
|
||||
}
|
||||
|
||||
// Add all modules to instance group.
|
||||
instance.serviceGroup = mgr.NewGroup(
|
||||
instance.base,
|
||||
instance.database,
|
||||
instance.config,
|
||||
instance.api,
|
||||
instance.metrics,
|
||||
instance.runtime,
|
||||
instance.rng,
|
||||
|
||||
instance.core,
|
||||
instance.updates,
|
||||
instance.geoip,
|
||||
instance.netenv,
|
||||
|
||||
instance.access,
|
||||
instance.cabin,
|
||||
instance.navigator,
|
||||
instance.captain,
|
||||
instance.crew,
|
||||
instance.docks,
|
||||
instance.patrol,
|
||||
instance.ships,
|
||||
instance.sluice,
|
||||
instance.terminal,
|
||||
)
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
// AddModule validates the given module and adds it to the service group, if all requirements are met.
|
||||
// All modules must be added before anything is done with the instance.
|
||||
func (i *Instance) AddModule(m mgr.Module) {
|
||||
i.serviceGroup.Add(m)
|
||||
}
|
||||
|
||||
// SleepyModule is an interface for modules that can enter some sort of sleep mode.
|
||||
type SleepyModule interface {
|
||||
SetSleep(enabled bool)
|
||||
}
|
||||
|
||||
// SetSleep sets sleep mode on all modules that satisfy the SleepyModule interface.
|
||||
func (i *Instance) SetSleep(enabled bool) {
|
||||
for _, module := range i.serviceGroup.Modules() {
|
||||
if sm, ok := module.(SleepyModule); ok {
|
||||
sm.SetSleep(enabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Database returns the database module.
|
||||
func (i *Instance) Database() *dbmodule.DBModule {
|
||||
return i.database
|
||||
}
|
||||
|
||||
// Config returns the config module.
|
||||
func (i *Instance) Config() *config.Config {
|
||||
return i.config
|
||||
}
|
||||
|
||||
// API returns the api module.
|
||||
func (i *Instance) API() *api.API {
|
||||
return i.api
|
||||
}
|
||||
|
||||
// Metrics returns the metrics module.
|
||||
func (i *Instance) Metrics() *metrics.Metrics {
|
||||
return i.metrics
|
||||
}
|
||||
|
||||
// Runtime returns the runtime module.
|
||||
func (i *Instance) Runtime() *runtime.Runtime {
|
||||
return i.runtime
|
||||
}
|
||||
|
||||
// Rng returns the rng module.
|
||||
func (i *Instance) Rng() *rng.Rng {
|
||||
return i.rng
|
||||
}
|
||||
|
||||
// Base returns the base module.
|
||||
func (i *Instance) Base() *base.Base {
|
||||
return i.base
|
||||
}
|
||||
|
||||
// Updates returns the updates module.
|
||||
func (i *Instance) Updates() *updates.Updates {
|
||||
return i.updates
|
||||
}
|
||||
|
||||
// GeoIP returns the geoip module.
|
||||
func (i *Instance) GeoIP() *geoip.GeoIP {
|
||||
return i.geoip
|
||||
}
|
||||
|
||||
// NetEnv returns the netenv module.
|
||||
func (i *Instance) NetEnv() *netenv.NetEnv {
|
||||
return i.netenv
|
||||
}
|
||||
|
||||
// Access returns the access module.
|
||||
func (i *Instance) Access() *access.Access {
|
||||
return i.access
|
||||
}
|
||||
|
||||
// Cabin returns the cabin module.
|
||||
func (i *Instance) Cabin() *cabin.Cabin {
|
||||
return i.cabin
|
||||
}
|
||||
|
||||
// Captain returns the captain module.
|
||||
func (i *Instance) Captain() *captain.Captain {
|
||||
return i.captain
|
||||
}
|
||||
|
||||
// Crew returns the crew module.
|
||||
func (i *Instance) Crew() *crew.Crew {
|
||||
return i.crew
|
||||
}
|
||||
|
||||
// Docks returns the crew module.
|
||||
func (i *Instance) Docks() *docks.Docks {
|
||||
return i.docks
|
||||
}
|
||||
|
||||
// Navigator returns the navigator module.
|
||||
func (i *Instance) Navigator() *navigator.Navigator {
|
||||
return i.navigator
|
||||
}
|
||||
|
||||
// Patrol returns the patrol module.
|
||||
func (i *Instance) Patrol() *patrol.Patrol {
|
||||
return i.patrol
|
||||
}
|
||||
|
||||
// Ships returns the ships module.
|
||||
func (i *Instance) Ships() *ships.Ships {
|
||||
return i.ships
|
||||
}
|
||||
|
||||
// Sluice returns the ships module.
|
||||
func (i *Instance) Sluice() *sluice.SluiceModule {
|
||||
return i.sluice
|
||||
}
|
||||
|
||||
// Terminal returns the terminal module.
|
||||
func (i *Instance) Terminal() *terminal.TerminalModule {
|
||||
return i.terminal
|
||||
}
|
||||
|
||||
// FilterLists returns the filterLists module.
|
||||
func (i *Instance) FilterLists() *filterlists.FilterLists {
|
||||
return i.filterLists
|
||||
}
|
||||
|
||||
// Core returns the core module.
|
||||
func (i *Instance) Core() *core.Core {
|
||||
return i.core
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
// GetEventSPNConnected return the event manager for the SPN connected event.
|
||||
func (i *Instance) GetEventSPNConnected() *mgr.EventMgr[struct{}] {
|
||||
return i.captain.EventSPNConnected
|
||||
}
|
||||
|
||||
// Special functions
|
||||
|
||||
// SetCmdLineOperation sets a command line operation to be executed instead of starting the system. This is useful when functions need all modules to be prepared for a special operation.
|
||||
func (i *Instance) SetCmdLineOperation(f func() error) {
|
||||
i.CommandLineOperation = f
|
||||
}
|
||||
|
||||
// GetStates returns the current states of all group modules.
|
||||
func (i *Instance) GetStates() []mgr.StateUpdate {
|
||||
return i.serviceGroup.GetStates()
|
||||
}
|
||||
|
||||
// AddStatesCallback adds the given callback function to all group modules that
|
||||
// expose a state manager at States().
|
||||
func (i *Instance) AddStatesCallback(callbackName string, callback mgr.EventCallbackFunc[mgr.StateUpdate]) {
|
||||
i.serviceGroup.AddStatesCallback(callbackName, callback)
|
||||
}
|
||||
|
||||
// Ready returns whether all modules in the main service module group have been started and are still running.
|
||||
func (i *Instance) Ready() bool {
|
||||
return i.serviceGroup.Ready()
|
||||
}
|
||||
|
||||
// Ctx returns the instance context.
|
||||
// It is only canceled on shutdown.
|
||||
func (i *Instance) Ctx() context.Context {
|
||||
return i.ctx
|
||||
}
|
||||
|
||||
// Start starts the instance.
|
||||
func (i *Instance) Start() error {
|
||||
return i.serviceGroup.Start()
|
||||
}
|
||||
|
||||
// Stop stops the instance and cancels the instance context when done.
|
||||
func (i *Instance) Stop() error {
|
||||
defer i.cancelCtx()
|
||||
return i.serviceGroup.Stop()
|
||||
}
|
||||
|
||||
// RestartExitCode will instruct portmaster-start to restart the process immediately, potentially with a new version.
|
||||
const RestartExitCode = 23
|
||||
|
||||
// Restart asynchronously restarts the instance.
|
||||
// This only works if the underlying system/process supports this.
|
||||
func (i *Instance) Restart() {
|
||||
// Send a restart event, give it 10ms extra to propagate.
|
||||
i.core.EventRestart.Submit(struct{}{})
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
i.shutdown(RestartExitCode)
|
||||
}
|
||||
|
||||
// Shutdown asynchronously stops the instance.
|
||||
func (i *Instance) Shutdown() {
|
||||
// Send a shutdown event, give it 10ms extra to propagate.
|
||||
i.core.EventShutdown.Submit(struct{}{})
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
i.shutdown(0)
|
||||
}
|
||||
|
||||
func (i *Instance) shutdown(exitCode int) {
|
||||
// Set given exit code.
|
||||
i.exitCode.Store(int32(exitCode))
|
||||
|
||||
m := mgr.New("instance")
|
||||
m.Go("shutdown", func(w *mgr.WorkerCtx) error {
|
||||
for {
|
||||
if err := i.Stop(); err != nil {
|
||||
w.Error("failed to shutdown", "err", err, "retry", "1s")
|
||||
time.Sleep(1 * time.Second)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Stopping returns whether the instance is shutting down.
|
||||
func (i *Instance) Stopping() bool {
|
||||
return i.ctx.Err() == nil
|
||||
}
|
||||
|
||||
// Stopped returns a channel that is triggered when the instance has shut down.
|
||||
func (i *Instance) Stopped() <-chan struct{} {
|
||||
return i.ctx.Done()
|
||||
}
|
||||
|
||||
// ExitCode returns the set exit code of the instance.
|
||||
func (i *Instance) ExitCode() int {
|
||||
return int(i.exitCode.Load())
|
||||
}
|
||||
|
||||
// SPNGroup fakes interface conformance.
|
||||
// SPNGroup is only needed on SPN clients.
|
||||
func (i *Instance) SPNGroup() *mgr.ExtendedGroup {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unsupported Modules.
|
||||
|
||||
// Notifications returns nil.
|
||||
func (i *Instance) Notifications() *notifications.Notifications { return nil }
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
|
||||
"github.com/awalterschulze/gographviz"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
)
|
||||
@@ -52,7 +52,6 @@ func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/pins`,
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
StructFunc: handleMapPinsRequest,
|
||||
Name: "Get SPN map pins",
|
||||
Description: "Returns a list of pins on the map.",
|
||||
@@ -63,7 +62,6 @@ func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/intel/update`,
|
||||
Write: api.PermitSelf,
|
||||
BelongsTo: module,
|
||||
ActionFunc: handleIntelUpdateRequest,
|
||||
Name: "Update map intelligence.",
|
||||
Description: "Updates the intel data of the map.",
|
||||
@@ -74,7 +72,6 @@ func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/optimization`,
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
StructFunc: handleMapOptimizationRequest,
|
||||
Name: "Get SPN map optimization",
|
||||
Description: "Returns the calculated optimization for the map.",
|
||||
@@ -85,7 +82,6 @@ func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/optimization/table`,
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
DataFunc: handleMapOptimizationTableRequest,
|
||||
Name: "Get SPN map optimization as a table",
|
||||
Description: "Returns the calculated optimization for the map as a table.",
|
||||
@@ -96,7 +92,6 @@ func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/measurements`,
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
StructFunc: handleMapMeasurementsRequest,
|
||||
Name: "Get SPN map measurements",
|
||||
Description: "Returns the measurements of the map.",
|
||||
@@ -108,7 +103,6 @@ func registerAPIEndpoints() error {
|
||||
Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/measurements/table`,
|
||||
MimeType: api.MimeTypeText,
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
DataFunc: handleMapMeasurementsTableRequest,
|
||||
Name: "Get SPN map measurements as a table",
|
||||
Description: "Returns the measurements of the map as a table.",
|
||||
@@ -119,7 +113,6 @@ func registerAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/graph{format:\.[a-z]{2,4}}`,
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
HandlerFunc: handleMapGraphRequest,
|
||||
Name: "Get SPN map graph",
|
||||
Description: "Returns a graph of the given SPN map.",
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/service/intel"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
@@ -25,7 +25,6 @@ func registerRouteAPIEndpoints() error {
|
||||
if err := api.RegisterEndpoint(api.Endpoint{
|
||||
Path: `spn/map/{map:[A-Za-z0-9]{1,255}}/route/to/{destination:[a-z0-9_\.:-]{1,255}}`,
|
||||
Read: api.PermitUser,
|
||||
BelongsTo: module,
|
||||
ActionFunc: handleRouteCalculationRequest,
|
||||
Name: "Calculate Route through SPN",
|
||||
Description: "Returns a textual representation of the routing process.",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package navigator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/iterator"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/database/storage"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/database/iterator"
|
||||
"github.com/safing/portmaster/base/database/query"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/base/database/storage"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
)
|
||||
|
||||
var mapDBController *database.Controller
|
||||
@@ -82,7 +82,7 @@ func (s *StorageInterface) Query(q *query.Query, local, internal bool) (*iterato
|
||||
|
||||
// Start query worker.
|
||||
it := iterator.New()
|
||||
module.StartWorker("map query", func(_ context.Context) error {
|
||||
module.mgr.Go("map query", func(_ *mgr.WorkerCtx) error {
|
||||
s.processQuery(m, q, it)
|
||||
return nil
|
||||
})
|
||||
@@ -131,10 +131,10 @@ func withdrawMapDatabase() {
|
||||
|
||||
// PushPinChanges pushes all changed pins to subscribers.
|
||||
func (m *Map) PushPinChanges() {
|
||||
module.StartWorker("push pin changes", m.pushPinChangesWorker)
|
||||
module.mgr.Go("push pin changes", m.pushPinChangesWorker)
|
||||
}
|
||||
|
||||
func (m *Map) pushPinChangesWorker(ctx context.Context) error {
|
||||
func (m *Map) pushPinChangesWorker(ctx *mgr.WorkerCtx) error {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
|
||||
@@ -155,7 +155,7 @@ func (pin *Pin) pushChange() {
|
||||
}
|
||||
|
||||
// Start worker to push changes.
|
||||
module.StartWorker("push pin change", func(ctx context.Context) error {
|
||||
module.mgr.Go("push pin change", func(ctx *mgr.WorkerCtx) error {
|
||||
if pin.pushChanges.SetToIf(true, false) {
|
||||
mapDBController.PushUpdate(pin.Export())
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ func TestFindNearest(t *testing.T) {
|
||||
fakeLock.Lock()
|
||||
defer fakeLock.Unlock()
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
// Create a random destination address
|
||||
ip4, loc4 := createGoodIP(true)
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestFindNearest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
// Create a random destination address
|
||||
ip6, loc6 := createGoodIP(true)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ func TestFindRoutes(t *testing.T) {
|
||||
fakeLock.Lock()
|
||||
defer fakeLock.Unlock()
|
||||
|
||||
for i := 0; i < 1; i++ {
|
||||
for i := range 1 {
|
||||
// Create a random destination address
|
||||
dstIP, _ := createGoodIP(i%2 == 0)
|
||||
|
||||
@@ -37,13 +37,13 @@ func BenchmarkFindRoutes(b *testing.B) {
|
||||
|
||||
// Pre-generate 100 IPs
|
||||
preGenIPs := make([]net.IP, 0, 100)
|
||||
for i := 0; i < cap(preGenIPs); i++ {
|
||||
for i := range cap(preGenIPs) {
|
||||
ip, _ := createGoodIP(i%2 == 0)
|
||||
preGenIPs = append(preGenIPs, ip)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for i := range b.N {
|
||||
routes, err := m.FindRoutes(preGenIPs[i%len(preGenIPs)], m.DefaultOptions())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
"github.com/safing/portmaster/service/profile/endpoints"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
|
||||
@@ -58,7 +58,7 @@ func createRandomTestMap(seed int64, size int) *Map {
|
||||
}
|
||||
|
||||
// Create Hub list.
|
||||
var hubs []*hub.Hub
|
||||
hubs := make([]*hub.Hub, 0, size)
|
||||
|
||||
// Create Intel data structure.
|
||||
mapIntel := &hub.Intel{
|
||||
@@ -69,7 +69,7 @@ func createRandomTestMap(seed int64, size int) *Map {
|
||||
var currentGroup string
|
||||
|
||||
// Create [size] fake Hubs.
|
||||
for i := 0; i < size; i++ {
|
||||
for i := range size {
|
||||
// Change group every 5 Hubs.
|
||||
if i%5 == 0 {
|
||||
currentGroup = gofakeit.Username()
|
||||
@@ -81,7 +81,7 @@ func createRandomTestMap(seed int64, size int) *Map {
|
||||
}
|
||||
|
||||
// Fake three superseeded Hubs.
|
||||
for i := 0; i < 3; i++ {
|
||||
for i := range 3 {
|
||||
h := hubs[size-1-i]
|
||||
|
||||
// Set FirstSeen in the past and copy an IP address of an existing Hub.
|
||||
@@ -95,7 +95,7 @@ func createRandomTestMap(seed int64, size int) *Map {
|
||||
|
||||
// Create Lanes between Hubs in order to create the network.
|
||||
totalConnections := size * 10
|
||||
for i := 0; i < totalConnections; i++ {
|
||||
for range totalConnections {
|
||||
// Get new random indexes.
|
||||
indexA := gofakeit.Number(0, size-1)
|
||||
indexB := gofakeit.Number(0, size-1)
|
||||
@@ -246,7 +246,7 @@ func createFakeHub(group string, randomFailes bool, mapIntel *hub.Intel) *hub.Hu
|
||||
|
||||
func createGoodIP(v4 bool) (net.IP, *geoip.Location) {
|
||||
var candidate net.IP
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
if v4 {
|
||||
candidate = net.ParseIP(gofakeit.IPv4Address())
|
||||
} else {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package navigator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
"github.com/safing/portmaster/spn/terminal"
|
||||
)
|
||||
@@ -25,7 +24,7 @@ const (
|
||||
// 1000c -> 100h -> capped to 50h.
|
||||
)
|
||||
|
||||
func (m *Map) measureHubs(ctx context.Context, _ *modules.Task) error {
|
||||
func (m *Map) measureHubs(wc *mgr.WorkerCtx) error {
|
||||
if home, _ := m.GetHome(); home == nil {
|
||||
log.Debug("spn/navigator: skipping measuring, no home hub set")
|
||||
return nil
|
||||
@@ -74,7 +73,7 @@ func (m *Map) measureHubs(ctx context.Context, _ *modules.Task) error {
|
||||
}
|
||||
|
||||
// Measure connection.
|
||||
tErr := docks.MeasureHub(ctx, pin.Hub, checkWithTTL)
|
||||
tErr := docks.MeasureHub(wc.Ctx(), pin.Hub, checkWithTTL)
|
||||
|
||||
// Independent of outcome, recalculate the cost.
|
||||
latency, _ := pin.measurements.GetLatency()
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/metrics"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/metrics"
|
||||
)
|
||||
|
||||
var metricsRegistered = abool.New()
|
||||
|
||||
@@ -2,12 +2,13 @@ package navigator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
)
|
||||
|
||||
@@ -33,8 +34,27 @@ var (
|
||||
ErrAllPinsDisregarded = errors.New("all pins have been disregarded")
|
||||
)
|
||||
|
||||
type Navigator struct {
|
||||
mgr *mgr.Manager
|
||||
|
||||
instance instance
|
||||
}
|
||||
|
||||
func (n *Navigator) Manager() *mgr.Manager {
|
||||
return n.mgr
|
||||
}
|
||||
|
||||
func (n *Navigator) Start() error {
|
||||
return start()
|
||||
}
|
||||
|
||||
func (n *Navigator) Stop() error {
|
||||
return stop()
|
||||
}
|
||||
|
||||
var (
|
||||
module *modules.Module
|
||||
module *Navigator
|
||||
shimLoaded atomic.Bool
|
||||
|
||||
// Main is the primary map used.
|
||||
Main *Map
|
||||
@@ -44,10 +64,6 @@ var (
|
||||
cfgOptionTrustNodeNodes config.StringArrayOption
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("navigator", prep, start, stop, "terminal", "geoip", "netenv")
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
return registerAPIEndpoints()
|
||||
}
|
||||
@@ -55,65 +71,65 @@ func prep() error {
|
||||
func start() error {
|
||||
Main = NewMap(conf.MainMapName, true)
|
||||
devMode = config.Concurrent.GetAsBool(config.CfgDevModeKey, false)
|
||||
cfgOptionRoutingAlgorithm = config.Concurrent.GetAsString(cfgOptionRoutingAlgorithmKey, DefaultRoutingProfileID)
|
||||
cfgOptionTrustNodeNodes = config.Concurrent.GetAsStringArray(cfgOptionTrustNodeNodesKey, []string{})
|
||||
|
||||
if conf.Integrated() {
|
||||
cfgOptionRoutingAlgorithm = config.Concurrent.GetAsString(cfgOptionRoutingAlgorithmKey, DefaultRoutingProfileID)
|
||||
} else {
|
||||
cfgOptionRoutingAlgorithm = func() string { return DefaultRoutingProfileID }
|
||||
}
|
||||
|
||||
err := registerMapDatabase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for geoip databases to be ready.
|
||||
// Try again if not yet ready, as this is critical.
|
||||
// The "wait" parameter times out after 1 second.
|
||||
// Allow 30 seconds for both databases to load.
|
||||
geoInitCheck:
|
||||
for i := 0; i < 30; i++ {
|
||||
switch {
|
||||
case !geoip.IsInitialized(false, true): // First, IPv4.
|
||||
case !geoip.IsInitialized(true, true): // Then, IPv6.
|
||||
default:
|
||||
break geoInitCheck
|
||||
module.mgr.Go("initializing hubs", func(wc *mgr.WorkerCtx) error {
|
||||
// Wait for geoip databases to be ready.
|
||||
// Try again if not yet ready, as this is critical.
|
||||
// The "wait" parameter times out after 1 second.
|
||||
// Allow 30 seconds for both databases to load.
|
||||
geoInitCheck:
|
||||
for range 30 {
|
||||
switch {
|
||||
case !geoip.IsInitialized(false, true): // First, IPv4.
|
||||
case !geoip.IsInitialized(true, true): // Then, IPv6.
|
||||
default:
|
||||
break geoInitCheck
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = Main.InitializeFromDatabase()
|
||||
if err != nil {
|
||||
// Wait for three seconds, then try again.
|
||||
time.Sleep(3 * time.Second)
|
||||
err = Main.InitializeFromDatabase()
|
||||
if err != nil {
|
||||
// Even if the init fails, we can try to start without it and get data along the way.
|
||||
log.Warningf("spn/navigator: %s", err)
|
||||
// Wait for three seconds, then try again.
|
||||
time.Sleep(3 * time.Second)
|
||||
err = Main.InitializeFromDatabase()
|
||||
if err != nil {
|
||||
// Even if the init fails, we can try to start without it and get data along the way.
|
||||
log.Warningf("spn/navigator: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
err = Main.RegisterHubUpdateHook()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: delete superseded hubs after x amount of time
|
||||
|
||||
module.NewTask("update states", Main.updateStates).
|
||||
Repeat(1 * time.Hour).
|
||||
Schedule(time.Now().Add(3 * time.Minute))
|
||||
|
||||
module.NewTask("update failing states", Main.updateFailingStates).
|
||||
Repeat(1 * time.Minute).
|
||||
Schedule(time.Now().Add(3 * time.Minute))
|
||||
|
||||
if conf.PublicHub() {
|
||||
// Only measure Hubs on public Hubs.
|
||||
module.NewTask("measure hubs", Main.measureHubs).
|
||||
Repeat(5 * time.Minute).
|
||||
Schedule(time.Now().Add(1 * time.Minute))
|
||||
|
||||
// Only register metrics on Hubs, as they only make sense there.
|
||||
err := registerMetrics()
|
||||
err = Main.RegisterHubUpdateHook()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: delete superseded hubs after x amount of time
|
||||
_ = module.mgr.Delay("update states", 3*time.Minute, Main.updateStates).Repeat(1 * time.Hour)
|
||||
_ = module.mgr.Delay("update failing states", 3*time.Minute, Main.updateFailingStates).Repeat(1 * time.Minute)
|
||||
|
||||
if conf.PublicHub() {
|
||||
// Only measure Hubs on public Hubs.
|
||||
module.mgr.Delay("measure hubs", 5*time.Minute, Main.measureHubs).Repeat(1 * time.Minute)
|
||||
|
||||
// Only register metrics on Hubs, as they only make sense there.
|
||||
err := registerMetrics()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -127,3 +143,22 @@ func stop() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New returns a new Navigator module.
|
||||
func New(instance instance) (*Navigator, error) {
|
||||
if !shimLoaded.CompareAndSwap(false, true) {
|
||||
return nil, errors.New("only one instance allowed")
|
||||
}
|
||||
m := mgr.New("Navigator")
|
||||
module = &Navigator{
|
||||
mgr: m,
|
||||
instance: instance,
|
||||
}
|
||||
if err := prep(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
||||
|
||||
type instance interface{}
|
||||
|
||||
@@ -1,13 +1,136 @@
|
||||
package navigator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/service/core/pmtesting"
|
||||
"github.com/safing/portmaster/base/api"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database/dbmodule"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/notifications"
|
||||
"github.com/safing/portmaster/service/core/base"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
log.SetLogLevel(log.DebugLevel)
|
||||
pmtesting.TestMain(m, module)
|
||||
type testInstance struct {
|
||||
db *dbmodule.DBModule
|
||||
api *api.API
|
||||
config *config.Config
|
||||
updates *updates.Updates
|
||||
base *base.Base
|
||||
geoip *geoip.GeoIP
|
||||
}
|
||||
|
||||
func (stub *testInstance) Updates() *updates.Updates {
|
||||
return stub.updates
|
||||
}
|
||||
|
||||
func (stub *testInstance) API() *api.API {
|
||||
return stub.api
|
||||
}
|
||||
|
||||
func (stub *testInstance) Config() *config.Config {
|
||||
return stub.config
|
||||
}
|
||||
|
||||
func (stub *testInstance) Base() *base.Base {
|
||||
return stub.base
|
||||
}
|
||||
|
||||
func (stub *testInstance) Notifications() *notifications.Notifications {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stub *testInstance) Ready() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (stub *testInstance) Restart() {}
|
||||
|
||||
func (stub *testInstance) Shutdown() {}
|
||||
|
||||
func (stub *testInstance) SetCmdLineOperation(f func() error) {}
|
||||
|
||||
func runTest(m *testing.M) error {
|
||||
api.SetDefaultAPIListenAddress("0.0.0.0:8080")
|
||||
ds, err := config.InitializeUnitTestDataroot("test-navigator")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize dataroot: %w", err)
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(ds) }()
|
||||
|
||||
stub := &testInstance{}
|
||||
log.SetLogLevel(log.DebugLevel)
|
||||
|
||||
// Init
|
||||
stub.db, err = dbmodule.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create db: %w", err)
|
||||
}
|
||||
stub.api, err = api.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create api: %w", err)
|
||||
}
|
||||
stub.config, err = config.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create config: %w", err)
|
||||
}
|
||||
stub.updates, err = updates.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create updates: %w", err)
|
||||
}
|
||||
stub.base, err = base.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create base: %w", err)
|
||||
}
|
||||
stub.geoip, err = geoip.New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create geoip: %w", err)
|
||||
}
|
||||
module, err = New(stub)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create navigator module: %w", err)
|
||||
}
|
||||
// Start
|
||||
err = stub.db.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start db module: %w", err)
|
||||
}
|
||||
err = stub.api.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start api: %w", err)
|
||||
}
|
||||
err = stub.config.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start config: %w", err)
|
||||
}
|
||||
err = stub.updates.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start updates: %w", err)
|
||||
}
|
||||
err = stub.base.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start base module: %w", err)
|
||||
}
|
||||
err = stub.geoip.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start geoip module: %w", err)
|
||||
}
|
||||
err = module.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start navigator module: %w", err)
|
||||
}
|
||||
|
||||
m.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if err := runTest(m); err != nil {
|
||||
fmt.Printf("%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ func (m *Map) optimizeForDistanceConstraint(result *OptimizationResult, max int)
|
||||
// Add approach.
|
||||
result.addApproach(fmt.Sprintf("Satisfy max hop constraint of %d globally.", optimizationHopDistanceTarget))
|
||||
|
||||
for i := 0; i < max; i++ {
|
||||
for range max {
|
||||
// Sort by lowest cost.
|
||||
sort.Sort(sortBySuggestedHopDistanceAndLowestMeasuredCost(m.regardedPins))
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package navigator
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/intel"
|
||||
"github.com/safing/portmaster/service/profile"
|
||||
"github.com/safing/portmaster/service/profile/endpoints"
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/intel"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
"github.com/safing/portmaster/spn/docks"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package navigator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/service/intel"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
)
|
||||
@@ -96,3 +97,25 @@ func (pin *Pin) Export() *PinExport {
|
||||
|
||||
return export
|
||||
}
|
||||
|
||||
// HumanName returns a human-readable version of a Hub's name.
|
||||
// This name will likely consist of two parts: the given name and the ending of the ID to make it unique.
|
||||
func (h *PinExport) HumanName() string {
|
||||
if len(h.ID) < 8 {
|
||||
return fmt.Sprintf("<Hub %s>", h.ID)
|
||||
}
|
||||
|
||||
shortenedID := h.ID[len(h.ID)-8:len(h.ID)-4] +
|
||||
"-" +
|
||||
h.ID[len(h.ID)-4:]
|
||||
|
||||
// Be more careful, as the Hub name is user input.
|
||||
switch {
|
||||
case h.Info.Name == "":
|
||||
return fmt.Sprintf("<Hub %s>", shortenedID)
|
||||
case len(h.Info.Name) > 16:
|
||||
return fmt.Sprintf("<Hub %s %s>", h.Info.Name[:16], shortenedID)
|
||||
default:
|
||||
return fmt.Sprintf("<Hub %s %s>", h.Info.Name, shortenedID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"math"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/profile/endpoints"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package navigator
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/profile"
|
||||
)
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ import (
|
||||
"github.com/tevino/abool"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/utils"
|
||||
"github.com/safing/portmaster/base/config"
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/database/query"
|
||||
"github.com/safing/portmaster/base/database/record"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
"github.com/safing/portmaster/service/intel/geoip"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/netenv"
|
||||
"github.com/safing/portmaster/service/profile"
|
||||
"github.com/safing/portmaster/spn/hub"
|
||||
@@ -409,7 +409,7 @@ func (m *Map) updateHubLane(pin *Pin, lane *hub.Lane, peer *Pin) {
|
||||
}
|
||||
|
||||
// ResetFailingStates resets the failing state on all pins.
|
||||
func (m *Map) ResetFailingStates(ctx context.Context) {
|
||||
func (m *Map) ResetFailingStates() {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
@@ -420,7 +420,7 @@ func (m *Map) ResetFailingStates(ctx context.Context) {
|
||||
m.PushPinChanges()
|
||||
}
|
||||
|
||||
func (m *Map) updateFailingStates(ctx context.Context, task *modules.Task) error {
|
||||
func (m *Map) updateFailingStates(ctx *mgr.WorkerCtx) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
@@ -433,7 +433,7 @@ func (m *Map) updateFailingStates(ctx context.Context, task *modules.Task) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Map) updateStates(ctx context.Context, task *modules.Task) error {
|
||||
func (m *Map) updateStates(ctx *mgr.WorkerCtx) error {
|
||||
var toDelete []string
|
||||
|
||||
m.Lock()
|
||||
@@ -458,7 +458,7 @@ pinLoop:
|
||||
|
||||
// Delete hubs async, as deleting triggers a couple hooks that lock the map.
|
||||
if len(toDelete) > 0 {
|
||||
module.StartWorker("delete hubs", func(_ context.Context) error {
|
||||
module.mgr.Go("delete hubs", func(_ *mgr.WorkerCtx) error {
|
||||
for _, idToDelete := range toDelete {
|
||||
err := hub.RemoveHubAndMsgs(m.Name, idToDelete)
|
||||
if err != nil {
|
||||
@@ -558,8 +558,8 @@ func (m *Map) addBootstrapHub(bootstrapTransport string) error {
|
||||
}
|
||||
|
||||
// UpdateConfigQuickSettings updates config quick settings with available countries.
|
||||
func (m *Map) UpdateConfigQuickSettings(ctx context.Context) error {
|
||||
ctx, tracer := log.AddTracer(ctx)
|
||||
func (m *Map) UpdateConfigQuickSettings(wc *mgr.WorkerCtx) error {
|
||||
ctx, tracer := log.AddTracer(wc.Ctx())
|
||||
tracer.Trace("navigator: updating SPN rules country quick settings")
|
||||
defer tracer.Submit()
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
)
|
||||
|
||||
@@ -22,9 +22,9 @@ func HTTPSConnectivityConfirmed() bool {
|
||||
return httpsConnectivityConfirmed.IsSet()
|
||||
}
|
||||
|
||||
func connectivityCheckTask(ctx context.Context, task *modules.Task) error {
|
||||
func connectivityCheckTask(wc *mgr.WorkerCtx) error {
|
||||
// Start tracing logs.
|
||||
ctx, tracer := log.AddTracer(ctx)
|
||||
ctx, tracer := log.AddTracer(wc.Ctx())
|
||||
defer tracer.Submit()
|
||||
|
||||
// Run checks and report status.
|
||||
@@ -32,14 +32,14 @@ func connectivityCheckTask(ctx context.Context, task *modules.Task) error {
|
||||
if success {
|
||||
tracer.Info("spn/patrol: all connectivity checks succeeded")
|
||||
if httpsConnectivityConfirmed.SetToIf(false, true) {
|
||||
module.TriggerEvent(ChangeSignalEventName, nil)
|
||||
module.EventChangeSignal.Submit(struct{}{})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
tracer.Errorf("spn/patrol: connectivity check failed")
|
||||
if httpsConnectivityConfirmed.SetToIf(true, false) {
|
||||
module.TriggerEvent(ChangeSignalEventName, nil)
|
||||
module.EventChangeSignal.Submit(struct{}{})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func checkHTTPSConnectivity(ctx context.Context, network string, checks int, req
|
||||
|
||||
// Run tests.
|
||||
var succeeded int
|
||||
for i := 0; i < checks; i++ {
|
||||
for range checks {
|
||||
if checkHTTPSConnection(ctx, network) {
|
||||
succeeded++
|
||||
}
|
||||
|
||||
@@ -1,32 +1,57 @@
|
||||
package patrol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/spn/conf"
|
||||
)
|
||||
|
||||
// ChangeSignalEventName is the name of the event that signals any change in the patrol system.
|
||||
const ChangeSignalEventName = "change signal"
|
||||
|
||||
var module *modules.Module
|
||||
type Patrol struct {
|
||||
mgr *mgr.Manager
|
||||
instance instance
|
||||
|
||||
func init() {
|
||||
module = modules.Register("patrol", prep, start, nil, "rng")
|
||||
EventChangeSignal *mgr.EventMgr[struct{}]
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
module.RegisterEvent(ChangeSignalEventName, false)
|
||||
|
||||
return nil
|
||||
func (p *Patrol) Manager() *mgr.Manager {
|
||||
return p.mgr
|
||||
}
|
||||
|
||||
func start() error {
|
||||
func (p *Patrol) Start() error {
|
||||
if conf.PublicHub() {
|
||||
module.NewTask("connectivity test", connectivityCheckTask).
|
||||
Repeat(5 * time.Minute)
|
||||
p.mgr.Repeat("connectivity test", 5*time.Minute, connectivityCheckTask)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Patrol) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
module *Patrol
|
||||
shimLoaded atomic.Bool
|
||||
)
|
||||
|
||||
// New returns a new Config module.
|
||||
func New(instance instance) (*Patrol, error) {
|
||||
if !shimLoaded.CompareAndSwap(false, true) {
|
||||
return nil, errors.New("only one instance allowed")
|
||||
}
|
||||
m := mgr.New("Patrol")
|
||||
module = &Patrol{
|
||||
mgr: m,
|
||||
instance: instance,
|
||||
|
||||
EventChangeSignal: mgr.NewEventMgr[struct{}](ChangeSignalEventName, m),
|
||||
}
|
||||
return module, nil
|
||||
}
|
||||
|
||||
type instance interface{}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user