Merge pull request #575 from safing/feature/debug-data-improvements

Improve debug data
This commit is contained in:
Daniel 2022-03-22 15:08:46 +01:00 committed by GitHub
commit 010d9a97a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 56997 additions and 15 deletions

10
compat/debug_default.go Normal file
View file

@ -0,0 +1,10 @@
//go:build !windows && !linux
package compat
import "github.com/safing/portbase/utils/debug"
// AddToDebugInfo adds compatibility data to the given debug.Info.
func AddToDebugInfo(di *debug.Info) {
// Not yet implemented on this platform.
}

28
compat/debug_linux.go Normal file
View file

@ -0,0 +1,28 @@
package compat
import (
"fmt"
"github.com/safing/portbase/utils/debug"
)
// AddToDebugInfo adds compatibility data to the given debug.Info.
func AddToDebugInfo(di *debug.Info) {
// Get iptables state and add error info if it fails.
chains, err := GetIPTablesChains()
if err != nil {
di.AddSection(
"Compatibility: IPTables Chains (failed)",
debug.UseCodeSection,
err.Error(),
)
return
}
// Add data as section.
di.AddSection(
fmt.Sprintf("Compatibility: IPTables Chains (%d)", len(chains)-10),
debug.UseCodeSection|debug.AddContentLineBreaks,
chains...,
)
}

30
compat/debug_windows.go Normal file
View file

@ -0,0 +1,30 @@
package compat
import (
"fmt"
"strings"
"github.com/safing/portbase/utils/debug"
)
// AddToDebugInfo adds compatibility data to the given debug.Info.
func AddToDebugInfo(di *debug.Info) {
// Get WFP state and add error info if it fails.
wfp, err := GetWFPState()
if err != nil {
di.AddSection(
"Compatibility: WFP State (failed)",
debug.UseCodeSection,
err.Error(),
)
return
}
// Add data as section.
wfpTable := wfp.AsTable()
di.AddSection(
fmt.Sprintf("Compatibility: WFP State (%d)", strings.Count(wfpTable, "\n")),
debug.UseCodeSection,
wfpTable,
)
}

60
compat/iptables.go Normal file
View file

@ -0,0 +1,60 @@
//go:build linux
package compat
import (
"fmt"
"github.com/coreos/go-iptables/iptables"
)
var (
iptProtocols = []iptables.Protocol{
iptables.ProtocolIPv4,
iptables.ProtocolIPv6,
}
iptTables = []string{
"filter",
"nat",
"mangle",
"raw",
}
)
// GetIPTablesChains returns the chain names currently in ip(6)tables.
func GetIPTablesChains() ([]string, error) {
chains := make([]string, 0, 100)
// Iterate over protocols.
for _, protocol := range iptProtocols {
if protocol == iptables.ProtocolIPv4 {
chains = append(chains, "v4")
} else {
chains = append(chains, "v6")
}
// Get iptables access for protocol.
tbls, err := iptables.NewWithProtocol(protocol)
if err != nil {
return nil, err
}
// Iterate over tables.
for _, table := range iptTables {
chains = append(chains, " "+table)
// Get chain names
chainNames, err := tbls.ListChains(table)
if err != nil {
return nil, fmt.Errorf("failed to get chains of table %s: %w", table, err)
}
// Add chain names to list.
for _, name := range chainNames {
chains = append(chains, " "+name)
}
}
}
return chains, nil
}

24
compat/iptables_test.go Normal file
View file

@ -0,0 +1,24 @@
//go:build linux
package compat
import (
"testing"
)
func TestIPTablesChains(t *testing.T) {
// Skip in CI.
if testing.Short() {
t.Skip()
}
t.Parallel()
chain, err := GetIPTablesChains()
if err != nil {
t.Fatal(err)
}
if len(chain) < 35 {
t.Errorf("Expected at least 35 output lines, not %d", len(chain))
}
}

528
compat/wfpstate.go Normal file
View file

