mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +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 (
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/modules/subsystems"
|
||||
"github.com/safing/spn/captain"
|
||||
|
||||
"github.com/safing/portbase/modules"
|
||||
|
||||
|
@ -13,6 +14,7 @@ import (
|
|||
var (
|
||||
filterModule *modules.Module
|
||||
filterEnabled config.BoolOption
|
||||
tunnelEnabled config.BoolOption
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -28,8 +30,8 @@ func init() {
|
|||
Key: CfgOptionEnableFilterKey,
|
||||
Description: "Start the Privacy Filter module. If turned off, all privacy filter protections are fully disabled on this device.",
|
||||
OptType: config.OptTypeBool,
|
||||
ExpertiseLevel: config.ExpertiseLevelUser,
|
||||
ReleaseLevel: config.ReleaseLevelBeta,
|
||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
DefaultValue: true,
|
||||
Annotations: config.Annotations{
|
||||
config.CategoryAnnotation: "General",
|
||||
|
@ -44,6 +46,7 @@ func filterPrep() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
filterEnabled = config.GetAsBool(CfgOptionEnableFilterKey, true)
|
||||
filterEnabled = config.Concurrent.GetAsBool(CfgOptionEnableFilterKey, true)
|
||||
tunnelEnabled = config.Concurrent.GetAsBool(captain.CfgOptionEnableSPNKey, false)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/spn/captain"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/safing/portmaster/netenv"
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
@ -22,7 +24,7 @@ import (
|
|||
"github.com/safing/portmaster/network"
|
||||
"github.com/safing/portmaster/network/netutils"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
"github.com/safing/spn/captain"
|
||||
"github.com/safing/spn/crew"
|
||||
"github.com/safing/spn/sluice"
|
||||
|
||||
// module dependencies
|
||||
|
@ -346,32 +348,41 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
|||
return
|
||||
}
|
||||
|
||||
// check if filtering is enabled
|
||||
if !filterEnabled() {
|
||||
conn.Inspecting = false
|
||||
// TODO: enable inspecting again
|
||||
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.StopFirewallHandler()
|
||||
issueVerdict(conn, pkt, 0, true)
|
||||
return
|
||||
}
|
||||
|
||||
log.Tracer(pkt.Ctx()).Trace("filter: starting decision process")
|
||||
DecideOnConnection(pkt.Ctx(), conn, pkt)
|
||||
conn.Inspecting = false // TODO: enable inspecting again
|
||||
// Tunnel, if enabled.
|
||||
if pkt.IsOutbound() && conn.Entity.IPScope.IsGlobal() &&
|
||||
tunnelEnabled() && conn.Verdict == network.VerdictAccept &&
|
||||
conn.Process().Profile() != nil &&
|
||||
conn.Process().Profile().UseSPN() {
|
||||
|
||||
// tunneling
|
||||
// TODO: add implementation for forced tunneling
|
||||
if pkt.IsOutbound() &&
|
||||
captain.ClientReady() &&
|
||||
conn.Entity.IPScope.IsGlobal() &&
|
||||
conn.Verdict == network.VerdictAccept {
|
||||
// try to tunnel
|
||||
err := sluice.AwaitRequest(pkt.Info(), conn.Entity.Domain)
|
||||
if err != nil {
|
||||
log.Tracer(pkt.Ctx()).Tracef("filter: not tunneling: %s", err)
|
||||
} else {
|
||||
log.Tracer(pkt.Ctx()).Trace("filter: tunneling request")
|
||||
conn.Verdict = network.VerdictRerouteToTunnel
|
||||
// Exclude requests of the SPN itself.
|
||||
if !captain.IsExcepted(conn.Entity.IP) {
|
||||
// Check if client is ready.
|
||||
if captain.ClientReady() {
|
||||
// Queue request in sluice.
|
||||
err := sluice.AwaitRequest(conn, crew.HandleSluiceRequest)
|
||||
if err != nil {
|
||||
log.Tracer(pkt.Ctx()).Warningf("failed to rqeuest tunneling: %s", err)
|
||||
conn.Failed("failed to request tunneling", "")
|
||||
} else {
|
||||
log.Tracer(pkt.Ctx()).Trace("filter: tunneling requested")
|
||||
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
|
||||
}
|
||||
|
||||
log.Debugf("intel/filterlists: searching for entries with %s", key)
|
||||
// log.Debugf("intel/filterlists: searching for entries with %s", key)
|
||||
entry, err := getEntityRecordByKey(key)
|
||||
if err != nil {
|
||||
if err == database.ErrNotFound {
|
||||
|
|
|
@ -2,7 +2,9 @@ package netenv
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -22,9 +24,7 @@ var (
|
|||
locationTestingIPv4 = "1.1.1.1"
|
||||
locationTestingIPv4Addr *net.IPAddr
|
||||
|
||||
locations = &DeviceLocations{
|
||||
All: make(map[string]*DeviceLocation),
|
||||
}
|
||||
locations = &DeviceLocations{}
|
||||
locationsLock sync.Mutex
|
||||
gettingLocationsLock sync.Mutex
|
||||
locationNetworkChangedFlag = GetNetworkChangedFlag()
|
||||
|
@ -36,8 +36,32 @@ func prepLocation() (err error) {
|
|||
}
|
||||
|
||||
type DeviceLocations struct {
|
||||
Best *DeviceLocation
|
||||
All map[string]*DeviceLocation
|
||||
All []*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 {
|
||||
|
@ -45,23 +69,20 @@ func copyDeviceLocations() *DeviceLocations {
|
|||
defer locationsLock.Unlock()
|
||||
|
||||
// Create a copy of the locations, but not the entries.
|
||||
cp := *locations
|
||||
cp.All = make(map[string]*DeviceLocation, len(locations.All))
|
||||
for k, v := range locations.All {
|
||||
cp.All[k] = v
|
||||
cp := &DeviceLocations{
|
||||
All: make([]*DeviceLocation, len(locations.All)),
|
||||
}
|
||||
copy(cp.All, locations.All)
|
||||
|
||||
return &cp
|
||||
return cp
|
||||
}
|
||||
|
||||
// DeviceLocation represents a single IP and metadata. It must not be changed
|
||||
// once created.
|
||||
type DeviceLocation struct {
|
||||
IP net.IP
|
||||
Continent string
|
||||
Country string
|
||||
ASN uint
|
||||
ASOrg string
|
||||
IPVersion packet.IPVersion
|
||||
Location *geoip.Location
|
||||
Source DeviceLocationSource
|
||||
SourceAccuracy int
|
||||
}
|
||||
|
@ -71,19 +92,43 @@ type DeviceLocation struct {
|
|||
func (dl *DeviceLocation) IsMoreAccurateThan(other *DeviceLocation) bool {
|
||||
switch {
|
||||
case dl.SourceAccuracy > other.SourceAccuracy:
|
||||
// Higher accuracy is better.
|
||||
// Higher source accuracy is better.
|
||||
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.
|
||||
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.
|
||||
return true
|
||||
case dl.Location.Coordinates.AccuracyRadius < other.Location.Coordinates.AccuracyRadius:
|
||||
// Higher geo accuracy is better.
|
||||
return true
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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) {
|
||||
// Check if IP is global.
|
||||
if netutils.GetIPScope(ip) != netutils.Global {
|
||||
|
@ -123,39 +174,41 @@ func SetInternetLocation(ip net.IP, source DeviceLocationSource) (ok bool) {
|
|||
Source: source,
|
||||
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.
|
||||
geoLoc, err := geoip.GetLocation(ip)
|
||||
if err != nil {
|
||||
log.Warningf("netenv: failed to get geolocation data of %s (from %s): %s", ip, source, err)
|
||||
} else {
|
||||
loc.Continent = geoLoc.Continent.Code
|
||||
loc.Country = geoLoc.Country.ISOCode
|
||||
loc.ASN = geoLoc.AutonomousSystemNumber
|
||||
loc.ASOrg = geoLoc.AutonomousSystemOrganization
|
||||
loc.Location = geoLoc
|
||||
}
|
||||
|
||||
locationsLock.Lock()
|
||||
defer locationsLock.Unlock()
|
||||
|
||||
// Add to locations, if better.
|
||||
key := loc.IP.String()
|
||||
existing, ok := locations.All[key]
|
||||
if ok && existing.IsMoreAccurateThan(loc) {
|
||||
// Existing entry is more accurate, abort adding.
|
||||
// Return true, because the IP address is already part of the locations.
|
||||
return true
|
||||
}
|
||||
locations.All[key] = loc
|
||||
|
||||
// Find best location.
|
||||
best := loc
|
||||
for _, dl := range locations.All {
|
||||
if dl.IsMoreAccurateThan(best) {
|
||||
best = dl
|
||||
var exists bool
|
||||
for i, existing := range locations.All {
|
||||
if ip.Equal(existing.IP) {
|
||||
exists = true
|
||||
if loc.IsMoreAccurateThan(existing) {
|
||||
// Replace
|
||||
locations.All[i] = loc
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
locations.Best = best
|
||||
if !exists {
|
||||
locations.All = append(locations.All, loc)
|
||||
}
|
||||
|
||||
// Sort locations.
|
||||
sort.Sort(sortLocationsByAccuracy(locations.All))
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -163,10 +216,10 @@ func SetInternetLocation(ip net.IP, source DeviceLocationSource) (ok bool) {
|
|||
// DEPRECATED: Please use GetInternetLocation instead.
|
||||
func GetApproximateInternetLocation() (net.IP, error) {
|
||||
loc, ok := GetInternetLocation()
|
||||
if !ok {
|
||||
if !ok || loc.Best() == nil {
|
||||
return nil, errors.New("no location data available")
|
||||
}
|
||||
return loc.Best.IP, nil
|
||||
return loc.Best().IP, nil
|
||||
}
|
||||
|
||||
func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) {
|
||||
|
@ -179,38 +232,54 @@ func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) {
|
|||
}
|
||||
locationNetworkChangedFlag.Refresh()
|
||||
|
||||
// Check different sources, return on first success.
|
||||
switch {
|
||||
case getLocationFromInterfaces():
|
||||
case getLocationFromTraceroute():
|
||||
default:
|
||||
// Get all assigned addresses.
|
||||
v4s, v6s, err := GetAssignedAddresses()
|
||||
if err != nil {
|
||||
log.Warningf("netenv: failed to get assigned addresses: %s", err)
|
||||
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 gathered locations.
|
||||
cp := copyDeviceLocations()
|
||||
return cp, cp.Best != nil
|
||||
return cp, true
|
||||
}
|
||||
|
||||
func getLocationFromInterfaces() (ok bool) {
|
||||
func getLocationFromInterfaces() (v4ok, v6ok bool) {
|
||||
globalIPv4, globalIPv6, err := GetAssignedGlobalAddresses()
|
||||
if err != nil {
|
||||
log.Warningf("netenv: location: failed to get assigned global addresses: %s", err)
|
||||
return false
|
||||
return false, false
|
||||
}
|
||||
|
||||
for _, ip := range globalIPv4 {
|
||||
if SetInternetLocation(ip, SourceInterface) {
|
||||
ok = true
|
||||
v4ok = true
|
||||
}
|
||||
}
|
||||
for _, ip := range globalIPv6 {
|
||||
if SetInternetLocation(ip, SourceInterface) {
|
||||
ok = true
|
||||
v6ok = true
|
||||
}
|
||||
}
|
||||
|
||||
return ok
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
conn, err := net.ListenPacket("ip4:icmp", "")
|
||||
if err != nil {
|
||||
|
|
|
@ -502,10 +502,14 @@ func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey s
|
|||
conn.Verdict = newVerdict
|
||||
conn.Reason.Msg = reason
|
||||
conn.Reason.Context = reasonCtx
|
||||
|
||||
conn.Reason.Profile = ""
|
||||
conn.Reason.OptionKey = ""
|
||||
if reasonOptionKey != "" && conn.Process() != nil {
|
||||
conn.Reason.OptionKey = reasonOptionKey
|
||||
conn.Reason.Profile = conn.Process().Profile().GetProfileSource(conn.Reason.OptionKey)
|
||||
conn.Reason.OptionKey = reasonOptionKey
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -206,7 +206,7 @@ func (ipHelper *IPHelper) getTable(ipVersion, protocol uint8) (connections []*so
|
|||
case winErrInsufficientBuffer:
|
||||
if i >= maxTries {
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -109,9 +109,9 @@ func registerConfiguration() error {
|
|||
// ask - ask mode: if not verdict is found, the user is consulted
|
||||
// block - allowlist mode: everything is blocked unless explicitly allowed
|
||||
err := config.Register(&config.Option{
|
||||
Name: "Default Action",
|
||||
Name: "Default Network Action",
|
||||
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,
|
||||
DefaultValue: "permit",
|
||||
Annotations: config.Annotations{
|
||||
|
@ -169,6 +169,7 @@ func registerConfiguration() error {
|
|||
|
||||
- By address: "192.168.0.1"
|
||||
- By network: "192.168.0.1/24"
|
||||
- By network scope: "Localhost", "LAN" or "Internet"
|
||||
- By domain:
|
||||
- Matching a distinct domain: "example.com"
|
||||
- Matching a domain with subdomains: ".example.com"
|
||||
|
@ -176,6 +177,7 @@ func registerConfiguration() error {
|
|||
- Matching with a wildcard suffix: "example.*"
|
||||
- Matching domains containing text: "*example*"
|
||||
- By country (based on IP): "US"
|
||||
- By AS number: "AS123456"
|
||||
- By filter list - use the filterlist ID prefixed with "L:": "L:MAL"
|
||||
- Match anything: "*"
|
||||
|
||||
|
@ -333,9 +335,9 @@ The lists are automatically updated every hour using incremental updates.
|
|||
|
||||
// Block Scope Local
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Block Device-Local Connections",
|
||||
Name: "Force Block Device-Local Connections",
|
||||
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,
|
||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
DefaultValue: status.SecurityLevelOff,
|
||||
|
@ -354,9 +356,9 @@ The lists are automatically updated every hour using incremental updates.
|
|||
|
||||
// Block Scope LAN
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Block LAN",
|
||||
Name: "Force Block LAN",
|
||||
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,
|
||||
DefaultValue: status.SecurityLevelsHighAndExtreme,
|
||||
PossibleValues: status.AllSecurityLevelValues,
|
||||
|
@ -374,9 +376,9 @@ The lists are automatically updated every hour using incremental updates.
|
|||
|
||||
// Block Scope Internet
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Block Internet Access",
|
||||
Name: "Force Block Internet Access",
|
||||
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,
|
||||
DefaultValue: status.SecurityLevelOff,
|
||||
PossibleValues: status.AllSecurityLevelValues,
|
||||
|
@ -394,7 +396,7 @@ The lists are automatically updated every hour using incremental updates.
|
|||
|
||||
// Block Peer to Peer Connections
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Block P2P/Direct Connections",
|
||||
Name: "Force Block P2P/Direct Connections",
|
||||
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).",
|
||||
OptType: config.OptTypeInt,
|
||||
|
@ -414,7 +416,7 @@ The lists are automatically updated every hour using incremental updates.
|
|||
|
||||
// Block Inbound Connections
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Block Incoming Connections",
|
||||
Name: "Force Block Incoming Connections",
|
||||
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).",
|
||||
OptType: config.OptTypeInt,
|
||||
|
|
|
@ -32,6 +32,8 @@ var (
|
|||
ErrFailure = errors.New("query failed")
|
||||
// ErrContinue is returned when the resolver has no answer, and the next resolver should be asked
|
||||
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
|
||||
|
||||
|
@ -275,6 +277,8 @@ retry:
|
|||
case <-time.After(maxRequestTimeout):
|
||||
// something went wrong with the query, retry
|
||||
goto retry
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// but that someone is taking too long
|
||||
|
@ -331,7 +335,7 @@ resolveLoop:
|
|||
for i = 0; i < 2; i++ {
|
||||
for _, resolver := range resolvers {
|
||||
if module.IsStopping() {
|
||||
return nil, errors.New("shutting down")
|
||||
return nil, ErrShuttingDown
|
||||
}
|
||||
|
||||
// check if resolver failed recently (on first run)
|
||||
|
@ -364,6 +368,12 @@ resolveLoop:
|
|||
resolver.Conn.ReportFailure()
|
||||
log.Tracer(ctx).Debugf("resolver: query to %s timed out", resolver.Info.ID())
|
||||
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:
|
||||
resolver.Conn.ReportFailure()
|
||||
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 {
|
||||
return rrCache, nil
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
// Respond with NXDomain.
|
||||
|
|
|
@ -105,7 +105,7 @@ func (tr *TCPResolver) UseTLS() *TCPResolver {
|
|||
return tr
|
||||
}
|
||||
|
||||
func (tr *TCPResolver) getOrCreateResolverConn() (*tcpResolverConn, error) {
|
||||
func (tr *TCPResolver) getOrCreateResolverConn(ctx context.Context) (*tcpResolverConn, error) {
|
||||
tr.Lock()
|
||||
defer tr.Unlock()
|
||||
|
||||
|
@ -117,6 +117,10 @@ func (tr *TCPResolver) getOrCreateResolverConn() (*tcpResolverConn, error) {
|
|||
return tr.resolverConn, nil
|
||||
case <-time.After(heartbeatTimeout):
|
||||
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.
|
||||
var err error
|
||||
conn, err := tr.dnsClient.Dial(tr.resolver.ServerAddress)
|
||||
if err != nil {
|
||||
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.
|
||||
func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) {
|
||||
// Get resolver connection.
|
||||
resolverConn, err := tr.getOrCreateResolverConn()
|
||||
resolverConn, err := tr.getOrCreateResolverConn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -185,6 +188,10 @@ func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) {
|
|||
// Submit query request to live connection.
|
||||
select {
|
||||
case resolverConn.queries <- tq:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-module.Stopping():
|
||||
return nil, ErrShuttingDown
|
||||
case <-time.After(defaultRequestTimeout):
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
|
@ -193,6 +200,10 @@ func (tr *TCPResolver) Query(ctx context.Context, q *Query) (*RRCache, error) {
|
|||
var reply *dns.Msg
|
||||
select {
|
||||
case reply = <-tq.Response:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-module.Stopping():
|
||||
return nil, ErrShuttingDown
|
||||
case <-time.After(defaultRequestTimeout):
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
|
|
|
@ -3,11 +3,18 @@ package helper
|
|||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
const (
|
||||
onWindows = runtime.GOOS == "windows"
|
||||
)
|
||||
const 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.
|
||||
func PlatformIdentifier(identifier string) string {
|
||||
|
@ -20,33 +27,10 @@ func PlatformIdentifier(identifier string) string {
|
|||
// MandatoryUpdates returns mandatory updates that should be loaded on install
|
||||
// or reset.
|
||||
func MandatoryUpdates() (identifiers []string) {
|
||||
// Binaries
|
||||
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
|
||||
// Intel
|
||||
identifiers = append(
|
||||
identifiers,
|
||||
|
||||
// User interface components
|
||||
PlatformIdentifier("app/portmaster-app.zip"),
|
||||
"all/ui/modules/portmaster.zip",
|
||||
"all/ui/modules/assets.zip",
|
||||
|
||||
// Filter lists data
|
||||
"all/intel/lists/index.dsd",
|
||||
"all/intel/lists/base.dsdl",
|
||||
|
@ -58,11 +42,50 @@ func MandatoryUpdates() (identifiers []string) {
|
|||
"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
|
||||
}
|
||||
|
||||
// AutoUnpackUpdates returns assets that need unpacking.
|
||||
func AutoUnpackUpdates() []string {
|
||||
if intelOnly.IsSet() {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
return []string{
|
||||
PlatformIdentifier("app/portmaster-app.zip"),
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue