safing-portmaster/spn/terminal/init.go
Daniel Hååvi 80664d1a27
Restructure modules ()
* 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>
2024-08-09 18:15:48 +03:00

210 lines
5.3 KiB
Go

package terminal
import (
"context"
"github.com/safing/jess"
"github.com/safing/portmaster/spn/cabin"
"github.com/safing/portmaster/spn/hub"
"github.com/safing/structures/container"
"github.com/safing/structures/dsd"
"github.com/safing/structures/varint"
)
/*
Terminal Init Message Format:
- Version [varint]
- Data Block [bytes; not blocked]
- TerminalOpts as DSD
*/
const (
minSupportedTerminalVersion = 1
maxSupportedTerminalVersion = 1
)
// TerminalOpts holds configuration for the terminal.
type TerminalOpts struct { //nolint:golint,maligned // TODO: Rename.
Version uint8 `json:"-"`
Encrypt bool `json:"e,omitempty"`
Padding uint16 `json:"p,omitempty"`
FlowControl FlowControlType `json:"fc,omitempty"`
FlowControlSize uint32 `json:"qs,omitempty"` // Previously was "QueueSize".
UsePriorityDataMsgs bool `json:"pr,omitempty"`
}
// ParseTerminalOpts parses terminal options from the container and checks if
// they are valid.
func ParseTerminalOpts(c *container.Container) (*TerminalOpts, *Error) {
// Parse and check version.
version, err := c.GetNextN8()
if err != nil {
return nil, ErrMalformedData.With("failed to parse version: %w", err)
}
if version < minSupportedTerminalVersion || version > maxSupportedTerminalVersion {
return nil, ErrUnsupportedVersion.With("requested terminal version %d", version)
}
// Parse init message.
initMsg := &TerminalOpts{}
_, err = dsd.Load(c.CompileData(), initMsg)
if err != nil {
return nil, ErrMalformedData.With("failed to parse init message: %w", err)
}
initMsg.Version = version
// Check if options are valid.
tErr := initMsg.Check(false)
if tErr != nil {
return nil, tErr
}
return initMsg, nil
}
// Pack serialized the terminal options and checks if they are valid.
func (opts *TerminalOpts) Pack() (*container.Container, *Error) {
// Check if options are valid.
tErr := opts.Check(true)
if tErr != nil {
return nil, tErr
}
// Pack init message.
optsData, err := dsd.Dump(opts, dsd.CBOR)
if err != nil {
return nil, ErrInternalError.With("failed to pack init message: %w", err)
}
// Compile init message.
return container.New(
varint.Pack8(opts.Version),
optsData,
), nil
}
// Check checks if terminal options are valid.
func (opts *TerminalOpts) Check(useDefaultsForRequired bool) *Error {
// Version is required - use default when permitted.
if opts.Version == 0 && useDefaultsForRequired {
opts.Version = 1
}
if opts.Version < minSupportedTerminalVersion || opts.Version > maxSupportedTerminalVersion {
return ErrInvalidOptions.With("unsupported terminal version %d", opts.Version)
}
// FlowControl is optional.
switch opts.FlowControl {
case FlowControlDefault:
// Set to default flow control.
opts.FlowControl = defaultFlowControl
case FlowControlNone, FlowControlDFQ:
// Ok.
default:
return ErrInvalidOptions.With("unknown flow control type: %d", opts.FlowControl)
}
// FlowControlSize is required as it needs to be same on both sides.
// Use default when permitted.
if opts.FlowControlSize == 0 && useDefaultsForRequired {
opts.FlowControlSize = opts.FlowControl.DefaultSize()
}
if opts.FlowControlSize <= 0 || opts.FlowControlSize > MaxQueueSize {
return ErrInvalidOptions.With("invalid flow control size of %d", opts.FlowControlSize)
}
return nil
}
// NewLocalBaseTerminal creates a new local terminal base for use with inheriting terminals.
func NewLocalBaseTerminal(
ctx context.Context,
id uint32,
parentID string,
remoteHub *hub.Hub,
initMsg *TerminalOpts,
upstream Upstream,
) (
t *TerminalBase,
initData *container.Container,
err *Error,
) {
// Pack, check and add defaults to init message.
initData, err = initMsg.Pack()
if err != nil {
return nil, nil, err
}
// Create baseline.
t, err = createTerminalBase(ctx, id, parentID, false, initMsg, upstream)
if err != nil {
return nil, nil, err
}
// Setup encryption if enabled.
if remoteHub != nil {
initMsg.Encrypt = true
// Select signet (public key) of remote Hub to use.
s := remoteHub.SelectSignet()
if s == nil {
return nil, nil, ErrHubNotReady.With("failed to select signet of remote hub")
}
// Create new session.
env := jess.NewUnconfiguredEnvelope()
env.SuiteID = jess.SuiteWireV1
env.Recipients = []*jess.Signet{s}
jession, err := env.WireCorrespondence(nil)
if err != nil {
return nil, nil, ErrIntegrity.With("failed to initialize encryption: %w", err)
}
t.jession = jession
// Encryption is ready for sending.
close(t.encryptionReady)
}
return t, initData, nil
}
// NewRemoteBaseTerminal creates a new remote terminal base for use with inheriting terminals.
func NewRemoteBaseTerminal(
ctx context.Context,
id uint32,
parentID string,
identity *cabin.Identity,
initData *container.Container,
upstream Upstream,
) (
t *TerminalBase,
initMsg *TerminalOpts,
err *Error,
) {
// Parse init message.
initMsg, err = ParseTerminalOpts(initData)
if err != nil {
return nil, nil, err
}
// Create baseline.
t, err = createTerminalBase(ctx, id, parentID, true, initMsg, upstream)
if err != nil {
return nil, nil, err
}
// Setup encryption if enabled.
if initMsg.Encrypt {
if identity == nil {
return nil, nil, ErrInternalError.With("missing identity for setting up incoming encryption")
}
t.identity = identity
}
return t, initMsg, nil
}