@ -0,0 +1,528 @@
package compat
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"text/tabwriter"
"github.com/safing/portbase/utils/osdetail"
)
// GetWFPState queries the system for the WFP state and returns a simplified
// and cleaned version.
func GetWFPState() (*SimplifiedWFPState, error) {
// Use a file to get the wfp state, as the terminal isn't able to return the
// data encoded in UTF-8.
tmpDir, err := os.MkdirTemp("", "portmaster-debug-data-wfpstate")
if err != nil {
return nil, fmt.Errorf("failed to create tmp dir for wfpstate: %w", err)
}
defer func() {
_ = os.RemoveAll(tmpDir)
}()
tmpFile := filepath.Join(tmpDir, "wfpstate.xml")
// Get wfp state and write it to the tmp file.
_, err = osdetail.RunCmd(
"netsh.exe",
"wfp",
"show",
"state",
tmpFile,
)
if err != nil {
return nil, fmt.Errorf("failed to write wfp state to tmp file: %w", err)
}
// Get tmp file contents.
output, err := ioutil.ReadFile(tmpFile)
if err != nil {
return nil, fmt.Errorf("failed to read wfp state to tmp file: %w", err)
}
if len(output) == 0 {
return nil, errors.New("wfp state tmp file was empty")
}
// Parse wfp state.
parsedState, err := parseWFPState(output)
if err != nil {
return nil, fmt.Errorf("failed to parse wfpstate: %w", err)
}
// Return simplified and cleaned state.
return parsedState.simplified(), nil
}
/*
Interesting data is found at:
providers->item[]
->displayData->name
->displayData->description
->providerKey
subLayers->item[]
->displayData->name
->displayData->description
->subLayerKey
layers->item[]->callouts->item[]
->displayData->name
->displayData->description
->calloutKey
->providerKey
->applicableLayer
layers->item[]->filters->item[]
->displayData->name
->displayData->description
->filterKey
->providerKey
->layerKey
->subLayerKey
*/
// SimplifiedWFPState is a simplified version of the full WFP state.
type SimplifiedWFPState struct {
Providers []*WFPProvider
SubLayers []*WFPSubLayer
Callouts []*WFPCallout
Filters []*WFPFilter
}
// WFPProvider represents a WFP Provider.
type WFPProvider struct {
Name string
Description string
ProviderKey string
}
// WFPSubLayer represents a WFP SubLayer.
type WFPSubLayer struct {
Name string
Description string
SubLayerKey string
}
// WFPCallout represents a WFP Callout.
type WFPCallout struct {
Name string
Description string
CalloutKey string
ProviderKey string
ApplicableLayer string
}
// WFPFilter represents a WFP Filter.
type WFPFilter struct {
Name string
Description string
FilterKey string
ProviderKey string
LayerKey string
SubLayerKey string
}
// Keys returns all keys found in the WFP state.
func (sw *SimplifiedWFPState) Keys() map[string]struct{} {
lookupMap := make(map[string]struct{}, len(sw.Providers)+len(sw.SubLayers)+len(sw.Callouts)+len(sw.Filters))
// Collect keys.
for _, provider := range sw.Providers {
lookupMap[provider.ProviderKey] = struct{}{}
}
for _, subLayer := range sw.SubLayers {
lookupMap[subLayer.SubLayerKey] = struct{}{}
}
for _, callout := range sw.Callouts {
lookupMap[callout.CalloutKey] = struct{}{}
}
for _, filter := range sw.Filters {
lookupMap[filter.FilterKey] = struct{}{}
}
return lookupMap
}
// AsTable formats the simplified WFP state as a table.
func (sw *SimplifiedWFPState) AsTable() string {
rows := make([]string, 0, len(sw.Providers)+len(sw.SubLayers)+len(sw.Callouts)+len(sw.Filters))
// Collect data and put it into rows.
for _, provider := range sw.Providers {
rows = append(rows, strings.Join([]string{
provider.Name,
"Provider",
provider.Description,
provider.ProviderKey,
}, "\t"))
}
for _, subLayer := range sw.SubLayers {
rows = append(rows, strings.Join([]string{
subLayer.Name,
"SubLayer",
subLayer.Description,
subLayer.SubLayerKey,
}, "\t"))
}
for _, callout := range sw.Callouts {
rows = append(rows, strings.Join([]string{
callout.Name,
"Callout",
callout.Description,
callout.CalloutKey,
callout.ProviderKey,
callout.ApplicableLayer,
}, "\t"))
}
for _, filter := range sw.Filters {
rows = append(rows, strings.Join([]string{
filter.Name,
"Filter",
filter.Description,
filter.FilterKey,
filter.ProviderKey,
filter.LayerKey,
filter.SubLayerKey,
}, "\t"))
}
// Sort and build table.
sort.Strings(rows)
buf := bytes.NewBuffer(nil)
tabWriter := tabwriter.NewWriter(buf, 8, 4, 3, ' ', 0)
for _, row := range rows {
fmt.Fprint(tabWriter, row)
fmt.Fprint(tabWriter, "\n")
}
_ = tabWriter.Flush()
return buf.String()
}
// wfpState is the WFP state as returned by `netsh.exe wfp show state -`.
type wfpState struct {
XMLName xml.Name `xml:"wfpstate"`
Text string `xml:",chardata"`
TimeStamp string `xml:"timeStamp"`
Providers struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []struct {
Text string `xml:",chardata"`
ProviderKey string `xml:"providerKey"`
DisplayData struct {
Text string `xml:",chardata"`
Name string `xml:"name"`
Description string `xml:"description"`
} `xml:"displayData"`
Flags struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item string `xml:"item"`
} `xml:"flags"`
ProviderData string `xml:"providerData"`
ServiceName string `xml:"serviceName"`
} `xml:"item"`
} `xml:"providers"`
SubLayers struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []struct {
Text string `xml:",chardata"`
SubLayerKey string `xml:"subLayerKey"`
DisplayData struct {
Text string `xml:",chardata"`
Name string `xml:"name"`
Description string `xml:"description"`
} `xml:"displayData"`
Flags struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item string `xml:"item"`
} `xml:"flags"`
ProviderKey string `xml:"providerKey"`
ProviderData string `xml:"providerData"`
Weight string `xml:"weight"`
} `xml:"item"`
} `xml:"subLayers"`
Layers struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []struct {
Text string `xml:",chardata"`
Layer struct {
Text string `xml:",chardata"`
LayerKey string `xml:"layerKey"`
DisplayData struct {
Text string `xml:",chardata"`
Name string `xml:"name"`
Description string `xml:"description"`
} `xml:"displayData"`
Flags struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []string `xml:"item"`
} `xml:"flags"`
Field struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []struct {
Text string `xml:",chardata"`
FieldKey string `xml:"fieldKey"`
Type string `xml:"type"`
DataType string `xml:"dataType"`
} `xml:"item"`
} `xml:"field"`
DefaultSubLayerKey string `xml:"defaultSubLayerKey"`
LayerID string `xml:"layerId"`
} `xml:"layer"`
Callouts struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []struct {
Text string `xml:",chardata"`
CalloutKey string `xml:"calloutKey"`
DisplayData struct {
Text string `xml:",chardata"`
Name string `xml:"name"`
Description string `xml:"description"`
} `xml:"displayData"`
Flags struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []string `xml:"item"`
} `xml:"flags"`
ProviderKey string `xml:"providerKey"`
ProviderData string `xml:"providerData"`
ApplicableLayer string `xml:"applicableLayer"`
CalloutID string `xml:"calloutId"`
} `xml:"item"`
} `xml:"callouts"`
Filters struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []struct {
Text string `xml:",chardata"`
FilterKey string `xml:"filterKey"`
DisplayData struct {
Text string `xml:",chardata"`
Name string `xml:"name"`
Description string `xml:"description"`
} `xml:"displayData"`
Flags struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []string `xml:"item"`
} `xml:"flags"`
ProviderKey string `xml:"providerKey"`
ProviderData struct {
Text string `xml:",chardata"`
Data string `xml:"data"`
AsString string `xml:"asString"`
} `xml:"providerData"`
LayerKey string `xml:"layerKey"`
SubLayerKey string `xml:"subLayerKey"`
Weight struct {
Text string `xml:",chardata"`
Type string `xml:"type"`
Uint8 string `xml:"uint8"`
Uint64 string `xml:"uint64"`
} `xml:"weight"`
FilterCondition struct {
Text string `xml:",chardata"`
NumItems string `xml:"numItems,attr"`
Item []struct {
Text string `xml:",chardata"`
FieldKey string `xml:"fieldKey"`
MatchType string `xml:"matchType"`
ConditionValue struct {
Text string `xml:",chardata"`
Type string `xml:"type"`
Uint32 string `xml:"uint32"`
Uint16 string `xml:"uint16"`
RangeValue struct {
Text string `xml:",chardata"`
ValueLow struct {
Text string `xml:",chardata"`
Type string `xml:"type"`
Uint16 string `xml:"uint16"`
Uint32 string `xml:"uint32"`
ByteArray16 string `xml:"byteArray16"`
} `xml:"valueLow"`
ValueHigh struct {
Text string `xml:",chardata"`
Type string `xml:"type"`
Uint16 string `xml:"uint16"`
Uint32 string `xml:"uint32"`
ByteArray16 string `xml:"byteArray16"`
} `xml:"valueHigh"`
} `xml:"rangeValue"`
Uint8 string `xml:"uint8"`
ByteBlob struct {
Text string `xml:",chardata"`
Data string `xml:"data"`
AsString string `xml:"asString"`
} `xml:"byteBlob"`
Sd string `xml:"sd"`
Sid string `xml:"sid"`
Uint64 string `xml:"uint64"`
} `xml:"conditionValue"`
} `xml:"item"`
} `xml:"filterCondition"`
Action struct {
Text string `xml:",chardata"`
Type string `xml:"type"`
FilterType string `xml:"filterType"`
} `xml:"action"`
RawContext string `xml:"rawContext"`
Reserved string `xml:"reserved"`
FilterID string `xml:"filterId"`
EffectiveWeight struct {
Text string `xml:",chardata"`
Type string `xml:"type"`
Uint64 string `xml:"uint64"`
} `xml:"effectiveWeight"`
ProviderContextKey string `xml:"providerContextKey"`
} `xml:"item"`
} `xml:"filters"`
} `xml:"item"`
} `xml:"layers"`
}
func parseWFPState(data []byte) (*wfpState, error) {
w := &wfpState{}
err := xml.Unmarshal(data, w)
if err != nil {
return nil, err
}
return w, nil
}
func (w *wfpState) simplified() *SimplifiedWFPState {
sw := &SimplifiedWFPState{
Providers: make([]*WFPProvider, 0, len(w.Providers.Item)),
SubLayers: make([]*WFPSubLayer, 0, len(w.SubLayers.Item)),
Callouts: make([]*WFPCallout, 0, len(w.Layers.Item)),
Filters: make([]*WFPFilter, 0, len(w.Layers.Item)),
}
// Collect data.
for _, provider := range w.Providers.Item {
if isIgnoredProvider(provider.DisplayData.Name, provider.ProviderKey) {
continue
}
sw.Providers = append(sw.Providers, &WFPProvider{
Name: defaultTo(provider.DisplayData.Name, "[no name]"),
Description: defaultTo(provider.DisplayData.Description, "[no description]"),
ProviderKey: defaultTo(provider.ProviderKey, "[no provider key]"),
})
}
for _, subLayer := range w.SubLayers.Item {
if isIgnoredProvider(subLayer.DisplayData.Name, "") {
continue
}
sw.SubLayers = append(sw.SubLayers, &WFPSubLayer{
Name: defaultTo(subLayer.DisplayData.Name, "[no name]"),
Description: defaultTo(subLayer.DisplayData.Description, "[no description]"),
SubLayerKey: defaultTo(subLayer.SubLayerKey, "[no sublayer key]"),
})
}
for _, layer := range w.Layers.Item {
for _, callout := range layer.Callouts.Item {
if isIgnoredProvider(callout.DisplayData.Name, callout.ProviderKey) {
continue
}
sw.Callouts = append(sw.Callouts, &WFPCallout{
Name: defaultTo(callout.DisplayData.Name, "[no name]"),
Description: defaultTo(callout.DisplayData.Description, "[no description]"),
CalloutKey: defaultTo(callout.CalloutKey, "[no callout key]"),
ProviderKey: defaultTo(callout.ProviderKey, "[no provider key]"),
ApplicableLayer: defaultTo(callout.ApplicableLayer, "[no applicable layer]"),
})
}
for _, filter := range layer.Filters.Item {
if isIgnoredProvider(filter.DisplayData.Name, filter.ProviderKey) {
continue
}
sw.Filters = append(sw.Filters, &WFPFilter{
Name: defaultTo(filter.DisplayData.Name, "[no name]"),
Description: defaultTo(filter.DisplayData.Description, "[no description]"),
FilterKey: defaultTo(filter.FilterKey, "[no filter key]"),
ProviderKey: defaultTo(filter.ProviderKey, "[no provider key]"),
LayerKey: defaultTo(filter.LayerKey, "[no layer key]"),
SubLayerKey: defaultTo(filter.SubLayerKey, "[no sublayer key]"),
})
}
}
return sw
}
func isIgnoredProvider(name, key string) bool {
// Check provider key.
if key != "" {
matched := true
switch key {
case "{1bebc969-61a5-4732-a177-847a0817862a}": // Microsoft Windows Defender Firewall IPsec Provider.
case "{4b153735-1049-4480-aab4-d1b9bdc03710}": // Microsoft Windows Defender Firewall Provider.
case "{893a4f22-9bba-49b7-8c66-3d40929c8fd5}": // Microsoft Windows Teredo firewall provider.
case "{8e44982a-f477-11df-85ce-78e7d1810190}": // Windows Network Data Usage (NDU) Provider.
case "{9c2532b4-0314-434f-8274-0cbaebdbda56}": // Microsoft Windows edge traversal socket option authorization provider.
case "{aa6a7d87-7f8f-4d2a-be53-fda555cd5fe3}": // Microsoft Windows Defender Firewall IPsec Provider.
case "{c698301d-9129-450c-937c-f4b834bfb374}": // Microsoft Windows edge traversal socket option authorization provider.
case "{decc16ca-3f33-4346-be1e-8fb4ae0f3d62}": // Microsoft Windows Defender Firewall Provider.
case "FWPM_PROVIDER_IKEEXT": // Microsoft Windows WFP Built-in IKEEXT provider used to identify filters added by IKE/AuthIP.
case "FWPM_PROVIDER_IPSEC_DOSP_CONFIG": // Microsoft Windows WFP Built-in IPsec DoS Protection configuration provider used to identify filters added by IPsec Denial of Service Protection.
case "FWPM_PROVIDER_MPSSVC_APP_ISOLATION": // Microsoft Windows WFP Built-in MPSSVC App Isolation provider.
case "FWPM_PROVIDER_MPSSVC_EDP": // Microsoft Windows WFP Built-in MPSSVC Enterprise Data Protection provider.
case "FWPM_PROVIDER_MPSSVC_TENANT_RESTRICTIONS": // Microsoft Windows WFP Built-in MPSSVC Tenant Restrictions provider.
case "FWPM_PROVIDER_MPSSVC_WF": // Microsoft Windows WFP Built-in MPSSVC Windows Firewall provider.
case "FWPM_PROVIDER_MPSSVC_WSH": // Microsoft Windows WFP Built-in MPSSVC Windows Service Hardening and Quarantine provider.
case "FWPM_PROVIDER_TCP_CHIMNEY_OFFLOAD": // Microsoft Windows WFP Built-in TCP Chimney Offload provider used to identify filters added by TCP Chimney Offload.
case "FWPM_PROVIDER_TCP_TEMPLATES": // Microsoft Windows WFP Built-in TCP Templates provider used to identify filters added by TCP Template based configuration.
default:
matched = false
}
if matched {
return true
}
}
// Some entries don't have a provider key (set).
// These are pretty generic, but the output strings are localized.
if name != "" {
switch {
case strings.Contains(name, "Microsoft Corporation"):
return true
case strings.Contains(name, "windefend"):
return true
case strings.Contains(name, "WFP"):
return true
case strings.Contains(name, "RPC"):
return true
case strings.Contains(name, "NDU"):
return true
}
}
return false
}
func defaultTo(a, b string) string {
if a != "" {
return a
}
return b
}

