// Construct a response for a single DNS request. func (ds *DjdnsServer) Handle(query *dns.Msg) (*dns.Msg, error) { response := new(dns.Msg) response.MsgHdr.Id = query.MsgHdr.Id response.Question = query.Question if len(query.Question) > 0 { // Ignore secondary questions question := query.Question[0] records, err := ds.GetRecords(question.Name) if err != nil { return nil, err } response.Answer = make([]dns.RR, len(records)) for i, record := range records { answer, err := record.ToDns() if err != nil { return nil, err } response.Answer[i] = answer } response.Ns = make([]dns.RR, 0) response.Extra = make([]dns.RR, 0) } return response, nil }
func RenewDnsMsg(m *dns.Msg) { m.Extra = nil m.Answer = nil m.AuthenticatedData = false m.CheckingDisabled = false m.Question = nil }
func main() { if len(os.Args) != 2 { fmt.Printf("%s NAMESERVER\n", os.Args[0]) os.Exit(1) } conf, _ := dns.ClientConfigFromFile("/etc/resolv.conf") m := new(dns.Msg) m.Question = make([]dns.Question, 1) c := new(dns.Client) addr := addresses(conf, c, os.Args[1]) if len(addr) == 0 { fmt.Printf("No address found for %s\n", os.Args[1]) os.Exit(1) } for _, a := range addr { m.Question[0] = dns.Question{"version.bind.", dns.TypeTXT, dns.ClassCHAOS} in, rtt, _ := c.ExchangeRtt(m, a) if in != nil && len(in.Answer) > 0 { fmt.Printf("(time %.3d µs) %v\n", rtt/1e3, in.Answer[0]) } m.Question[0] = dns.Question{"hostname.bind.", dns.TypeTXT, dns.ClassCHAOS} in, rtt, _ = c.ExchangeRtt(m, a) if in != nil && len(in.Answer) > 0 { fmt.Printf("(time %.3d µs) %v\n", rtt/1e3, in.Answer[0]) } } }
// Performs the actual query by service name (browse) or service instance name (lookup), // start response listeners goroutines and loops over the entries channel. func (c *client) query(params *LookupParams) error { var serviceName, serviceInstanceName string serviceName = fmt.Sprintf("%s.%s.", trimDot(params.Service), trimDot(params.Domain)) if params.Instance != "" { serviceInstanceName = fmt.Sprintf("%s.%s", params.Instance, serviceName) } // send the query m := new(dns.Msg) if serviceInstanceName != "" { m.Question = []dns.Question{ dns.Question{serviceInstanceName, dns.TypeSRV, dns.ClassINET}, dns.Question{serviceInstanceName, dns.TypeTXT, dns.ClassINET}, } m.RecursionDesired = false } else { m.SetQuestion(serviceName, dns.TypePTR) m.RecursionDesired = false } if err := c.sendQuery(m); err != nil { return err } return nil }
func (self *DnsResolver) lookupHost(host string, triesLeft int) ([]net.IP, error) { m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{dns.Fqdn(host), dns.TypeA, dns.ClassINET} in, err := dns.Exchange(m1, self.Servers[self.r.Intn(len(self.Servers))]) result := []net.IP{} if err != nil { if strings.HasSuffix(err.Error(), "i/o timeout") && triesLeft > 0 { triesLeft -= 1 return self.lookupHost(host, triesLeft) } else { return result, err } } if in != nil && in.Rcode != dns.RcodeSuccess { return result, errors.New(dns.RcodeToString[in.Rcode]) } for _, record := range in.Answer { if t, ok := record.(*dns.A); ok { result = append(result, t.A) } } return result, err }
func newMsg(host string, qClass uint16) *dns.Msg { m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{host, qClass, dns.ClassINET} return m1 }
func localQuery(mychan chan DNSreply, qname string, qtype uint16) { var result DNSreply var trials uint result.qname = qname result.qtype = qtype result.r = nil result.err = errors.New("No name server to answer the question") localm := new(dns.Msg) localm.Id = dns.Id() localm.RecursionDesired = true localm.Question = make([]dns.Question, 1) localm.SetEdns0(EDNSBUFFERSIZE, false) // Even if no EDNS requested, see #9 May be we should retry without it if timeout? localc := new(dns.Client) localc.ReadTimeout = timeout localm.Question[0] = dns.Question{qname, qtype, dns.ClassINET} Tests: for trials = 0; trials < uint(*maxTrials); trials++ { Resolvers: for serverIndex := range conf.Servers { server := conf.Servers[serverIndex] result.nameserver = server // Brackets around the server address are necessary for IPv6 name servers r, rtt, err := localc.Exchange(localm, "["+server+"]:"+conf.Port) // Do not use net.JoinHostPort, see https://github.com/bortzmeyer/check-soa/commit/3e4edb13855d8c4016768796b2892aa83eda1933#commitcomment-2355543 if r == nil { result.r = nil result.err = err if strings.Contains(err.Error(), "timeout") { // Try another resolver break Resolvers } else { // We give in break Tests } } else { result.rtt = rtt if r.Rcode == dns.RcodeSuccess { // TODO: as a result, NODATA (NOERROR/ANSWER=0) are silently ignored (try "foo", for instance, the name exists but no IP address) // TODO: for rcodes like SERVFAIL, trying another resolver could make sense result.r = r result.err = nil break Tests } else { // All the other codes are errors. Yes, it may // happens that one resolver returns REFUSED // and the others work but we do not handle // this case. TODO: delete the resolver from // the list and try another one result.r = r result.err = errors.New(dns.RcodeToString[r.Rcode]) break Tests } } } } if *debug { fmt.Printf("DEBUG: end of DNS request \"%s\" / %d\n", qname, qtype) } mychan <- result }
func prepareFailureMsg(req *dns.Msg) *dns.Msg { failMsg := new(dns.Msg) failMsg.Id = req.Id failMsg.Response = true failMsg.Authoritative = true failMsg.Question = req.Question failMsg.Rcode = dns.RcodeNameError return failMsg }
// DNS requests go to this function func dnsHandle(w dns.ResponseWriter, r *dns.Msg) { name := r.Question[0].Name if !namePattern.MatchString(name) { kilog.Debug("%v does not match pattern, forwarding", name) dnsForward(w, r) return } // otherwise kilog.Debug("%v matches pattern, handling", name) dnsLock.Lock() defer dnsLock.Unlock() // check in table first fakeIP, ok := nameToIP[name] if !ok { // place in table var nwIP string for { haha := ipAlloc().String() _, exists := ipToName[haha] if exists { continue } nwIP = haha break } fakeIP = nwIP nameToIP[name] = fakeIP ipToName[fakeIP] = name // remove in 30 minutes go func() { time.Sleep(time.Minute * 30) dnsLock.Lock() defer dnsLock.Unlock() delete(nameToIP, name) delete(ipToName, fakeIP) }() } // return the fake IP to the user resp := new(dns.A) resp.Hdr.Name = name resp.Hdr.Ttl = 1 // very short resp.Hdr.Class = dns.ClassINET resp.Hdr.Rrtype = dns.TypeA resp.A = net.ParseIP(fakeIP) towrite := new(dns.Msg) towrite.Id = r.Id towrite.RecursionAvailable = true towrite.RecursionDesired = true towrite.Response = true towrite.Question = r.Question towrite.Answer = make([]dns.RR, 1) towrite.Answer[0] = resp w.WriteMsg(towrite) kilog.Debug("returning mapping %v -> %v", name, fakeIP) }
func (d *DnsDomain) Test() bool { if !(*Domain)(d).Test() { return false } fqdn := d.Name if strings.HasPrefix(fqdn, "*.") { fqdn = "a" + fqdn[1:] } if !strings.HasSuffix(fqdn, ".") { fqdn = fqdn + "." } any_ok := false d.DNS = make([]*DnsRecords, 0, len(DNS_servers)) for name, addr := range DNS_servers { records := new(DnsRecords) records.Server = name records.NS = addr d.DNS = append(d.DNS, records) req := new(dns.Msg) req.Id = dns.Id() req.RecursionDesired = true req.Question = []dns.Question{ dns.Question{fqdn, dns.TypeA, dns.ClassINET}, } resp, err := dns_client.Exchange(req, addr) if err != nil { records.Status = 900 records.Message = err.Error() continue } records.IPs = make([]string, 0, len(resp.Answer)) for _, rr := range resp.Answer { switch a := rr.(type) { case *dns.RR_A: records.IPs = append(records.IPs, a.A.String()) } } if len(records.IPs) > 0 { any_ok = true } else { records.Status = 900 records.Message = "No records" } } return any_ok }
func prepareAnswerMsg(req *dns.Msg, answers []dns.RR) *dns.Msg { answerMsg := new(dns.Msg) answerMsg.Id = req.Id answerMsg.Response = true answerMsg.Authoritative = true answerMsg.Question = req.Question answerMsg.Answer = answers answerMsg.Rcode = dns.RcodeSuccess answerMsg.Extra = []dns.RR{} return answerMsg }
func TestServeDNSOverrideIPv6(t *testing.T) { t.Parallel() const hostname = "foo.com." var a App a.Overrides.Store(Overrides{hostname: net.ParseIP("1.2.3.4")}) req := new(dns.Msg) req.Opcode = dns.OpcodeQuery req.Question = []dns.Question{{Name: hostname, Qtype: qtypeIPv6}} var w fDNSResponseWriter a.ServeDNS(&w, req) ensure.DeepEqual(t, len(w.msg.Answer), 0) }
func findSoaNs(domain string) (string, string, string) { var cname string var soa string var ns string add := func(c, s, n string) { cname += c soa += s ns += n return } cname += domain + "," m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{domain, dns.TypeSOA, dns.ClassINET} in, _ := dns.Exchange(m1, (cf.Servers[1] + ":53")) rrList := [...][]dns.RR{in.Answer, in.Ns, in.Extra} for _, rr := range rrList { for i := len(rr) - 1; i >= 0; i-- { switch rr[i].Header().Rrtype { case dns.TypeCNAME: temp_cname := rr[i].(*dns.CNAME) add(findSoaNs(temp_cname.Target)) // fmt.Println( "temp_cname:" , temp_cname ) return cname, soa, ns break case dns.TypeNS: temp_ns := rr[i].(*dns.NS) ns += temp_ns.Ns + "," // + "|" + fmt.Sprint( temp_ns.Hdr.Ttl ) + "," // fmt.Println( "temp_ns:" , temp_ns ) break case dns.TypeSOA: temp_soa := rr[i].(*dns.SOA) soa += temp_soa.Ns + "," // + "|" + fmt.Sprint( temp_soa.Hdr.Ttl ) + "," // fmt.Println( "temp_soa:" , temp_soa ) break } } } return cname, soa, ns }
func (this *UDPNameServer) BuildQueryA(domain string, id uint16) *alloc.Buffer { buffer := alloc.NewBuffer() msg := new(dns.Msg) msg.Id = id msg.RecursionDesired = true msg.Question = []dns.Question{ dns.Question{ Name: dns.Fqdn(domain), Qtype: dns.TypeA, Qclass: dns.ClassINET, }} writtenBuffer, _ := msg.PackBuffer(buffer.Value) buffer.Slice(0, len(writtenBuffer)) return buffer }
func fakeMsg(dom string, rrHeader uint16, proto string, serverPort int) (*dns.Msg, error) { qc := uint16(dns.ClassINET) c := new(dns.Client) c.Net = proto m := new(dns.Msg) m.Question = make([]dns.Question, 1) m.Question[0] = dns.Question{ Name: dns.Fqdn(dom), Qtype: rrHeader, Qclass: qc, } m.RecursionDesired = true in, _, err := c.Exchange(m, "127.0.0.1:"+strconv.Itoa(serverPort)) return in, err }
func route(w dns.ResponseWriter, req *dns.Msg) { if len(req.Question) != 1 { failWithRcode(w, req, dns.RcodeRefused) return } question := req.Question[0] qtype := question.Qtype if question.Qclass != dns.ClassINET { failWithRcode(w, req, dns.RcodeRefused) return } remoteIP := w.RemoteAddr().(*net.UDPAddr).IP m := new(dns.Msg) m.Id = req.Id switch qtype { case dns.TypeA: if remoteIP4 := remoteIP.To4(); remoteIP4 != nil { rr := new(dns.A) rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: question.Qtype, Class: dns.ClassINET, Ttl: 10} rr.A = remoteIP4 m.Answer = []dns.RR{rr} } case dns.TypeAAAA: if remoteIP16 := remoteIP.To16(); remoteIP16 != nil { rr := new(dns.AAAA) rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: question.Qtype, Class: dns.ClassINET, Ttl: 10} rr.AAAA = remoteIP16 m.Answer = []dns.RR{rr} } case dns.TypeTXT: rr := new(dns.TXT) rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: question.Qtype, Class: dns.ClassINET, Ttl: 10} rr.Txt = []string{fmt.Sprintf("Resolver IP: %v", remoteIP.String())} m.Answer = []dns.RR{rr} } m.Question = req.Question m.Response = true m.Authoritative = true w.WriteMsg(m) }
func (s *server) HealthCheck() { c := new(dns.Client) c.Net = "tcp" m := new(dns.Msg) m.Question = make([]dns.Question, 1) m.Question[0] = dns.Question{HealthQuery, dns.TypeTXT, dns.ClassCHAOS} // doing this in the loop is not the best idea for _, serv := range s.router.Servers() { if !check(c, m, serv) { // do it again if !check(c, m, serv) { log.Printf("healthcheck failed for %s", serv) s.router.RemoveServer(serv) } } } }
/* * makeMessage() - construct DNS message structure */ func makeMessage(c *Context, qname, qtype, qclass string, ext Extension) *dns.Msg { m := new(dns.Msg) m.Id = dns.Id() if c.restype == RESOLUTION_STUB { m.RecursionDesired = true } else { m.RecursionDesired = false } if c.adflag { m.AuthenticatedData = true } if c.cdflag { m.CheckingDisabled = true } if ext["dnssec_return_status"] || ext["dnssec_return_only_secure"] || ext["dnssec_return_validation_chain"] { opt := new(dns.OPT) opt.Hdr.Name = "." opt.Hdr.Rrtype = dns.TypeOPT opt.SetDo() m.Extra = append(m.Extra, opt) } m.Question = make([]dns.Question, 1) qtype_int, ok := dns.StringToType[strings.ToUpper(qtype)] if !ok { fmt.Printf("%s: Unrecognized query type.\n", qtype) return nil } qclass_int, ok := dns.StringToClass[strings.ToUpper(qclass)] if !ok { fmt.Printf("%s: Unrecognized query class.\n", qclass) return nil } m.Question[0] = dns.Question{qname, qtype_int, qclass_int} return m }
func main() { if len(os.Args) != 2 { fmt.Printf("%s NAME\n", os.Args[0]) os.Exit(1) } name := os.Args[1] conf, _ := dns.ClientConfigFromFile("/etc/resolv.conf") client := new(dns.Client) message := new(dns.Msg) message.Question = make([]dns.Question, 1) message.Question[0] = dns.Question{BASE, dns.TypeTXT, dns.ClassINET} message.SetEdns0(4096, true) message.RecursionDesired = true reply, _, err := client.Exchange(message, conf.Servers[0]+":"+conf.Port) if err != nil { fmt.Printf("Cannot get info for %s: %s\n", BASE, err) os.Exit(1) } if reply.Rcode != dns.RcodeSuccess { fmt.Printf("Bad answer from the resolver: %v\n", reply.Rcode) os.Exit(1) } if len(reply.Answer) == 0 { fmt.Printf("Zero answer for %s\n", BASE) os.Exit(1) } toReporter := make(chan instanceQuery) urls := 0 for _, rr := range reply.Answer { switch rr.(type) { case *dns.TXT: url := rr.(*dns.TXT).Txt[0] urls++ go queryOne(toReporter, url, name) } // Otherwise, ignore it. Probably a DNSSEC signature } fromReporter := make(chan string) go reporter(toReporter, fromReporter, urls) <-fromReporter }
func (v *UDPNameServer) BuildQueryA(domain string, id uint16) *buf.Buffer { msg := new(dns.Msg) msg.Id = id msg.RecursionDesired = true msg.Question = []dns.Question{ { Name: dns.Fqdn(domain), Qtype: dns.TypeA, Qclass: dns.ClassINET, }} buffer := buf.New() buffer.AppendSupplier(func(b []byte) (int, error) { writtenBuffer, err := msg.PackBuffer(b) return len(writtenBuffer), err }) return buffer }
func TestServeDNSForwardNonOverrideQuery(t *testing.T) { t.Parallel() const ns = "a" res := new(dns.Msg) a := App{ Nameservers: []string{ns}, dnsUDPclient: fDNSClient{ exchange: func(m *dns.Msg, a string) (*dns.Msg, time.Duration, error) { ensure.DeepEqual(t, a, ns) return res, time.Minute, nil }, }, } a.Overrides.Store(Overrides{}) req := new(dns.Msg) req.Opcode = dns.OpcodeQuery req.Question = []dns.Question{{Name: "foo.com."}} var w fDNSResponseWriter a.ServeDNS(&w, req) ensure.DeepEqual(t, w.msg, res) }
// Edns0Version checks the EDNS version in the request. If error // is nil everything is OK and we can invoke the middleware. If non-nil, the // returned Msg is valid to be returned to the client (and should). For some // reason this response should not contain a question RR in the question section. func Edns0Version(req *dns.Msg) (*dns.Msg, error) { opt := req.IsEdns0() if opt == nil { return nil, nil } if opt.Version() == 0 { return nil, nil } m := new(dns.Msg) m.SetReply(req) // zero out question section, wtf. m.Question = nil o := new(dns.OPT) o.Hdr.Name = "." o.Hdr.Rrtype = dns.TypeOPT o.SetVersion(0) o.SetExtendedRcode(dns.RcodeBadVers) m.Extra = []dns.RR{o} return m, errors.New("EDNS0 BADVERS") }
func query(dom string) { nameserver := "127.0.0.1:8053" qt := dns.TypeA qc := uint16(dns.ClassINET) c := new(dns.Client) c.Net = "udp" m := new(dns.Msg) m.Question = make([]dns.Question, 1) m.Question[0] = dns.Question{ Name: dns.Fqdn(dom), Qtype: qt, Qclass: qc, } _, _, err := c.Exchange(m, nameserver) if err != nil { fmt.Println(err) } }
// Get a single metric from dnsmasq. Returns the numeric value of the // metric. func (mc *metricsClient) getSingleMetric(name string) (int64, error) { msg := new(dns.Msg) msg.Id = dns.Id() msg.RecursionDesired = false msg.Question = make([]dns.Question, 1) msg.Question[0] = dns.Question{ Name: name, Qtype: dns.TypeTXT, Qclass: dns.ClassCHAOS, } in, _, err := mc.dnsClient.Exchange(msg, mc.addrPort) if err != nil { return 0, err } if len(in.Answer) != 1 { return 0, fmt.Errorf("Invalid number of Answer records for %s: %d", name, len(in.Answer)) } if t, ok := in.Answer[0].(*dns.TXT); ok { glog.V(4).Infof("Got valid TXT response %+v for %s", t, name) if len(t.Txt) != 1 { return 0, fmt.Errorf("Invalid number of TXT records for %s: %d", name, len(t.Txt)) } value, err := strconv.ParseInt(t.Txt[0], 10, 64) if err != nil { return 0, err } return value, nil } return 0, fmt.Errorf("missing txt record for %s", name) }
func (r Resolver) ResolveA(addr string) (net.IP, bool) { // log.Println("Looking up A for " + addr) m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true m1.Question = make([]dns.Question, 1) m1.Question[0] = dns.Question{addr, dns.TypeA, dns.ClassINET} c := new(dns.Client) in, _, err := c.Exchange(m1, "10.1.3.254:53") if err != nil { log.Println("Failed on c.Exchange call: " + err.Error()) return nil, false } if a, ok := in.Answer[0].(*dns.A); ok { // log.Println("I found an answer for " + addr) return a.A, true } // log.Println("Nothing found for " + addr) return nil, false }
func TestServeDNSOverrideIPv4(t *testing.T) { t.Parallel() const hostname = "foo.com." ip := net.ParseIP("1.2.3.4") var a App a.Overrides.Store(Overrides{hostname: ip}) req := new(dns.Msg) req.Opcode = dns.OpcodeQuery req.Question = []dns.Question{{Name: hostname, Qtype: qtypeIPv4}} var w fDNSResponseWriter a.ServeDNS(&w, req) ensure.DeepEqual(t, w.msg.Answer, []dns.RR{ &dns.A{ A: ip, Hdr: dns.RR_Header{ Name: hostname, Rrtype: 1, Class: 1, Ttl: 100, Rdlength: 4, }, }, }) }
func proxyServe(w dns.ResponseWriter, req *dns.Msg) { var ( key string m *dns.Msg err error tried bool data []byte id uint16 query []string questions []dns.Question used string ) defer func() { if err := recover(); err != nil { fmt.Println(err) } }() if req.MsgHdr.Response == true { // supposed responses sent to us are bogus return } query = make([]string, len(req.Question)) for i, q := range req.Question { if q.Qtype != dns.TypeAAAA || *ipv6 { questions = append(questions, q) } query[i] = fmt.Sprintf("(%s %s %s)", q.Name, dns.ClassToString[q.Qclass], dns.TypeToString[q.Qtype]) } if len(questions) == 0 { return } req.Question = questions id = req.Id req.Id = 0 key = toMd5(req.String()) req.Id = id if ENCACHE { if reply, ok := conn.Get(key); ok { data, _ = reply.([]byte) } if data != nil && len(data) > 0 { m = &dns.Msg{} m.Unpack(data) m.Id = id err = w.WriteMsg(m) if DEBUG > 0 { log.Printf("id: %5d cache: HIT %v\n", id, query) } goto end } else { if DEBUG > 0 { log.Printf("id: %5d cache: MISS %v\n", id, query) } } } for i, parts := range DNS { dns := parts[0] proto := parts[1] tried = i > 0 if DEBUG > 0 { if tried { log.Printf("id: %5d try: %v %s %s\n", id, query, dns, proto) } else { log.Printf("id: %5d resolve: %v %s %s\n", id, query, dns, proto) } } client := clientUDP if proto == "tcp" { client = clientTCP } m, _, err = client.Exchange(req, dns) if err == nil && len(m.Answer) > 0 { used = dns break } } if err == nil { if DEBUG > 0 { if tried { if len(m.Answer) == 0 { log.Printf("id: %5d failed: %v\n", id, query) } else { log.Printf("id: %5d bingo: %v %s\n", id, query, used) } } } data, err = m.Pack() if err == nil { _, err = w.Write(data) if err == nil { if ENCACHE { m.Id = 0 data, _ = m.Pack() ttl := 0 if len(m.Answer) > 0 { ttl = int(m.Answer[0].Header().Ttl) if ttl < 0 { ttl = 0 } } conn.Set(key, data, time.Second*time.Duration(ttl)) m.Id = id if DEBUG > 0 { log.Printf("id: %5d cache: CACHED %v TTL %v\n", id, query, ttl) } } } } } end: if DEBUG > 1 { fmt.Println(req) if m != nil { fmt.Println(m) } } if err != nil { log.Printf("id: %5d error: %v %s\n", id, query, err) } if DEBUG > 1 { fmt.Println("====================================================") } }
func main() { short = flag.Bool("short", false, "abbreviate long DNSSEC records") dnssec := flag.Bool("dnssec", false, "request DNSSEC records") query := flag.Bool("question", false, "show question") check := flag.Bool("check", false, "check internal DNSSEC consistency") raw := flag.Bool("raw", false, "do not strip 'http://' from the qname") six := flag.Bool("6", false, "use IPv6 only") four := flag.Bool("4", false, "use IPv4 only") anchor := flag.String("anchor", "", "use the DNSKEY in this file for interal DNSSEC consistency") tsig := flag.String("tsig", "", "request tsig with key: [hmac:]name:key") port := flag.Int("port", 53, "port number to use") aa := flag.Bool("aa", false, "set AA flag in query") ad := flag.Bool("ad", false, "set AD flag in query") cd := flag.Bool("cd", false, "set CD flag in query") rd := flag.Bool("rd", true, "set RD flag in query") fallback := flag.Bool("fallback", false, "fallback to 4096 bytes bufsize and after that TCP") tcp := flag.Bool("tcp", false, "TCP mode") nsid := flag.Bool("nsid", false, "set edns nsid option") client := flag.String("client", "", "set edns client-subnet option") //serial := flag.Int("serial", 0, "perform an IXFR with this serial") flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage: %s [options] [@server] [qtype] [qclass] [name ...]\n", os.Args[0]) flag.PrintDefaults() } qtype := uint16(0) qclass := uint16(dns.ClassINET) var qname []string flag.Parse() if *anchor != "" { f, err := os.Open(*anchor) if err != nil { fmt.Fprintf(os.Stderr, "Failure to open %s: %s\n", *anchor, err.Error()) } r, err := dns.ReadRR(f, *anchor) if err != nil { fmt.Fprintf(os.Stderr, "Failure to read an RR from %s: %s\n", *anchor, err.Error()) } if k, ok := r.(*dns.DNSKEY); !ok { fmt.Fprintf(os.Stderr, "No DNSKEY read from %s\n", *anchor) } else { dnskey = k } } var nameserver string Flags: for i := 0; i < flag.NArg(); i++ { // If it starts with @ it is a nameserver if flag.Arg(i)[0] == '@' { nameserver = flag.Arg(i) continue Flags } // First class, then type, to make ANY queries possible // And if it looks like type, it is a type if k, ok := dns.StringToType[strings.ToUpper(flag.Arg(i))]; ok { qtype = k continue Flags } // If it looks like a class, it is a class if k, ok := dns.StringToClass[strings.ToUpper(flag.Arg(i))]; ok { qclass = k continue Flags } // If it starts with TYPExxx it is unknown rr if strings.HasPrefix(flag.Arg(i), "TYPE") { i, e := strconv.Atoi(string([]byte(flag.Arg(i))[4:])) if e == nil { qtype = uint16(i) continue Flags } } // Anything else is a qname qname = append(qname, flag.Arg(i)) } if len(qname) == 0 { qname = make([]string, 1) qname[0] = "." qtype = dns.TypeNS } if qtype == 0 { qtype = dns.TypeA } if len(nameserver) == 0 { conf, err := dns.ClientConfigFromFile("/etc/resolv.conf") if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(2) } nameserver = "@" + conf.Servers[0] } nameserver = string([]byte(nameserver)[1:]) // chop off @ // if the nameserver is from /etc/resolv.conf the [ and ] are already // added, thereby breaking net.ParseIP. Check for this and don't // fully qualify such a name if nameserver[0] == '[' && nameserver[len(nameserver)-1] == ']' { nameserver = nameserver[1 : len(nameserver)-1] } if i := net.ParseIP(nameserver); i != nil { nameserver = net.JoinHostPort(nameserver, strconv.Itoa(*port)) } else { nameserver = dns.Fqdn(nameserver) + ":" + strconv.Itoa(*port) } c := new(dns.Client) if *tcp { c.Net = "tcp" if *four { c.Net = "tcp4" } if *six { c.Net = "tcp6" } } else { c.Net = "udp" if *four { c.Net = "udp4" } if *six { c.Net = "udp6" } } m := new(dns.Msg) m.MsgHdr.Authoritative = *aa m.MsgHdr.AuthenticatedData = *ad m.MsgHdr.CheckingDisabled = *cd m.MsgHdr.RecursionDesired = *rd m.Question = make([]dns.Question, 1) if *dnssec || *nsid || *client != "" { o := new(dns.OPT) o.Hdr.Name = "." o.Hdr.Rrtype = dns.TypeOPT if *dnssec { o.SetDo() o.SetUDPSize(dns.DefaultMsgSize) } if *nsid { e := new(dns.EDNS0_NSID) e.Code = dns.EDNS0NSID o.Option = append(o.Option, e) // NSD will not return nsid when the udp message size is too small o.SetUDPSize(dns.DefaultMsgSize) } if *client != "" { e := new(dns.EDNS0_SUBNET) e.Code = dns.EDNS0SUBNET e.SourceScope = 0 e.Address = net.ParseIP(*client) if e.Address == nil { fmt.Fprintf(os.Stderr, "Failure to parse IP address: %s\n", *client) return } e.Family = 1 // IP4 e.SourceNetmask = net.IPv4len * 8 if e.Address.To4() == nil { e.Family = 2 // IP6 e.SourceNetmask = net.IPv6len * 8 } o.Option = append(o.Option, e) } m.Extra = append(m.Extra, o) } for _, v := range qname { if !*raw && strings.HasPrefix(v, "http://") { v = v[7:] if v[len(v)-1] == '/' { v = v[:len(v)-1] } } m.Question[0] = dns.Question{dns.Fqdn(v), qtype, qclass} m.Id = dns.Id() // Add tsig if *tsig != "" { if algo, name, secret, ok := tsigKeyParse(*tsig); ok { m.SetTsig(name, algo, 300, time.Now().Unix()) c.TsigSecret = map[string]string{name: secret} } else { fmt.Fprintf(os.Stderr, "TSIG key data error\n") return } } if *query { fmt.Printf("%s", m.String()) fmt.Printf("\n;; size: %d bytes\n\n", m.Len()) } if qtype == dns.TypeAXFR { c.Net = "tcp" doXfr(c, m, nameserver) continue } if qtype == dns.TypeIXFR { doXfr(c, m, nameserver) continue } r, rtt, e := c.Exchange(m, nameserver) Redo: if e != nil { fmt.Printf(";; %s\n", e.Error()) continue } if r.Id != m.Id { fmt.Fprintf(os.Stderr, "Id mismatch\n") return } if r.MsgHdr.Truncated && *fallback { if c.Net != "tcp" { if !*dnssec { fmt.Printf(";; Truncated, trying %d bytes bufsize\n", dns.DefaultMsgSize) o := new(dns.OPT) o.Hdr.Name = "." o.Hdr.Rrtype = dns.TypeOPT o.SetUDPSize(dns.DefaultMsgSize) m.Extra = append(m.Extra, o) r, rtt, e = c.Exchange(m, nameserver) *dnssec = true goto Redo } else { // First EDNS, then TCP fmt.Printf(";; Truncated, trying TCP\n") c.Net = "tcp" r, rtt, e = c.Exchange(m, nameserver) goto Redo } } } if r.MsgHdr.Truncated && !*fallback { fmt.Printf(";; Truncated\n") } if *check { sigCheck(r, nameserver, *tcp) } if *short { r = shortMsg(r) } fmt.Printf("%v", r) fmt.Printf("\n;; query time: %.3d µs, server: %s(%s), size: %d bytes\n", rtt/1e3, nameserver, c.Net, r.Len()) } }
func TestRecursiveCompress(t *testing.T) { const ( hostname = "foo.example." maxSize = 512 ) // Construct a response that is >512 when uncompressed, <512 when compressed response := dns.Msg{} response.Authoritative = true response.Answer = []dns.RR{} header := dns.RR_Header{ Name: hostname, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 10, } for response.Len() <= maxSize { ip := address.Address(rand.Uint32()).IP4() response.Answer = append(response.Answer, &dns.A{Hdr: header, A: ip}) } response.Compress = true require.True(t, response.Len() <= maxSize) // A dns server that returns the above response var gotRequest = make(chan struct{}, 1) handleRecursive := func(w dns.ResponseWriter, req *dns.Msg) { gotRequest <- struct{}{} require.Equal(t, req.Question[0].Name, hostname) response.SetReply(req) err := w.WriteMsg(&response) require.Nil(t, err) } mux := dns.NewServeMux() mux.HandleFunc(topDomain, handleRecursive) udpListener, err := net.ListenPacket("udp", "0.0.0.0:0") require.Nil(t, err) udpServer := &dns.Server{PacketConn: udpListener, Handler: mux} udpServerPort := udpListener.LocalAddr().(*net.UDPAddr).Port go udpServer.ActivateAndServe() defer udpServer.Shutdown() // The weavedns server, pointed at the above server dnsserver, _, udpPort, _ := startServer(t, &dns.ClientConfig{ Servers: []string{"127.0.0.1"}, Port: strconv.Itoa(udpServerPort), Ndots: 1, Timeout: 5, Attempts: 2, }) defer dnsserver.Stop() // Now do lookup, check its what we expected. // NB this doesn't really test golang's resolver behaves correctly, as I can't see // a way to point golangs resolver at a specific hosts. req := new(dns.Msg) req.Id = dns.Id() req.RecursionDesired = true req.Question = make([]dns.Question, 1) req.Question[0] = dns.Question{ Name: hostname, Qtype: dns.TypeA, Qclass: dns.ClassINET, } c := new(dns.Client) res, _, err := c.Exchange(req, fmt.Sprintf("127.0.0.1:%d", udpPort)) require.Nil(t, err) require.True(t, len(gotRequest) > 0) require.True(t, res.Len() > maxSize) }
func route(w dns.ResponseWriter, req *dns.Msg) { keyP, err := getKey(req) if err != nil { failWithRcode(w, req, dns.RcodeRefused) return } if handleSpecialNames(w, req) { return } maxPayloadSize := getMaxPayloadSize(req) var resp *dns.Msg cacheValP, _ := cache.Get(*keyP) if cacheValP != nil { cacheVal := cacheValP.(CacheVal) remaining := -time.Since(cacheVal.ValidUntil) if remaining > 0 { resp = cacheVal.Response.Copy() resp.Id = req.Id resp.Question = req.Question } } if *debug { question := req.Question[0] cachedStr := "" if resp != nil { cachedStr = " (cached)" } log.Printf("%v\t%v %v%v\n", w.RemoteAddr(), question.Name, dns.TypeToString[question.Qtype], cachedStr) } if resp == nil { slipValue := atomic.LoadUint32(&slip) if slipValue > 0 && slipValue%2 == 0 { atomic.CompareAndSwapUint32(&slip, slipValue, slipValue+1) if slipValue%4 == 0 { sendTruncated(w, req.MsgHdr) } else { w.Close() } return } } if resp == nil { resp, err = resolve(req, keyP.DNSSEC) if err == nil { validUntil := time.Now().Add(getMinTTL(resp)) cache.Add(*keyP, CacheVal{ValidUntil: validUntil, Response: resp}) } else { if cacheValP == nil { w.Close() return } cacheVal := cacheValP.(CacheVal) resp = cacheVal.Response.Copy() resp.Id = req.Id resp.Question = req.Question } } packed, _ := resp.Pack() packedLen := len(packed) if uint16(packedLen) > maxPayloadSize { sendTruncated(w, resp.MsgHdr) } else { w.WriteMsg(resp) } }