From 0f48d32ac295e2bc1a3f58fbd6e23cad2496da7a Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 12 Oct 2021 16:18:05 +0200 Subject: [PATCH 1/2] Fix routing for Portmaster connections --- firewall/interception.go | 32 +++++++++++++++----------------- go.sum | 2 ++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/firewall/interception.go b/firewall/interception.go index 165af81a..3b2b3421 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -322,39 +322,32 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) { func initialHandler(conn *network.Connection, pkt packet.Packet) { log.Tracer(pkt.Ctx()).Trace("filter: handing over to connection-based handler") - // Check for pre-authenticated port. - if !conn.Inbound && localPortIsPreAuthenticated(conn.Entity.Protocol, conn.LocalPort) { + switch { + case !conn.Inbound && localPortIsPreAuthenticated(conn.Entity.Protocol, conn.LocalPort): // Approve connection. conn.Accept("connection by Portmaster", noReasonOptionKey) conn.Internal = true - // Finalize connection. - conn.StopFirewallHandler() - issueVerdict(conn, pkt, 0, true) - return - } - // Redirect rogue dns requests to the Portmaster. - if pkt.IsOutbound() && + + case pkt.IsOutbound() && pkt.Info().DstPort == 53 && conn.Process().Pid != ownPID && nameserverIPMatcherReady.IsSet() && - !nameserverIPMatcher(pkt.Info().Dst) { + !nameserverIPMatcher(pkt.Info().Dst): + // Reroute rogue dns queries back to Portmaster. conn.Verdict = network.VerdictRerouteToNameserver conn.Reason.Msg = "redirecting rogue dns query" conn.Internal = true + // End directly, as no other processing is necessary. conn.StopFirewallHandler() issueVerdict(conn, pkt, 0, true) return - } - // TODO: enable inspecting again - conn.Inspecting = false - - // Filter, if enabled. - if filterEnabled() { + case filterEnabled(): log.Tracer(pkt.Ctx()).Trace("filter: starting decision process") DecideOnConnection(pkt.Ctx(), conn, pkt) - } else { + + default: conn.Accept("privacy filter disabled", noReasonOptionKey) } @@ -366,6 +359,8 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) { // Exclude requests of the SPN itself. if !captain.IsExcepted(conn.Entity.IP) { + conn.Tunneled = true + // Check if client is ready. if captain.ClientReady() { // Queue request in sluice. @@ -385,6 +380,9 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) { } } + // TODO: enable inspecting again + conn.Inspecting = false + switch { case conn.Inspecting: log.Tracer(pkt.Ctx()).Trace("filter: start inspecting") diff --git a/go.sum b/go.sum index 60684256..21328ea0 100644 --- a/go.sum +++ b/go.sum @@ -480,6 +480,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tannerryan/ring v1.1.2 h1:iXayOjqHQOLzuy9GwSKuG3nhWfzQkldMlQivcgIr7gQ= @@ -1000,6 +1001,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 960101d3a9380437b3d5c5771dc679cb338ec098 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 12 Oct 2021 16:18:36 +0200 Subject: [PATCH 2/2] Add custom routing for DNS server connections --- firewall/interception.go | 2 + firewall/master.go | 4 ++ firewall/tunnel.go | 130 +++++++-------------------------------- network/connection.go | 6 +- resolver/resolvers.go | 15 +++++ 5 files changed, 46 insertions(+), 111 deletions(-) diff --git a/firewall/interception.go b/firewall/interception.go index 3b2b3421..a67849c9 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -328,6 +328,8 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) { conn.Accept("connection by Portmaster", noReasonOptionKey) conn.Internal = true + // Set tunnel options. + setCustomTunnelOptionsForPortmaster(conn) case pkt.IsOutbound() && pkt.Info().DstPort == 53 && diff --git a/firewall/master.go b/firewall/master.go index fcf946a5..e5fc0440 100644 --- a/firewall/master.go +++ b/firewall/master.go @@ -141,6 +141,10 @@ func checkPortmasterConnection(ctx context.Context, conn *network.Connection, _ log.Tracer(ctx).Infof("filter: granting own connection %s", conn) conn.Accept("connection by Portmaster", noReasonOptionKey) conn.Internal = true + + // Set tunnel options. + setCustomTunnelOptionsForPortmaster(conn) + return true } diff --git a/firewall/tunnel.go b/firewall/tunnel.go index 22f880f2..27435466 100644 --- a/firewall/tunnel.go +++ b/firewall/tunnel.go @@ -1,111 +1,23 @@ package firewall -// var ( -// TunnelNet4 *net.IPNet -// TunnelNet6 *net.IPNet -// TunnelEntry4 = net.IPv4(127, 0, 0, 17) -// TunnelEntry6 = net.ParseIP("fd17::17") -// -// ipToDomainMap = make(map[string]*TunnelInfo) -// ipToDomainMapLock sync.RWMutex -// ) -// -// func init() { -// var err error -// _, TunnelNet4, err = net.ParseCIDR("127.17.0.0/16") -// if err != nil { -// log.Fatalf("portmaster: could not parse 127.17.0.0/16: %s", err) -// } -// _, TunnelNet6, err = net.ParseCIDR("fd17::/64") -// if err != nil { -// log.Fatalf("portmaster: could not parse fd17::/64: %s", err) -// } -// -// go tunnelInfoCleaner() -// } -// -// type TunnelInfo struct { -// IP net.IP -// Domain string -// RRCache *intel.RRCache -// Expires int64 -// } -// -// func (ti *TunnelInfo) ExportTunnelIP() *intel.RRCache { -// return &intel.RRCache{ -// Answer: []dns.RR{ -// &dns.A{ -// Hdr: dns.RR_Header{ -// Name: ti.Domain, -// Rrtype: 1, -// Class: 1, -// Ttl: 17, -// Rdlength: 8, -// }, -// A: ti.IP, -// }, -// }, -// } -// } -// -// func AssignTunnelIP(domain string) (*TunnelInfo, error) { -// ipToDomainMapLock.Lock() -// defer ipToDomainMapLock.Unlock() -// -// for i := 0; i < 100; i++ { -// // get random IP -// r, err := random.Bytes(2) -// if err != nil { -// return nil, err -// } -// randomIP := net.IPv4(127, 17, r[0], r[1]) -// -// // clean after every 20 tries -// if i > 0 && i%20 == 0 { -// cleanExpiredTunnelInfos() -// } -// -// // if it does not exist yet, set and return -// _, ok := ipToDomainMap[randomIP.String()] -// if !ok { -// tunnelInfo := &TunnelInfo{ -// IP: randomIP, -// Domain: domain, -// Expires: time.Now().Add(5 * time.Minute).Unix(), -// } -// ipToDomainMap[randomIP.String()] = tunnelInfo -// return tunnelInfo, nil -// } -// } -// -// return nil, errors.New("could not find available tunnel IP, please retry later") -// } -// -// func GetTunnelInfo(tunnelIP net.IP) (tunnelInfo *TunnelInfo) { -// ipToDomainMapLock.RLock() -// defer ipToDomainMapLock.RUnlock() -// var ok bool -// tunnelInfo, ok = ipToDomainMap[tunnelIP.String()] -// if ok && tunnelInfo.Expires >= time.Now().Unix() { -// return tunnelInfo -// } -// return nil -// } -// -// func tunnelInfoCleaner() { -// for { -// time.Sleep(5 * time.Minute) -// ipToDomainMapLock.Lock() -// cleanExpiredTunnelInfos() -// ipToDomainMapLock.Unlock() -// } -// } -// -// func cleanExpiredTunnelInfos() { -// now := time.Now().Unix() -// for domain, tunnelInfo := range ipToDomainMap { -// if tunnelInfo.Expires < now { -// delete(ipToDomainMap, domain) -// } -// } -// } +import ( + "github.com/safing/portmaster/network" + "github.com/safing/portmaster/resolver" + "github.com/safing/spn/navigator" +) + +func setCustomTunnelOptionsForPortmaster(conn *network.Connection) { + switch { + case !tunnelEnabled(): + // Ignore when tunneling is not enabled. + return + case !conn.Entity.IPScope.IsGlobal(): + // Ignore if destination is not in global address space. + return + case resolver.IsResolverAddress(conn.Entity.IP, conn.Entity.Port): + // Set custom tunnel options for DNS servers. + conn.TunnelOpts = &navigator.Options{ + RoutingProfile: navigator.RoutingProfileHomeName, + } + } +} diff --git a/network/connection.go b/network/connection.go index af5ba4bb..84a63db1 100644 --- a/network/connection.go +++ b/network/connection.go @@ -7,15 +7,15 @@ import ( "sync" "time" - "github.com/safing/portmaster/netenv" - "github.com/safing/portbase/database/record" "github.com/safing/portbase/log" "github.com/safing/portmaster/intel" + "github.com/safing/portmaster/netenv" "github.com/safing/portmaster/network/netutils" "github.com/safing/portmaster/network/packet" "github.com/safing/portmaster/process" "github.com/safing/portmaster/resolver" + "github.com/safing/spn/navigator" ) // FirewallHandler defines the function signature for a firewall @@ -137,6 +137,8 @@ type Connection struct { //nolint:maligned // TODO: fix alignment Tunneled bool // Encrypted is currently unused and MUST be ignored. Encrypted bool + // TunnelOpts holds options for tunneling the connection. + TunnelOpts *navigator.Options // ProcessContext holds additional information about the process // that iniated the connection. It is set once when the connection // object is created and is considered immutable afterwards. diff --git a/resolver/resolvers.go b/resolver/resolvers.go index c235b85f..4cbaf46d 100644 --- a/resolver/resolvers.go +++ b/resolver/resolvers.go @@ -395,3 +395,18 @@ func checkSearchScope(searchDomain string) (ok bool) { return true } + +// IsResolverAddress returns whether the given ip and port match a configured resolver. +func IsResolverAddress(ip net.IP, port uint16) bool { + resolversLock.RLock() + defer resolversLock.RUnlock() + + // Check if the given IP and port matches a resolver. + for _, r := range globalResolvers { + if port == r.Info.Port && r.Info.IP.Equal(ip) { + return true + } + } + + return false +}