diff --git a/nameserver/only/nameserver.go b/nameserver/only/nameserver.go deleted file mode 100644 index 6d5cb5cb..00000000 --- a/nameserver/only/nameserver.go +++ /dev/null @@ -1,236 +0,0 @@ -package only - -import ( - "context" - "net" - "strings" - - "github.com/safing/portbase/database" - "github.com/safing/portbase/log" - "github.com/safing/portbase/modules" - "github.com/safing/portmaster/netenv" - "github.com/safing/portmaster/network/netutils" - "github.com/safing/portmaster/resolver" - - "github.com/miekg/dns" -) - -var ( - module *modules.Module - dnsServer *dns.Server - mtDNSRequest = "dns request" - - listenAddress = "127.0.0.1:53" - ipv4Localhost = net.IPv4(127, 0, 0, 1) - localhostRRs []dns.RR -) - -func init() { - module = modules.Register("nameserver", initLocalhostRRs, start, stop, "core", "resolver", "network", "netenv") -} - -func initLocalhostRRs() error { - 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 - } - - localhostRRs = []dns.RR{localhostIPv4, localhostIPv6} - return nil -} - -func start() error { - dnsServer = &dns.Server{Addr: listenAddress, Net: "udp"} - dns.HandleFunc(".", handleRequestAsMicroTask) - - module.StartServiceWorker("dns resolver", 0, func(ctx context.Context) error { - err := dnsServer.ListenAndServe() - if err != nil { - // check if we are shutting down - if module.IsStopping() { - return nil - } - } - return err - }) - - return nil -} - -func stop() error { - if dnsServer != nil { - return dnsServer.Shutdown() - } - return nil -} - -func returnNXDomain(w dns.ResponseWriter, query *dns.Msg) { - m := new(dns.Msg) - m.SetRcode(query, dns.RcodeNameError) - _ = w.WriteMsg(m) -} - -func returnServerFailure(w dns.ResponseWriter, query *dns.Msg) { - m := new(dns.Msg) - m.SetRcode(query, dns.RcodeServerFailure) - _ = w.WriteMsg(m) -} - -func handleRequestAsMicroTask(w dns.ResponseWriter, query *dns.Msg) { - err := module.RunMicroTask(&mtDNSRequest, func(ctx context.Context) error { - return handleRequest(ctx, w, query) - }) - if err != nil { - log.Warningf("nameserver: failed to handle dns request: %s", err) - } -} - -func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) error { - // return with server failure if offline - if netenv.GetOnlineStatus() == netenv.StatusOffline { - returnServerFailure(w, query) - return nil - } - - // only process first question, that's how everyone does it. - question := query.Question[0] - q := &resolver.Query{ - FQDN: question.Name, - QType: dns.Type(question.Qtype), - } - - // check class - if question.Qclass != dns.ClassINET { - // we only serve IN records, return nxdomain - returnNXDomain(w, query) - return nil - } - - // handle request for localhost - if strings.HasSuffix(q.FQDN, "localhost.") { - m := new(dns.Msg) - m.SetReply(query) - m.Answer = localhostRRs - _ = w.WriteMsg(m) - return nil - } - - // get addresses - 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) - return nil - } - if !remoteAddr.IP.Equal(ipv4Localhost) { - // if request is not coming from 127.0.0.1, check if 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 valid domain name - if !netutils.IsValidFqdn(q.FQDN) { - log.Debugf("nameserver: domain name %s is invalid, returning nxdomain", q.FQDN) - returnNXDomain(w, query) - return nil - } - - // start tracer - ctx, tracer := log.AddTracer(ctx) - tracer.Tracef("nameserver: handling new request for %s%s from %s:%d", q.FQDN, q.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 - - // get intel and RRs - rrCache, err := resolver.Resolve(ctx, q) - if err != nil { - // TODO: analyze nxdomain requests, malware could be trying DGA-domains - tracer.Warningf("nameserver: request for %s%s: %s", q.FQDN, q.QType, err) - returnNXDomain(w, query) - return nil - } - - // 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 - } - - // 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) - } - } - } - - // reply to query - m := new(dns.Msg) - m.SetReply(query) - m.Answer = rrCache.Answer - m.Ns = rrCache.Ns - m.Extra = rrCache.Extra - _ = w.WriteMsg(m) - tracer.Debugf("nameserver: returning response %s%s", q.FQDN, q.QType) - - return nil -}