diff --git a/firewall/api.go b/firewall/api.go index 5ce9367d..168d25bc 100644 --- a/firewall/api.go +++ b/firewall/api.go @@ -97,7 +97,7 @@ func apiAuthenticator(r *http.Request, s *http.Server) (token *api.AuthToken, er log.Tracer(r.Context()).Tracef("filter: authenticating API request from %s", r.RemoteAddr) - // It is very important that this works, retry extensively (every 250ms for 5s) + // It is important that this works, retry 5 times: every 500ms for 2.5s. var retry bool for tries := 0; tries < 5; tries++ { retry, err = authenticateAPIRequest( diff --git a/firewall/config.go b/firewall/config.go index 4119e27f..cd7a4d2a 100644 --- a/firewall/config.go +++ b/firewall/config.go @@ -6,7 +6,7 @@ import ( "github.com/safing/portmaster/core" ) -// Configuration Keys +// Configuration Keys. var ( CfgOptionEnableFilterKey = "filter/enable" diff --git a/firewall/prompt.go b/firewall/prompt.go index 9a1d2787..a6302546 100644 --- a/firewall/prompt.go +++ b/firewall/prompt.go @@ -32,6 +32,8 @@ const ( var ( promptNotificationCreation sync.Mutex + + decisionTimeout int64 = 10 // in seconds ) type promptData struct { @@ -45,10 +47,16 @@ type promptProfile struct { LinkedPath string } -func prompt(ctx context.Context, conn *network.Connection, pkt packet.Packet) { //nolint:gocognit // TODO +func prompt(ctx context.Context, conn *network.Connection, pkt packet.Packet) { // Create notification. n := createPrompt(ctx, conn, pkt) + // Get decision timeout and make sure it does not exceed the ask timeout. + timeout := decisionTimeout + if timeout > askTimeout() { + timeout = askTimeout() + } + // wait for response/timeout select { case promptResponse := <-n.Response(): @@ -59,7 +67,7 @@ func prompt(ctx context.Context, conn *network.Connection, pkt packet.Packet) { conn.Deny("blocked via prompt", profile.CfgOptionEndpointsKey) } - case <-time.After(1 * time.Second): + case <-time.After(time.Duration(timeout) * time.Second): log.Tracer(ctx).Debugf("filter: continuing prompting async") conn.Deny("prompting in progress, please respond to prompt", profile.CfgOptionDefaultActionKey) diff --git a/process/config.go b/process/config.go index ce43776e..6b5fcdf1 100644 --- a/process/config.go +++ b/process/config.go @@ -4,10 +4,11 @@ import ( "github.com/safing/portbase/config" ) -// Configuration Keys +// Configuration Keys. var ( CfgOptionEnableProcessDetectionKey = "core/enableProcessDetection" - enableProcessDetection config.BoolOption + + enableProcessDetection config.BoolOption ) func registerConfiguration() error { diff --git a/process/find.go b/process/find.go index 78e0942d..e4b1ef0f 100644 --- a/process/find.go +++ b/process/find.go @@ -45,21 +45,21 @@ func GetProcessByConnection(ctx context.Context, pktInfo *packet.Info) (process return process, connInbound, nil } -func GetNetworkHost(ctx context.Context, remoteIP net.IP) (process *Process, err error) { +func GetNetworkHost(ctx context.Context, remoteIP net.IP) (process *Process, err error) { //nolint:interfacer now := time.Now().Unix() networkHost := &Process{ Name: fmt.Sprintf("Network Host %s", remoteIP), UserName: "Unknown", - UserID: -255, - Pid: -255, - ParentPid: -255, + UserID: NetworkHostProcessID, + Pid: NetworkHostProcessID, + ParentPid: NetworkHostProcessID, Path: fmt.Sprintf("net:%s", remoteIP), FirstSeen: now, LastSeen: now, } // Get the (linked) local profile. - networkHostProfile, err := profile.GetNetworkHostProfile(remoteIP.String()) + networkHostProfile, err := profile.GetProfile(profile.SourceNetwork, remoteIP.String(), "") if err != nil { return nil, err } diff --git a/process/process.go b/process/process.go index 6a2683a1..619e832a 100644 --- a/process/process.go +++ b/process/process.go @@ -25,7 +25,7 @@ const ( var getProcessSingleInflight singleflight.Group -// A Process represents a process running on the operating system +// A Process represents a process running on the operating system. type Process struct { record.Base sync.Mutex diff --git a/process/special.go b/process/special.go index 829e16aa..24e3aa8b 100644 --- a/process/special.go +++ b/process/special.go @@ -9,9 +9,14 @@ import ( "golang.org/x/sync/singleflight" ) -// UnidentifiedProcessID is the PID used for anything that could not be -// attributed to a PID for any reason. -const UnidentifiedProcessID = -1 +const ( + // UnidentifiedProcessID is the PID used for anything that could not be + // attributed to a PID for any reason. + UnidentifiedProcessID = -1 + + // NetworkHostProcessID is the PID used for requests served to the network. + NetworkHostProcessID = -255 +) var ( // unidentifiedProcess is used when a process cannot be found. diff --git a/profile/active.go b/profile/active.go index 2bf649d5..9eb83470 100644 --- a/profile/active.go +++ b/profile/active.go @@ -58,6 +58,13 @@ func addActiveProfile(profile *Profile) { activeProfilesLock.Lock() defer activeProfilesLock.Unlock() + // Mark any previous profile as outdated. + previous, ok := activeProfiles[profile.ScopedID()] + if ok { + previous.outdated.Set() + } + + // Mark new profile active and add to active profiles. profile.MarkStillActive() activeProfiles[profile.ScopedID()] = profile } diff --git a/profile/config.go b/profile/config.go index 21b52f79..5a0d37bb 100644 --- a/profile/config.go +++ b/profile/config.go @@ -8,7 +8,7 @@ import ( "github.com/safing/portmaster/status" ) -// Configuration Keys +// Configuration Keys. var ( cfgStringOptions = make(map[string]config.StringOption) cfgStringArrayOptions = make(map[string]config.StringArrayOption) diff --git a/profile/database.go b/profile/database.go index 4775bc82..9c968a8f 100644 --- a/profile/database.go +++ b/profile/database.go @@ -47,7 +47,6 @@ func startProfileUpdateChecker() error { } module.StartServiceWorker("update active profiles", 0, func(ctx context.Context) (err error) { - feedSelect: for { select { case r := <-profilesSub.Feed: @@ -56,14 +55,6 @@ func startProfileUpdateChecker() error { return errors.New("subscription canceled") } - // check if internal save - if !r.IsWrapped() { - profile, ok := r.(*Profile) - if ok && profile.internalSave { - continue feedSelect - } - } - // mark as outdated markActiveProfileAsOutdated(strings.TrimPrefix(r.Key(), profilesDBPath)) case <-ctx.Done(): diff --git a/profile/get.go b/profile/get.go index b6559fb4..e83cc3d1 100644 --- a/profile/get.go +++ b/profile/get.go @@ -28,7 +28,7 @@ const ( var getProfileSingleInflight singleflight.Group -// GetProfile fetches a profile. This function ensure that the profile loaded +// GetProfile fetches a profile. This function ensures that the loaded profile // is shared among all callers. You must always supply both the scopedID and // linkedPath parameters whenever available. func GetProfile(source profileSource, id, linkedPath string) ( //nolint:gocognit @@ -105,12 +105,8 @@ func GetProfile(source profileSource, id, linkedPath string) ( //nolint:gocognit // Process profiles coming directly from the database. // As we don't use any caching, these will be new objects. - // Mark the profile as being saved internally in order to not trigger an - // update after saving it to the database. - profile.internalSave = true - - // Add a layeredProfile to local profiles. - if profile.Source == SourceLocal { + // Add a layeredProfile to local and network profiles. + if profile.Source == SourceLocal || profile.Source == SourceNetwork { // If we are refetching, assign the layered profile from the previous version. if previousVersion != nil { profile.layeredProfile = previousVersion.layeredProfile @@ -138,76 +134,6 @@ func GetProfile(source profileSource, id, linkedPath string) ( //nolint:gocognit return p.(*Profile), nil } -func GetNetworkHostProfile(remoteIP string) ( //nolint:gocognit - profile *Profile, - err error, -) { - scopedID := makeScopedID(SourceNetwork, remoteIP) - - p, err, _ := getProfileSingleInflight.Do(scopedID, func() (interface{}, error) { - var previousVersion *Profile - - // Get profile via the scoped ID. - // Check if there already is an active and not outdated profile. - profile = getActiveProfile(scopedID) - if profile != nil { - profile.MarkStillActive() - - if profile.outdated.IsSet() { - previousVersion = profile - } else { - return profile, nil - } - } - - // Get from database. - profile, err = getProfile(scopedID) - switch { - case err == nil: - // Continue. - case errors.Is(err, database.ErrNotFound): - // Create new profile. - // If there was no profile in the database, create a new one, and return it. - profile = New(SourceNetwork, remoteIP, "") - default: - return nil, err - } - - // Process profiles coming directly from the database. - // As we don't use any caching, these will be new objects. - - // Mark the profile as being saved internally in order to not trigger an - // update after saving it to the database. - profile.internalSave = true - - // Add a layeredProfile to network profiles. - - // If we are refetching, assign the layered profile from the previous version. - if previousVersion != nil { - profile.layeredProfile = previousVersion.layeredProfile - } - - // Network profiles must have a layered profile, create a new one if it - // does not yet exist. - if profile.layeredProfile == nil { - profile.layeredProfile = NewLayeredProfile(profile) - } - - // Add the profile to the currently active profiles. - addActiveProfile(profile) - - return profile, nil - }) - if err != nil { - return nil, err - } - if p == nil { - return nil, errors.New("profile getter returned nil") - } - - return p.(*Profile), nil -} - // getProfile fetches the profile for the given scoped ID. func getProfile(scopedID string) (profile *Profile, err error) { // Get profile from the database. @@ -266,13 +192,13 @@ func prepProfile(r record.Record) (*Profile, error) { // prepare config err = profile.prepConfig() if err != nil { - log.Warningf("profiles: profile %s has (partly) invalid configuration: %s", profile.ID, err) + log.Errorf("profiles: profile %s has (partly) invalid configuration: %s", profile.ID, err) } // parse config err = profile.parseConfig() if err != nil { - log.Warningf("profiles: profile %s has (partly) invalid configuration: %s", profile.ID, err) + log.Errorf("profiles: profile %s has (partly) invalid configuration: %s", profile.ID, err) } // return parsed profile diff --git a/profile/profile.go b/profile/profile.go index 5c838c89..46668249 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -127,8 +127,6 @@ type Profile struct { //nolint:maligned // not worth the effort // Lifecycle Management outdated *abool.AtomicBool lastActive *int64 - - internalSave bool } func (profile *Profile) prepConfig() (err error) { @@ -197,12 +195,11 @@ func (profile *Profile) parseConfig() error { // New returns a new Profile. func New(source profileSource, id string, linkedPath string) *Profile { profile := &Profile{ - ID: id, - Source: source, - LinkedPath: linkedPath, - Created: time.Now().Unix(), - Config: make(map[string]interface{}), - internalSave: true, + ID: id, + Source: source, + LinkedPath: linkedPath, + Created: time.Now().Unix(), + Config: make(map[string]interface{}), } // Generate random ID if none is given. @@ -301,18 +298,6 @@ func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) { } }() - // When finished increase the revision counter of the layered profile. - defer func() { - if !changed || profile.layeredProfile == nil { - return - } - - profile.layeredProfile.Lock() - defer profile.layeredProfile.Unlock() - - profile.layeredProfile.RevisionCounter++ - }() - // Lock the profile for editing. profile.Lock() defer profile.Unlock() @@ -350,7 +335,7 @@ func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) { profile.dataParsed = false err := profile.parseConfig() if err != nil { - log.Warningf("profile: failed to parse %s config after adding endpoint: %s", profile, err) + log.Errorf("profile: failed to parse %s config after adding endpoint: %s", profile, err) } }