Refactoring of FilterDNSResponse and DecideOnConnection
This commit is contained in:
230
firewall/dns.go
Normal file
230
firewall/dns.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/network"
|
||||
"github.com/safing/portmaster/network/netutils"
|
||||
"github.com/safing/portmaster/profile"
|
||||
"github.com/safing/portmaster/profile/endpoints"
|
||||
"github.com/safing/portmaster/resolver"
|
||||
)
|
||||
|
||||
func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) ([]dns.RR, []string, int) {
|
||||
goodEntries := make([]dns.RR, 0, len(entries))
|
||||
filteredRecords := make([]string, 0, len(entries))
|
||||
|
||||
// keeps track of the number of valid and allowed
|
||||
// A and AAAA records.
|
||||
var allowedAddressRecords int
|
||||
|
||||
for _, rr := range entries {
|
||||
// get IP and classification
|
||||
var ip net.IP
|
||||
switch v := rr.(type) {
|
||||
case *dns.A:
|
||||
ip = v.A
|
||||
case *dns.AAAA:
|
||||
ip = v.AAAA
|
||||
default:
|
||||
// add non A/AAAA entries
|
||||
goodEntries = append(goodEntries, rr)
|
||||
continue
|
||||
}
|
||||
classification := netutils.ClassifyIP(ip)
|
||||
|
||||
if p.RemoveOutOfScopeDNS() {
|
||||
switch {
|
||||
case classification == netutils.HostLocal:
|
||||
// No DNS should return localhost addresses
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
continue
|
||||
case scope == netutils.Global && (classification == netutils.SiteLocal || classification == netutils.LinkLocal):
|
||||
// No global DNS should return LAN addresses
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if p.RemoveBlockedDNS() {
|
||||
// filter by flags
|
||||
switch {
|
||||
case p.BlockScopeInternet() && classification == netutils.Global:
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
continue
|
||||
case p.BlockScopeLAN() && (classification == netutils.SiteLocal || classification == netutils.LinkLocal):
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
continue
|
||||
case p.BlockScopeLocal() && classification == netutils.HostLocal:
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: filter by endpoint list (IP only)
|
||||
}
|
||||
|
||||
// if survived, add to good entries
|
||||
allowedAddressRecords++
|
||||
goodEntries = append(goodEntries, rr)
|
||||
}
|
||||
|
||||
return goodEntries, filteredRecords, allowedAddressRecords
|
||||
}
|
||||
|
||||
func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *resolver.RRCache {
|
||||
// do not modify own queries
|
||||
if conn.Process().Pid == os.Getpid() {
|
||||
return rrCache
|
||||
}
|
||||
|
||||
// get profile
|
||||
p := conn.Process().Profile()
|
||||
if p == nil {
|
||||
conn.Block("no profile")
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if DNS response filtering is completely turned off
|
||||
if !p.RemoveOutOfScopeDNS() && !p.RemoveBlockedDNS() {
|
||||
return rrCache
|
||||
}
|
||||
|
||||
// duplicate entry
|
||||
rrCache = rrCache.ShallowCopy()
|
||||
rrCache.FilteredEntries = make([]string, 0)
|
||||
|
||||
var filteredRecords []string
|
||||
var validIPs int
|
||||
|
||||
rrCache.Answer, filteredRecords, validIPs = filterDNSSection(rrCache.Answer, p, rrCache.ServerScope)
|
||||
rrCache.FilteredEntries = append(rrCache.FilteredEntries, filteredRecords...)
|
||||
|
||||
// FIXME(ppacher): should we consider valid IPs from the extra section?
|
||||
rrCache.Extra, filteredRecords, _ = filterDNSSection(rrCache.Extra, p, rrCache.ServerScope)
|
||||
rrCache.FilteredEntries = append(rrCache.FilteredEntries, filteredRecords...)
|
||||
|
||||
if len(rrCache.FilteredEntries) > 0 {
|
||||
rrCache.Filtered = true
|
||||
if validIPs == 0 {
|
||||
conn.Block("no addresses returned for this domain are permitted")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("filter: filtered DNS replies for %s: %s", conn, strings.Join(rrCache.FilteredEntries, ", "))
|
||||
}
|
||||
|
||||
return rrCache
|
||||
}
|
||||
|
||||
// DecideOnResolvedDNS filters a dns response according to the application profile and settings.
|
||||
func DecideOnResolvedDNS(conn *network.Connection, q *resolver.Query, rrCache *resolver.RRCache) *resolver.RRCache {
|
||||
updatedRR := filterDNSResponse(conn, rrCache)
|
||||
if updatedRR == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
updateIPsAndCNAMEs(q, rrCache, conn)
|
||||
|
||||
if mayBlockCNAMEs(conn) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Gate17 integration
|
||||
// tunnelInfo, err := AssignTunnelIP(fqdn)
|
||||
|
||||
return updatedRR
|
||||
}
|
||||
|
||||
func mayBlockCNAMEs(conn *network.Connection) bool {
|
||||
// if we have CNAMEs and the profile is configured to filter them
|
||||
// we need to re-check the lists and endpoints here
|
||||
if conn.Process().Profile().FilterCNAMEs() {
|
||||
conn.Entity.ResetLists()
|
||||
conn.Entity.EnableCNAMECheck(true)
|
||||
|
||||
result, reason := conn.Process().Profile().MatchEndpoint(conn.Entity)
|
||||
if result == endpoints.Denied {
|
||||
conn.BlockWithContext(reason.String(), reason.Context())
|
||||
return true
|
||||
}
|
||||
|
||||
if result == endpoints.NoMatch {
|
||||
result, reason = conn.Process().Profile().MatchFilterLists(conn.Entity)
|
||||
if result == endpoints.Denied {
|
||||
conn.BlockWithContext(reason.String(), reason.Context())
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func updateIPsAndCNAMEs(q *resolver.Query, rrCache *resolver.RRCache, conn *network.Connection) {
|
||||
// save IP addresses to IPInfo
|
||||
cnames := make(map[string]string)
|
||||
ips := make(map[string]struct{})
|
||||
|
||||
for _, rr := range append(rrCache.Answer, rrCache.Extra...) {
|
||||
switch v := rr.(type) {
|
||||
case *dns.CNAME:
|
||||
cnames[v.Hdr.Name] = v.Target
|
||||
|
||||
case *dns.A:
|
||||
ips[v.A.String()] = struct{}{}
|
||||
|
||||
case *dns.AAAA:
|
||||
ips[v.AAAA.String()] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for ip := range ips {
|
||||
record := resolver.ResolvedDomain{
|
||||
Domain: q.FQDN,
|
||||
}
|
||||
|
||||
// resolve all CNAMEs in the correct order.
|
||||
var domain = q.FQDN
|
||||
for {
|
||||
nextDomain, isCNAME := cnames[domain]
|
||||
if !isCNAME {
|
||||
break
|
||||
}
|
||||
|
||||
record.CNAMEs = append(record.CNAMEs, nextDomain)
|
||||
domain = nextDomain
|
||||
}
|
||||
|
||||
// update the entity to include the cnames
|
||||
conn.Entity.CNAME = record.CNAMEs
|
||||
|
||||
// get the existing IP info or create a new one
|
||||
var save bool
|
||||
info, err := resolver.GetIPInfo(ip)
|
||||
if err != nil {
|
||||
if err != database.ErrNotFound {
|
||||
log.Errorf("nameserver: failed to search for IP info record: %s", err)
|
||||
}
|
||||
|
||||
info = &resolver.IPInfo{
|
||||
IP: ip,
|
||||
}
|
||||
save = true
|
||||
}
|
||||
|
||||
// and the new resolved domain record and save
|
||||
if new := info.AddDomain(record); new {
|
||||
save = true
|
||||
}
|
||||
if save {
|
||||
if err := info.Save(); err != nil {
|
||||
log.Errorf("nameserver: failed to save IP info record: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package firewall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -14,10 +13,8 @@ import (
|
||||
"github.com/safing/portmaster/process"
|
||||
"github.com/safing/portmaster/profile"
|
||||
"github.com/safing/portmaster/profile/endpoints"
|
||||
"github.com/safing/portmaster/resolver"
|
||||
|
||||
"github.com/agext/levenshtein"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Call order:
|
||||
@@ -26,7 +23,7 @@ import (
|
||||
// 1. DecideOnConnection
|
||||
// is called when a DNS query is made, may set verdict to Undeterminable to permit a DNS reply.
|
||||
// is called with a nil packet.
|
||||
// 2. FilterDNSResponse
|
||||
// 2. DecideOnResolvedDNS
|
||||
// is called to (possibly) filter out A/AAAA records that the filter would deny later.
|
||||
//
|
||||
// Network Connection:
|
||||
@@ -35,7 +32,7 @@ import (
|
||||
|
||||
// DecideOnConnection makes a decision about a connection.
|
||||
// When called, the connection and profile is already locked.
|
||||
func DecideOnConnection(conn *network.Connection, pkt packet.Packet) { //nolint:gocognit,gocyclo // TODO
|
||||
func DecideOnConnection(conn *network.Connection, pkt packet.Packet) {
|
||||
// update profiles and check if communication needs reevaluation
|
||||
if conn.UpdateAndCheck() {
|
||||
log.Infof("filter: re-evaluating verdict on %s", conn)
|
||||
@@ -46,14 +43,47 @@ func DecideOnConnection(conn *network.Connection, pkt packet.Packet) { //nolint:
|
||||
}
|
||||
}
|
||||
|
||||
var deciders = []func(*network.Connection, packet.Packet) bool{
|
||||
checkPortmasterConnection,
|
||||
checkSelfCommunication,
|
||||
checkProfileExists,
|
||||
checkConnectionType,
|
||||
checkConnectionScope,
|
||||
checkEndpointLists,
|
||||
checkBypassPrevention,
|
||||
checkFilterLists,
|
||||
checkInbound,
|
||||
checkDefaultPermit,
|
||||
checkAutoPermitRelated,
|
||||
checkDefaultAction,
|
||||
}
|
||||
|
||||
for _, decider := range deciders {
|
||||
if decider(conn, pkt) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultAction == DefaultActionBlock
|
||||
conn.Deny("endpoint is not whitelisted (default=block)")
|
||||
}
|
||||
|
||||
// checkPortmasterConnection allows all connection that originate from
|
||||
// portmaster itself.
|
||||
func checkPortmasterConnection(conn *network.Connection, _ packet.Packet) bool {
|
||||
// grant self
|
||||
if conn.Process().Pid == os.Getpid() {
|
||||
log.Infof("filter: granting own connection %s", conn)
|
||||
conn.Verdict = network.VerdictAccept
|
||||
conn.Internal = true
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// checkSelfCommunication checks if the process is communicating with itself.
|
||||
func checkSelfCommunication(conn *network.Connection, pkt packet.Packet) bool {
|
||||
// check if process is communicating with itself
|
||||
if pkt != nil {
|
||||
// TODO: evaluate the case where different IPs in the 127/8 net are used.
|
||||
@@ -77,89 +107,30 @@ func DecideOnConnection(conn *network.Connection, pkt packet.Packet) { //nolint:
|
||||
} else if otherProcess.Pid == conn.Process().Pid {
|
||||
conn.Accept("connection to self")
|
||||
conn.Internal = true
|
||||
return
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get profile
|
||||
p := conn.Process().Profile()
|
||||
if p == nil {
|
||||
conn.Block("no profile")
|
||||
return
|
||||
}
|
||||
|
||||
// check conn type
|
||||
switch conn.Scope {
|
||||
case network.IncomingHost, network.IncomingLAN, network.IncomingInternet, network.IncomingInvalid:
|
||||
if p.BlockInbound() {
|
||||
if conn.Scope == network.IncomingHost {
|
||||
conn.Block("inbound connections blocked")
|
||||
} else {
|
||||
conn.Drop("inbound connections blocked")
|
||||
}
|
||||
return
|
||||
}
|
||||
case network.PeerLAN, network.PeerInternet, network.PeerInvalid:
|
||||
// Important: PeerHost is and should be missing!
|
||||
if p.BlockP2P() {
|
||||
conn.Block("direct connections (P2P) blocked")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// check scopes
|
||||
if conn.Entity.IP != nil {
|
||||
classification := netutils.ClassifyIP(conn.Entity.IP)
|
||||
|
||||
switch classification {
|
||||
case netutils.Global, netutils.GlobalMulticast:
|
||||
if p.BlockScopeInternet() {
|
||||
conn.Deny("Internet access blocked") // Block Outbound / Drop Inbound
|
||||
return
|
||||
}
|
||||
case netutils.SiteLocal, netutils.LinkLocal, netutils.LocalMulticast:
|
||||
if p.BlockScopeLAN() {
|
||||
conn.Block("LAN access blocked") // Block Outbound / Drop Inbound
|
||||
return
|
||||
}
|
||||
case netutils.HostLocal:
|
||||
if p.BlockScopeLocal() {
|
||||
conn.Block("Localhost access blocked") // Block Outbound / Drop Inbound
|
||||
return
|
||||
}
|
||||
default: // netutils.Invalid
|
||||
conn.Deny("invalid IP") // Block Outbound / Drop Inbound
|
||||
return
|
||||
}
|
||||
} else if conn.Entity.Domain != "" {
|
||||
// DNS Query
|
||||
// DNS is expected to resolve to LAN or Internet addresses
|
||||
// TODO: handle domains mapped to localhost
|
||||
if p.BlockScopeInternet() && p.BlockScopeLAN() {
|
||||
conn.Block("Internet and LAN access blocked")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if p.PreventBypassing() {
|
||||
// check for bypass protection
|
||||
result, reason := PreventBypassing(conn)
|
||||
switch result {
|
||||
case endpoints.Denied:
|
||||
conn.Block("bypass prevention: " + reason)
|
||||
return
|
||||
case endpoints.Permitted:
|
||||
conn.Accept("bypass prevention: " + reason)
|
||||
return
|
||||
case endpoints.NoMatch:
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkProfileExists(conn *network.Connection, _ packet.Packet) bool {
|
||||
if conn.Process().Profile() == nil {
|
||||
conn.Block("unknown process or profile")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkEndpointLists(conn *network.Connection, _ packet.Packet) bool {
|
||||
var result endpoints.EPResult
|
||||
var reason endpoints.Reason
|
||||
|
||||
// there must always be a profile.
|
||||
p := conn.Process().Profile()
|
||||
|
||||
// check endpoints list
|
||||
if conn.Inbound {
|
||||
result, reason = p.MatchServiceEndpoint(conn.Entity)
|
||||
@@ -169,167 +140,151 @@ func DecideOnConnection(conn *network.Connection, pkt packet.Packet) { //nolint:
|
||||
switch result {
|
||||
case endpoints.Denied:
|
||||
conn.DenyWithContext(reason.String(), reason.Context())
|
||||
return
|
||||
return true
|
||||
case endpoints.Permitted:
|
||||
conn.AcceptWithContext(reason.String(), reason.Context())
|
||||
return
|
||||
return true
|
||||
}
|
||||
// continuing with result == NoMatch
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkConnectionType(conn *network.Connection, _ packet.Packet) bool {
|
||||
p := conn.Process().Profile()
|
||||
|
||||
// check conn type
|
||||
switch conn.Scope {
|
||||
case network.IncomingHost, network.IncomingLAN, network.IncomingInternet, network.IncomingInvalid:
|
||||
if p.BlockInbound() {
|
||||
if conn.Scope == network.IncomingHost {
|
||||
conn.Block("inbound connections blocked")
|
||||
} else {
|
||||
conn.Drop("inbound connections blocked")
|
||||
}
|
||||
return true
|
||||
}
|
||||
case network.PeerLAN, network.PeerInternet, network.PeerInvalid:
|
||||
// Important: PeerHost is and should be missing!
|
||||
if p.BlockP2P() {
|
||||
conn.Block("direct connections (P2P) blocked")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkConnectionScope(conn *network.Connection, _ packet.Packet) bool {
|
||||
p := conn.Process().Profile()
|
||||
|
||||
// check scopes
|
||||
if conn.Entity.IP != nil {
|
||||
classification := netutils.ClassifyIP(conn.Entity.IP)
|
||||
|
||||
switch classification {
|
||||
case netutils.Global, netutils.GlobalMulticast:
|
||||
if p.BlockScopeInternet() {
|
||||
conn.Deny("Internet access blocked") // Block Outbound / Drop Inbound
|
||||
return true
|
||||
}
|
||||
case netutils.SiteLocal, netutils.LinkLocal, netutils.LocalMulticast:
|
||||
if p.BlockScopeLAN() {
|
||||
conn.Block("LAN access blocked") // Block Outbound / Drop Inbound
|
||||
return true
|
||||
}
|
||||
case netutils.HostLocal:
|
||||
if p.BlockScopeLocal() {
|
||||
conn.Block("Localhost access blocked") // Block Outbound / Drop Inbound
|
||||
return true
|
||||
}
|
||||
default: // netutils.Invalid
|
||||
conn.Deny("invalid IP") // Block Outbound / Drop Inbound
|
||||
return true
|
||||
}
|
||||
} else if conn.Entity.Domain != "" {
|
||||
// DNS Query
|
||||
// DNS is expected to resolve to LAN or Internet addresses
|
||||
// TODO: handle domains mapped to localhost
|
||||
if p.BlockScopeInternet() && p.BlockScopeLAN() {
|
||||
conn.Block("Internet and LAN access blocked")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkBypassPrevention(conn *network.Connection, _ packet.Packet) bool {
|
||||
if conn.Process().Profile().PreventBypassing() {
|
||||
// check for bypass protection
|
||||
result, reason := PreventBypassing(conn)
|
||||
switch result {
|
||||
case endpoints.Denied:
|
||||
conn.Block("bypass prevention: " + reason)
|
||||
return true
|
||||
case endpoints.Permitted:
|
||||
conn.Accept("bypass prevention: " + reason)
|
||||
return true
|
||||
case endpoints.NoMatch:
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkFilterLists(conn *network.Connection, _ packet.Packet) bool {
|
||||
// apply privacy filter lists
|
||||
result, reason = p.MatchFilterLists(conn.Entity)
|
||||
p := conn.Process().Profile()
|
||||
|
||||
result, reason := p.MatchFilterLists(conn.Entity)
|
||||
switch result {
|
||||
case endpoints.Denied:
|
||||
conn.DenyWithContext(reason.String(), reason.Context())
|
||||
return
|
||||
return true
|
||||
case endpoints.NoMatch:
|
||||
// nothing to do
|
||||
default:
|
||||
log.Debugf("filter: filter lists returned unsupported verdict: %s", result)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkInbound(conn *network.Connection, _ packet.Packet) bool {
|
||||
// implicit default=block for inbound
|
||||
if conn.Inbound {
|
||||
conn.Drop("endpoint is not whitelisted (incoming is always default=block)")
|
||||
return
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkDefaultPermit(conn *network.Connection, _ packet.Packet) bool {
|
||||
// check default action
|
||||
p := conn.Process().Profile()
|
||||
if p.DefaultAction() == profile.DefaultActionPermit {
|
||||
conn.Accept("endpoint is not blacklisted (default=permit)")
|
||||
return
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// check relation
|
||||
func checkAutoPermitRelated(conn *network.Connection, _ packet.Packet) bool {
|
||||
p := conn.Process().Profile()
|
||||
if !p.DisableAutoPermit() {
|
||||
related, reason := checkRelation(conn)
|
||||
if related {
|
||||
conn.Accept(reason)
|
||||
return
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// prompt
|
||||
if p.DefaultAction() == profile.DefaultActionAsk {
|
||||
prompt(conn, pkt)
|
||||
return
|
||||
}
|
||||
|
||||
// DefaultAction == DefaultActionBlock
|
||||
conn.Deny("endpoint is not whitelisted (default=block)")
|
||||
return false
|
||||
}
|
||||
|
||||
// FilterDNSResponse filters a dns response according to the application profile and settings.
|
||||
func FilterDNSResponse(conn *network.Connection, q *resolver.Query, rrCache *resolver.RRCache) *resolver.RRCache { //nolint:gocognit // TODO
|
||||
// do not modify own queries
|
||||
if conn.Process().Pid == os.Getpid() {
|
||||
return rrCache
|
||||
}
|
||||
|
||||
// get profile
|
||||
func checkDefaultAction(conn *network.Connection, pkt packet.Packet) bool {
|
||||
p := conn.Process().Profile()
|
||||
if p == nil {
|
||||
conn.Block("no profile")
|
||||
return nil
|
||||
if p.DefaultAction() == profile.DefaultActionAsk {
|
||||
prompt(conn, pkt)
|
||||
return true
|
||||
}
|
||||
|
||||
// check if DNS response filtering is completely turned off
|
||||
if !p.RemoveOutOfScopeDNS() && !p.RemoveBlockedDNS() {
|
||||
return rrCache
|
||||
}
|
||||
|
||||
// duplicate entry
|
||||
rrCache = rrCache.ShallowCopy()
|
||||
rrCache.FilteredEntries = make([]string, 0)
|
||||
|
||||
// change information
|
||||
var addressesRemoved int
|
||||
var addressesOk int
|
||||
|
||||
// loop vars
|
||||
var classification int8
|
||||
var ip net.IP
|
||||
|
||||
// filter function
|
||||
filterEntries := func(entries []dns.RR) (goodEntries []dns.RR) {
|
||||
goodEntries = make([]dns.RR, 0, len(entries))
|
||||
|
||||
for _, rr := range entries {
|
||||
|
||||
// get IP and classification
|
||||
switch v := rr.(type) {
|
||||
case *dns.A:
|
||||
ip = v.A
|
||||
case *dns.AAAA:
|
||||
ip = v.AAAA
|
||||
default:
|
||||
// add non A/AAAA entries
|
||||
goodEntries = append(goodEntries, rr)
|
||||
continue
|
||||
}
|
||||
classification = netutils.ClassifyIP(ip)
|
||||
|
||||
if p.RemoveOutOfScopeDNS() {
|
||||
switch {
|
||||
case classification == netutils.HostLocal:
|
||||
// No DNS should return localhost addresses
|
||||
addressesRemoved++
|
||||
rrCache.FilteredEntries = append(rrCache.FilteredEntries, rr.String())
|
||||
continue
|
||||
case rrCache.ServerScope == netutils.Global && (classification == netutils.SiteLocal || classification == netutils.LinkLocal):
|
||||
// No global DNS should return LAN addresses
|
||||
addressesRemoved++
|
||||
rrCache.FilteredEntries = append(rrCache.FilteredEntries, rr.String())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if p.RemoveBlockedDNS() {
|
||||
// filter by flags
|
||||
switch {
|
||||
case p.BlockScopeInternet() && classification == netutils.Global:
|
||||
addressesRemoved++
|
||||
rrCache.FilteredEntries = append(rrCache.FilteredEntries, rr.String())
|
||||
continue
|
||||
case p.BlockScopeLAN() && (classification == netutils.SiteLocal || classification == netutils.LinkLocal):
|
||||
addressesRemoved++
|
||||
rrCache.FilteredEntries = append(rrCache.FilteredEntries, rr.String())
|
||||
continue
|
||||
case p.BlockScopeLocal() && classification == netutils.HostLocal:
|
||||
addressesRemoved++
|
||||
rrCache.FilteredEntries = append(rrCache.FilteredEntries, rr.String())
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: filter by endpoint list (IP only)
|
||||
}
|
||||
|
||||
// if survived, add to good entries
|
||||
addressesOk++
|
||||
goodEntries = append(goodEntries, rr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
rrCache.Answer = filterEntries(rrCache.Answer)
|
||||
rrCache.Extra = filterEntries(rrCache.Extra)
|
||||
|
||||
if addressesRemoved > 0 {
|
||||
rrCache.Filtered = true
|
||||
if addressesOk == 0 {
|
||||
conn.Block("no addresses returned for this domain are permitted")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if rrCache.Filtered {
|
||||
log.Infof("filter: filtered DNS replies for %s: %s", conn, strings.Join(rrCache.FilteredEntries, ", "))
|
||||
}
|
||||
|
||||
// TODO: Gate17 integration
|
||||
// tunnelInfo, err := AssignTunnelIP(fqdn)
|
||||
|
||||
return rrCache
|
||||
return false
|
||||
}
|
||||
|
||||
// checkRelation tries to find a relation between a process and a communication. This is for better out of the box experience and is _not_ meant to thwart intentional malware.
|
||||
|
||||
Reference in New Issue
Block a user