diff --git a/firewall/interception/nfqueue_linux.go b/firewall/interception/nfqueue_linux.go index eb5d2008..409a6e73 100644 --- a/firewall/interception/nfqueue_linux.go +++ b/firewall/interception/nfqueue_linux.go @@ -80,7 +80,7 @@ func init() { "mangle INPUT -j C171", "filter OUTPUT -j C17", "filter INPUT -j C17", - "nat OUTPUT -m mark --mark 1799 -p udp -j DNAT --to 127.0.0.1:53", + "nat OUTPUT -m mark --mark 1799 -p udp -j DNAT --to 127.0.0.17:53", "nat OUTPUT -m mark --mark 1717 -p tcp -j DNAT --to 127.0.0.17:717", "nat OUTPUT -m mark --mark 1717 -p udp -j DNAT --to 127.0.0.17:717", // "nat OUTPUT -m mark --mark 1717 ! -p tcp ! -p udp -j DNAT --to 127.0.0.17", @@ -115,7 +115,7 @@ func init() { "mangle INPUT -j C171", "filter OUTPUT -j C17", "filter INPUT -j C17", - "nat OUTPUT -m mark --mark 1799 -p udp -j DNAT --to [::1]:53", + "nat OUTPUT -m mark --mark 1799 -p udp -j DNAT --to [fd17::17]:53", "nat OUTPUT -m mark --mark 1717 -p tcp -j DNAT --to [fd17::17]:717", "nat OUTPUT -m mark --mark 1717 -p udp -j DNAT --to [fd17::17]:717", // "nat OUTPUT -m mark --mark 1717 ! -p tcp ! -p udp -j DNAT --to [fd17::17]", diff --git a/firewall/master.go b/firewall/master.go index 3989057d..07c7fb22 100644 --- a/firewall/master.go +++ b/firewall/master.go @@ -301,15 +301,15 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack // we don't apply any checks here and let the request through // because a malformed domain-name will likely be dropped by // checks better suited for that. - log.Tracer(ctx).Warningf("nameserver: failed to get eTLD+1: %s", err) + log.Tracer(ctx).Warningf("filter: failed to get eTLD+1: %s", err) return false } domainToCheck := strings.Split(etld1, ".")[0] score := dga.LmsScore(domainToCheck) if score < 5 { - log.Tracer(ctx).Warningf( - "nameserver: possible data tunnel by %s in eTLD+1 %s: %s has an lms score of %.2f, returning nxdomain", + log.Tracer(ctx).Debugf( + "filter: possible data tunnel by %s in eTLD+1 %s: %s has an lms score of %.2f", conn.Process(), etld1, domainToCheck, @@ -318,7 +318,7 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack conn.Block("possible DGA domain commonly used by malware") return true } - log.Tracer(ctx).Infof("LMS score of eTLD+1 %s is %.2f", etld1, score) + log.Tracer(ctx).Tracef("filter: LMS score of eTLD+1 %s is %.2f", etld1, score) // 100 is a somewhat arbitrary threshold to ensure we don't mess // around with CDN domain names to early. They use short second-level @@ -328,8 +328,8 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack domainToCheck = trimmedDomain[0:len(etld1)] score := dga.LmsScoreOfDomain(domainToCheck) if score < 10 { - log.Tracer(ctx).Warningf( - "nameserver: possible data tunnel by %s in subdomain %s: %s has an lms score of %.2f, returning nxdomain", + log.Tracer(ctx).Debugf( + "filter: possible data tunnel by %s in subdomain of %s: %s has an lms score of %.2f", conn.Process(), conn.Entity.Domain, domainToCheck, @@ -338,7 +338,7 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack conn.Block("possible data tunnel for covert communication and protection bypassing") return true } - log.Tracer(ctx).Infof("LMS score of entire domain is %.2f", score) + log.Tracer(ctx).Tracef("filter: LMS score of entire domain is %.2f", score) } return false diff --git a/nameserver/nameserver.go b/nameserver/nameserver.go index ad457131..4ba385c1 100644 --- a/nameserver/nameserver.go +++ b/nameserver/nameserver.go @@ -138,26 +138,22 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er return nil } - // get addresses + // get remote address remoteAddr, ok := w.RemoteAddr().(*net.UDPAddr) if !ok { - log.Warningf("nameserver: could not get remote address of request for %s%s, ignoring", q.FQDN, q.QType) + log.Warningf("nameserver: failed to get remote address of request for %s%s, ignoring", q.FQDN, q.QType) return nil } - if !netutils.IPIsLocalhost(remoteAddr.IP) { - // If request is not from a localhost address, check it it's really local. - localAddr, ok := w.RemoteAddr().(*net.UDPAddr) - if !ok { - log.Warningf("nameserver: could not get local address of request for %s%s, ignoring", q.FQDN, q.QType) - return nil - } - - // ignore external request - if !remoteAddr.IP.Equal(localAddr.IP) { - log.Warningf("nameserver: external request for %s%s, ignoring", q.FQDN, q.QType) - return nil - } + // check if the request is local + local, err := netenv.IsMyIP(remoteAddr.IP) + if err != nil { + log.Warningf("nameserver: failed to check if request for %s%s is local: %s", q.FQDN, q.QType, err) + return nil + } + if !local { + log.Warningf("nameserver: external request for %s%s, ignoring", q.FQDN, q.QType) + return nil } // check if valid domain name diff --git a/netenv/addresses.go b/netenv/addresses.go index 19e2893d..15e14999 100644 --- a/netenv/addresses.go +++ b/netenv/addresses.go @@ -1,9 +1,11 @@ package netenv import ( + "fmt" "net" - "strings" + "sync" + "github.com/safing/portbase/log" "github.com/safing/portmaster/network/netutils" ) @@ -14,13 +16,16 @@ func GetAssignedAddresses() (ipv4 []net.IP, ipv6 []net.IP, err error) { return nil, nil, err } for _, addr := range addrs { - ip := net.ParseIP(strings.Split(addr.String(), "/")[0]) - if ip != nil { - if ip4 := ip.To4(); ip4 != nil { - ipv4 = append(ipv4, ip4) - } else { - ipv6 = append(ipv6, ip) - } + netAddr, ok := addr.(*net.IPNet) + if !ok { + log.Warningf("netenv: interface address of unexpected type %T", addr) + continue + } + + if ip4 := netAddr.IP.To4(); ip4 != nil { + ipv4 = append(ipv4, ip4) + } else { + ipv6 = append(ipv6, netAddr.IP) } } return @@ -44,3 +49,50 @@ func GetAssignedGlobalAddresses() (ipv4 []net.IP, ipv6 []net.IP, err error) { } return } + +var ( + myIPs []net.IP + myIPsLock sync.Mutex +) + +// IsMyIP returns whether the given IP is currently configured on the local host. +func IsMyIP(ip net.IP) (yes bool, err error) { + if netutils.IPIsLocalhost(ip) { + return true, nil + } + + myIPsLock.Lock() + defer myIPsLock.Unlock() + + // check + for _, myIP := range myIPs { + if ip.Equal(myIP) { + return true, nil + } + } + + // refresh IPs + myAddrs, err := net.InterfaceAddrs() + if err != nil { + return false, fmt.Errorf("failed to refresh interface addresses: %s", err) + } + myIPs = make([]net.IP, 0, len(myAddrs)) + for _, addr := range myAddrs { + netAddr, ok := addr.(*net.IPNet) + if !ok { + log.Warningf("netenv: interface address of unexpected type %T", addr) + continue + } + + myIPs = append(myIPs, netAddr.IP) + } + + // check again + for _, myIP := range myIPs { + if ip.Equal(myIP) { + return true, nil + } + } + + return false, nil +} diff --git a/network/state/udp.go b/network/state/udp.go index db238e47..ad596fa6 100644 --- a/network/state/udp.go +++ b/network/state/udp.go @@ -2,6 +2,7 @@ package state import ( "context" + "strconv" "sync" "time" @@ -155,5 +156,5 @@ func (table *udpTable) cleanStates(now time.Time) { func makeUDPStateKey(address socket.Address) string { // This could potentially go wrong, but as all IPs are created by the same source, everything should be fine. - return string(address.IP) + string(address.Port) + return string(address.IP) + strconv.Itoa(int(address.Port)) }