56270
compat/wfpstate_test.go Normal file

File diff suppressed because it is too large Load diff

View file

@ -4,12 +4,15 @@ import (
"net/http"
"github.com/safing/portbase/api"
"github.com/safing/portbase/config"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules"
"github.com/safing/portbase/utils/debug"
"github.com/safing/portmaster/compat"
"github.com/safing/portmaster/resolver"
"github.com/safing/portmaster/status"
"github.com/safing/portmaster/updates"
"github.com/safing/spn/captain"
)
func registerAPIEndpoints() error {
@ -85,7 +88,10 @@ func debugInfo(ar *api.Request) (data []byte, err error) {
di.AddVersionInfo()
di.AddPlatformInfo(ar.Context())
status.AddToDebugInfo(di)
config.AddToDebugInfo(di)
resolver.AddToDebugInfo(di)
captain.AddToDebugInfo(di)
compat.AddToDebugInfo(di)
di.AddLastReportedModuleError()
di.AddLastUnexpectedLogs()
di.AddGoroutineStack()

14
go.mod
View file

@ -11,18 +11,20 @@ require (
github.com/google/gopacket v1.1.19
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.4.0
github.com/mdlayher/socket v0.2.2 // indirect
github.com/miekg/dns v1.1.46
github.com/mdlayher/socket v0.2.3 // indirect
github.com/miekg/dns v1.1.47
github.com/oschwald/maxminddb-golang v1.8.0
github.com/safing/portbase v0.14.0
github.com/safing/spn v0.4.3
github.com/safing/portbase v0.14.1
github.com/safing/spn v0.4.4
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cobra v1.3.0
github.com/spf13/cobra v1.4.0
github.com/stretchr/testify v1.7.0
github.com/tannerryan/ring v1.1.2
github.com/tevino/abool v1.2.0
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8
golang.org/x/tools v0.1.10 // indirect
)

30
go.sum
View file

@ -663,6 +663,8 @@ github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGk
github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q=
github.com/mdlayher/netlink v1.5.0/go.mod h1:1Kr8BBFxGyUyNmztC9WLOayqYVAd2wsgOZm18nqGuzQ=
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=
github.com/mdlayher/socket v0.0.0-20210624160740-9dbe287ded84/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=
@ -671,8 +673,9 @@ github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZY
github.com/mdlayher/socket v0.1.0/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/mdlayher/socket v0.2.0/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.2.2 h1:UOh5gQk70kRl1YMLCTRwRF4MvsAQsudjkEA+ZDXS4jo=
github.com/mdlayher/socket v0.2.2/go.mod h1:IcNFWYJJuSGgnfKie27UfpEDWytPDqy+TrDd9I5hUKQ=
github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM=
github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
@ -682,8 +685,9 @@ github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJys
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.44/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.46 h1:uzwpxRtSVxtcIZmz/4Uz6/Rn7G11DvsaslXoy5LxQio=
github.com/miekg/dns v1.1.46/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.47 h1:J9bWiXbqMbnZPcY8Qi2E3EWIBsIm6MZzzJB9VRg5gL8=
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
@ -807,8 +811,9 @@ github.com/safing/portbase v0.13.3/go.mod h1:5vj5IK5WJoSGareDe6yCMZfnF7txVRx7jZy
github.com/safing/portbase v0.13.4/go.mod h1:5vj5IK5WJoSGareDe6yCMZfnF7txVRx7jZyTZInISP0=
github.com/safing/portbase v0.13.5/go.mod h1:5vj5IK5WJoSGareDe6yCMZfnF7txVRx7jZyTZInISP0=
github.com/safing/portbase v0.13.6/go.mod h1:G0maDSQxYDuluNhMzA1zVd/nfXawfECv5H7+fnTfVhM=
github.com/safing/portbase v0.14.0 h1:6+sdUs1tdRCKnyuzy/zHrvUsdO1GdI0l4gZaoYJmJ5Q=
github.com/safing/portbase v0.14.0/go.mod h1:z9sRR/vqohAGdYSSx2B+o8tND4WVvcxPL6XBBtN3bDI=
github.com/safing/portbase v0.14.1 h1:k/SgywE5QutDbf5hHbEArrQnjBByerGm0nhh5n8VnDw=
github.com/safing/portbase v0.14.1/go.mod h1:z9sRR/vqohAGdYSSx2B+o8tND4WVvcxPL6XBBtN3bDI=
github.com/safing/portmaster v0.7.3/go.mod h1:o//kZ8eE+5vT1V22mgnxHIAdlEz42sArsK5OF2Lf/+s=
github.com/safing/portmaster v0.7.4/go.mod h1:Q93BWdF1oAL0oUMukshl8W1aPZhmrlTGi6tFTFc3pTw=
github.com/safing/portmaster v0.7.6/go.mod h1:qOs9hQtvAzTVICRbwLg3vddqOaqJHeWBjWQ0C+TJ/Bw=
@ -822,6 +827,7 @@ github.com/safing/portmaster v0.7.18/go.mod h1:NXd1l1z0oKy0WfXMhv/gpAXz+pUHnlNuY
github.com/safing/portmaster v0.7.21/go.mod h1:Jy0G6x6m5dE36Mv9grXHI77cxysQ0fIQV1EYQ00WEiQ=
github.com/safing/portmaster v0.8.0/go.mod h1:lY2/WvOlH8kl1AwkixdWCjlo+PZQv+oEOQhIaSS/+wA=
github.com/safing/portmaster v0.8.5-interdep/go.mod h1:A+zAVEKjr057ktgiMSJRdUmOF+FPW8XY/5LqGnbsKbU=
github.com/safing/portmaster v0.8.5/go.mod h1:MqOlFwHcIx/109Ugutz/CG23znuuXCRVHcIcfX0VC/c=
github.com/safing/spn v0.3.4/go.mod h1:TfzNsZCbnlWv0UFDILFOUSudVKJZlnBVoR1fDXrjOK0=
github.com/safing/spn v0.3.5/go.mod h1:jHkFF2Yu1fnjFu4KXjVA+iagMr/z4eB4p3jiwikvKj8=
github.com/safing/spn v0.3.6/go.mod h1:RSeFb/h5Wt3yDVezXj3lhXJ/Iwd7FbtsGf5E+p5J2YQ=
@ -837,6 +843,8 @@ github.com/safing/spn v0.4.0/go.mod h1:0jBetnYCfxqO5PJskhPOxJ/v6VRfE+bQU98XW240B
github.com/safing/spn v0.4.2/go.mod h1:yZPezHDEYyhei8n13tTxjQCGq6LRr5svz9WFAAeDPec=
github.com/safing/spn v0.4.3 h1:iEFmpzyrThJ8QF9Qpbxk/m4w2+ZvbVPyuqJ4EwnpfDg=
github.com/safing/spn v0.4.3/go.mod h1:YHtg3FkZviN8T7db4BdRffbYO1pO7w9SydQatLmvW2M=
github.com/safing/spn v0.4.4 h1:DGCkHaCgkQ0ivsD3J3BkUJ2auMpCEhzZdlD3JDmK/U4=
github.com/safing/spn v0.4.4/go.mod h1:mkQA5pYM1SUd4JkTyuwXFycFMGQXLTd9RUJuY2vqccM=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
@ -879,8 +887,9 @@ github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKv
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@ -1059,8 +1068,10 @@ golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1097,8 +1108,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1329,8 +1341,9 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -1425,8 +1438,9 @@ golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyj
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View file

@ -28,6 +28,8 @@ func sendResponse(
return nil
}
// Signify that we are a recursive resolver.
// While we do not handle recursion directly, we can safely assume, that we
// always forward to a recursive resolver.
reply.RecursionAvailable = true
// Add extra RRs through a custom RRProvider.

View file

@ -110,7 +110,7 @@ func getDefaultInterface() *defaultNetInterface {
newIf := &defaultNetInterface{}
// Scan data for needed fields.
scanner := bufio.NewScanner(bytes.NewBufferString(interfaceData))
scanner := bufio.NewScanner(bytes.NewBuffer(interfaceData))
scanner.Split(bufio.ScanLines)
var segmentKey, segmentValue, previousKey string
for scanner.Scan() {

View file

@ -264,6 +264,7 @@ Important: DNS Requests are only matched against domain and filter list rules, a
Key: CfgOptionEndpointsKey,
Description: "Rules that apply to outgoing network connections. Cannot overrule Network Scopes and Connection Types (see above).",
Help: rulesHelp,
Sensitive: true,
OptType: config.OptTypeStringArray,
DefaultValue: []string{},
Annotations: config.Annotations{
@ -287,6 +288,7 @@ Important: DNS Requests are only matched against domain and filter list rules, a
Key: CfgOptionServiceEndpointsKey,
Description: "Rules that apply to incoming network connections. Cannot overrule Network Scopes and Connection Types (see above). Also note that the default action for incoming connections is to always block.",
Help: rulesHelp,
Sensitive: true,
OptType: config.OptTypeStringArray,
DefaultValue: []string{"+ Localhost"},
ExpertiseLevel: config.ExpertiseLevelExpert,
@ -620,6 +622,7 @@ Please note that if you are using the system resolver, bypass attempts might be
Key: CfgOptionSPNUsagePolicyKey,
Description: `Customize which websites should or should not be routed through the SPN. Only active if "Use SPN" is enabled.`,
Help: rulesHelp,
Sensitive: true,
OptType: config.OptTypeStringArray,
DefaultValue: []string{},
Annotations: config.Annotations{
@ -646,6 +649,7 @@ Please note that if you are using the system resolver, bypass attempts might be
By default, the Portmaster tries to choose the node closest to the destination as the Exit Node. This reduces your exposure to the open Internet. Exit Nodes are chosen for every destination separately.`,
Help: SPNRulesHelp,
Sensitive: true,
OptType: config.OptTypeStringArray,
DefaultValue: []string{},
Annotations: config.Annotations{

View file

@ -205,12 +205,15 @@ func createResolver(resolverURL, source string) (*Resolver, bool, error) {
}
func configureSearchDomains(resolver *Resolver, searches []string, hardfail bool) error {
resolver.Search = make([]string, 0, len(searches))
// Check all search domains.
for i, value := range searches {
trimmedDomain := strings.ToLower(strings.Trim(value, "."))
err := checkSearchScope(trimmedDomain)
if err != nil {
if hardfail {
resolver.Search = nil
return fmt.Errorf("failed to validate search domain #%d: %w", i+1, err)
}
log.Warningf("resolver: skipping invalid search domain for resolver %s: %s", resolver, utils.SafeFirst16Chars(value))

View file

@ -117,6 +117,7 @@ func upgradeCoreNotify() error {
),
ShowOnSystem: true,
AvailableActions: []*notifications.Action{
// TODO: Use special UI action in order to reload UI on restart.
{
ID: "restart",
Text: "Restart",