// Handles an incoming DNS request packet. This function decides whether // the hostname listed in the DNS packet is worthy of manipulation, or // not. The IP addresses listed in the reply to the user for a target // hostname are added to the routing table at this time before a // reply is sent back to the user, otherwise the user agent of the client // might connect faster than the routing changes can be made. func handleRequest(w dns.ResponseWriter, req *dns.Msg) { var m *dns.Msg // check if the the hostname in the request matches the target if len(req.Question) > 0 && isTargetZone(req.Question[0].Name) { // handle `A` and `AAAA` types accordingly // other record types will be forwarded without manipulation switch req.Question[0].Qtype { case dns.TypeA: m = handleV4Hijack(w, req) case dns.TypeAAAA: m = handleV6Hijack(w, req) } } // if no reply was previously set, forward it if m == nil { m = getServerReply(w, req) } // send reply back to user w.WriteMsg(m) }
// handleTest is used to handle DNS queries in the ".consul." domain func (d *DNSServer) handleTest(resp dns.ResponseWriter, req *dns.Msg) { q := req.Question[0] defer func(s time.Time) { d.logger.Printf("[DEBUG] dns: request for %v (%v)", q, time.Now().Sub(s)) }(time.Now()) if !(q.Qtype == dns.TypeANY || q.Qtype == dns.TypeTXT) { return } if q.Name != testQuery { return } // Always respond with TXT "ok" m := new(dns.Msg) m.SetReply(req) m.Authoritative = true m.RecursionAvailable = true header := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0} txt := &dns.TXT{header, []string{"ok"}} m.Answer = append(m.Answer, txt) d.addSOA(consulDomain, m) if err := resp.WriteMsg(m); err != nil { d.logger.Printf("[WARN] dns: failed to respond: %v", err) } }
// handleDNS is a handler function to actualy perform the dns querey response func (c *CatchAll) handleDNS(w dns.ResponseWriter, r *dns.Msg) { defer w.Close() var rr dns.RR domainSpoof := r.Question[0].Name msgResp := new(dns.Msg) msgResp.SetReply(r) msgResp.Compress = false rr = new(dns.A) if c.SpoofDomain { rr.(*dns.A).Hdr = dns.RR_Header{Name: domainSpoof, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} } else { rr.(*dns.A).Hdr = dns.RR_Header{Name: c.Domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} } rr.(*dns.A).A = c.IP switch r.Question[0].Qtype { case dns.TypeA: msgResp.Answer = append(msgResp.Answer, rr) default: log.Warnf("Unknown dns type %T", r.Question[0].Qtype) return } w.WriteMsg(msgResp) }
// handleQUery is used to handle DNS queries in the configured domain func (d *DNSServer) handleQuery(resp dns.ResponseWriter, req *dns.Msg) { q := req.Question[0] defer func(s time.Time) { d.logger.Printf("[DEBUG] dns: request for %v (%v)", q, time.Now().Sub(s)) }(time.Now()) // Switch to TCP if the client is network := "udp" if _, ok := resp.RemoteAddr().(*net.TCPAddr); ok { network = "tcp" } // Setup the message response m := new(dns.Msg) m.SetReply(req) m.Authoritative = true m.RecursionAvailable = (len(d.recursors) > 0) // Only add the SOA if requested if req.Question[0].Qtype == dns.TypeSOA { d.addSOA(d.domain, m) } // Dispatch the correct handler d.dispatch(network, req, m) // Write out the complete response if err := resp.WriteMsg(m); err != nil { d.logger.Printf("[WARN] dns: failed to respond: %v", err) } }
// resolver responds to all DNS A record requests with an address from addrpool, // maintaining a mapping to the domain's actual IP address. func resolver(w dns.ResponseWriter, req *dns.Msg) { msg, err := dns.Exchange(req, dnsserver) if err != nil { log.Printf("Couldn't query: %v", err) // TODO return error Msg return } for _, rr := range msg.Answer { // TODO do this for only one record, delete the others. if rr.Header().Rrtype == dns.TypeA { a := rr.(*dns.A) addrpool.Lock() addr, ok := addrpool.domains[a.Hdr.Name] // Maybe we should also Get it on ok to push it up the LRU cache. if !ok { addrpool.pool.RemoveOldest() addrpool.pool.Add(ip4touint32(addrpool.freeaddr), a.Hdr.Name) log.Printf("Adding %v -> %s", addrpool.freeaddr, a.Hdr.Name) addr = addrpool.freeaddr addrpool.domains[a.Hdr.Name] = addr } addrpool.Unlock() log.Println("Type A:", a.A) a.A = addr a.Hdr.Ttl = 1 } } w.WriteMsg(msg) }
func (s *soa) TransferHandler(w dns.ResponseWriter, req *dns.Msg) { m := new(dns.Msg) m.SetReply(req) m.Answer = make([]dns.RR, 1) m.Answer[0] = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, s.serial)) w.WriteMsg(m) }
// Create DNS packet with the config in line with the meta zone // paper from Vixie func metazone(w dns.ResponseWriter, req *dns.Msg, c *Config) { logPrintf("metazone command") // Only called when the class is CHAOS // PTR zone. -> get a list of zone names // Top level zone stuff -- list them if strings.ToUpper(req.Question[0].Name) == "ZONE." { m := new(dns.Msg) m.SetReply(req) for _, z := range c.Zones { ptr, _ := dns.NewRR("zone. 0 CH PTR " + z.Origin) m.Answer = append(m.Answer, ptr) } w.WriteMsg(m) return } // Top level user stuff -- list them if strings.ToUpper(req.Question[0].Name) == "USER." { } // <zone>.ZONE. formerr(w, req) return }
func (r *resolver) forwardQueryStart(w dns.ResponseWriter, msg *dns.Msg, queryID uint16) bool { proto := w.LocalAddr().Network() dnsID := uint16(rand.Intn(maxDNSID)) cc := clientConn{ dnsID: queryID, respWriter: w, } r.queryLock.Lock() defer r.queryLock.Unlock() if r.count == maxConcurrent { return false } r.count++ switch proto { case "tcp": break case "udp": for ok := true; ok == true; dnsID = uint16(rand.Intn(maxDNSID)) { _, ok = r.client[dnsID] } log.Debugf("client dns id %v, changed id %v", queryID, dnsID) r.client[dnsID] = cc msg.Id = dnsID default: log.Errorf("Invalid protocol..") return false } return true }
func (r *resolver) forwardQueryEnd(w dns.ResponseWriter, msg *dns.Msg) dns.ResponseWriter { var ( cc clientConn ok bool ) proto := w.LocalAddr().Network() r.queryLock.Lock() defer r.queryLock.Unlock() if r.count == 0 { log.Errorf("Invalid concurrent query count") } else { r.count-- } switch proto { case "tcp": break case "udp": if cc, ok = r.client[msg.Id]; ok == false { log.Debugf("Can't retrieve client context for dns id %v", msg.Id) return nil } log.Debugf("dns msg id %v, client id %v", msg.Id, cc.dnsID) delete(r.client, msg.Id) msg.Id = cc.dnsID w = cc.respWriter default: log.Errorf("Invalid protocol") return nil } return w }
func (s *DNS) handleDNSExternal(w dns.ResponseWriter, req *dns.Msg) { network := "udp" if _, ok := w.RemoteAddr().(*net.TCPAddr); ok { network = "tcp" } c := &dns.Client{Net: network} var r *dns.Msg var err error for _, recursor := range s.recursors { if recursor == "" { log.Printf("Found empty recursor") continue } log.Printf("Forwarding request to external recursor for: %s", req.Question[0].Name) r, _, err = c.Exchange(req, recursor) if err == nil { if err := w.WriteMsg(r); err != nil { log.Printf("DNS lookup failed %v", err) } return } } dns.HandleFailed(w, req) }
func handleDnsRequest(w dns.ResponseWriter, r *dns.Msg) { m := new(dns.Msg) m.SetReply(r) records := make([]dns.RR, 0) q := r.Question[0] if q.Qtype == dns.TypeA && strings.HasSuffix(q.Name, ".docker.") { docker, _ := dockerclient.NewDockerClient("unix:///var/run/docker.sock", &tls.Config{}) name := strings.SplitN(q.Name, ".", 2)[0] containers, err := docker.ListContainers(false, false, fmt.Sprintf("{\"name\":[\"%s\"]}", name)) if err != nil { log.Fatal(err) } for _, c := range containers { info, _ := docker.InspectContainer(c.Id) log.Printf("Container %s[%6s] has ip %s\n", name, info.Id, info.NetworkSettings.IPAddress) records = append(records, &dns.A{ Hdr: dns.RR_Header{ Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60}, A: net.ParseIP(info.NetworkSettings.IPAddress), }) } } m.Answer = append(m.Answer, records...) defer w.WriteMsg(m) }
func (s *DNS) handleDNSInternal(w dns.ResponseWriter, req *dns.Msg) { q := req.Question[0] if q.Qtype == dns.TypeA && q.Qclass == dns.ClassINET { if record, ok := s.cache.Get(q.Name); ok { log.Printf("Found internal record for %s", q.Name) m := new(dns.Msg) m.SetReply(req) rr_header := dns.RR_Header{ Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0, } a := &dns.A{rr_header, net.ParseIP(record.ip)} m.Answer = append(m.Answer, a) w.WriteMsg(m) return } log.Printf("No internal record found for %s", q.Name) dns.HandleFailed(w, req) } log.Printf("Only handling type A requests, skipping") dns.HandleFailed(w, req) }
func (s *DNS) handleReverseDNSLookup(w dns.ResponseWriter, req *dns.Msg) { q := req.Question[0] if q.Qtype == dns.TypePTR && q.Qclass == dns.ClassINET { if record, ok := s.cache.Get(q.Name); ok { log.Printf("Found internal record for %s", q.Name) m := new(dns.Msg) m.SetReply(req) rr_header := dns.RR_Header{ Name: q.Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0, } a := &dns.PTR{rr_header, record.fqdn} m.Answer = append(m.Answer, a) w.WriteMsg(m) return } log.Printf("Forwarding request to external recursor for: %s", q.Name) // Forward the request s.handleDNSExternal(w, req) } dns.HandleFailed(w, req) }
// Forwards a DNS request to the specified nameservers. On success, the // original reply packet will be returned to the caller. On failure, a // new packet will be returned with `RCODE` set to `SERVFAIL`. // Even though the original `ResponseWriter` object is taken as an argument, // this function does not send a reply to the client. Instead, the // packet is returned for further processing by the caller. func getServerReply(w dns.ResponseWriter, req *dns.Msg) *dns.Msg { if *verbose { log.Print("Forwarding ", req.Question[0].Name, "/", dns.Type(req.Question[0].Qtype).String()) } // create a new DNS client client := &dns.Client{Net: "udp", ReadTimeout: 4 * time.Second, WriteTimeout: 4 * time.Second, SingleInflight: true} if _, tcp := w.RemoteAddr().(*net.TCPAddr); tcp { client.Net = "tcp" } var r *dns.Msg var err error // loop through the specified nameservers and forward them the request // the request ID is used as a starting point in order to introduce at least // some element of randomness, instead of always hitting the first nameserver for i := 0; i < len(nameservers); i++ { r, _, err = client.Exchange(req, nameservers[(int(req.Id)+i)%len(nameservers)]) if err == nil { r.Compress = true return r } } log.Print("Failed to forward request.", err) return getEmptyMsg(w, req, dns.RcodeServerFailure) }
func (h *Handler) handleQuery(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] m := &dns.Msg{} m.SetReply(r) switch q.Qtype { case dns.TypePTR: if q.Name == h.reverse { rr := &dns.PTR{} rr.Hdr = dns.RR_Header{Name: q.Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0} rr.Ptr = h.name m.Answer = append(m.Answer, rr) } case dns.TypeA: if q.Name == h.name { rr := &dns.A{} rr.Hdr = dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0} rr.A = h.ip m.Answer = append(m.Answer, rr) } } w.WriteMsg(m) }
func (s *jujuNameServer) handleRequest(w dns.ResponseWriter, r *dns.Msg) { m := new(dns.Msg) m.SetReply(r) for _, q := range r.Question { rr, err := s.answer(q) if err != nil { m.SetRcodeFormatError(r) t := new(dns.TXT) t.Hdr = dns.RR_Header{ Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassNONE, } t.Txt = []string{err.Error()} m.Extra = append(m.Extra, t) continue } else if rr != nil { m.Answer = append(m.Answer, rr) } } m.Authoritative = true // recursion isn't really available, but it's apparently // necessary to set this to make nslookup happy. m.RecursionAvailable = true w.WriteMsg(m) }
// interceptRequest gets called upon each DNS request, and we determine // whether we want to deal with it or not func interceptRequest(w dns.ResponseWriter, r *dns.Msg) { m := r.Copy() err := error(nil) defer w.Close() if len(r.Question) < 1 { return } // Hijack the response to point to us instead if matchesCriteria(r.Question[0].Name) { fmt.Println("Matches!", r.Question[0].Name) if !(inAddressCache(r.Question[0].Name)) { updateAddressCache(r) } m = hijackResponse(r) // Pass it upstream, return the answer } else { //fmt.Println("Passing on ", r.Question[0].Name) m, err = upstreamLookup(r) if err != nil { fmt.Println("Error when passing request through upstream - network problem?") // in this instance, our response (m) has no answer } } w.WriteMsg(m) }
func (h dnsHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { if len(r.Question) != 1 { h.t.Fatalf("bad: %#v", r.Question) } name := "join.service.consul." question := r.Question[0] if question.Name != name || question.Qtype != dns.TypeANY { h.t.Fatalf("bad: %#v", question) } m := new(dns.Msg) m.SetReply(r) m.Authoritative = true m.RecursionAvailable = false m.Answer = append(m.Answer, &dns.A{ Hdr: dns.RR_Header{ Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET}, A: net.ParseIP("127.0.0.1"), }) m.Answer = append(m.Answer, &dns.AAAA{ Hdr: dns.RR_Header{ Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET}, AAAA: net.ParseIP("2001:db8:a0b:12f0::1"), }) if err := w.WriteMsg(m); err != nil { h.t.Fatalf("err: %v", err) } }
func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := middleware.State{W: w, Req: r} for _, rule := range l.Rules { if middleware.Name(rule.NameScope).Matches(state.Name()) { responseRecorder := middleware.NewResponseRecorder(w) rcode, err := l.Next.ServeDNS(ctx, responseRecorder, r) if rcode > 0 { // There was an error up the chain, but no response has been written yet. // The error must be handled here so the log entry will record the response size. if l.ErrorFunc != nil { l.ErrorFunc(responseRecorder, r, rcode) } else { rc := middleware.RcodeToString(rcode) answer := new(dns.Msg) answer.SetRcode(r, rcode) state.SizeAndDo(answer) metrics.Report(state, metrics.Dropped, rc, answer.Len(), time.Now()) w.WriteMsg(answer) } rcode = 0 } rep := middleware.NewReplacer(r, responseRecorder, CommonLogEmptyValue) rule.Log.Println(rep.Replace(rule.Format)) return rcode, err } } return l.Next.ServeDNS(ctx, w, r) }
func (h ENUMHandler) ServeDNS(writer dns.ResponseWriter, request *dns.Msg) { defer func(s time.Time) { h.Trace.Printf("dns request for %v (%s) (%v) from client %s (%s)", request.Question[0], "udp", time.Now().Sub(s), writer.RemoteAddr().String(), writer.RemoteAddr().Network()) }(time.Now()) if answer, err := h.createAnswer(request); err == nil { if answer == nil { h.Trace.Printf("no result found for %s", request.Question[0]) notfound := &dns.Msg{} notfound.SetReply(request) notfound.SetRcode(request, dns.RcodeSuccess) writer.WriteMsg(notfound) return } if err := writer.WriteMsg(answer); err != nil { h.Error.Printf("error sending answer: %v", err) } } else { h.Error.Printf("[ERR] Error getting the answer: %v", err) error := &dns.Msg{} error.SetReply(request) error.SetRcode(request, dns.RcodeServerFailure) writer.WriteMsg(error) } }
func handle(writer dns.ResponseWriter, request *dns.Msg) { message := new(dns.Msg) message.SetReply(request) message.SetRcode(message, dns.RcodeSuccess) question := request.Question[0] switch request.Opcode { case dns.OpcodeNotify: log.Println(fmt.Sprintf("Recieved NOTIFY for %s", question.Name)) message = handle_notify(question, message, writer) case dns.OpcodeQuery: log.Println(fmt.Sprintf("Recieved QUERY for %s", question.Name)) message = handle_query(question, message, writer) default: message = handle_error(message, writer, "REFUSED") } // Apparently this dns library takes the question out on // certain RCodes, like REFUSED, which is not right. So we reinsert it. message.Question[0].Name = question.Name message.Question[0].Qtype = question.Qtype message.Question[0].Qclass = question.Qclass message.MsgHdr.Opcode = request.Opcode // Send an authoritative answer message.MsgHdr.Authoritative = true writer.WriteMsg(message) }
func handleSpecialNames(w dns.ResponseWriter, req *dns.Msg) bool { question := req.Question[0] nameLC := strings.ToLower(question.Name) for _, localRR := range localRRS { if nameLC == localRR.Name { m := new(dns.Msg) m.Id = req.Id m.Answer = []dns.RR{*localRR.RR} m.Response = true w.WriteMsg(m) return true } } if question.Qtype != dns.TypeANY { return false } m := new(dns.Msg) m.Id = req.Id hinfo := new(dns.HINFO) hinfo.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeHINFO, Class: dns.ClassINET, Ttl: 86400} hinfo.Cpu = "ANY is not supported any more" hinfo.Os = "See draft-jabley-dnsop-refuse-any" m.Answer = []dns.RR{hinfo} m.Response = true w.WriteMsg(m) return true }
func (self *TrivialDnsServer) redirectQuery(w dns.ResponseWriter, r *dns.Msg, newName string) { self.Count("redirected_requests") if !strings.HasSuffix(newName, ".") { newName = newName + "." } newR := new(dns.Msg) newR.SetQuestion(dns.Fqdn(newName), dns.TypeA) if response, _, err := self.exchangeWithUpstream(newR); err == nil { ip := self.getSingleSimpleAnswer(response) if ip == nil { debug("%s redirect to %s yielded no answer", w.RemoteAddr(), newName) self.Count("redirected_nowhere") self.refuse(w, r) return } self.Count("redirected_successively") self.respondSuccessively(w, r, *ip) } else { self.Count("upstream_errors") self.refuse(w, r) log.Printf("%s: error: %s", w.RemoteAddr(), err) } }
func (manager *DnsManager) dnsHandler(w dns.ResponseWriter, req *dns.Msg) { m := new(dns.Msg) m.SetReply(req) name := req.Question[0].Name name = strings.TrimSuffix(name, "."+manager.BaseName) manager.dbMutex.Lock() entry, ok := manager.db[name] manager.dbMutex.Unlock() if ok { switch req.Question[0].Qtype { case dns.TypeSRV: m.Answer = manager.makeAllSRV(entry) case dns.TypeA: m.Answer = manager.makeAllA(entry) if manager.PushSRV { m.Extra = manager.makeAllSRV(entry) } } } w.WriteMsg(m) }
// ServeDNS implements the middleware.Handler interface. func (d Dnssec) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := middleware.State{W: w, Req: r} do := state.Do() qname := state.Name() qtype := state.QType() zone := middleware.Zones(d.zones).Matches(qname) if zone == "" { return d.Next.ServeDNS(ctx, w, r) } // Intercept queries for DNSKEY, but only if one of the zones matches the qname, otherwise we let // the query through. if qtype == dns.TypeDNSKEY { for _, z := range d.zones { if qname == z { resp := d.getDNSKEY(state, z, do) state.SizeAndDo(resp) w.WriteMsg(resp) return dns.RcodeSuccess, nil } } } drr := NewDnssecResponseWriter(w, d) return d.Next.ServeDNS(ctx, drr, r) }
func (p ReverseProxy) ServeDNS(w dns.ResponseWriter, r *dns.Msg, extra []dns.RR) error { var ( reply *dns.Msg err error ) switch { case middleware.Proto(w) == "tcp": reply, err = middleware.Exchange(p.Client.TCP, r, p.Host) default: reply, err = middleware.Exchange(p.Client.UDP, r, p.Host) } if reply != nil && reply.Truncated { // Suppress proxy error for truncated responses err = nil } if err != nil { return err } reply.Compress = true reply.Id = r.Id w.WriteMsg(reply) return nil }
func proxy(addr string, w dns.ResponseWriter, req *dns.Msg) { transport := "udp" if _, ok := w.RemoteAddr().(*net.TCPAddr); ok { transport = "tcp" } if isTransfer(req) { if transport != "tcp" { dns.HandleFailed(w, req) return } t := new(dns.Transfer) c, err := t.In(req, addr) if err != nil { dns.HandleFailed(w, req) return } if err = t.Out(w, req, c); err != nil { dns.HandleFailed(w, req) return } return } c := &dns.Client{Net: transport} resp, _, err := c.Exchange(req, addr) if err != nil { dns.HandleFailed(w, req) return } w.WriteMsg(resp) }
// handleDomain handles DNS queries that come to the cluster func (d *DnsServer) handleDomain(w dns.ResponseWriter, r *dns.Msg) { dom, qType := parseQuestion(r) q := dns.TypeToString[qType] + " " + dom log.Printf("--> Internal: %s", q) m := new(dns.Msg) m.SetReply(r) m.Authoritative = true supported, found, recs := d.queryRR(qType, dom) if !supported { log.Printf("<-x %s: NOTIMP", q) m.SetRcode(r, dns.RcodeNotImplemented) // NOTIMP } else if !found { log.Printf("<-x %s: NXDOMAIN", q) m.SetRcode(r, dns.RcodeNameError) // NXDOMAIN } else { for _, rec := range recs { rr, err := rrtype.ToRR(qType, dom, rec) if err != nil { log.Printf("<-x %s SERVFAIL: record conv err: %v", q, err) m.SetRcode(r, dns.RcodeServerFailure) break } else { log.Printf("<-- %s: %s", q, rr.String()) m.Answer = append(m.Answer, rr) } } } w.WriteMsg(m) }
// reply writes the given dns.Msg out to the given dns.ResponseWriter, // compressing the message first and truncating it accordingly. func reply(w dns.ResponseWriter, m *dns.Msg) { m.Compress = true // https://github.com/mesosphere/mesos-dns/issues/{170,173,174} if err := w.WriteMsg(truncate(m, isUDP(w))); err != nil { logging.Error.Println(err) } }
// handlerFunc receives requests, looks up the result and returns what is found. func handlerFunc(res dns.ResponseWriter, req *dns.Msg) { message := new(dns.Msg) switch req.Opcode { case dns.OpcodeQuery: message.SetReply(req) message.Compress = false message.Answer = make([]dns.RR, 0) for _, question := range message.Question { answers := answerQuestion(strings.ToLower(question.Name), question.Qtype) if len(answers) > 0 { for i := range answers { message.Answer = append(message.Answer, answers[i]) } } else { // If there are no records, go back through and search for SOA records for _, question := range message.Question { answers := answerQuestion(strings.ToLower(question.Name), dns.TypeSOA) for i := range answers { message.Ns = append(message.Ns, answers[i]) } } } } if len(message.Answer) == 0 && len(message.Ns) == 0 { message.Rcode = dns.RcodeNameError } default: message = message.SetRcode(req, dns.RcodeNotImplemented) } res.WriteMsg(message) }