19

The dig +short command (such as described in "dig show only answer") is great for batch processing names into IP addresses. It does a simple job and does it well.

Unfortunately when there's a CNAME even +short isn't short enough. For example:

$ dig +short docs.sbonds.org
ghs.google.com.
173.194.69.121

I've tried +noall but it doesn't seem like it changes the behavior of +short. I've also tried specifying -t a just to ensure it didn't think I meant an A record or CNAME, but that (unsurprisingly) changes nothing.

$ dig +noall +short docs.sbonds.org
ghs.google.com.
173.194.69.121

I'm using RedHat 7's dig:

# dig -v
DiG 9.9.4-RedHat-9.9.4-73.el7_6

I can filter out the CNAMEs with trusty grep, but it seems like dig should have some way to give "Just the IP, ma'am."

What is that way?

0

2 Answers 2

18

dig is a troubleshooting tool, so it sends DNS queries and receives DNS answers and as Andreas said the answer is both CNAME and A records, as designed. Your wish is to give "Just the IP, ma'am.", so that is not a DNS troubleshooting, it is "just" the resolution, for which dig is too much.

nslookup

nslookup is inferior to dig but will still give you too much:

$ nslookup docs.sbonds.org
Server:     192.0.2.254
Address:    192.0.2.254#53

Non-authoritative answer:
docs.sbonds.org canonical name = ghs.google.com.
Name:   ghs.google.com
Address: 172.217.4.179

host

host is simpler but will still return "too much" for you (but note that it returns also the IPv6 address which is good):

$ host docs.sbonds.org
docs.sbonds.org is an alias for ghs.google.com.
ghs.google.com has address 172.217.15.83
ghs.google.com has IPv6 address 2607:f8b0:4004:815::2013

getent

Depending on your Unix system, getent can be used. Note however that this may or may not do a DNS query because you configure in /etc/nsswitch.conf the source of data per service, and for hosts it will probably be a mix of both files (which is the venerable /etc/hosts) and the DNS.

$ getent hosts docs.sbonds.org
2607:f8b0:4007:801::2013 ghs.google.com docs.sbonds.org

Observe also that on a proper Unix setup it will favor IPv6 over IPv4 so that may be a problem for you (this should depend on the configuration in /etc/gai.conf)

In fact hosts does not honor /etc/gai.conf, you need to use ahosts instead, which will use getaddrinfo and hence gai.conf. Observe that you get a list (whose order depends on gai.conf configuration):

$ getent ahosts docs.sbonds.org
172.217.4.179   STREAM ghs.google.com
172.217.4.179   DGRAM
172.217.4.179   RAW
2607:f8b0:4007:801::2013 STREAM
2607:f8b0:4007:801::2013 DGRAM
2607:f8b0:4007:801::2013 RAW

Perl

If you are allowed to write a simple script, you have many solutions, like:

$ perl -MSocket -E 'say inet_ntoa(inet_aton("docs.sbonds.org"))'
172.217.4.179

DOH

Or use any DOH (DNS over HTTPS) endpoint (or similar) with any HTTP client. Examples:

$ curl --silent 'https://dns.google.com/resolve?name=docs.sbonds.org&type=A' | jq -c '.Answer[] | select(.type == 1) | .data'
"172.217.3.83"


$ curl --silent -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=docs.sbonds.org&type=A' | jq -c '.Answer[] | select(.type == 1) | .data'
"172.217.12.147"

Systemd

Systemd has its own resolver application:

$ systemd-resolve docs.sbonds.org
docs.sbonds.org: 172.217.9.51
                 2607:f8b0:4009:801::2013
                 (ghs.google.com)

-- Information acquired via protocol DNS in 239.1ms.
-- Data is authenticated: no

You still need to parse it in some way, but it does give the direct final IP addresses (there is a flag when you invoke it to make it not follow CNAME records for whatever use cases that need that)

7
  • 1
    "Write a simple script" is where I've always ended up before. It seemed like there must be a better way, but it looks like there is not. May 2, 2019 at 0:39
  • 1
    @SteveBonds see my update with a DOH case (similar to) May 2, 2019 at 0:59
  • 1
    You have no idea how much I wish "jq" was more widely installed. Nonetheless, that was an excellent addition to the solution! May 2, 2019 at 13:50
  • Just in case this helps, this is what I end up doing: dig +noall +answer host.example.com | awk '/ IN A\t/ { print $5 }'
    – dossy
    Apr 28, 2022 at 21:18
  • @dossy Your solution doesn't work (because it can be a space, not a tab after A depending on lots of things). Try with www.icann.org which is a CNAME. Parsing dig output is never a good idea and will yield a ton of edge cases. Apr 28, 2022 at 22:53
15

Both the CNAME and the corresponding A (or whatever type you asked for) belong to the answer-section. As such there is no dig option (at least not in RHEL7's dig) that could filter out the CNAME response.

I think you will have to rely on dig +short [...] | grep -v '\.$' to remove the CNAME responses.

4
  • 3
    Or dig +short ... | tail -1 May 1, 2019 at 23:14
  • 5
    not exactly. |tail -1 will show the last line. If there is more than one address in the RRset the CNAME points to this will give the wrong result. May 2, 2019 at 18:14
  • 1
    I agree but the user request could be understood as the IP, singular, not all :-). Indeed, whatever filtering is applied can have edge cases (filtering on end dot can fail if remote nameserver is broken in some way, like returning IP addresses there too; not supposed to happen but in DNS world everything can happen) May 2, 2019 at 18:36
  • Sigh, it is frustrating that dig +short -t A will output the canonical name of a CNAME if a CNAME exists and an A record doesn't. I would much rather it output nothing or an error message like host does.
    – bdrx
    Mar 20 at 22:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .