mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
* 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>
202 lines
4.2 KiB
Go
202 lines
4.2 KiB
Go
package network
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"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"
|
|
"github.com/safing/portmaster/service/process"
|
|
)
|
|
|
|
const (
|
|
dbScopeNone = ""
|
|
dbScopeDNS = "dns"
|
|
dbScopeIP = "ip"
|
|
)
|
|
|
|
var (
|
|
dbController *database.Controller
|
|
|
|
dnsConns = newConnectionStore()
|
|
conns = newConnectionStore()
|
|
)
|
|
|
|
// StorageInterface provices a storage.Interface to the
|
|
// configuration manager.
|
|
type StorageInterface struct {
|
|
storage.InjectBase
|
|
}
|
|
|
|
// Database prefixes:
|
|
// Processes: network:tree/<PID>
|
|
// DNS Requests: network:tree/<PID>/dns/<ID>
|
|
// IP Connections: network:tree/<PID>/ip/<ID>
|
|
|
|
func makeKey(pid int, scope, id string) string {
|
|
if scope == "" {
|
|
return "network:tree/" + strconv.Itoa(pid)
|
|
}
|
|
return fmt.Sprintf("network:tree/%d/%s/%s", pid, scope, id)
|
|
}
|
|
|
|
func parseDBKey(key string) (processKey string, scope, id string, ok bool) {
|
|
// Split into segments.
|
|
segments := strings.Split(key, "/")
|
|
|
|
// Keys have 2 or 4 segments.
|
|
switch len(segments) {
|
|
case 4:
|
|
id = segments[3]
|
|
|
|
fallthrough
|
|
case 3:
|
|
scope = segments[2]
|
|
// Sanity check.
|
|
switch scope {
|
|
case dbScopeNone, dbScopeDNS, dbScopeIP:
|
|
// Parsed id matches possible values.
|
|
// The empty string is for matching a trailing slash for in query prefix.
|
|
// TODO: For queries, also prefixes of these values are valid.
|
|
default:
|
|
// Unknown scope.
|
|
return "", "", "", false
|
|
}
|
|
|
|
fallthrough
|
|
case 2:
|
|
processKey = segments[1]
|
|
return processKey, scope, id, true
|
|
case 1:
|
|
// This is a valid query prefix, but not process ID was given.
|
|
return "", "", "", true
|
|
default:
|
|
return "", "", "", false
|
|
}
|
|
}
|
|
|
|
// Get returns a database record.
|
|
func (s *StorageInterface) Get(key string) (record.Record, error) {
|
|
// Parse key and check if valid.
|
|
pid, scope, id, ok := parseDBKey(strings.TrimPrefix(key, "network:"))
|
|
if !ok || pid == "" {
|
|
return nil, storage.ErrNotFound
|
|
}
|
|
|
|
switch scope {
|
|
case dbScopeDNS:
|
|
if c, ok := dnsConns.get(id); ok && c.DataIsComplete() {
|
|
return c, nil
|
|
}
|
|
case dbScopeIP:
|
|
if c, ok := conns.get(id); ok && c.DataIsComplete() {
|
|
return c, nil
|
|
}
|
|
case dbScopeNone:
|
|
if proc, ok := process.GetProcessFromStorage(pid); ok {
|
|
return proc, nil
|
|
}
|
|
}
|
|
|
|
return nil, storage.ErrNotFound
|
|
}
|
|
|
|
// Query returns a an iterator for the supplied query.
|
|
func (s *StorageInterface) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) {
|
|
it := iterator.New()
|
|
|
|
module.mgr.Go("connection query", func(_ *mgr.WorkerCtx) error {
|
|
s.processQuery(q, it)
|
|
return nil
|
|
})
|
|
|
|
return it, nil
|
|
}
|
|
|
|
func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
|
|
var matches bool
|
|
pid, scope, _, ok := parseDBKey(q.DatabaseKeyPrefix())
|
|
if !ok {
|
|
it.Finish(nil)
|
|
return
|
|
}
|
|
|
|
if pid == "" {
|
|
// processes
|
|
for _, proc := range process.All() {
|
|
func() {
|
|
proc.Lock()
|
|
defer proc.Unlock()
|
|
matches = q.Matches(proc)
|
|
}()
|
|
if matches {
|
|
it.Next <- proc
|
|
}
|
|
}
|
|
}
|
|
|
|
if scope == dbScopeNone || scope == dbScopeDNS {
|
|
// dns scopes only
|
|
for _, dnsConn := range dnsConns.clone() {
|
|
if !dnsConn.DataIsComplete() {
|
|
continue
|
|
}
|
|
|
|
func() {
|
|
dnsConn.Lock()
|
|
defer dnsConn.Unlock()
|
|
matches = q.Matches(dnsConn)
|
|
}()
|
|
|
|
if matches {
|
|
it.Next <- dnsConn
|
|
}
|
|
}
|
|
}
|
|
|
|
if scope == dbScopeNone || scope == dbScopeIP {
|
|
// connections
|
|
for _, conn := range conns.clone() {
|
|
if !conn.DataIsComplete() {
|
|
continue
|
|
}
|
|
|
|
func() {
|
|
conn.Lock()
|
|
defer conn.Unlock()
|
|
matches = q.Matches(conn)
|
|
}()
|
|
|
|
if matches {
|
|
it.Next <- conn
|
|
}
|
|
}
|
|
}
|
|
|
|
it.Finish(nil)
|
|
}
|
|
|
|
func registerAsDatabase() error {
|
|
_, err := database.Register(&database.Database{
|
|
Name: "network",
|
|
Description: "Network and Firewall Data",
|
|
StorageType: "injected",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
controller, err := database.InjectDatabase("network", &StorageInterface{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dbController = controller
|
|
process.SetDBController(dbController)
|
|
return nil
|
|
}
|