mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
Refactoring
This commit is contained in:
parent
420da81b33
commit
35697989e5
4 changed files with 214 additions and 76 deletions
|
@ -616,21 +616,34 @@ matchLoop:
|
|||
}
|
||||
|
||||
func checkCustomFilterList(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||
// block domains that are in the custom list
|
||||
// block if the domain name appears in the custom filter list
|
||||
if conn.Entity.Domain != "" {
|
||||
if customlists.LookupDomain(conn.Entity.Domain) {
|
||||
// FIXME: add proper messages
|
||||
log.Debugf("Blocked %s", conn.Entity.Domain)
|
||||
conn.Block("Domains appiers in the custom user list", profile.CfgOptionRemoveBlockedDNSKey)
|
||||
conn.Block("Domains appears in the custom user list", customlists.CfgOptionCustomListBlockingKey)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// block if ip addresses appears in the custom filter list
|
||||
if conn.Entity.IP != nil {
|
||||
if customlists.LookupIPv4(&conn.Entity.IP) {
|
||||
// FIXME: add proper messages
|
||||
log.Debugf("Blocked %s", conn.Entity.IP)
|
||||
conn.Block("IP appiers in the custom user list", profile.CfgOptionBlockScopeInternetKey)
|
||||
if customlists.LookupIP(&conn.Entity.IP) {
|
||||
conn.Block("IP appears in the custom filter list", customlists.CfgOptionCustomListBlockingKey)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// block autonomous system by its number if it appears in the custom filter list
|
||||
if conn.Entity.ASN != 0 {
|
||||
if customlists.LookupASN(conn.Entity.ASN) {
|
||||
conn.Block("ASN appears in the custom filter list", customlists.CfgOptionCustomListBlockingKey)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// block if the country appears in the custom filter list
|
||||
if conn.Entity.Country != "" {
|
||||
if customlists.LookupCountry(conn.Entity.Country) {
|
||||
conn.Block("Country appears in the custom filter list", customlists.CfgOptionCustomListBlockingKey)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
39
intel/customlists/config.go
Normal file
39
intel/customlists/config.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package customlists
|
||||
|
||||
import "github.com/safing/portbase/config"
|
||||
|
||||
var (
|
||||
// CfgOptionCustomListBlockingKey is the config key for the listen address..
|
||||
CfgOptionCustomListBlockingKey = "filter/customListBlocking"
|
||||
cfgOptionCustomListBlockingOrder = 37
|
||||
cfgOptionCustomListCategoryAnnotation = "Filter Lists"
|
||||
)
|
||||
|
||||
var (
|
||||
getFilePath func() string
|
||||
)
|
||||
|
||||
func registerConfig() error {
|
||||
// register a setting for the file path in the ui
|
||||
err := config.Register(&config.Option{
|
||||
Name: "Custom Filter List",
|
||||
Key: CfgOptionCustomListBlockingKey,
|
||||
Description: "Path to the file that contains a list of Domain, IP addresses, country codes and autonomous systems that you want to block",
|
||||
OptType: config.OptTypeString,
|
||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
DefaultValue: "",
|
||||
RequiresRestart: false,
|
||||
Annotations: config.Annotations{
|
||||
config.DisplayOrderAnnotation: cfgOptionCustomListBlockingOrder,
|
||||
config.CategoryAnnotation: cfgOptionCustomListCategoryAnnotation,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
getFilePath = config.GetAsString(CfgOptionCustomListBlockingKey, "")
|
||||
|
||||
return nil
|
||||
}
|
88
intel/customlists/lists.go
Normal file
88
intel/customlists/lists.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package customlists
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/network/netutils"
|
||||
)
|
||||
|
||||
var (
|
||||
countryCodesFilterList map[string]struct{}
|
||||
ipAddressesFilterList map[string]struct{}
|
||||
autonomousSystemsFilterList map[uint]struct{}
|
||||
domainsFilterList map[string]struct{}
|
||||
)
|
||||
|
||||
func parseFile(filePath string) error {
|
||||
// open the file if possible
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
log.Warningf("Custom filter: failed to parse file: \"%s\"", filePath)
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// initialize maps to hold data from the file
|
||||
countryCodesFilterList = make(map[string]struct{})
|
||||
ipAddressesFilterList = make(map[string]struct{})
|
||||
autonomousSystemsFilterList = make(map[uint]struct{})
|
||||
domainsFilterList = make(map[string]struct{})
|
||||
|
||||
// read filter file line by line
|
||||
scanner := bufio.NewScanner(file)
|
||||
// the scanner will error out if the line is greater than 64K, in this case it is enough
|
||||
for scanner.Scan() {
|
||||
parseLine(scanner.Text())
|
||||
}
|
||||
|
||||
// check for scanner error
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Custom filter: list loaded successful: %s", filePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseLine(line string) {
|
||||
// ignore empty lines and comment lines
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
return
|
||||
}
|
||||
|
||||
// everything after the first field will be ignored
|
||||
field := strings.Fields(line)[0]
|
||||
|
||||
// check if it'a a country code
|
||||
if isCountryCode(field) {
|
||||
countryCodesFilterList[field] = struct{}{}
|
||||
}
|
||||
|
||||
// try to parse IP address
|
||||
ip := net.ParseIP(field)
|
||||
if ip != nil {
|
||||
ipAddressesFilterList[ip.String()] = struct{}{}
|
||||
}
|
||||
|
||||
// check if it's a Autonomous system (example AS123)
|
||||
if isAutonomousSystem(field) {
|
||||
asNumber, err := strconv.ParseUint(field[2:], 10, 32)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
autonomousSystemsFilterList[uint(asNumber)] = struct{}{}
|
||||
}
|
||||
|
||||
// check if it's a domain
|
||||
domain := dns.Fqdn(field)
|
||||
if netutils.IsValidFqdn(domain) {
|
||||
domainsFilterList[domain] = struct{}{}
|
||||
}
|
||||
}
|
|
@ -1,112 +1,110 @@
|
|||
package customlists
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/network/netutils"
|
||||
)
|
||||
|
||||
var module *modules.Module
|
||||
|
||||
const configChangeEvent = "config change"
|
||||
|
||||
// Helper variables for parsing the input file
|
||||
var (
|
||||
countryCodes = map[string]struct{}{"AF": {}, "AX": {}, "AL": {}, "DZ": {}, "AS": {}, "AD": {}, "AO": {}, "AI": {}, "AQ": {}, "AG": {}, "AR": {}, "AM": {}, "AW": {}, "AU": {}, "AT": {}, "AZ": {}, "BH": {}, "BS": {}, "BD": {}, "BB": {}, "BY": {}, "BE": {}, "BZ": {}, "BJ": {}, "BM": {}, "BT": {}, "BO": {}, "BQ": {}, "BA": {}, "BW": {}, "BV": {}, "BR": {}, "IO": {}, "BN": {}, "BG": {}, "BF": {}, "BI": {}, "KH": {}, "CM": {}, "CA": {}, "CV": {}, "KY": {}, "CF": {}, "TD": {}, "CL": {}, "CN": {}, "CX": {}, "CC": {}, "CO": {}, "KM": {}, "CG": {}, "CD": {}, "CK": {}, "CR": {}, "CI": {}, "HR": {}, "CU": {}, "CW": {}, "CY": {}, "CZ": {}, "DK": {}, "DJ": {}, "DM": {}, "DO": {}, "EC": {}, "EG": {}, "SV": {}, "GQ": {}, "ER": {}, "EE": {}, "ET": {}, "FK": {}, "FO": {}, "FJ": {}, "FI": {}, "FR": {}, "GF": {}, "PF": {}, "TF": {}, "GA": {}, "GM": {}, "GE": {}, "DE": {}, "GH": {}, "GI": {}, "GR": {}, "GL": {}, "GD": {}, "GP": {}, "GU": {}, "GT": {}, "GG": {}, "GN": {}, "GW": {}, "GY": {}, "HT": {}, "HM": {}, "VA": {}, "HN": {}, "HK": {}, "HU": {}, "IS": {}, "IN": {}, "ID": {}, "IR": {}, "IQ": {}, "IE": {}, "IM": {}, "IL": {}, "IT": {}, "JM": {}, "JP": {}, "JE": {}, "JO": {}, "KZ": {}, "KE": {}, "KI": {}, "KP": {}, "KR": {}, "KW": {}, "KG": {}, "LA": {}, "LV": {}, "LB": {}, "LS": {}, "LR": {}, "LY": {}, "LI": {}, "LT": {}, "LU": {}, "MO": {}, "MK": {}, "MG": {}, "MW": {}, "MY": {}, "MV": {}, "ML": {}, "MT": {}, "MH": {}, "MQ": {}, "MR": {}, "MU": {}, "YT": {}, "MX": {}, "FM": {}, "MD": {}, "MC": {}, "MN": {}, "ME": {}, "MS": {}, "MA": {}, "MZ": {}, "MM": {}, "NA": {}, "NR": {}, "NP": {}, "NL": {}, "NC": {}, "NZ": {}, "NI": {}, "NE": {}, "NG": {}, "NU": {}, "NF": {}, "MP": {}, "NO": {}, "OM": {}, "PK": {}, "PW": {}, "PS": {}, "PA": {}, "PG": {}, "PY": {}, "PE": {}, "PH": {}, "PN": {}, "PL": {}, "PT": {}, "PR": {}, "QA": {}, "RE": {}, "RO": {}, "RU": {}, "RW": {}, "BL": {}, "SH": {}, "KN": {}, "LC": {}, "MF": {}, "PM": {}, "VC": {}, "WS": {}, "SM": {}, "ST": {}, "SA": {}, "SN": {}, "RS": {}, "SC": {}, "SL": {}, "SG": {}, "SX": {}, "SK": {}, "SI": {}, "SB": {}, "SO": {}, "ZA": {}, "GS": {}, "SS": {}, "ES": {}, "LK": {}, "SD": {}, "SR": {}, "SJ": {}, "SZ": {}, "SE": {}, "CH": {}, "SY": {}, "TW": {}, "TJ": {}, "TZ": {}, "TH": {}, "TL": {}, "TG": {}, "TK": {}, "TO": {}, "TT": {}, "TN": {}, "TR": {}, "TM": {}, "TC": {}, "TV": {}, "UG": {}, "UA": {}, "AE": {}, "GB": {}, "US": {}, "UM": {}, "UY": {}, "UZ": {}, "VU": {}, "VE": {}, "VN": {}, "VG": {}, "VI": {}, "WF": {}, "EH": {}, "YE": {}, "ZM": {}, "ZW": {}}
|
||||
isCountryCode = regexp.MustCompile("^[A-Z]{2}$").MatchString
|
||||
isAutonomousSystem = regexp.MustCompile(`^AS[0-9]+$`).MatchString
|
||||
)
|
||||
|
||||
var (
|
||||
filteredCountryCodes map[string]struct{}
|
||||
filteredIPAddresses map[string]struct{}
|
||||
filteredAutonomousSystems map[string]struct{}
|
||||
filteredDomains map[string]struct{}
|
||||
filterListFilePath string
|
||||
filterListFileModifiedTime time.Time
|
||||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("customlists", prep, nil, nil, "base")
|
||||
module = modules.Register("customlists", prep, start, nil, "base")
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
filteredCountryCodes = make(map[string]struct{})
|
||||
filteredIPAddresses = make(map[string]struct{})
|
||||
filteredAutonomousSystems = make(map[string]struct{})
|
||||
filteredDomains = make(map[string]struct{})
|
||||
|
||||
file, err := os.Open("/home/vladimir/Dev/Safing/filterlists/custom.txt")
|
||||
// register the config in the ui
|
||||
err := registerConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// read filter file line by line
|
||||
scanner := bufio.NewScanner(file)
|
||||
// the scanner will error out if the line is greater than 64K, in this case it is enough
|
||||
for scanner.Scan() {
|
||||
parseFilterLine(scanner.Text())
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
// register to hook to update after config change.
|
||||
if err := module.RegisterEventHook(
|
||||
module.Name,
|
||||
configChangeEvent,
|
||||
"update custom filter list",
|
||||
func(ctx context.Context, obj interface{}) error {
|
||||
_ = checkAndUpdateFilterList()
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Criticalf("filteredCountryCodes: %v", filteredCountryCodes)
|
||||
log.Criticalf("filteredIPAddresses: %v", filteredIPAddresses)
|
||||
log.Criticalf("filteredAutonomousSystems: %v", filteredAutonomousSystems)
|
||||
log.Criticalf("filteredDomains: %v", filteredDomains)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseFilterLine(line string) {
|
||||
// ignore empty lines and comment lines
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
return
|
||||
}
|
||||
func start() error {
|
||||
// register timer to run every periodically and check for file updates
|
||||
module.NewTask("Custom filter list file update check", func(context.Context, *modules.Task) error {
|
||||
_ = checkAndUpdateFilterList()
|
||||
return nil
|
||||
}).Repeat(10 * time.Minute)
|
||||
|
||||
fields := strings.Fields(line)
|
||||
|
||||
// everything after the first field will be ignored
|
||||
firstField := fields[0]
|
||||
|
||||
// check if it'a a country code
|
||||
if _, ok := countryCodes[firstField]; ok {
|
||||
filteredCountryCodes[firstField] = struct{}{}
|
||||
}
|
||||
|
||||
// try to parse IP address
|
||||
ip := net.ParseIP(firstField)
|
||||
if ip != nil {
|
||||
filteredIPAddresses[ip.String()] = struct{}{}
|
||||
}
|
||||
|
||||
// check if it's a Autonomous system (example AS123)
|
||||
if isAutonomousSystem(firstField) {
|
||||
filteredAutonomousSystems[firstField] = struct{}{}
|
||||
}
|
||||
|
||||
// check if it's a domain
|
||||
potentialDomain := dns.Fqdn(firstField)
|
||||
if netutils.IsValidFqdn(potentialDomain) {
|
||||
filteredDomains[potentialDomain] = struct{}{}
|
||||
}
|
||||
// parse the file for the first time at start
|
||||
_ = parseFile(getFilePath())
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupIPv4 checks if the IP is in a custom filter list
|
||||
func LookupIPv4(ip *net.IP) bool {
|
||||
log.Debugf("Checking ip %s", ip.String())
|
||||
_, ok := filteredIPAddresses[ip.String()]
|
||||
func checkAndUpdateFilterList() error {
|
||||
// get path and try to get its info
|
||||
filePath := getFilePath()
|
||||
fileInfo, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
modifiedTime := fileInfo.ModTime()
|
||||
|
||||
// check if file path has changed or if modified time has changed
|
||||
if filterListFilePath != filePath || !filterListFileModifiedTime.Equal(modifiedTime) {
|
||||
err := parseFile(filePath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
filterListFileModifiedTime = modifiedTime
|
||||
filterListFilePath = filePath
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupIP checks if the IP address is in a custom filter list
|
||||
func LookupIP(ip *net.IP) bool {
|
||||
_, ok := ipAddressesFilterList[ip.String()]
|
||||
return ok
|
||||
}
|
||||
|
||||
// LookupDomain checks if the Domain is in a custom filter list
|
||||
func LookupDomain(domain string) bool {
|
||||
log.Debugf("Checking domain %s", domain)
|
||||
_, ok := filteredDomains[domain]
|
||||
_, ok := domainsFilterList[domain]
|
||||
return ok
|
||||
}
|
||||
|
||||
// LookupASN checks if the Autonomous system number is in a custom filter list
|
||||
func LookupASN(number uint) bool {
|
||||
_, ok := autonomousSystemsFilterList[number]
|
||||
return ok
|
||||
}
|
||||
|
||||
// LookupCountry checks if the country code is in a custom filter list
|
||||
func LookupCountry(countryCode string) bool {
|
||||
_, ok := countryCodesFilterList[countryCode]
|
||||
return ok
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue