func LookupIP(domain string, timeout int) ([]string, error) { var c dns.Client var err error ips := []string{} domain = strings.TrimRight(domain, ".") + "." c.DialTimeout = time.Duration(timeout) * time.Millisecond c.ReadTimeout = time.Duration(timeout) * time.Millisecond c.WriteTimeout = time.Duration(timeout) * time.Millisecond m := new(dns.Msg) m.SetQuestion(domain, dns.TypeA) ret, _, err := c.Exchange(m, "114.114.114.114:53") if err != nil { domain = strings.TrimRight(domain, ".") e := fmt.Sprintf("lookup error: %s, %s", domain, err.Error()) return ips, errors.New(e) } for _, i := range ret.Answer { result := strings.Split(i.String(), "\t") if result[3] == "A" && IsIP(result[4]) { ips = append(ips, result[4]) } } return ips, err }
// NewDNSResolverImpl constructs a new DNS resolver object that utilizes the // provided list of DNS servers for resolution. func NewDNSResolverImpl( readTimeout time.Duration, servers []string, caaSERVFAILExceptions map[string]bool, stats metrics.Scope, clk clock.Clock, maxTries int, ) *DNSResolverImpl { // TODO(jmhodges): make constructor use an Option func pattern dnsClient := new(dns.Client) // Set timeout for underlying net.Conn dnsClient.ReadTimeout = readTimeout dnsClient.Net = "tcp" return &DNSResolverImpl{ dnsClient: dnsClient, servers: servers, allowRestrictedAddresses: false, caaSERVFAILExceptions: caaSERVFAILExceptions, maxTries: maxTries, clk: clk, stats: stats, txtStats: stats.NewScope("TXT"), aStats: stats.NewScope("A"), aaaaStats: stats.NewScope("AAAA"), caaStats: stats.NewScope("CAA"), mxStats: stats.NewScope("MX"), } }
func (this Server) DoDNSquery(m dns.Msg, TransProString string, server []string, timeout time.Duration) (*dns.Msg, error) { dnsClient := new(dns.Client) if dnsClient == nil { return nil, errors.New("Cannot create DNS client") } dnsClient.ReadTimeout = timeout dnsClient.WriteTimeout = timeout if TransProString != "TCP" && TransProString != "UDP" { return nil, errors.New("TransProString run") } dnsClient.Net = strings.ToLower(TransProString) ServerStr := server[rand.Intn(len(server))] ServerAddr := net.ParseIP(ServerStr) if ServerAddr.To16() != nil { ServerStr = "[" + ServerStr + "]:" + this.Port } else if ServerAddr.To4() != nil { ServerStr = ServerStr + this.Port } else { return nil, errors.New("invalid Server Address") } dnsResponse, _, err := dnsClient.Exchange(&m, ServerStr) if err != nil { return nil, err } return dnsResponse, nil }
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 lookupHost(host string, config *dns.ClientConfig) (addrs []string, err error) { if utilNet.IsValidIpv4(host) || utilNet.IsValidIpv6(host) { return []string{host}, nil } if host == "localhost" { return []string{"127.0.0.1", "::1"}, nil } c := new(dns.Client) c.DialTimeout = DialTimeout c.ReadTimeout = ReadTimeout c.WriteTimeout = WriteTimeout m := new(dns.Msg) m.SetQuestion(dns.Fqdn(host), dns.TypeA) var r *dns.Msg for i := 0; i < len(config.Servers); i++ { r, _, err = c.Exchange(m, config.Servers[i]+":"+config.Port) if err != nil { continue } err = nil } if err != nil { return nil, e.Forward(err) } if r.Rcode != dns.RcodeSuccess { return nil, e.New("can't resolve %v", host) } addrs = make([]string, 0, 10) for _, a := range r.Answer { if addr, ok := a.(*dns.A); ok { addrs = append(addrs, addr.A.String()) } } m.SetQuestion(dns.Fqdn(host), dns.TypeAAAA) for i := 0; i < len(config.Servers); i++ { r, _, err = c.Exchange(m, config.Servers[0]+":"+config.Port) if err != nil { continue } err = nil } if err != nil { return nil, e.Forward(err) } if r.Rcode != dns.RcodeSuccess { return nil, e.New("no success") } for _, a := range r.Answer { if addr, ok := a.(*dns.AAAA); ok { addrs = append(addrs, addr.AAAA.String()) } } return }
func (self *TrivialDnsServer) exchangeWithUpstream(r *dns.Msg) (*dns.Msg, time.Duration, error) { self.Count("upstream_queries") c := new(dns.Client) c.ReadTimeout = CommonTimeout c.WriteTimeout = CommonTimeout i := rand.Intn(len(self.Servers)) upstreamServer := self.Servers[i] response, rtt, err := c.Exchange(r, upstreamServer) return response, rtt, err }
func (this ClientProxy) getServerIP() error { var dns_servers []string dnsClient := new(dns.Client) if dnsClient == nil { return errors.New("Can not new dns Client") } dnsClient.WriteTimeout = this.timeout dnsClient.ReadTimeout = this.timeout for _, serverstring := range this.SERVERS { ipaddress := net.ParseIP(serverstring) if ipaddress != nil { dns_servers = append(dns_servers, serverstring) } else { //used for unitest need to delete after test. /*if strings.EqualFold(serverstring, "example.com") { dns_servers = append(dns_servers, "127.0.0.1") continue } IPResult, err := net.LookupIP(serverstring) if err == nil { for _, appendStr := range IPResult { dns_servers = append(dns_servers, appendStr.String()) } } else { return err }*/ dnsResponse, err := searchServerIP(serverstring, 4, this.DNS_SERVERS) if err != nil { for i := 0; i < len(dnsResponse.Answer); i++ { dns_servers = append(dns_servers, dnsResponse.Answer[i].String()) } } else { return err } dnsResponse, err = searchServerIP(serverstring, 6, this.DNS_SERVERS) if err == nil { for i := 0; i < len(dnsResponse.Answer); i++ { dns_servers = append(dns_servers, "["+dnsResponse.Answer[i].String()+"]") } } else { return err } } } this.SERVERS = dns_servers return nil }
// Returns true if domain has a Name Server associated func queryNS(domain string, dnsServers []string, proto string) (int, error) { c := new(dns.Client) c.ReadTimeout = time.Duration(2 * time.Second) c.WriteTimeout = time.Duration(2 * time.Second) c.Net = proto m := new(dns.Msg) m.RecursionDesired = true dnsServer := dnsServers[rand.Intn(len(dnsServers))] m.SetQuestion(dns.Fqdn(domain), dns.TypeNS) in, _, err := c.Exchange(m, dnsServer+":53") if err == nil { return in.Rcode, err } return dns.RcodeRefused, err }
// our ServeDNS interface, which gets invoked on every DNS message func (this ServerProxy) ServeDNS(w dns.ResponseWriter, request *dns.Msg) { // see if we have our groovy custom EDNS0 option client_supports_appfrag := false opt := request.IsEdns0() if opt != nil { for ofs, e := range opt.Option { if e.Option() == dns.EDNS0LOCALSTART { _D("%s QID:%d found EDNS0LOCALSTART", w.RemoteAddr(), request.Id) client_supports_appfrag = true // go ahead and use the maximum UDP size for the local communication // with our server opt.SetUDPSize(65535) // remove the fragmentation option opt.Option = append(opt.Option[0:ofs], opt.Option[ofs+1:]...) // in principle we should only have one of these options break } } } // proxy the query c := new(dns.Client) c.ReadTimeout = this.timeout c.WriteTimeout = this.timeout response, rtt, err := c.Exchange(request, this.SERVERS[rand.Intn(this.s_len)]) if err != nil { _D("%s QID:%d error proxying query: %s", w.RemoteAddr(), request.Id, err) this.SRVFAIL(w, request) return } _D("%s QID:%d request took %s", w.RemoteAddr(), request.Id, rtt) // if the client does not support fragmentation, we just send the response back and finish if !client_supports_appfrag { _D("%s QID:%d sending raw response to client", w.RemoteAddr(), request.Id) w.WriteMsg(response) return } // otherwise lets get our fragments all_frags := frag(response) // send our fragments for n, frag := range all_frags { _D("%s QID:%d sending fragment %d", w.RemoteAddr(), request.Id, n) w.WriteMsg(&frag) } }
func LookupIp(ip string) (host string, err error) { if !utilNet.IsValidIpv4(ip) && !utilNet.IsValidIpv6(ip) { return "", e.New("not a valid ip address") } if ip == "127.0.0.1" || ip == "::1" { return "localhost", nil } config, err := dns.ClientConfigFromFile(ConfigurationFile) if err != nil { return "", e.Forward(err) } config.Timeout = Timeout c := new(dns.Client) c.DialTimeout = DialTimeout c.ReadTimeout = ReadTimeout c.WriteTimeout = WriteTimeout m := new(dns.Msg) rev, err := dns.ReverseAddr(ip) if err != nil { return "", e.Forward(err) } m.SetQuestion(rev, dns.TypePTR) var r *dns.Msg for i := 0; i < len(config.Servers); i++ { r, _, err = c.Exchange(m, config.Servers[i]+":"+config.Port) if err != nil { continue } err = nil } if err != nil { return "", e.Forward(err) } if r.Rcode != dns.RcodeSuccess { return "", e.New("can't resolve %v", ip) } for _, a := range r.Answer { if ptr, ok := a.(*dns.PTR); ok { return strings.TrimSuffix(ptr.Ptr, "."), nil } } return "", e.New("no ptr available") }
// defaultExtResolver queries other nameserver, potentially recurses; callers should probably be invoking extResolver // instead since that's the pluggable entrypoint into external resolution. func (res *Resolver) defaultExtResolver(r *dns.Msg, nameserver string, proto string, cnt int) (*dns.Msg, error) { var in *dns.Msg var err error c := new(dns.Client) c.Net = proto var t time.Duration = 5 * 1e9 if res.config.Timeout != 0 { t = time.Duration(int64(res.config.Timeout * 1e9)) } c.DialTimeout = t c.ReadTimeout = t c.WriteTimeout = t in, _, err = c.Exchange(r, nameserver) if err != nil { return in, err } // recurse if (in != nil) && (len(in.Answer) == 0) && (!in.MsgHdr.Authoritative) && (len(in.Ns) > 0) && (err != nil) { if cnt == recurseCnt { logging.CurLog.NonMesosRecursed.Inc() } if cnt > 0 { if soa, ok := (in.Ns[0]).(*dns.SOA); ok { return res.defaultExtResolver(r, net.JoinHostPort(soa.Ns, "53"), proto, cnt-1) } } } return in, err }
func testSoa(msg *dns.Msg, server string, tries uint) (soa *dns.Msg, err error) { c := new(dns.Client) c.ReadTimeout = timeout * 1e9 tests := uint(0) over := false for !over { soa, _, err = c.Exchange(msg, server) if err != nil { if e, ok := err.(net.Error); ok && e.Timeout() { tests++ if tests < tries { continue } else { over = true } } else { over = true } } else { over = true } } return soa, err }
func (d *DnsQuery) getDnsQueryTime(domain string, server string) (float64, error) { dnsQueryTime := float64(0) c := new(dns.Client) c.ReadTimeout = time.Duration(d.Timeout) * time.Second m := new(dns.Msg) recordType, err := d.parseRecordType() if err != nil { return dnsQueryTime, err } m.SetQuestion(dns.Fqdn(domain), recordType) m.RecursionDesired = true r, rtt, err := c.Exchange(m, net.JoinHostPort(server, strconv.Itoa(d.Port))) if err != nil { return dnsQueryTime, err } if r.Rcode != dns.RcodeSuccess { return dnsQueryTime, errors.New(fmt.Sprintf("Invalid answer name %s after %s query for %s\n", domain, d.RecordType, domain)) } dnsQueryTime = float64(rtt.Nanoseconds()) / 1e6 return dnsQueryTime, nil }
func main() { var err error if len(os.Args) != 2 { fmt.Printf("%s ZONE\n", os.Args[0]) os.Exit(1) } conf, err = dns.ClientConfigFromFile("/etc/resolv.conf") if conf == nil { fmt.Printf("Cannot initialize the local resolver: %s\n", err) os.Exit(1) } localm = new(dns.Msg) localm.RecursionDesired = true localm.Question = make([]dns.Question, 1) localc = new(dns.Client) localc.ReadTimeout = TIMEOUT * 1e9 r, err := localQuery(dns.Fqdn(os.Args[1]), dns.TypeNS) if r == nil { fmt.Printf("Cannot retrieve the list of name servers for %s: %s\n", dns.Fqdn(os.Args[1]), err) os.Exit(1) } if r.Rcode == dns.RcodeNameError { fmt.Printf("No such domain %s\n", dns.Fqdn(os.Args[1])) os.Exit(1) } m := new(dns.Msg) m.RecursionDesired = false m.Question = make([]dns.Question, 1) c := new(dns.Client) c.ReadTimeout = TIMEOUT * 1e9 success := true numNS := 0 for _, ans := range r.Answer { switch ans.(type) { case *dns.NS: nameserver := ans.(*dns.NS).Ns numNS += 1 ips := make([]string, 0) fmt.Printf("%s : ", nameserver) ra, err := localQuery(nameserver, dns.TypeA) if ra == nil { fmt.Printf("Error getting the IPv4 address of %s: %s\n", nameserver, err) os.Exit(1) } if ra.Rcode != dns.RcodeSuccess { fmt.Printf("Error getting the IPv4 address of %s: %s\n", nameserver, dns.RcodeToString[ra.Rcode]) os.Exit(1) } for _, ansa := range ra.Answer { switch ansa.(type) { case *dns.A: ips = append(ips, ansa.(*dns.A).A.String()) } } raaaa, err := localQuery(nameserver, dns.TypeAAAA) if raaaa == nil { fmt.Printf("Error getting the IPv6 address of %s: %s\n", nameserver, err) os.Exit(1) } if raaaa.Rcode != dns.RcodeSuccess { fmt.Printf("Error getting the IPv6 address of %s: %s\n", nameserver, dns.RcodeToString[raaaa.Rcode]) os.Exit(1) } for _, ansaaaa := range raaaa.Answer { switch ansaaaa.(type) { case *dns.AAAA: ips = append(ips, ansaaaa.(*dns.AAAA).AAAA.String()) } } if len(ips) == 0 { success = false fmt.Printf("No IP address for this server") } for _, ip := range ips { m.Question[0] = dns.Question{dns.Fqdn(os.Args[1]), dns.TypeSOA, dns.ClassINET} nsAddressPort := "" if strings.ContainsAny(":", ip) { // IPv6 address nsAddressPort = "[" + ip + "]:53" } else { nsAddressPort = ip + ":53" } soa, _, err := c.Exchange(m, nsAddressPort) // TODO: retry if timeout? Otherwise, one lost UDP packet and it is the end if soa == nil { success = false fmt.Printf("%s (%s) ", ip, err) goto Next } if soa.Rcode != dns.RcodeSuccess { success = false fmt.Printf("%s (%s) ", ips, dns.RcodeToString[soa.Rcode]) goto Next } if len(soa.Answer) == 0 { // May happen if the server is a recursor, not authoritative, since we query with RD=0 success = false fmt.Printf("%s (0 answer) ", ip) goto Next } rsoa := soa.Answer[0] switch rsoa.(type) { case *dns.SOA: if soa.Authoritative { // TODO: test if all name servers have the same serial ? fmt.Printf("%s (%d) ", ips, rsoa.(*dns.SOA).Serial) } else { success = false fmt.Printf("%s (not authoritative) ", ips) } } } Next: fmt.Printf("\n") } } if numNS == 0 { fmt.Printf("No NS records for \"%s\". It is probably a CNAME to a domain but not a zone\n", dns.Fqdn(os.Args[1])) os.Exit(1) } if success { os.Exit(0) } os.Exit(1) }
func sendNotify(servers []string, domain string) []NotifyResponse { if !strings.HasSuffix(domain, ".") { domain = domain + "." } if len(servers) == 0 { fmt.Println("No servers") resp := NotifyResponse{Result: "No servers", Error: true} fmt.Println("No servers") return []NotifyResponse{resp} } c := new(dns.Client) c.ReadTimeout = time.Duration(*timeout) * time.Millisecond m := new(dns.Msg) m.SetNotify(domain) wg := new(sync.WaitGroup) responseChannel := make(chan NotifyResponse, len(servers)) for _, server := range servers { go func(server string) { result := NotifyResponse{Server: server} wg.Add(1) defer func() { wg.Done() if result.Error || !*quiet { fmt.Printf("%s: %s\n", result.Server, result.Result) } responseChannel <- result }() target, err := fixupHost(server) if err != nil { result.Result = fmt.Sprintf("%s: %s", server, err) fmt.Println(result.Result) result.Error = true return } result.Server = target if *verbose { fmt.Println("Sending notify to", target) } resp, rtt, err := c.Exchange(m, target) if err != nil { result.Error = true result.Result = err.Error() return } ok := "ok" if !resp.Authoritative { ok = fmt.Sprintf("not ok (%s)", dns.RcodeToString[resp.Rcode]) } result.Result = fmt.Sprintf("%s: %s (%s)", target, ok, rtt.String()) responseChannel <- result }(server) } responses := make([]NotifyResponse, len(servers)) for i := 0; i < len(servers); i++ { responses[i] = <-responseChannel } wg.Wait() return responses }
func soaQuery(mychan chan SOAreply, zone string, name string, server string) { var result SOAreply var trials uint result.retrieved = false result.name = name result.address = server result.msg = "UNKNOWN" m := new(dns.Msg) if !*noedns { m.SetEdns0(EDNSBUFFERSIZE, !*nodnssec) } m.Id = dns.Id() if *recursion { m.RecursionDesired = true } else { m.RecursionDesired = false } m.Question = make([]dns.Question, 1) c := new(dns.Client) c.ReadTimeout = timeout // Seems ignored for TCP? if *tcp { c.Net = "tcp" } m.Question[0] = dns.Question{zone, dns.TypeSOA, dns.ClassINET} nsAddressPort := "" nsAddressPort = net.JoinHostPort(server, "53") if *debug { fmt.Printf("DEBUG Querying SOA from %s\n", nsAddressPort) } for trials = 0; trials < uint(*maxTrials); trials++ { soa, rtt, err := c.Exchange(m, nsAddressPort) if soa == nil { result.rtt = 0 result.msg = fmt.Sprintf("%s", err.Error()) } else { result.rtt = rtt if soa.Rcode != dns.RcodeSuccess { result.msg = dns.RcodeToString[soa.Rcode] break } else { if len(soa.Answer) == 0 { /* May happen if the server is a recursor, not authoritative, since we query with RD=0 */ result.msg = "0 answer" break } else { gotSoa := false for _, rsoa := range soa.Answer { switch rsoa.(type) { case *dns.SOA: if *noauthrequired || soa.MsgHdr.Authoritative { result.retrieved = true result.serial = rsoa.(*dns.SOA).Serial result.msg = "OK" } else { result.msg = "Not authoritative" } gotSoa = true case *dns.CNAME: /* Bad practice but common */ fmt.Printf("Apparently not a zone but an alias\n") os.Exit(1) case *dns.RRSIG: /* Ignore them. See bug #8 */ default: // TODO: a name server can send us other RR types. fmt.Printf("Internal error when processing %s, unexpected record type\n", rsoa) os.Exit(1) } } if !gotSoa { result.msg = "No SOA record in reply" } break } } break // We got a reply } } mychan <- result }
//not sure how to make a server fail, error 501? func (this Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { TransProString := r.Header.Get("X-Proxy-DNS-Transport") if TransProString == "tcp" { this.TransPro = TCPcode } else if TransProString == "udp" { this.TransPro = UDPcode } else { _D("Transport protol not udp or tcp") http.Error(w, "unknown transport protocol", 415) return } contentTypeStr := r.Header.Get("Content-Type") if contentTypeStr != "application/X-DNSoverHTTP" { _D("Content-Type illegal") http.Error(w, "unknown content type", 415) return } var requestBody []byte requestBody, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "error in reading request", 400) _D("error in reading HTTP request, error message: %s", err) return } if len(requestBody) < (int)(r.ContentLength) { http.Error(w, "error in reading request", 400) _D("fail to read all HTTP content") return } var dnsRequest dns.Msg err = dnsRequest.Unpack(requestBody) if err != nil { http.Error(w, "bad DNS request", 400) _D("error in packing HTTP response to DNS, error message: %s", err) return } dnsClient := new(dns.Client) if dnsClient == nil { http.Error(w, "Server Error", 500) _D("cannot create DNS client") return } dnsClient.ReadTimeout = this.timeout dnsClient.WriteTimeout = this.timeout dnsClient.Net = TransProString //will use a parameter to let user address resolver in future dnsResponse, RTT, err := dnsClient.Exchange(&dnsRequest, this.SERVERS[rand.Intn(len(this.SERVERS))]) //dnsResponse, RTT, err := dnsClient.Exchange(&dnsRequest, this.SERVERS[0]) if err != nil { _D("error in communicate with resolver, error message: %s", err) http.Error(w, "Server Error", 500) return } else { _D("request took %s", RTT) } if dnsResponse == nil { _D("no response back") http.Error(w, "Server Error:No Recursive response", 500) return } response_bytes, err := dnsResponse.Pack() if err != nil { http.Error(w, "error packing reply", 500) _D("error in packing request, error message: %s", err) return } _, err = w.Write(response_bytes) if err != nil { _D("Can not write response rightly, error message: %s", err) return } //don't know how to creat a response here }