mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
Fix dnsonly nameserver
This commit is contained in:
parent
e37f706223
commit
79c62036f3
1 changed files with 97 additions and 25 deletions
|
@ -1,6 +1,7 @@
|
|||
package only
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
|
@ -12,10 +13,33 @@ import (
|
|||
"github.com/Safing/portmaster/analytics/algs"
|
||||
"github.com/Safing/portmaster/intel"
|
||||
"github.com/Safing/portmaster/network/netutils"
|
||||
"github.com/Safing/portmaster/status"
|
||||
)
|
||||
|
||||
var (
|
||||
localhostIPs []dns.RR
|
||||
)
|
||||
|
||||
func init() {
|
||||
modules.Register("nameserver", nil, start, nil, "intel")
|
||||
modules.Register("nameserver", prep, start, nil, "intel")
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
intel.SetLocalAddrFactory(func(network string) net.Addr { return nil })
|
||||
|
||||
localhostIPv4, err := dns.NewRR("localhost. 17 IN A 127.0.0.1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localhostIPv6, err := dns.NewRR("localhost. 17 IN AAAA ::1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
localhostIPs = []dns.RR{localhostIPv4, localhostIPv6}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func start() error {
|
||||
|
@ -44,62 +68,110 @@ func nxDomain(w dns.ResponseWriter, query *dns.Msg) {
|
|||
|
||||
func handleRequest(w dns.ResponseWriter, query *dns.Msg) {
|
||||
|
||||
// TODO: if there are 3 request for the same domain/type in a row, delete all caches of that domain
|
||||
// TODO: handle securityLevelOff
|
||||
|
||||
// only process first question, that's how everyone does it.
|
||||
question := query.Question[0]
|
||||
fqdn := dns.Fqdn(question.Name)
|
||||
qtype := dns.Type(question.Qtype)
|
||||
|
||||
// debug log
|
||||
rAddr, ok := w.RemoteAddr().(*net.UDPAddr)
|
||||
if !ok {
|
||||
log.Warningf("nameserver: could not get address of request, returning nxdomain")
|
||||
// check class
|
||||
if question.Qclass != dns.ClassINET {
|
||||
// we only serve IN records, return nxdomain
|
||||
nxDomain(w, query)
|
||||
return
|
||||
}
|
||||
// log.Tracef("nameserver: got request for %s%s from %s:%d", fqdn, qtype, rAddr.IP, uint16(rAddr.Port))
|
||||
|
||||
// use this to time how long it takes process this request
|
||||
// timed := time.Now()
|
||||
// defer log.Tracef("nameserver: took %s to handle request for %s%s", time.Now().Sub(timed).String(), fqdn, qtype.String())
|
||||
// handle request for localhost
|
||||
if fqdn == "localhost." {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(query)
|
||||
m.Answer = localhostIPs
|
||||
w.WriteMsg(m)
|
||||
}
|
||||
|
||||
// get addresses
|
||||
remoteAddr, ok := w.RemoteAddr().(*net.UDPAddr)
|
||||
if !ok {
|
||||
log.Warningf("nameserver: could not get remote address of request for %s%s, ignoring", fqdn, qtype)
|
||||
return
|
||||
}
|
||||
localAddr, ok := w.RemoteAddr().(*net.UDPAddr)
|
||||
if !ok {
|
||||
log.Warningf("nameserver: could not get local address of request for %s%s, ignoring", fqdn, qtype)
|
||||
return
|
||||
}
|
||||
|
||||
// ignore external request
|
||||
if !remoteAddr.IP.Equal(localAddr.IP) {
|
||||
log.Warningf("nameserver: external request for %s%s, ignoring", fqdn, qtype)
|
||||
return
|
||||
}
|
||||
|
||||
// check if valid domain name
|
||||
if !netutils.IsValidFqdn(fqdn) {
|
||||
log.Tracef("nameserver: domain name %s is invalid, returning nxdomain", fqdn)
|
||||
log.Debugf("nameserver: domain name %s is invalid, returning nxdomain", fqdn)
|
||||
nxDomain(w, query)
|
||||
return
|
||||
}
|
||||
|
||||
// start tracer
|
||||
ctx := log.AddTracer(context.Background())
|
||||
log.Tracer(ctx).Tracef("nameserver: handling new request for %s%s from %s:%d", fqdn, qtype, remoteAddr.IP, remoteAddr.Port)
|
||||
|
||||
// TODO: if there are 3 request for the same domain/type in a row, delete all caches of that domain
|
||||
|
||||
// check for possible DNS tunneling / data transmission
|
||||
// TODO: improve this
|
||||
lms := algs.LmsScoreOfDomain(fqdn)
|
||||
log.Tracef("nameserver: domain %s has lms score of %f", fqdn, lms)
|
||||
// log.Tracef("nameserver: domain %s has lms score of %f", fqdn, lms)
|
||||
if lms < 10 {
|
||||
log.Tracef("nameserver: possible data tunnel: %s has lms score of %f, returning nxdomain", fqdn, lms)
|
||||
nxDomain(w, query)
|
||||
return
|
||||
}
|
||||
|
||||
// check class
|
||||
if question.Qclass != dns.ClassINET {
|
||||
// we only serve IN records, send NXDOMAIN
|
||||
log.WarningTracef(ctx, "nameserver: possible data tunnel by %s:%d: %s has lms score of %f, returning nxdomain", remoteAddr.IP, remoteAddr.Port, fqdn, lms)
|
||||
nxDomain(w, query)
|
||||
return
|
||||
}
|
||||
|
||||
// get intel and RRs
|
||||
// start = time.Now()
|
||||
_, rrCache := intel.GetIntelAndRRs(fqdn, qtype, 0)
|
||||
_, rrCache := intel.GetIntelAndRRs(ctx, fqdn, qtype, status.SecurityLevelDynamic)
|
||||
// log.Tracef("nameserver: took %s to get intel and RRs", time.Since(start))
|
||||
if rrCache == nil {
|
||||
// TODO: analyze nxdomain requests, malware could be trying DGA-domains
|
||||
log.Infof("nameserver: %s%s is nxdomain", fqdn, qtype)
|
||||
log.WarningTracef(ctx, "nameserver: %s:%d requested %s%s, is nxdomain", remoteAddr.IP, remoteAddr.Port, fqdn, qtype)
|
||||
nxDomain(w, query)
|
||||
return
|
||||
}
|
||||
|
||||
// save IP addresses to IPInfo
|
||||
for _, rr := range append(rrCache.Answer, rrCache.Extra...) {
|
||||
switch v := rr.(type) {
|
||||
case *dns.A:
|
||||
ipInfo, err := intel.GetIPInfo(v.A.String())
|
||||
if err != nil {
|
||||
ipInfo = &intel.IPInfo{
|
||||
IP: v.A.String(),
|
||||
Domains: []string{fqdn},
|
||||
}
|
||||
ipInfo.Save()
|
||||
} else {
|
||||
if ipInfo.AddDomain(fqdn) {
|
||||
ipInfo.Save()
|
||||
}
|
||||
}
|
||||
case *dns.AAAA:
|
||||
ipInfo, err := intel.GetIPInfo(v.AAAA.String())
|
||||
if err != nil {
|
||||
ipInfo = &intel.IPInfo{
|
||||
IP: v.AAAA.String(),
|
||||
Domains: []string{fqdn},
|
||||
}
|
||||
ipInfo.Save()
|
||||
} else {
|
||||
if ipInfo.AddDomain(fqdn) {
|
||||
ipInfo.Save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reply to query
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(query)
|
||||
|
@ -107,5 +179,5 @@ func handleRequest(w dns.ResponseWriter, query *dns.Msg) {
|
|||
m.Ns = rrCache.Ns
|
||||
m.Extra = rrCache.Extra
|
||||
w.WriteMsg(m)
|
||||
log.Tracef("nameserver: replied to request for %s%s from %s:%d", fqdn, qtype, rAddr.IP, uint16(rAddr.Port))
|
||||
log.DebugTracef(ctx, "nameserver: returning response %s%s to %s:%d", fqdn, qtype, remoteAddr.IP, remoteAddr.Port)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue