5

I'm looking for a way (in Go) to get the CNAME (if any) of a given host, whether that host's DNS resolution fails or not.

The DNS resolution of a host may fail and yield NXDOMAIN, but the host in question may still have a CNAME record. At the time of writing this question, tst1crmapps.starbucks.com is an example of such a host:

$ dig tst1crmapps.starbucks.com | grep -E 'NXDOMAIN|CNAME'
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 50444
tst1crmapps.starbucks.com. 86400 IN CNAME   bigip-tst1crmapps-starbucks.oracle.com.

(run on macOS)

Function net.LookupCNAME seems to be right up my alley, but it fails to return the CNAME if the host's DNS resolution fails. Running

package main

import (
    "log"
    "net"
)

func main() {
    cname, err := net.LookupCNAME("tst1crmapps.starbucks.com")
    if err != nil {
        log.Fatal(err)
    }
    log.Println(cname)
}

yields

yyyy/MM/dd hh:mm:ss lookup tst1crmapps.starbucks.com: no such host

Am I misunderstanding net.LookupCNAME? Or am I using the wrong function?

jub0bs
  • 46,795
  • 22
  • 148
  • 157

1 Answers1

10

Go's LookupCNAME function tries to follow a CNAME chain all the way to its end. This means it considers a DNS name without both CNAME and A records an error:

A canonical name is the final name after following zero or more CNAME records. LookupCNAME does not return an error if host does not contain DNS "CNAME" records, as long as host resolves to address records.

Go does not provide a low-level DNS API in the standard library. The de-facto standard package to use for that is github.com/miekg/dns (error handling and type checking elided for brevity):

package main

import (
    "fmt"

    "github.com/miekg/dns"
)

func main() {
    config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
    c := new(dns.Client)
    m := new(dns.Msg)

    // Note the trailing dot. miekg/dns is very low-level and expects canonical names.
    m.SetQuestion("tst1crmapps.starbucks.com.", dns.TypeCNAME)
    m.RecursionDesired = true
    r, _, _ := c.Exchange(m, config.Servers[0]+":"+config.Port)

    fmt.Println(r.Answer[0].(*dns.CNAME).Target) // bigip-tst1crmapps-starbucks.oracle.com.
}

Peter
  • 24,684
  • 4
  • 36
  • 46
  • 1
    I'm surprised the standard library doesn't provide the functionality I was looking for. Thanks for this! – jub0bs Jul 02 '19 at 16:23