mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
149 lines
3.5 KiB
Go
149 lines
3.5 KiB
Go
package endpoints
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/safing/portmaster/intel"
|
|
)
|
|
|
|
// Endpoints is a list of permitted or denied endpoints.
|
|
type Endpoints []Endpoint
|
|
|
|
// EPResult represents the result of a check against an EndpointPermission.
|
|
type EPResult uint8
|
|
|
|
// Endpoint matching return values.
|
|
const (
|
|
NoMatch EPResult = iota
|
|
MatchError
|
|
Denied
|
|
Permitted
|
|
)
|
|
|
|
// IsDecision returns true if result represents a decision
|
|
// and false if result is NoMatch or Undeterminable.
|
|
func IsDecision(result EPResult) bool {
|
|
return result == Denied || result == Permitted || result == MatchError
|
|
}
|
|
|
|
// ParseEndpoints parses a list of endpoints and returns a list of Endpoints for matching.
|
|
func ParseEndpoints(entries []string) (Endpoints, error) {
|
|
var firstErr error
|
|
var errCnt int
|
|
endpoints := make(Endpoints, 0, len(entries))
|
|
|
|
entriesLoop:
|
|
for _, entry := range entries {
|
|
ep, err := parseEndpoint(entry)
|
|
if err != nil {
|
|
errCnt++
|
|
if firstErr == nil {
|
|
firstErr = err
|
|
}
|
|
continue entriesLoop
|
|
}
|
|
|
|
endpoints = append(endpoints, ep)
|
|
}
|
|
|
|
if firstErr != nil {
|
|
if errCnt > 0 {
|
|
return endpoints, fmt.Errorf("encountered %d errors, first was: %w", errCnt, firstErr)
|
|
}
|
|
return endpoints, firstErr
|
|
}
|
|
|
|
return endpoints, nil
|
|
}
|
|
|
|
// ListEntryValidationRegex is a regex to bullshit check endpoint list entries.
|
|
var ListEntryValidationRegex = strings.Join([]string{
|
|
`^(\+|\-) `, // Rule verdict.
|
|
`(! +)?`, // Invert matching.
|
|
`[A-z0-9\.:\-*/]+`, // Entity matching.
|
|
`( `, // Start of optional matching.
|
|
`[A-z0-9*]+`, // Protocol matching.
|
|
`(/[A-z0-9]+(\-[A-z0-9]+)?)?`, // Port and port range matching.
|
|
`)?`, // End of optional matching.
|
|
`( +#.*)?`, // Optional comment.
|
|
}, "")
|
|
|
|
// ValidateEndpointListConfigOption validates the given value.
|
|
func ValidateEndpointListConfigOption(value interface{}) error {
|
|
list, ok := value.([]string)
|
|
if !ok {
|
|
return errors.New("invalid type")
|
|
}
|
|
|
|
_, err := ParseEndpoints(list)
|
|
return err
|
|
}
|
|
|
|
// IsSet returns whether the Endpoints object is "set".
|
|
func (e Endpoints) IsSet() bool {
|
|
return len(e) > 0
|
|
}
|
|
|
|
// Match checks whether the given entity matches any of the endpoint definitions in the list.
|
|
func (e Endpoints) Match(ctx context.Context, entity *intel.Entity) (result EPResult, reason Reason) {
|
|
for _, entry := range e {
|
|
if entry == nil {
|
|
continue
|
|
}
|
|
|
|
if result, reason = entry.Matches(ctx, entity); result != NoMatch {
|
|
return
|
|
}
|
|
}
|
|
|
|
return NoMatch, nil
|
|
}
|
|
|
|
// MatchMulti checks whether the given entities match any of the endpoint
|
|
// definitions in the list. Every rule is evaluated against all given entities
|
|
// and only if not match was registered, the next rule is evaluated.
|
|
func (e Endpoints) MatchMulti(ctx context.Context, entities ...*intel.Entity) (result EPResult, reason Reason) {
|
|
for _, entry := range e {
|
|
if entry == nil {
|
|
continue
|
|
}
|
|
|
|
for _, entity := range entities {
|
|
if entity == nil {
|
|
continue
|
|
}
|
|
|
|
if result, reason = entry.Matches(ctx, entity); result != NoMatch {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
return NoMatch, nil
|
|
}
|
|
|
|
func (e Endpoints) String() string {
|
|
s := make([]string, 0, len(e))
|
|
for _, entry := range e {
|
|
s = append(s, entry.String())
|
|
}
|
|
return fmt.Sprintf("[%s]", strings.Join(s, ", "))
|
|
}
|
|
|
|
func (epr EPResult) String() string {
|
|
switch epr {
|
|
case NoMatch:
|
|
return "No Match"
|
|
case MatchError:
|
|
return "Match Error"
|
|
case Denied:
|
|
return "Denied"
|
|
case Permitted:
|
|
return "Permitted"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|