mirror of
https://github.com/safing/portmaster.git
synced 2026-06-19 20:44:35 +00:00
Add EventStartStopState event manager and IsStarted() method to the Interception module so other modules can react to start/stop state changes. Update IVPN interop to subscribe to interception start/stop events and skip applying custom DNS settings when interception is inactive, ensuring correct behavior when Portmaster is in the Paused state.
133 lines
3.1 KiB
Go
133 lines
3.1 KiB
Go
package interception
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"sync/atomic"
|
|
|
|
"github.com/safing/portmaster/base/log"
|
|
"github.com/safing/portmaster/service/mgr"
|
|
"github.com/safing/portmaster/service/network/packet"
|
|
"github.com/safing/portmaster/service/updates"
|
|
)
|
|
|
|
// Interception is the packet interception module.
|
|
type Interception struct {
|
|
mgr *mgr.Manager
|
|
instance instance
|
|
|
|
EventStartStopState *mgr.EventMgr[bool] // true if started, false if stopped
|
|
}
|
|
|
|
// Manager returns the module manager.
|
|
func (i *Interception) Manager() *mgr.Manager {
|
|
return i.mgr
|
|
}
|
|
|
|
// Start starts the module.
|
|
func (i *Interception) Start() error {
|
|
defer func() { i.EventStartStopState.Submit(isStarted.Load()) }()
|
|
return start()
|
|
}
|
|
|
|
// Stop stops the module.
|
|
func (i *Interception) Stop() error {
|
|
defer func() { i.EventStartStopState.Submit(isStarted.Load()) }()
|
|
return stop()
|
|
}
|
|
|
|
// IsStarted returns whether the interception is currently started.
|
|
func (i *Interception) IsStarted() bool {
|
|
return isStarted.Load()
|
|
}
|
|
|
|
var (
|
|
// Packets is a stream of interception network packets.
|
|
Packets = make(chan packet.Packet, 1000)
|
|
|
|
// BandwidthUpdates is a stream of bandwidth usage update for connections.
|
|
BandwidthUpdates = make(chan *packet.BandwidthUpdate, 1000)
|
|
|
|
disableInterception bool
|
|
isStarted atomic.Bool
|
|
)
|
|
|
|
func init() {
|
|
flag.BoolVar(&disableInterception, "disable-interception", false, "disable packet interception; this breaks a lot of functionality")
|
|
}
|
|
|
|
// Start starts the interception.
|
|
func start() error {
|
|
if disableInterception {
|
|
log.Warning("interception: packet interception is disabled via flag - this breaks a lot of functionality")
|
|
return nil
|
|
}
|
|
|
|
if !isStarted.CompareAndSwap(false, true) {
|
|
return nil // already running
|
|
}
|
|
|
|
inputPackets := Packets
|
|
if packetMetricsDestination != "" {
|
|
go metrics.writeMetrics()
|
|
inputPackets = make(chan packet.Packet)
|
|
go func() {
|
|
for p := range inputPackets {
|
|
Packets <- tracePacket(p)
|
|
}
|
|
}()
|
|
}
|
|
|
|
err := startInterception(inputPackets)
|
|
if err != nil {
|
|
log.Errorf("interception: failed to start module: %q", err)
|
|
log.Debug("interception: cleaning up after failed start...")
|
|
metrics.stop()
|
|
if e := stopInterception(); e != nil {
|
|
log.Debugf("interception: error cleaning up after failed start: %q", e.Error())
|
|
}
|
|
isStarted.Store(false)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Stop starts the interception.
|
|
func stop() error {
|
|
if disableInterception {
|
|
return nil
|
|
}
|
|
|
|
if !isStarted.CompareAndSwap(true, false) {
|
|
return nil // not running
|
|
}
|
|
|
|
metrics.stop()
|
|
if err := stopInterception(); err != nil {
|
|
log.Errorf("failed to stop interception module: %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
module *Interception
|
|
shimLoaded atomic.Bool
|
|
)
|
|
|
|
// New returns a new Interception module.
|
|
func New(instance instance) (*Interception, error) {
|
|
if !shimLoaded.CompareAndSwap(false, true) {
|
|
return nil, errors.New("only one instance allowed")
|
|
}
|
|
m := mgr.New("Interception")
|
|
module = &Interception{
|
|
mgr: m,
|
|
instance: instance,
|
|
EventStartStopState: mgr.NewEventMgr[bool]("IsStarted", m),
|
|
}
|
|
return module, nil
|
|
}
|
|
|
|
type instance interface {
|
|
BinaryUpdates() *updates.Updater
|
|
}
|