如何询问“dig”只返回CNAME记录中的IP地址?

dig +short 命令(如 "dig show only answer" 中所述)非常适合将名称批处理为 IP 地址。它执行简单的任务并且做得很好。 不幸的是,即使使用 +short,当存在 CNAME 时也不够简短。例如:
$ dig +short docs.sbonds.org
ghs.google.com.
173.194.69.121
我尝试了+noall,但似乎它并没有改变+short的行为。我还尝试了指定-t a,只是为了确保它不会认为我指的是A记录或CNAME,但(毫不奇怪地)什么也没改变。
$ dig +noall +short docs.sbonds.org
ghs.google.com.
173.194.69.121
我正在使用RedHat 7的`dig`命令。
# dig -v
DiG 9.9.4-RedHat-9.9.4-73.el7_6
我可以用可靠的grep过滤掉CNAME,但似乎dig应该有一种方法来提供“只有IP,女士”。 那种方法是什么?
2个回答

dig是一种故障排除工具,它发送DNS查询并接收DNS响应,正如安德烈亚斯所说,答案既是CNAME记录又是A记录,这是设计上的。您希望得到“只有IP地址”,因此这不是一个DNS故障排除,而是“只是”解析问题,对于这个问题,dig太过强大。

nslookup

nslookupdig差,但仍会给出太多信息:

$ 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

主机

主机更简单,但仍会为您返回“太多”(但请注意它还返回IPv6地址,这是好的):

$ 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
根据您的Unix系统,可以使用getent命令。但请注意,这可能会或可能不会执行DNS查询,因为您可以在/etc/nsswitch.conf中配置每个服务的数据源,并且对于hosts来说,它可能是files(即古老的/etc/hosts)和DNS的混合体。
$ getent hosts docs.sbonds.org
2607:f8b0:4007:801::2013 ghs.google.com docs.sbonds.org

还要注意,在正确的Unix设置上,它会优先使用IPv6而不是IPv4,这可能对您造成问题(这应该取决于/etc/gai.conf中的配置)。

事实上,hosts不遵守/etc/gai.conf,您需要改用ahosts,它将使用getaddrinfo,因此也会使用gai.conf。请注意,您会得到一个列表(其顺序取决于gai.conf的配置):

$ 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 如果你被允许编写一个简单的脚本,你有很多解决方案,比如:
$ perl -MSocket -E 'say inet_ntoa(inet_aton("docs.sbonds.org"))'
172.217.4.179

DOH

或者使用任何 DOH(DNS over HTTPS)端点(或类似的)与任何 HTTP 客户端。示例:

$ 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有自己的解析器应用程序:

$ 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
你仍然需要以某种方式解析它,但它确实提供了直接的最终IP地址(当你调用它时,有一个标志可以让它不遵循CNAME记录,以满足特定需求)。

1写一个简单的脚本,这是我之前一直停留的地方。看起来似乎有更好的方法,但实际上并没有。 - Steve Bonds
1@SteveBonds 请看一下我的更新,有一个类似的DOH案例。 - Patrick Mevzek
1你不知道我有多希望“jq”能够更广泛地安装。尽管如此,那是一个非常出色的解决方案的补充! - Steve Bonds
以防有所帮助,这是我最终采取的方法:dig +noall +answer host.example.com | awk '/ IN A\t/ { print $5 }' - dossy
@dossy 你的解决方案不起作用(因为在A后面可能是一个空格,而不是制表符,这取决于很多事情)。试试使用 www.icann.org 这个CNAME。解析 dig 的输出永远都不是一个好主意,会产生很多特殊情况。 - Patrick Mevzek
@PatrickMevzek 你说得对,可能是制表符或空格,所以这样会更可靠: dig +noall +answer www.icann.org | awk '/\sIN\s+A\s/ { print $5 }' - dossy
@dossy 别再试图解析适合人类阅读而非机器的 dig 输出了。但如果你坚持这样做,至少看看 +yaml 选项... - Patrick Mevzek

无论是CNAME还是对应的A记录(或者你要求的其他类型),都属于答案部分。 因此,没有dig选项(至少在RHEL7的dig中没有)可以过滤掉CNAME响应。 我认为你将不得不依赖于dig +short [...] | grep -v '\.$'来删除CNAME响应。

3或者 dig +short ... | tail -1 - Patrick Mevzek
5不完全正确。|tail -1 只会显示最后一行。如果 RRset 中有多个地址与 CNAME 指向同一个,这将给出错误的结果。 - Andreas Rogge
1我同意,但用户的请求可能被理解为IP地址(单数),而不是全部。事实上,无论应用了什么样的过滤,都可能存在一些特殊情况(例如,如果远程域名服务器出现故障,也会返回IP地址,那么以结束点进行过滤可能会失败;虽然不应该发生,但在DNS世界中,一切皆有可能)。 - Patrick Mevzek

  • 相关问题