mirror of
https://github.com/safing/portmaster
synced 2025-09-04 19:49:15 +00:00
Add debug interfaces for wfp and iptables
This commit is contained in:
parent
b0b2fff5d7
commit
45339ce0c2
9 changed files with 56957 additions and 1 deletions
10
compat/debug_default.go
Normal file
10
compat/debug_default.go
Normal 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
28
compat/debug_linux.go
Normal 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
30
compat/debug_windows.go
Normal 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
60
compat/iptables.go
Normal 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
24
compat/iptables_test.go
Normal 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
528
compat/wfpstate.go
Normal 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
56270
compat/wfpstate_test.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -4,12 +4,15 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/safing/portbase/api"
|
"github.com/safing/portbase/api"
|
||||||
|
"github.com/safing/portbase/config"
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
"github.com/safing/portbase/modules"
|
"github.com/safing/portbase/modules"
|
||||||
"github.com/safing/portbase/utils/debug"
|
"github.com/safing/portbase/utils/debug"
|
||||||
|
"github.com/safing/portmaster/compat"
|
||||||
"github.com/safing/portmaster/resolver"
|
"github.com/safing/portmaster/resolver"
|
||||||
"github.com/safing/portmaster/status"
|
"github.com/safing/portmaster/status"
|
||||||
"github.com/safing/portmaster/updates"
|
"github.com/safing/portmaster/updates"
|
||||||
|
"github.com/safing/spn/captain"
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerAPIEndpoints() error {
|
func registerAPIEndpoints() error {
|
||||||
|
@ -85,7 +88,10 @@ func debugInfo(ar *api.Request) (data []byte, err error) {
|
||||||
di.AddVersionInfo()
|
di.AddVersionInfo()
|
||||||
di.AddPlatformInfo(ar.Context())
|
di.AddPlatformInfo(ar.Context())
|
||||||
status.AddToDebugInfo(di)
|
status.AddToDebugInfo(di)
|
||||||
|
config.AddToDebugInfo(di)
|
||||||
resolver.AddToDebugInfo(di)
|
resolver.AddToDebugInfo(di)
|
||||||
|
captain.AddToDebugInfo(di)
|
||||||
|
compat.AddToDebugInfo(di)
|
||||||
di.AddLastReportedModuleError()
|
di.AddLastReportedModuleError()
|
||||||
di.AddLastUnexpectedLogs()
|
di.AddLastUnexpectedLogs()
|
||||||
di.AddGoroutineStack()
|
di.AddGoroutineStack()
|
||||||
|
|
|
@ -110,7 +110,7 @@ func getDefaultInterface() *defaultNetInterface {
|
||||||
newIf := &defaultNetInterface{}
|
newIf := &defaultNetInterface{}
|
||||||
|
|
||||||
// Scan data for needed fields.
|
// Scan data for needed fields.
|
||||||
scanner := bufio.NewScanner(bytes.NewBufferString(interfaceData))
|
scanner := bufio.NewScanner(bytes.NewBuffer(interfaceData))
|
||||||
scanner.Split(bufio.ScanLines)
|
scanner.Split(bufio.ScanLines)
|
||||||
var segmentKey, segmentValue, previousKey string
|
var segmentKey, segmentValue, previousKey string
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue