123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800 |
- // Copyright 2009 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
- // DNS client: see RFC 1035.
- // Has to be linked into package net for Dial.
- // TODO(rsc):
- // Could potentially handle many outstanding lookups faster.
- // Random UDP source port (net.Dial should do that for us).
- // Random request IDs.
- package net
- import (
- "context"
- "errors"
- "internal/itoa"
- "io"
- "os"
- "sync"
- "time"
- "golang.org/x/net/dns/dnsmessage"
- )
- const (
- // to be used as a useTCP parameter to exchange
- useTCPOnly = true
- useUDPOrTCP = false
- // Maximum DNS packet size.
- // Value taken from https://dnsflagday.net/2020/.
- maxDNSPacketSize = 1232
- )
- var (
- errLameReferral = errors.New("lame referral")
- errCannotUnmarshalDNSMessage = errors.New("cannot unmarshal DNS message")
- errCannotMarshalDNSMessage = errors.New("cannot marshal DNS message")
- errServerMisbehaving = errors.New("server misbehaving")
- errInvalidDNSResponse = errors.New("invalid DNS response")
- errNoAnswerFromDNSServer = errors.New("no answer from DNS server")
- // errServerTemporarilyMisbehaving is like errServerMisbehaving, except
- // that when it gets translated to a DNSError, the IsTemporary field
- // gets set to true.
- errServerTemporarilyMisbehaving = errors.New("server misbehaving")
- )
- func newRequest(q dnsmessage.Question) (id uint16, udpReq, tcpReq []byte, err error) {
- id = uint16(randInt())
- b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true})
- b.EnableCompression()
- if err := b.StartQuestions(); err != nil {
- return 0, nil, nil, err
- }
- if err := b.Question(q); err != nil {
- return 0, nil, nil, err
- }
- tcpReq, err = b.Finish()
- udpReq = tcpReq[2:]
- l := len(tcpReq) - 2
- tcpReq[0] = byte(l >> 8)
- tcpReq[1] = byte(l)
- return id, udpReq, tcpReq, err
- }
- func checkResponse(reqID uint16, reqQues dnsmessage.Question, respHdr dnsmessage.Header, respQues dnsmessage.Question) bool {
- if !respHdr.Response {
- return false
- }
- if reqID != respHdr.ID {
- return false
- }
- if reqQues.Type != respQues.Type || reqQues.Class != respQues.Class || !equalASCIIName(reqQues.Name, respQues.Name) {
- return false
- }
- return true
- }
- func dnsPacketRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
- if _, err := c.Write(b); err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, err
- }
- b = make([]byte, maxDNSPacketSize)
- for {
- n, err := c.Read(b)
- if err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, err
- }
- var p dnsmessage.Parser
- // Ignore invalid responses as they may be malicious
- // forgery attempts. Instead continue waiting until
- // timeout. See golang.org/issue/13281.
- h, err := p.Start(b[:n])
- if err != nil {
- continue
- }
- q, err := p.Question()
- if err != nil || !checkResponse(id, query, h, q) {
- continue
- }
- return p, h, nil
- }
- }
- func dnsStreamRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
- if _, err := c.Write(b); err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, err
- }
- b = make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
- if _, err := io.ReadFull(c, b[:2]); err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, err
- }
- l := int(b[0])<<8 | int(b[1])
- if l > len(b) {
- b = make([]byte, l)
- }
- n, err := io.ReadFull(c, b[:l])
- if err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, err
- }
- var p dnsmessage.Parser
- h, err := p.Start(b[:n])
- if err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
- }
- q, err := p.Question()
- if err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
- }
- if !checkResponse(id, query, h, q) {
- return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
- }
- return p, h, nil
- }
- // exchange sends a query on the connection and hopes for a response.
- func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Question, timeout time.Duration, useTCP bool) (dnsmessage.Parser, dnsmessage.Header, error) {
- q.Class = dnsmessage.ClassINET
- id, udpReq, tcpReq, err := newRequest(q)
- if err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage
- }
- var networks []string
- if useTCP {
- networks = []string{"tcp"}
- } else {
- networks = []string{"udp", "tcp"}
- }
- for _, network := range networks {
- ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
- defer cancel()
- c, err := r.dial(ctx, network, server)
- if err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, err
- }
- if d, ok := ctx.Deadline(); ok && !d.IsZero() {
- c.SetDeadline(d)
- }
- var p dnsmessage.Parser
- var h dnsmessage.Header
- if _, ok := c.(PacketConn); ok {
- p, h, err = dnsPacketRoundTrip(c, id, q, udpReq)
- } else {
- p, h, err = dnsStreamRoundTrip(c, id, q, tcpReq)
- }
- c.Close()
- if err != nil {
- return dnsmessage.Parser{}, dnsmessage.Header{}, mapErr(err)
- }
- if err := p.SkipQuestion(); err != dnsmessage.ErrSectionDone {
- return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
- }
- if h.Truncated { // see RFC 5966
- continue
- }
- return p, h, nil
- }
- return dnsmessage.Parser{}, dnsmessage.Header{}, errNoAnswerFromDNSServer
- }
- // checkHeader performs basic sanity checks on the header.
- func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header) error {
- if h.RCode == dnsmessage.RCodeNameError {
- return errNoSuchHost
- }
- _, err := p.AnswerHeader()
- if err != nil && err != dnsmessage.ErrSectionDone {
- return errCannotUnmarshalDNSMessage
- }
- // libresolv continues to the next server when it receives
- // an invalid referral response. See golang.org/issue/15434.
- if h.RCode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone {
- return errLameReferral
- }
- if h.RCode != dnsmessage.RCodeSuccess && h.RCode != dnsmessage.RCodeNameError {
- // None of the error codes make sense
- // for the query we sent. If we didn't get
- // a name error and we didn't get success,
- // the server is behaving incorrectly or
- // having temporary trouble.
- if h.RCode == dnsmessage.RCodeServerFailure {
- return errServerTemporarilyMisbehaving
- }
- return errServerMisbehaving
- }
- return nil
- }
- func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type) error {
- for {
- h, err := p.AnswerHeader()
- if err == dnsmessage.ErrSectionDone {
- return errNoSuchHost
- }
- if err != nil {
- return errCannotUnmarshalDNSMessage
- }
- if h.Type == qtype {
- return nil
- }
- if err := p.SkipAnswer(); err != nil {
- return errCannotUnmarshalDNSMessage
- }
- }
- }
- // Do a lookup for a single name, which must be rooted
- // (otherwise answer will not find the answers).
- func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) {
- var lastErr error
- serverOffset := cfg.serverOffset()
- sLen := uint32(len(cfg.servers))
- n, err := dnsmessage.NewName(name)
- if err != nil {
- return dnsmessage.Parser{}, "", errCannotMarshalDNSMessage
- }
- q := dnsmessage.Question{
- Name: n,
- Type: qtype,
- Class: dnsmessage.ClassINET,
- }
- for i := 0; i < cfg.attempts; i++ {
- for j := uint32(0); j < sLen; j++ {
- server := cfg.servers[(serverOffset+j)%sLen]
- p, h, err := r.exchange(ctx, server, q, cfg.timeout, cfg.useTCP)
- if err != nil {
- dnsErr := &DNSError{
- Err: err.Error(),
- Name: name,
- Server: server,
- }
- if nerr, ok := err.(Error); ok && nerr.Timeout() {
- dnsErr.IsTimeout = true
- }
- // Set IsTemporary for socket-level errors. Note that this flag
- // may also be used to indicate a SERVFAIL response.
- if _, ok := err.(*OpError); ok {
- dnsErr.IsTemporary = true
- }
- lastErr = dnsErr
- continue
- }
- if err := checkHeader(&p, h); err != nil {
- dnsErr := &DNSError{
- Err: err.Error(),
- Name: name,
- Server: server,
- }
- if err == errServerTemporarilyMisbehaving {
- dnsErr.IsTemporary = true
- }
- if err == errNoSuchHost {
- // The name does not exist, so trying
- // another server won't help.
- dnsErr.IsNotFound = true
- return p, server, dnsErr
- }
- lastErr = dnsErr
- continue
- }
- err = skipToAnswer(&p, qtype)
- if err == nil {
- return p, server, nil
- }
- lastErr = &DNSError{
- Err: err.Error(),
- Name: name,
- Server: server,
- }
- if err == errNoSuchHost {
- // The name does not exist, so trying another
- // server won't help.
- lastErr.(*DNSError).IsNotFound = true
- return p, server, lastErr
- }
- }
- }
- return dnsmessage.Parser{}, "", lastErr
- }
- // A resolverConfig represents a DNS stub resolver configuration.
- type resolverConfig struct {
- initOnce sync.Once // guards init of resolverConfig
- // ch is used as a semaphore that only allows one lookup at a
- // time to recheck resolv.conf.
- ch chan struct{} // guards lastChecked and modTime
- lastChecked time.Time // last time resolv.conf was checked
- mu sync.RWMutex // protects dnsConfig
- dnsConfig *dnsConfig // parsed resolv.conf structure used in lookups
- }
- var resolvConf resolverConfig
- // init initializes conf and is only called via conf.initOnce.
- func (conf *resolverConfig) init() {
- // Set dnsConfig and lastChecked so we don't parse
- // resolv.conf twice the first time.
- conf.dnsConfig = systemConf().resolv
- if conf.dnsConfig == nil {
- conf.dnsConfig = dnsReadConfig("/etc/resolv.conf")
- }
- conf.lastChecked = time.Now()
- // Prepare ch so that only one update of resolverConfig may
- // run at once.
- conf.ch = make(chan struct{}, 1)
- }
- // tryUpdate tries to update conf with the named resolv.conf file.
- // The name variable only exists for testing. It is otherwise always
- // "/etc/resolv.conf".
- func (conf *resolverConfig) tryUpdate(name string) {
- conf.initOnce.Do(conf.init)
- // Ensure only one update at a time checks resolv.conf.
- if !conf.tryAcquireSema() {
- return
- }
- defer conf.releaseSema()
- now := time.Now()
- if conf.lastChecked.After(now.Add(-5 * time.Second)) {
- return
- }
- conf.lastChecked = now
- var mtime time.Time
- if fi, err := os.Stat(name); err == nil {
- mtime = fi.ModTime()
- }
- if mtime.Equal(conf.dnsConfig.mtime) {
- return
- }
- dnsConf := dnsReadConfig(name)
- conf.mu.Lock()
- conf.dnsConfig = dnsConf
- conf.mu.Unlock()
- }
- func (conf *resolverConfig) tryAcquireSema() bool {
- select {
- case conf.ch <- struct{}{}:
- return true
- default:
- return false
- }
- }
- func (conf *resolverConfig) releaseSema() {
- <-conf.ch
- }
- func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) {
- if !isDomainName(name) {
- // We used to use "invalid domain name" as the error,
- // but that is a detail of the specific lookup mechanism.
- // Other lookups might allow broader name syntax
- // (for example Multicast DNS allows UTF-8; see RFC 6762).
- // For consistency with libc resolvers, report no such host.
- return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
- }
- resolvConf.tryUpdate("/etc/resolv.conf")
- resolvConf.mu.RLock()
- conf := resolvConf.dnsConfig
- resolvConf.mu.RUnlock()
- var (
- p dnsmessage.Parser
- server string
- err error
- )
- for _, fqdn := range conf.nameList(name) {
- p, server, err = r.tryOneName(ctx, conf, fqdn, qtype)
- if err == nil {
- break
- }
- if nerr, ok := err.(Error); ok && nerr.Temporary() && r.strictErrors() {
- // If we hit a temporary error with StrictErrors enabled,
- // stop immediately instead of trying more names.
- break
- }
- }
- if err == nil {
- return p, server, nil
- }
- if err, ok := err.(*DNSError); ok {
- // Show original name passed to lookup, not suffixed one.
- // In general we might have tried many suffixes; showing
- // just one is misleading. See also golang.org/issue/6324.
- err.Name = name
- }
- return dnsmessage.Parser{}, "", err
- }
- // avoidDNS reports whether this is a hostname for which we should not
- // use DNS. Currently this includes only .onion, per RFC 7686. See
- // golang.org/issue/13705. Does not cover .local names (RFC 6762),
- // see golang.org/issue/16739.
- func avoidDNS(name string) bool {
- if name == "" {
- return true
- }
- if name[len(name)-1] == '.' {
- name = name[:len(name)-1]
- }
- return stringsHasSuffixFold(name, ".onion")
- }
- // nameList returns a list of names for sequential DNS queries.
- func (conf *dnsConfig) nameList(name string) []string {
- if avoidDNS(name) {
- return nil
- }
- // Check name length (see isDomainName).
- l := len(name)
- rooted := l > 0 && name[l-1] == '.'
- if l > 254 || l == 254 && rooted {
- return nil
- }
- // If name is rooted (trailing dot), try only that name.
- if rooted {
- return []string{name}
- }
- hasNdots := count(name, '.') >= conf.ndots
- name += "."
- l++
- // Build list of search choices.
- names := make([]string, 0, 1+len(conf.search))
- // If name has enough dots, try unsuffixed first.
- if hasNdots {
- names = append(names, name)
- }
- // Try suffixes that are not too long (see isDomainName).
- for _, suffix := range conf.search {
- if l+len(suffix) <= 254 {
- names = append(names, name+suffix)
- }
- }
- // Try unsuffixed, if not tried first above.
- if !hasNdots {
- names = append(names, name)
- }
- return names
- }
- // hostLookupOrder specifies the order of LookupHost lookup strategies.
- // It is basically a simplified representation of nsswitch.conf.
- // "files" means /etc/hosts.
- type hostLookupOrder int
- const (
- // hostLookupCgo means defer to cgo.
- hostLookupCgo hostLookupOrder = iota
- hostLookupFilesDNS // files first
- hostLookupDNSFiles // dns first
- hostLookupFiles // only files
- hostLookupDNS // only DNS
- )
- var lookupOrderName = map[hostLookupOrder]string{
- hostLookupCgo: "cgo",
- hostLookupFilesDNS: "files,dns",
- hostLookupDNSFiles: "dns,files",
- hostLookupFiles: "files",
- hostLookupDNS: "dns",
- }
- func (o hostLookupOrder) String() string {
- if s, ok := lookupOrderName[o]; ok {
- return s
- }
- return "hostLookupOrder=" + itoa.Itoa(int(o)) + "??"
- }
- // goLookupHost is the native Go implementation of LookupHost.
- // Used only if cgoLookupHost refuses to handle the request
- // (that is, only if cgoLookupHost is the stub in cgo_stub.go).
- // Normally we let cgo use the C library resolver instead of
- // depending on our lookup code, so that Go and C get the same
- // answers.
- func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []string, err error) {
- return r.goLookupHostOrder(ctx, name, hostLookupFilesDNS)
- }
- func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) {
- if order == hostLookupFilesDNS || order == hostLookupFiles {
- // Use entries from /etc/hosts if they match.
- addrs = lookupStaticHost(name)
- if len(addrs) > 0 || order == hostLookupFiles {
- return
- }
- }
- ips, _, err := r.goLookupIPCNAMEOrder(ctx, "ip", name, order)
- if err != nil {
- return
- }
- addrs = make([]string, 0, len(ips))
- for _, ip := range ips {
- addrs = append(addrs, ip.String())
- }
- return
- }
- // lookup entries from /etc/hosts
- func goLookupIPFiles(name string) (addrs []IPAddr) {
- for _, haddr := range lookupStaticHost(name) {
- haddr, zone := splitHostZone(haddr)
- if ip := ParseIP(haddr); ip != nil {
- addr := IPAddr{IP: ip, Zone: zone}
- addrs = append(addrs, addr)
- }
- }
- sortByRFC6724(addrs)
- return
- }
- // goLookupIP is the native Go implementation of LookupIP.
- // The libc versions are in cgo_*.go.
- func (r *Resolver) goLookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
- order := systemConf().hostLookupOrder(r, host)
- addrs, _, err = r.goLookupIPCNAMEOrder(ctx, network, host, order)
- return
- }
- func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, network, name string, order hostLookupOrder) (addrs []IPAddr, cname dnsmessage.Name, err error) {
- if order == hostLookupFilesDNS || order == hostLookupFiles {
- addrs = goLookupIPFiles(name)
- if len(addrs) > 0 || order == hostLookupFiles {
- return addrs, dnsmessage.Name{}, nil
- }
- }
- if !isDomainName(name) {
- // See comment in func lookup above about use of errNoSuchHost.
- return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
- }
- resolvConf.tryUpdate("/etc/resolv.conf")
- resolvConf.mu.RLock()
- conf := resolvConf.dnsConfig
- resolvConf.mu.RUnlock()
- type result struct {
- p dnsmessage.Parser
- server string
- error
- }
- lane := make(chan result, 1)
- qtypes := []dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA}
- switch ipVersion(network) {
- case '4':
- qtypes = []dnsmessage.Type{dnsmessage.TypeA}
- case '6':
- qtypes = []dnsmessage.Type{dnsmessage.TypeAAAA}
- }
- var queryFn func(fqdn string, qtype dnsmessage.Type)
- var responseFn func(fqdn string, qtype dnsmessage.Type) result
- if conf.singleRequest {
- queryFn = func(fqdn string, qtype dnsmessage.Type) {}
- responseFn = func(fqdn string, qtype dnsmessage.Type) result {
- dnsWaitGroup.Add(1)
- defer dnsWaitGroup.Done()
- p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
- return result{p, server, err}
- }
- } else {
- queryFn = func(fqdn string, qtype dnsmessage.Type) {
- dnsWaitGroup.Add(1)
- go func(qtype dnsmessage.Type) {
- p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
- lane <- result{p, server, err}
- dnsWaitGroup.Done()
- }(qtype)
- }
- responseFn = func(fqdn string, qtype dnsmessage.Type) result {
- return <-lane
- }
- }
- var lastErr error
- for _, fqdn := range conf.nameList(name) {
- for _, qtype := range qtypes {
- queryFn(fqdn, qtype)
- }
- hitStrictError := false
- for _, qtype := range qtypes {
- result := responseFn(fqdn, qtype)
- if result.error != nil {
- if nerr, ok := result.error.(Error); ok && nerr.Temporary() && r.strictErrors() {
- // This error will abort the nameList loop.
- hitStrictError = true
- lastErr = result.error
- } else if lastErr == nil || fqdn == name+"." {
- // Prefer error for original name.
- lastErr = result.error
- }
- continue
- }
- // Presotto says it's okay to assume that servers listed in
- // /etc/resolv.conf are recursive resolvers.
- //
- // We asked for recursion, so it should have included all the
- // answers we need in this one packet.
- //
- // Further, RFC 1035 section 4.3.1 says that "the recursive
- // response to a query will be... The answer to the query,
- // possibly preface by one or more CNAME RRs that specify
- // aliases encountered on the way to an answer."
- //
- // Therefore, we should be able to assume that we can ignore
- // CNAMEs and that the A and AAAA records we requested are
- // for the canonical name.
- loop:
- for {
- h, err := result.p.AnswerHeader()
- if err != nil && err != dnsmessage.ErrSectionDone {
- lastErr = &DNSError{
- Err: "cannot marshal DNS message",
- Name: name,
- Server: result.server,
- }
- }
- if err != nil {
- break
- }
- switch h.Type {
- case dnsmessage.TypeA:
- a, err := result.p.AResource()
- if err != nil {
- lastErr = &DNSError{
- Err: "cannot marshal DNS message",
- Name: name,
- Server: result.server,
- }
- break loop
- }
- addrs = append(addrs, IPAddr{IP: IP(a.A[:])})
- case dnsmessage.TypeAAAA:
- aaaa, err := result.p.AAAAResource()
- if err != nil {
- lastErr = &DNSError{
- Err: "cannot marshal DNS message",
- Name: name,
- Server: result.server,
- }
- break loop
- }
- addrs = append(addrs, IPAddr{IP: IP(aaaa.AAAA[:])})
- default:
- if err := result.p.SkipAnswer(); err != nil {
- lastErr = &DNSError{
- Err: "cannot marshal DNS message",
- Name: name,
- Server: result.server,
- }
- break loop
- }
- continue
- }
- if cname.Length == 0 && h.Name.Length != 0 {
- cname = h.Name
- }
- }
- }
- if hitStrictError {
- // If either family hit an error with StrictErrors enabled,
- // discard all addresses. This ensures that network flakiness
- // cannot turn a dualstack hostname IPv4/IPv6-only.
- addrs = nil
- break
- }
- if len(addrs) > 0 {
- break
- }
- }
- if lastErr, ok := lastErr.(*DNSError); ok {
- // Show original name passed to lookup, not suffixed one.
- // In general we might have tried many suffixes; showing
- // just one is misleading. See also golang.org/issue/6324.
- lastErr.Name = name
- }
- sortByRFC6724(addrs)
- if len(addrs) == 0 {
- if order == hostLookupDNSFiles {
- addrs = goLookupIPFiles(name)
- }
- if len(addrs) == 0 && lastErr != nil {
- return nil, dnsmessage.Name{}, lastErr
- }
- }
- return addrs, cname, nil
- }
- // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
- func (r *Resolver) goLookupCNAME(ctx context.Context, host string) (string, error) {
- order := systemConf().hostLookupOrder(r, host)
- _, cname, err := r.goLookupIPCNAMEOrder(ctx, "ip", host, order)
- return cname.String(), err
- }
- // goLookupPTR is the native Go implementation of LookupAddr.
- // Used only if cgoLookupPTR refuses to handle the request (that is,
- // only if cgoLookupPTR is the stub in cgo_stub.go).
- // Normally we let cgo use the C library resolver instead of depending
- // on our lookup code, so that Go and C get the same answers.
- func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, error) {
- names := lookupStaticAddr(addr)
- if len(names) > 0 {
- return names, nil
- }
- arpa, err := reverseaddr(addr)
- if err != nil {
- return nil, err
- }
- p, server, err := r.lookup(ctx, arpa, dnsmessage.TypePTR)
- if err != nil {
- return nil, err
- }
- var ptrs []string
- for {
- h, err := p.AnswerHeader()
- if err == dnsmessage.ErrSectionDone {
- break
- }
- if err != nil {
- return nil, &DNSError{
- Err: "cannot marshal DNS message",
- Name: addr,
- Server: server,
- }
- }
- if h.Type != dnsmessage.TypePTR {
- err := p.SkipAnswer()
- if err != nil {
- return nil, &DNSError{
- Err: "cannot marshal DNS message",
- Name: addr,
- Server: server,
- }
- }
- continue
- }
- ptr, err := p.PTRResource()
- if err != nil {
- return nil, &DNSError{
- Err: "cannot marshal DNS message",
- Name: addr,
- Server: server,
- }
- }
- ptrs = append(ptrs, ptr.PTR.String())
- }
- return ptrs, nil
- }
|