mirror of
https://github.com/safing/portmaster
synced 2025-09-04 19:49:15 +00:00
Merge pull request #399 from safing/feature/patch-set-2
Updates and Improvements (related to SPN v0.3)
This commit is contained in:
commit
5c0bf25f95
11 changed files with 255 additions and 120 deletions
|
@ -3,6 +3,7 @@ package firewall
|
||||||
import (
|
import (
|
||||||
"github.com/safing/portbase/config"
|
"github.com/safing/portbase/config"
|
||||||
"github.com/safing/portbase/modules/subsystems"
|
"github.com/safing/portbase/modules/subsystems"
|
||||||
|
"github.com/safing/spn/captain"
|
||||||
|
|
||||||
"github.com/safing/portbase/modules"
|
"github.com/safing/portbase/modules"
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
var (
|
var (
|
||||||
filterModule *modules.Module
|
filterModule *modules.Module
|
||||||
filterEnabled config.BoolOption
|
filterEnabled config.BoolOption
|
||||||
|
tunnelEnabled config.BoolOption
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -28,8 +30,8 @@ func init() {
|
||||||
Key: CfgOptionEnableFilterKey,
|
Key: CfgOptionEnableFilterKey,
|
||||||
Description: "Start the Privacy Filter module. If turned off, all privacy filter protections are fully disabled on this device.",
|
Description: "Start the Privacy Filter module. If turned off, all privacy filter protections are fully disabled on this device.",
|
||||||
OptType: config.OptTypeBool,
|
OptType: config.OptTypeBool,
|
||||||
ExpertiseLevel: config.ExpertiseLevelUser,
|
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
||||||
ReleaseLevel: config.ReleaseLevelBeta,
|
ReleaseLevel: config.ReleaseLevelStable,
|
||||||
DefaultValue: true,
|
DefaultValue: true,
|
||||||
Annotations: config.Annotations{
|
Annotations: config.Annotations{
|
||||||
config.CategoryAnnotation: "General",
|
config.CategoryAnnotation: "General",
|
||||||
|
@ -44,6 +46,7 @@ func filterPrep() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
filterEnabled = config.GetAsBool(CfgOptionEnableFilterKey, true)
|
filterEnabled = config.Concurrent.GetAsBool(CfgOptionEnableFilterKey, true)
|
||||||
|
tunnelEnabled = config.Concurrent.GetAsBool(captain.CfgOptionEnableSPNKey, false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/safing/spn/captain"
|
||||||
|
|
||||||
"github.com/google/gopacket/layers"
|
"github.com/google/gopacket/layers"
|
||||||
"github.com/safing/portmaster/netenv"
|
"github.com/safing/portmaster/netenv"
|
||||||
"golang.org/x/sync/singleflight"
|
"golang.org/x/sync/singleflight"
|
||||||
|
@ -22,7 +24,7 @@ import (
|
||||||
"github.com/safing/portmaster/network"
|
"github.com/safing/portmaster/network"
|
||||||
"github.com/safing/portmaster/network/netutils"
|
"github.com/safing/portmaster/network/netutils"
|
||||||
"github.com/safing/portmaster/network/packet"
|
"github.com/safing/portmaster/network/packet"
|
||||||
"github.com/safing/spn/captain"
|
"github.com/safing/spn/crew"
|
||||||
"github.com/safing/spn/sluice"
|
"github.com/safing/spn/sluice"
|
||||||
|
|
||||||
// module dependencies
|
// module dependencies
|
||||||
|
@ -346,32 +348,41 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if filtering is enabled
|
// TODO: enable inspecting again
|
||||||
if !filterEnabled() {
|
conn.Inspecting = false
|
||||||
conn.Inspecting = false
|
|
||||||
|
// Filter, if enabled.
|
||||||
|
if filterEnabled() {
|
||||||
|
log.Tracer(pkt.Ctx()).Trace("filter: starting decision process")
|
||||||
|
DecideOnConnection(pkt.Ctx(), conn, pkt)
|
||||||
|
} else {
|
||||||
conn.Accept("privacy filter disabled", noReasonOptionKey)
|
conn.Accept("privacy filter disabled", noReasonOptionKey)
|
||||||
conn.StopFirewallHandler()
|
|
||||||
issueVerdict(conn, pkt, 0, true)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracer(pkt.Ctx()).Trace("filter: starting decision process")
|
// Tunnel, if enabled.
|
||||||
DecideOnConnection(pkt.Ctx(), conn, pkt)
|
if pkt.IsOutbound() && conn.Entity.IPScope.IsGlobal() &&
|
||||||
conn.Inspecting = false // TODO: enable inspecting again
|
tunnelEnabled() && conn.Verdict == network.VerdictAccept &&
|
||||||
|
conn.Process().Profile() != nil &&
|
||||||
|
conn.Process().Profile().UseSPN() {
|
||||||
|
|
||||||
// tunneling
|
// Exclude requests of the SPN itself.
|
||||||
// TODO: add implementation for forced tunneling
|
if !captain.IsExcepted(conn.Entity.IP) {
|
||||||
if pkt.IsOutbound() &&
|
// Check if client is ready.
|
||||||
captain.ClientReady() &&
|
if captain.ClientReady() {
|
||||||
conn.Entity.IPScope.IsGlobal() &&
|
// Queue request in sluice.
|
||||||
conn.Verdict == network.VerdictAccept {
|
err := sluice.AwaitRequest(conn, crew.HandleSluiceRequest)
|
||||||
// try to tunnel
|
if err != nil {
|
||||||
err := sluice.AwaitRequest(pkt.Info(), conn.Entity.Domain)
|
log.Tracer(pkt.Ctx()).Warningf("failed to rqeuest tunneling: %s", err)
|
||||||
if err != nil {
|
conn.Failed("failed to request tunneling", "")
|
||||||
log.Tracer(pkt.Ctx()).Tracef("filter: not tunneling: %s", err)
|
} else {
|
||||||
} else {
|
log.Tracer(pkt.Ctx()).Trace("filter: tunneling requested")
|
||||||
log.Tracer(pkt.Ctx()).Trace("filter: tunneling request")
|
conn.Verdict = network.VerdictRerouteToTunnel
|
||||||
conn.Verdict = network.VerdictRerouteToTunnel
|
}
|
||||||
|
} else {
|
||||||
|
// Block connection as SPN is not ready yet.
|
||||||
|
log.Tracer(pkt.Ctx()).Trace("SPN not ready for tunneling")
|
||||||
|
conn.Failed("SPN not ready for tunneling", "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ func lookupBlockLists(entity, value string) ([]string, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("intel/filterlists: searching for entries with %s", key)
|
// log.Debugf("intel/filterlists: searching for entries with %s", key)
|
||||||
entry, err := getEntityRecordByKey(key)
|
entry, err := getEntityRecordByKey(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == database.ErrNotFound {
|
if err == database.ErrNotFound {
|
||||||
|
|
|
@ -2,7 +2,9 @@ package netenv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
@ -22,9 +24,7 @@ var (
|
||||||
locationTestingIPv4 = "1.1.1.1"
|
locationTestingIPv4 = "1.1.1.1"
|
||||||
locationTestingIPv4Addr *net.IPAddr
|
locationTestingIPv4Addr *net.IPAddr
|
||||||
|
|
||||||
locations = &DeviceLocations{
|
locations = &DeviceLocations{}
|
||||||
All: make(map[string]*DeviceLocation),
|
|
||||||
}
|
|
||||||
locationsLock sync.Mutex
|
locationsLock sync.Mutex
|
||||||
gettingLocationsLock sync.Mutex
|
gettingLocationsLock sync.Mutex
|
||||||
locationNetworkChangedFlag = GetNetworkChangedFlag()
|
locationNetworkChangedFlag = GetNetworkChangedFlag()
|
||||||
|
@ -36,8 +36,32 @@ func prepLocation() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeviceLocations struct {
|
type DeviceLocations struct {
|
||||||
Best *DeviceLocation
|
All []*DeviceLocation
|
||||||
All map[string]*DeviceLocation
|
}
|
||||||
|
|
||||||
|
func (dl *DeviceLocations) Best() *DeviceLocation {
|
||||||
|
if len(dl.All) > 0 {
|
||||||
|
return dl.All[0]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dl *DeviceLocations) BestV4() *DeviceLocation {
|
||||||
|
for _, loc := range dl.All {
|
||||||
|
if loc.IPVersion == packet.IPv4 {
|
||||||
|
return loc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dl *DeviceLocations) BestV6() *DeviceLocation {
|
||||||
|
for _, loc := range dl.All {
|
||||||
|
if loc.IPVersion == packet.IPv6 {
|
||||||
|
return loc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyDeviceLocations() *DeviceLocations {
|
func copyDeviceLocations() *DeviceLocations {
|
||||||
|
@ -45,23 +69,20 @@ func copyDeviceLocations() *DeviceLocations {
|
||||||
defer locationsLock.Unlock()
|
defer locationsLock.Unlock()
|
||||||
|
|
||||||
// Create a copy of the locations, but not the entries.
|
// Create a copy of the locations, but not the entries.
|
||||||
cp := *locations
|
cp := &DeviceLocations{
|
||||||
cp.All = make(map[string]*DeviceLocation, len(locations.All))
|
All: make([]*DeviceLocation, len(locations.All)),
|
||||||
for k, v := range locations.All {
|
|
||||||
cp.All[k] = v
|
|
||||||
}
|
}
|
||||||
|
copy(cp.All, locations.All)
|
||||||
|
|
||||||
return &cp
|
return cp
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeviceLocation represents a single IP and metadata. It must not be changed
|
// DeviceLocation represents a single IP and metadata. It must not be changed
|
||||||
// once created.
|
// once created.
|
||||||
type DeviceLocation struct {
|
type DeviceLocation struct {
|
||||||
IP net.IP
|
IP net.IP
|
||||||
Continent string
|
IPVersion packet.IPVersion
|
||||||
Country string
|
Location *geoip.Location
|
||||||
ASN uint
|
|
||||||
ASOrg string
|
|
||||||
Source DeviceLocationSource
|
Source DeviceLocationSource
|
||||||
SourceAccuracy int
|
SourceAccuracy int
|
||||||
}
|
}
|
||||||
|
@ -71,19 +92,43 @@ type DeviceLocation struct {
|
||||||
func (dl *DeviceLocation) IsMoreAccurateThan(other *DeviceLocation) bool {
|
func (dl *DeviceLocation) IsMoreAccurateThan(other *DeviceLocation) bool {
|
||||||
switch {
|
switch {
|
||||||
case dl.SourceAccuracy > other.SourceAccuracy:
|
case dl.SourceAccuracy > other.SourceAccuracy:
|
||||||
// Higher accuracy is better.
|
// Higher source accuracy is better.
|
||||||
return true
|
return true
|
||||||
case dl.ASN != 0 && other.ASN == 0:
|
case dl.Location.AutonomousSystemNumber != 0 && other.Location.AutonomousSystemNumber == 0:
|
||||||
// Having an ASN is better than having none.
|
// Having an ASN is better than having none.
|
||||||
return true
|
return true
|
||||||
case dl.Country == "" && other.Country != "":
|
case dl.Location.Continent.Code != "" && other.Location.Continent.Code == "":
|
||||||
|
// Having a Continent is better than having none.
|
||||||
|
return true
|
||||||
|
case dl.Location.Country.ISOCode != "" && other.Location.Country.ISOCode == "":
|
||||||
// Having a Country is better than having none.
|
// Having a Country is better than having none.
|
||||||
return true
|
return true
|
||||||
|
case dl.Location.Coordinates.AccuracyRadius < other.Location.Coordinates.AccuracyRadius:
|
||||||
|
// Higher geo accuracy is better.
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dl *DeviceLocation) LocationOrNil() *geoip.Location {
|
||||||
|
if dl == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return dl.Location
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dl *DeviceLocation) String() string {
|
||||||
|
switch {
|
||||||
|
case dl == nil:
|
||||||
|
return "<none>"
|
||||||
|
case dl.Location == nil:
|
||||||
|
return dl.IP.String()
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%s (AS%d in %s)", dl.IP, dl.Location.AutonomousSystemNumber, dl.Location.Country.ISOCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type DeviceLocationSource string
|
type DeviceLocationSource string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -111,6 +156,12 @@ func (dls DeviceLocationSource) Accuracy() int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sortLocationsByAccuracy []*DeviceLocation
|
||||||
|
|
||||||
|
func (a sortLocationsByAccuracy) Len() int { return len(a) }
|
||||||
|
func (a sortLocationsByAccuracy) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a sortLocationsByAccuracy) Less(i, j int) bool { return a[j].IsMoreAccurateThan(a[i]) }
|
||||||
|
|
||||||
func SetInternetLocation(ip net.IP, source DeviceLocationSource) (ok bool) {
|
func SetInternetLocation(ip net.IP, source DeviceLocationSource) (ok bool) {
|
||||||
// Check if IP is global.
|
// Check if IP is global.
|
||||||
if netutils.GetIPScope(ip) != netutils.Global {
|
if netutils.GetIPScope(ip) != netutils.Global {
|
||||||
|
@ -123,39 +174,41 @@ func SetInternetLocation(ip net.IP, source DeviceLocationSource) (ok bool) {
|
||||||
Source: source,
|
Source: source,
|
||||||
SourceAccuracy: source.Accuracy(),
|
SourceAccuracy: source.Accuracy(),
|
||||||
}
|
}
|
||||||
|
if v4 := ip.To4(); v4 != nil {
|
||||||
|
loc.IPVersion = packet.IPv4
|
||||||
|
} else {
|
||||||
|
loc.IPVersion = packet.IPv6
|
||||||
|
}
|
||||||
|
|
||||||
// Get geoip information, but continue if it fails.
|
// Get geoip information, but continue if it fails.
|
||||||
geoLoc, err := geoip.GetLocation(ip)
|
geoLoc, err := geoip.GetLocation(ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("netenv: failed to get geolocation data of %s (from %s): %s", ip, source, err)
|
log.Warningf("netenv: failed to get geolocation data of %s (from %s): %s", ip, source, err)
|
||||||
} else {
|
} else {
|
||||||
loc.Continent = geoLoc.Continent.Code
|
loc.Location = geoLoc
|
||||||
loc.Country = geoLoc.Country.ISOCode
|
|
||||||
loc.ASN = geoLoc.AutonomousSystemNumber
|
|
||||||
loc.ASOrg = geoLoc.AutonomousSystemOrganization
|
|
||||||
}
|
}
|
||||||
|
|
||||||
locationsLock.Lock()
|
locationsLock.Lock()
|
||||||
defer locationsLock.Unlock()
|
defer locationsLock.Unlock()
|
||||||
|
|
||||||
// Add to locations, if better.
|
// Add to locations, if better.
|
||||||
key := loc.IP.String()
|
var exists bool
|
||||||
existing, ok := locations.All[key]
|
for i, existing := range locations.All {
|
||||||
if ok && existing.IsMoreAccurateThan(loc) {
|
if ip.Equal(existing.IP) {
|
||||||
// Existing entry is more accurate, abort adding.
|
exists = true
|
||||||
// Return true, because the IP address is already part of the locations.
|
if loc.IsMoreAccurateThan(existing) {
|
||||||
return true
|
// Replace
|
||||||
}
|
locations.All[i] = loc
|
||||||
locations.All[key] = loc
|
break
|
||||||
|
}
|
||||||
// Find best location.
|
|
||||||
best := loc
|
|
||||||
for _, dl := range locations.All {
|
|
||||||
if dl.IsMoreAccurateThan(best) {
|
|
||||||
best = dl
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
locations.Best = best
|
if !exists {
|
||||||
|
locations.All = append(locations.All, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort locations.
|
||||||
|
sort.Sort(sortLocationsByAccuracy(locations.All))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -163,10 +216,10 @@ func SetInternetLocation(ip net.IP, source DeviceLocationSource) (ok bool) {
|
||||||
// DEPRECATED: Please use GetInternetLocation instead.
|
// DEPRECATED: Please use GetInternetLocation instead.
|
||||||
func GetApproximateInternetLocation() (net.IP, error) {
|
func GetApproximateInternetLocation() (net.IP, error) {
|
||||||
loc, ok := GetInternetLocation()
|
loc, ok := GetInternetLocation()
|
||||||
if !ok {
|
if !ok || loc.Best() == nil {
|
||||||
return nil, errors.New("no location data available")
|
return nil, errors.New("no location data available")
|
||||||
}
|
}
|
||||||
return loc.Best.IP, nil
|
return loc.Best().IP, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) {
|
func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) {
|
||||||
|
@ -179,38 +232,54 @@ func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) {
|
||||||
}
|
}
|
||||||
locationNetworkChangedFlag.Refresh()
|
locationNetworkChangedFlag.Refresh()
|
||||||
|
|
||||||
// Check different sources, return on first success.
|
// Get all assigned addresses.
|
||||||
switch {
|
v4s, v6s, err := GetAssignedAddresses()
|
||||||
case getLocationFromInterfaces():
|
if err != nil {
|
||||||
case getLocationFromTraceroute():
|
log.Warningf("netenv: failed to get assigned addresses: %s", err)
|
||||||
default:
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check interfaces for global addresses.
|
||||||
|
v4ok, v6ok := getLocationFromInterfaces()
|
||||||
|
|
||||||
|
// Try other methods for missing locations.
|
||||||
|
if len(v4s) > 0 && !v4ok {
|
||||||
|
v4ok = getLocationFromTraceroute()
|
||||||
|
}
|
||||||
|
if len(v6s) > 0 && !v6ok {
|
||||||
|
// TODO
|
||||||
|
log.Warningf("netenv: could not get IPv6 location")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have any locations.
|
||||||
|
if !v4ok && !v6ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return gathered locations.
|
// Return gathered locations.
|
||||||
cp := copyDeviceLocations()
|
cp := copyDeviceLocations()
|
||||||
return cp, cp.Best != nil
|
return cp, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLocationFromInterfaces() (ok bool) {
|
func getLocationFromInterfaces() (v4ok, v6ok bool) {
|
||||||
globalIPv4, globalIPv6, err := GetAssignedGlobalAddresses()
|
globalIPv4, globalIPv6, err := GetAssignedGlobalAddresses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("netenv: location: failed to get assigned global addresses: %s", err)
|
log.Warningf("netenv: location: failed to get assigned global addresses: %s", err)
|
||||||
return false
|
return false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ip := range globalIPv4 {
|
for _, ip := range globalIPv4 {
|
||||||
if SetInternetLocation(ip, SourceInterface) {
|
if SetInternetLocation(ip, SourceInterface) {
|
||||||
ok = true
|
v4ok = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, ip := range globalIPv6 {
|
for _, ip := range globalIPv6 {
|
||||||
if SetInternetLocation(ip, SourceInterface) {
|
if SetInternetLocation(ip, SourceInterface) {
|
||||||
ok = true
|
v6ok = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ok
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check feasibility of getting the external IP via UPnP.
|
// TODO: Check feasibility of getting the external IP via UPnP.
|
||||||
|
@ -223,7 +292,7 @@ func getLocationFromUPnP() (ok bool) {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func getLocationFromTraceroute() (ok bool) {
|
func getLocationFromTraceroute() (v4ok bool) {
|
||||||
// Create connection.
|
// Create connection.
|
||||||
conn, err := net.ListenPacket("ip4:icmp", "")
|
conn, err := net.ListenPacket("ip4:icmp", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -502,10 +502,14 @@ func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey s
|
||||||
conn.Verdict = newVerdict
|
conn.Verdict = newVerdict
|
||||||
conn.Reason.Msg = reason
|
conn.Reason.Msg = reason
|
||||||
conn.Reason.Context = reasonCtx
|
conn.Reason.Context = reasonCtx
|
||||||
|
|
||||||
|
conn.Reason.Profile = ""
|
||||||
|
conn.Reason.OptionKey = ""
|
||||||
if reasonOptionKey != "" && conn.Process() != nil {
|
if reasonOptionKey != "" && conn.Process() != nil {
|
||||||
conn.Reason.OptionKey = reasonOptionKey
|
|
||||||
conn.Reason.Profile = conn.Process().Profile().GetProfileSource(conn.Reason.OptionKey)
|
conn.Reason.Profile = conn.Process().Profile().GetProfileSource(conn.Reason.OptionKey)
|
||||||
|
conn.Reason.OptionKey = reasonOptionKey
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -206,7 +206,7 @@ func (ipHelper *IPHelper) getTable(ipVersion, protocol uint8) (connections []*so
|
||||||
case winErrInsufficientBuffer:
|
case winErrInsufficientBuffer:
|
||||||
if i >= maxTries {
|
if i >= maxTries {
|
||||||
return nil, nil, fmt.Errorf(
|
return nil, nil, fmt.Errorf(
|
||||||
"insufficient buffer error (tried %d times): %s bytes required - [NT 0x%X] %s",
|
"insufficient buffer error (tried %d times): %d bytes required - [NT 0x%X] %s",
|
||||||
i, bufSize, r1, err,
|
i, bufSize, r1, err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,9 +109,9 @@ func registerConfiguration() error {
|
||||||
// ask - ask mode: if not verdict is found, the user is consulted
|
// ask - ask mode: if not verdict is found, the user is consulted
|
||||||
// block - allowlist mode: everything is blocked unless explicitly allowed
|
// block - allowlist mode: everything is blocked unless explicitly allowed
|
||||||
err := config.Register(&config.Option{
|
err := config.Register(&config.Option{
|
||||||
Name: "Default Action",
|
Name: "Default Network Action",
|
||||||
Key: CfgOptionDefaultActionKey,
|
Key: CfgOptionDefaultActionKey,
|
||||||
Description: `The default action when nothing else allows or blocks an outgoing connection. Incoming connections are always blocked by default.`,
|
Description: `The default network action is applied when nothing else allows or blocks an outgoing connection. Incoming connections are always blocked by default.`,
|
||||||
OptType: config.OptTypeString,
|
OptType: config.OptTypeString,
|
||||||
DefaultValue: "permit",
|
DefaultValue: "permit",
|
||||||
Annotations: config.Annotations{
|
Annotations: config.Annotations{
|
||||||
|
@ -169,6 +169,7 @@ func registerConfiguration() error {
|
||||||
|
|
||||||
- By address: "192.168.0.1"
|
- By address: "192.168.0.1"
|
||||||
- By network: "192.168.0.1/24"
|
- By network: "192.168.0.1/24"
|
||||||
|
- By network scope: "Localhost", "LAN" or "Internet"
|
||||||
- By domain:
|
- By domain:
|
||||||
- Matching a distinct domain: "example.com"
|
- Matching a distinct domain: "example.com"
|
||||||
- Matching a domain with subdomains: ".example.com"
|
- Matching a domain with subdomains: ".example.com"
|
||||||
|
@ -176,6 +177,7 @@ func registerConfiguration() error {
|
||||||
- Matching with a wildcard suffix: "example.*"
|
- Matching with a wildcard suffix: "example.*"
|
||||||
- Matching domains containing text: "*example*"
|
- Matching domains containing text: "*example*"
|
||||||
- By country (based on IP): "US"
|
- By country (based on IP): "US"
|
||||||
|
- By AS number: "AS123456"
|
||||||
- By filter list - use the filterlist ID prefixed with "L:": "L:MAL"
|
- By filter list - use the filterlist ID prefixed with "L:": "L:MAL"
|
||||||
- Match anything: "*"
|
- Match anything: "*"
|
||||||
|
|
||||||
|
@ -333,9 +335,9 @@ The lists are automatically updated every hour using incremental updates.
|
||||||
|
|
||||||
// Block Scope Local
|
// Block Scope Local
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block Device-Local Connections",
|
Name: "Force Block Device-Local Connections",
|
||||||
Key: CfgOptionBlockScopeLocalKey,
|
Key: CfgOptionBlockScopeLocalKey,
|
||||||
Description: "Block all internal connections on your own device, ie. localhost. Is stronger than Rules (see below).",
|
Description: "Force Block all internal connections on your own device, ie. localhost. Is stronger than Rules (see below).",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
DefaultValue: status.SecurityLevelOff,
|
DefaultValue: status.SecurityLevelOff,
|
||||||
|
@ -354,9 +356,9 @@ The lists are automatically updated every hour using incremental updates.
|
||||||
|
|
||||||
// Block Scope LAN
|
// Block Scope LAN
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block LAN",
|
Name: "Force Block LAN",
|
||||||
Key: CfgOptionBlockScopeLANKey,
|
Key: CfgOptionBlockScopeLANKey,
|
||||||
Description: "Block all connections from and to the Local Area Network. Is stronger than Rules (see below).",
|
Description: "Force Block all connections from and to the Local Area Network. Is stronger than Rules (see below).",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
DefaultValue: status.SecurityLevelsHighAndExtreme,
|
DefaultValue: status.SecurityLevelsHighAndExtreme,
|
||||||
PossibleValues: status.AllSecurityLevelValues,
|
PossibleValues: status.AllSecurityLevelValues,
|
||||||
|
@ -374,9 +376,9 @@ The lists are automatically updated every hour using incremental updates.
|
||||||
|
|
||||||
// Block Scope Internet
|
// Block Scope Internet
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block Internet Access",
|
Name: "Force Block Internet Access",
|
||||||
Key: CfgOptionBlockScopeInternetKey,
|
Key: CfgOptionBlockScopeInternetKey,
|
||||||
Description: "Block connections from and to the Internet. Is stronger than Rules (see below).",
|
Description: "Force Block connections from and to the Internet. Is stronger than Rules (see below).",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
DefaultValue: status.SecurityLevelOff,
|
DefaultValue: status.SecurityLevelOff,
|
||||||
PossibleValues: status.AllSecurityLevelValues,
|
PossibleValues: status.AllSecurityLevelValues,
|
||||||
|
@ -394,7 +396,7 @@ The lists are automatically updated every hour using incremental updates.
|
||||||
|
|
||||||
// Block Peer to Peer Connections
|
// Block Peer to Peer Connections
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block P2P/Direct Connections",
|
Name: "Force Block P2P/Direct Connections",
|
||||||
Key: CfgOptionBlockP2PKey,
|
Key: CfgOptionBlockP2PKey,
|
||||||
Description: "These are connections that are established directly to an IP address or peer on the Internet without resolving a domain name via DNS first. Is stronger than Rules (see below).",
|
Description: "These are connections that are established directly to an IP address or peer on the Internet without resolving a domain name via DNS first. Is stronger than Rules (see below).",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
|
@ -414,7 +416,7 @@ The lists are automatically updated every hour using incremental updates.
|
||||||
|
|
||||||
// Block Inbound Connections
|
// Block Inbound Connections
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block Incoming Connections",
|
Name: "Force Block Incoming Connections",
|
||||||
Key: CfgOptionBlockInboundKey,
|
Key: CfgOptionBlockInboundKey,
|
||||||
Description: "Connections initiated towards your device from the LAN or Internet. This will usually only be the case if you are running a network service or are using peer to peer software. Is stronger than Rules (see below).",
|
Description: "Connections initiated towards your device from the LAN or Internet. This will usually only be the case if you are running a network service or are using peer to peer software. Is stronger than Rules (see below).",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
|
|
|
@ -32,6 +32,8 @@ var (
|
||||||
ErrFailure = errors.New("query failed")
|
ErrFailure = errors.New("query failed")
|
||||||
// ErrContinue is returned when the resolver has no answer, and the next resolver should be asked
|
// ErrContinue is returned when the resolver has no answer, and the next resolver should be asked
|
||||||
ErrContinue = errors.New("resolver has no answer")
|
ErrContinue = errors.New("resolver has no answer")
|
||||||
|
// ErrShuttingDown is returned when the resolver is shutting down.
|
||||||
|
ErrShuttingDown = errors.New("resolver is shutting down")
|
||||||
|
|
||||||
// detailed errors
|
// detailed errors
|
||||||
|
|
||||||
|
@ -275,6 +277,8 @@ retry:
|
||||||
case <-time.After(maxRequestTimeout):
|
case <-time.After(maxRequestTimeout):
|
||||||
// something went wrong with the query, retry
|
// something went wrong with the query, retry
|
||||||
goto retry
|
goto retry
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// but that someone is taking too long
|
// but that someone is taking too long
|
||||||
|
@ -331,7 +335,7 @@ resolveLoop:
|
||||||
for i = 0; i < 2; i++ {
|
for i = 0; i < 2; i++ {
|
||||||
for _, resolver := range resolvers {
|
for _, resolver := range resolvers {
|
||||||
if module.IsStopping() {
|
if module.IsStopping() {
|
||||||
return nil, errors.New("shutting down")
|
return nil, ErrShuttingDown
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if resolver failed recently (on first run)
|
// check if resolver failed recently (on first run)
|
||||||
|
@ -364,6 +368,12 @@ resolveLoop:
|
||||||
resolver.Conn.ReportFailure()
|
resolver.Conn.ReportFailure()
|
||||||
log.Tracer(ctx).Debugf("resolver: query to %s timed out", resolver.Info.ID())
|
log.Tracer(ctx).Debugf("resolver: query to %s timed out", resolver.Info.ID())
|
||||||
continue
|
continue
|
||||||
|
case errors.Is(err, context.Canceled):
|
||||||
|
return nil, err
|
||||||
|
case errors.Is(err, context.DeadlineExceeded):
|
||||||
|
return nil, err
|
||||||
|
case errors.Is(err, ErrShuttingDown):
|
||||||
|
return nil, err
|
||||||
default:
|
default:
|
||||||
resolver.Conn.ReportFailure()
|
resolver.Conn.ReportFailure()
|
||||||
log.Tracer(ctx).Debugf("resolver: query to %s failed: %s", resolver.Info.ID(), err)
|
log.Tracer(ctx).Debugf("resolver: query to %s failed: %s", resolver.Info.ID(), err)
|
||||||
|
|
|
@ -418,6 +418,8 @@ func queryMulticastDNS(ctx context.Context, q *Query) (*RRCache, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rrCache, nil
|
return rrCache, nil
|
||||||
}
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond with NXDomain.
|
// Respond with NXDomain.
|
||||||
|
|
|
@ -105,7 +105,7 @@ func (tr *TCPResolver) UseTLS() *TCPResolver {
|
||||||
return tr
|
return tr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *TCPResolver) getOrCreateResolverConn() (*tcpResolverConn, error) {
|
func (tr *TCPResolver) getOrCreateResolverConn(ctx context.Context) (*tcpResolverConn, error) {
|
||||||
tr.Lock()
|
tr.Lock()
|
||||||
defer tr.Unlock()
|
defer tr.Unlock()
|
||||||
|
|
||||||
|
@ -117,6 +117,10 @@ func (tr *TCPResolver) getOrCreateResolverConn() (*tcpResolverConn, error) {
|
||||||
return tr.resolverConn, nil
|
return tr.resolverConn, nil
|
||||||
case <-time.After(heartbeatTimeout):
|
case <-time.After(heartbeatTimeout):
|
||||||
log.Warningf("resolver: heartbeat for dns client %s failed", tr.resolver.Info.DescriptiveName())
|
log.Warningf("resolver: heartbeat for dns client %s failed", tr.resolver.Info.DescriptiveName())
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-module.Stopping():
|
||||||
|
return nil, ErrShuttingDown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +134,6 @@ func (tr *TCPResolver) getOrCreateResolverConn() (*tcpResolverConn, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to server.
|
// Connect to server.
|
||||||
var err error
|
|
||||||
conn, err := tr.dnsClient.Dial(tr.resolver.ServerAddress)
|
conn, err := tr.dnsClient.Dial(tr.resolver.ServerAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("resolver: failed to connect to %s", tr.resolver.Info.DescriptiveName())
|
log.Debugf("resolver: failed to connect to %s", tr.resolver.Info.DescriptiveName())
|
||||||
|
@ -171,7 +174,7 @@ func (tr *TCPResolver) getOrCreateResolverConn() (*tcpResolverConn, error) {
|
||||||
// Query executes the given query against the resolver.
|
// Query executes the given query against the resolver.
|
||||||
func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) {
|
func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) {
|
||||||
// Get resolver connection.
|
// Get resolver connection.
|
||||||
resolverConn, err := tr.getOrCreateResolverConn()
|
resolverConn, err := tr.getOrCreateResolverConn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -185,6 +188,10 @@ func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) {
|
||||||
// Submit query request to live connection.
|
// Submit query request to live connection.
|
||||||
select {
|
select {
|
||||||
case resolverConn.queries <- tq:
|
case resolverConn.queries <- tq:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-module.Stopping():
|
||||||
|
return nil, ErrShuttingDown
|
||||||
case <-time.After(defaultRequestTimeout):
|
case <-time.After(defaultRequestTimeout):
|
||||||
return nil, ErrTimeout
|
return nil, ErrTimeout
|
||||||
}
|
}
|
||||||
|
@ -193,6 +200,10 @@ func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) {
|
||||||
var reply *dns.Msg
|
var reply *dns.Msg
|
||||||
select {
|
select {
|
||||||
case reply = <-tq.Response:
|
case reply = <-tq.Response:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-module.Stopping():
|
||||||
|
return nil, ErrShuttingDown
|
||||||
case <-time.After(defaultRequestTimeout):
|
case <-time.After(defaultRequestTimeout):
|
||||||
return nil, ErrTimeout
|
return nil, ErrTimeout
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,18 @@ package helper
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/tevino/abool"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const onWindows = runtime.GOOS == "windows"
|
||||||
onWindows = runtime.GOOS == "windows"
|
|
||||||
)
|
var intelOnly = abool.New()
|
||||||
|
|
||||||
|
// IntelOnly specifies that only intel data is mandatory.
|
||||||
|
func IntelOnly() {
|
||||||
|
intelOnly.Set()
|
||||||
|
}
|
||||||
|
|
||||||
// PlatformIdentifier converts identifier for the current platform.
|
// PlatformIdentifier converts identifier for the current platform.
|
||||||
func PlatformIdentifier(identifier string) string {
|
func PlatformIdentifier(identifier string) string {
|
||||||
|
@ -20,33 +27,10 @@ func PlatformIdentifier(identifier string) string {
|
||||||
// MandatoryUpdates returns mandatory updates that should be loaded on install
|
// MandatoryUpdates returns mandatory updates that should be loaded on install
|
||||||
// or reset.
|
// or reset.
|
||||||
func MandatoryUpdates() (identifiers []string) {
|
func MandatoryUpdates() (identifiers []string) {
|
||||||
// Binaries
|
// Intel
|
||||||
if onWindows {
|
|
||||||
identifiers = []string{
|
|
||||||
PlatformIdentifier("core/portmaster-core.exe"),
|
|
||||||
PlatformIdentifier("kext/portmaster-kext.dll"),
|
|
||||||
PlatformIdentifier("kext/portmaster-kext.sys"),
|
|
||||||
PlatformIdentifier("start/portmaster-start.exe"),
|
|
||||||
PlatformIdentifier("notifier/portmaster-notifier.exe"),
|
|
||||||
PlatformIdentifier("notifier/portmaster-snoretoast.exe"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
identifiers = []string{
|
|
||||||
PlatformIdentifier("core/portmaster-core"),
|
|
||||||
PlatformIdentifier("start/portmaster-start"),
|
|
||||||
PlatformIdentifier("notifier/portmaster-notifier"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Components, Assets and Data
|
|
||||||
identifiers = append(
|
identifiers = append(
|
||||||
identifiers,
|
identifiers,
|
||||||
|
|
||||||
// User interface components
|
|
||||||
PlatformIdentifier("app/portmaster-app.zip"),
|
|
||||||
"all/ui/modules/portmaster.zip",
|
|
||||||
"all/ui/modules/assets.zip",
|
|
||||||
|
|
||||||
// Filter lists data
|
// Filter lists data
|
||||||
"all/intel/lists/index.dsd",
|
"all/intel/lists/index.dsd",
|
||||||
"all/intel/lists/base.dsdl",
|
"all/intel/lists/base.dsdl",
|
||||||
|
@ -58,11 +42,50 @@ func MandatoryUpdates() (identifiers []string) {
|
||||||
"all/intel/geoip/geoipv6.mmdb.gz",
|
"all/intel/geoip/geoipv6.mmdb.gz",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Stop here if we only want intel data.
|
||||||
|
if intelOnly.IsSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binaries
|
||||||
|
if onWindows {
|
||||||
|
identifiers = append(
|
||||||
|
identifiers,
|
||||||
|
PlatformIdentifier("core/portmaster-core.exe"),
|
||||||
|
PlatformIdentifier("kext/portmaster-kext.dll"),
|
||||||
|
PlatformIdentifier("kext/portmaster-kext.sys"),
|
||||||
|
PlatformIdentifier("start/portmaster-start.exe"),
|
||||||
|
PlatformIdentifier("notifier/portmaster-notifier.exe"),
|
||||||
|
PlatformIdentifier("notifier/portmaster-snoretoast.exe"),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
identifiers = append(
|
||||||
|
identifiers,
|
||||||
|
PlatformIdentifier("core/portmaster-core"),
|
||||||
|
PlatformIdentifier("start/portmaster-start"),
|
||||||
|
PlatformIdentifier("notifier/portmaster-notifier"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Components, Assets and Data
|
||||||
|
identifiers = append(
|
||||||
|
identifiers,
|
||||||
|
|
||||||
|
// User interface components
|
||||||
|
PlatformIdentifier("app/portmaster-app.zip"),
|
||||||
|
"all/ui/modules/portmaster.zip",
|
||||||
|
"all/ui/modules/assets.zip",
|
||||||
|
)
|
||||||
|
|
||||||
return identifiers
|
return identifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoUnpackUpdates returns assets that need unpacking.
|
// AutoUnpackUpdates returns assets that need unpacking.
|
||||||
func AutoUnpackUpdates() []string {
|
func AutoUnpackUpdates() []string {
|
||||||
|
if intelOnly.IsSet() {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
return []string{
|
return []string{
|
||||||
PlatformIdentifier("app/portmaster-app.zip"),
|
PlatformIdentifier("app/portmaster-app.zip"),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue