mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
202 lines
5 KiB
Go
202 lines
5 KiB
Go
package hub
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"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"
|
|
)
|
|
|
|
var (
|
|
db = database.NewInterface(&database.Options{
|
|
Local: true,
|
|
Internal: true,
|
|
})
|
|
|
|
getFromNavigator func(mapName, hubID string) *Hub
|
|
)
|
|
|
|
// MakeHubDBKey makes a hub db key.
|
|
func MakeHubDBKey(mapName, hubID string) string {
|
|
return fmt.Sprintf("cache:spn/hubs/%s/%s", mapName, hubID)
|
|
}
|
|
|
|
// MakeHubMsgDBKey makes a hub msg db key.
|
|
func MakeHubMsgDBKey(mapName string, msgType MsgType, hubID string) string {
|
|
return fmt.Sprintf("cache:spn/msgs/%s/%s/%s", mapName, msgType, hubID)
|
|
}
|
|
|
|
// SetNavigatorAccess sets a shortcut function to access hubs from the navigator instead of having go through the database.
|
|
// This also reduces the number of object in RAM and better caches parsed attributes.
|
|
func SetNavigatorAccess(fn func(mapName, hubID string) *Hub) {
|
|
if getFromNavigator == nil {
|
|
getFromNavigator = fn
|
|
}
|
|
}
|
|
|
|
// GetHub get a Hub from the database - or the navigator, if configured.
|
|
func GetHub(mapName string, hubID string) (*Hub, error) {
|
|
if getFromNavigator != nil {
|
|
hub := getFromNavigator(mapName, hubID)
|
|
if hub != nil {
|
|
return hub, nil
|
|
}
|
|
}
|
|
|
|
return GetHubByKey(MakeHubDBKey(mapName, hubID))
|
|
}
|
|
|
|
// GetHubByKey returns a hub by its raw DB key.
|
|
func GetHubByKey(key string) (*Hub, error) {
|
|
r, err := db.Get(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hub, err := EnsureHub(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return hub, nil
|
|
}
|
|
|
|
// EnsureHub makes sure a database record is a Hub.
|
|
func EnsureHub(r record.Record) (*Hub, error) {
|
|
// unwrap
|
|
if r.IsWrapped() {
|
|
// only allocate a new struct, if we need it
|
|
newHub := &Hub{}
|
|
err := record.Unwrap(r, newHub)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newHub = prepHub(newHub)
|
|
|
|
// Fully validate when getting from database.
|
|
if err := newHub.Info.validateFormatting(); err != nil {
|
|
return nil, fmt.Errorf("announcement failed format validation: %w", err)
|
|
}
|
|
if err := newHub.Status.validateFormatting(); err != nil {
|
|
return nil, fmt.Errorf("status failed format validation: %w", err)
|
|
}
|
|
if err := newHub.Info.prepare(false); err != nil {
|
|
return nil, fmt.Errorf("failed to prepare announcement: %w", err)
|
|
}
|
|
|
|
return newHub, nil
|
|
}
|
|
|
|
// or adjust type
|
|
newHub, ok := r.(*Hub)
|
|
if !ok {
|
|
return nil, fmt.Errorf("record not of type *Hub, but %T", r)
|
|
}
|
|
newHub = prepHub(newHub)
|
|
|
|
// Prepare only when already parsed.
|
|
if err := newHub.Info.prepare(false); err != nil {
|
|
return nil, fmt.Errorf("failed to prepare announcement: %w", err)
|
|
}
|
|
|
|
// ensure status
|
|
return newHub, nil
|
|
}
|
|
|
|
func prepHub(h *Hub) *Hub {
|
|
if h.Status == nil {
|
|
h.Status = &Status{}
|
|
}
|
|
h.Measurements = getSharedMeasurements(h.ID, h.Measurements)
|
|
return h
|
|
}
|
|
|
|
// Save saves to Hub to the correct scope in the database.
|
|
func (h *Hub) Save() error {
|
|
if !h.KeyIsSet() {
|
|
h.SetKey(MakeHubDBKey(h.Map, h.ID))
|
|
}
|
|
|
|
return db.Put(h)
|
|
}
|
|
|
|
// RemoveHubAndMsgs deletes a Hub and it's saved messages from the database.
|
|
func RemoveHubAndMsgs(mapName string, hubID string) (err error) {
|
|
err = db.Delete(MakeHubDBKey(mapName, hubID))
|
|
if err != nil && !errors.Is(err, database.ErrNotFound) {
|
|
return fmt.Errorf("failed to delete main hub entry: %w", err)
|
|
}
|
|
|
|
err = db.Delete(MakeHubMsgDBKey(mapName, MsgTypeAnnouncement, hubID))
|
|
if err != nil && !errors.Is(err, database.ErrNotFound) {
|
|
return fmt.Errorf("failed to delete hub announcement data: %w", err)
|
|
}
|
|
|
|
err = db.Delete(MakeHubMsgDBKey(mapName, MsgTypeStatus, hubID))
|
|
if err != nil && !errors.Is(err, database.ErrNotFound) {
|
|
return fmt.Errorf("failed to delete hub status data: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// HubMsg stores raw Hub messages.
|
|
type HubMsg struct { //nolint:golint
|
|
record.Base
|
|
sync.Mutex
|
|
|
|
ID string
|
|
Map string
|
|
Type MsgType
|
|
Data []byte
|
|
|
|
Received int64
|
|
}
|
|
|
|
// SaveHubMsg saves a raw (and signed) message received by another Hub.
|
|
func SaveHubMsg(id string, mapName string, msgType MsgType, data []byte) error {
|
|
// create wrapper record
|
|
msg := &HubMsg{
|
|
ID: id,
|
|
Map: mapName,
|
|
Type: msgType,
|
|
Data: data,
|
|
Received: time.Now().Unix(),
|
|
}
|
|
// set key
|
|
msg.SetKey(MakeHubMsgDBKey(msg.Map, msg.Type, msg.ID))
|
|
// save
|
|
return db.PutNew(msg)
|
|
}
|
|
|
|
// QueryRawGossipMsgs queries the database for raw gossip messages.
|
|
func QueryRawGossipMsgs(mapName string, msgType MsgType) (it *iterator.Iterator, err error) {
|
|
it, err = db.Query(query.New(MakeHubMsgDBKey(mapName, msgType, "")))
|
|
return
|
|
}
|
|
|
|
// EnsureHubMsg makes sure a database record is a HubMsg.
|
|
func EnsureHubMsg(r record.Record) (*HubMsg, error) {
|
|
// unwrap
|
|
if r.IsWrapped() {
|
|
// only allocate a new struct, if we need it
|
|
newHubMsg := &HubMsg{}
|
|
err := record.Unwrap(r, newHubMsg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newHubMsg, nil
|
|
}
|
|
|
|
// or adjust type
|
|
newHubMsg, ok := r.(*HubMsg)
|
|
if !ok {
|
|
return nil, fmt.Errorf("record not of type *Hub, but %T", r)
|
|
}
|
|
return newHubMsg, nil
|
|
}
|