Atom

思考、故事和创意

DNS

whois xyz

Linux 中的 whois 客户端把各个顶域的 whois 服务器写在了代码里,所以不包含一些新的顶域。在 Linux 里可以给这个 whois 客户端添加配置/etc/whois.conf,指定顶域的 whois 服务器。

如果没有进行配置,查询一些新顶域的域名 whois 信息时就查不到了,比如大家喜闻乐见的电影搜索网站 51bt.xyz 这个网站的域名,或者 Google 新买的 abc.xyz 这个域名,whois 查询时就得到了这样的信息。

whois server is known for this kind of object.

为了能查询这些域名的 whois 信息,就要把这些域名顶域的 whois 服务器写在配置文件里,格式是这样 \.ac$ whois.nic.ac 意思是以 .ac 结尾的域名,去 whois.nic.ac 查询 whois 信息。

但是首先,要找到这些顶域的 whois 服务器是什么。

查找 whois 服务器的方法

IANA

首选的方法向 IANA 查询,IANA 相当于 DNS 解析过程中的根。例如查询顶域 xyz 的 whois 服务器 whois -h whois.iana.org,会得到很多内容

% IANA WHOIS server
% for more information on IANA, visit http://www.iana.org
% This query returned 1 object

domain:       XYZ

organisation: XYZ.COM LLC
address:      2121 E Tropicana Ave
address:      Las Vegas
address:      NV 89119
address:      United States

contact:      administrative
name:         General Counsel
organisation: XYZ.COM LLC
address:      2121 E Tropicana Ave., STE2
address:      Las Vegas
address:      NV 89119
address:      United States
phone:        +1.7027632191
e-mail:       hello@xyz.com

contact:      technical
name:         CTO
organisation: CentralNic
address:      35-39 Moorgate
address:      London EC2R 6AR
address:      United Kingdom
phone:        +44.2033880600
fax-no:       +44.2033880601
e-mail:       tld.ops@centralnic.com

nserver:      GENERATIONXYZ.NIC.XYZ 212.18.249.42 2a04:2b00:13ff:0:0:0:0:42
nserver:      X.NIC.XYZ 194.169.218.42 2001:67c:13cc:0:0:0:1:42
nserver:      Y.NIC.XYZ 185.24.64.42 2a04:2b00:13cc:0:0:0:1:42
nserver:      Z.NIC.XYZ 212.18.248.42 2a04:2b00:13ee:0:0:0:0:42
ds-rdata:     3599 8 1 3FA3B264F45DB5F38BEDEAF1A88B76AA318C2C7F
ds-rdata:     3599 8 2 B9733869BC84C86BB59D102BA5DA6B27B2088552332A39DCD54BC4E8D66B0499

whois:        whois.nic.xyz

status:       ACTIVE
remarks:      Registration information: http://nic.xyz

created:      2014-02-06
changed:      2015-03-18
source:       IANA

其中包含了 whois 的那一行,其值 whois.nic.xyz 就是要找的 xyz 的 whois 服务器了。

whois-servers.net

有些时候上面的方法竟然查不到,比如 whois -h whois.iana.org ad 查询 ad 顶域的信息,返回的结果里没有 whois 这一项。

可以尝试使用 dig ad.whois-servers.net 的方法,也就是以要查的顶域作为 whois-servers.net 的子域名进行 DNS 请求。检查返回结果中的 CNAME,就是这个顶域的 whois 服务器。

例如 dig +noall buzz.whois-servers.net +answer 结果是

buzz.whois-servers.net.	 551	 IN    CNAME    	whois.nic.buzz.
whois.nic.buzz.		     552     IN	    A        	156.154.115.28
whois.nic.buzz.		     552     IN     A           156.154.117.28

得到 buzz 顶域的 whois 服务器就是 whois.nic.buzz.


一个尝试获取顶域 whois 服务器的丑陋脚本

#!/bin/bash

set -o errexit 

TLDS_FILE="./tlds-alpha-by-domain.txt"
WHOIS_SERVER_FILE="./whois_server.txt"
ERROR_FILE="${WHOIS_SERVER_FILE}.err"
DOMAIN_REGEX='^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.([A-Za-z]{2,}|xn--[A-Za-z0-9]{2,}))\.?$'

if [[ ! -f $TLDS_FILE ]]
then
    # 下载顶域列表
    wget https://data.iana.org/TLD/tlds-alpha-by-domain.txt -O $TLDS_FILE
fi

cat /dev/null > $WHOIS_SERVER_FILE
cat /dev/null > $ERROR_FILE

while read tld
do
    # 过滤可能出现的空行和注释的内容
    if [[ -z $tld ]]; then continue; fi
    if [[ ${tld:0:1} = '#' ]]; then continue; fi
    ## 一律转换为小写字母
    tld=$(echo $tld | tr A-Z a-z)
    echo $tld

    # 首先通过 whois -h whois.iana.org xxx 的方式向 IANA 查询 tld 信息
    whois_server=$(whois -h whois.iana.org $tld | grep whois | head -1 | awk '{print $2}'|| echo "NOT FOUND")
    if [[ ! "x$whois_server" = "x" || "x$whois_server" = "xNOT FOUND" ]] &&  [[ $whois_server =~ $DOMAIN_REGEX ]]
    then
        echo "\\.$tld\$ $whois_server" >> $WHOIS_SERVER_FILE
    else
        # 如果上面的方法没查到,尝试使用 dig xxx.whois-servers.net 的方法,不太可靠,放弃
        echo "dig $tld.whois-servers.net"
        whois_server=$(dig +noall $tld.whois-servers.net +answer | head -1 | awk '{print $5}' || echo "NOT FOUND")
        if [[ ! "x$whois_server" = "x" || "x$whois_server" = "xNOT FOUND" ]] && [[ $whois_server =~ $DOMAIN_REGEX ]]
        then
            # 有些顶域查到了 whois.ripe.net,并不能从这里查到这些顶域的域名信息
            if [[ "$whois_server" = "whois.ripe.net." ]]
            then
                echo "$tld got $whois_server" >> $ERROR_FILE
            else
                echo "\\.$tld\$ $whois_server" >> $WHOIS_SERVER_FILE
            fi
        else
            echo "didn't find $tld whois server"
            echo "$tld" >> $ERROR_FILE
        fi
    fi
done < $TLDS_FILE

参考内容
Where to find an up-to-date list of WHOIS Servers for various tlds?
How to whois new TLDs?

检测递归 DNS 的后端 IP

我想知道当我请求 114.114.114.114 或者 8.8.8.8,它们的后端都有什么。我在授权 DNS 设置了分线路的记录,请求当地递归却收到了其他线路的结果,我想知道这个当地递归后端是什么。实际上,当我向递归发起请求之后,最终授权 DNS 收到的来自递归 DNS 的请求,是来自什么地址。

我们要找个办法,把递归向授权的请求“引导”到一个容易看到的地方来。

首先给我一个子域名设置一条 NS 记录,这个 NS 指向我自己的 VPS 地址

back.fixatom.com    NS  ns.fixatom.com
ns.fixatom.com	    A   1.2.3.4

通过这样,当我向递归请求 back.intxt.net 以及它的更多级子域名时,递归就会到我自己设置的 NS 上来请求。“引导”完成。

然后我在我的 VPS 上跑这样一段代码

#!/usr/bin/env python

import socket
import dnslib

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 53))

while True:
    data, addr = sock.recvfrom(1024)
    d = dnslib.DNSRecord.parse(data)
    print addr[0], d.questions[0].qname

作用很简单,就是输出请求来源 IP 和请求域名。


因为这个自己设置的 NS 根本就没有返回内容,所以发起的请求自然是没有结果的。

有的递归 DNS 后端 IP 不多,或者只会为你所在区域分配一个或一段 IP,这时候能够检测到得 IP 也就是这一个(段)。也有些递归 DNS 后端有好多 IP,为了避免递归缓存,不再继续向这个 NS 请求,最好变换子域名来请求。

for i in {1..10}
do
    dig xxxx$i.back.intxt.net @114.114.114.114
done

这是在我电脑上跑上面的 shell,在 VPS 上得到的结果

60.215.138.233 xxxx1.back.intxt.net.
60.215.138.233 xxxx1.back.intxt.net.
60.215.138.233 xxxx1.back.intxt.net.
60.215.138.233 xxxx1.back.intxt.net.
60.215.138.208 xxxx1.back.intxt.net.
60.215.138.208 xxxx1.back.intxt.net.
60.215.138.233 xxxx1.back.intxt.net.
60.215.138.208 xxxx1.back.intxt.net.
60.215.138.208 xxxx1.back.intxt.net.
60.215.138.208 xxxx2.back.intxt.net.
60.215.138.208 xxxx2.back.intxt.net.
60.215.138.208 xxxx1.back.intxt.net.
60.215.138.208 xxxx2.back.intxt.net.
60.215.138.208 xxxx2.back.intxt.net.
60.215.138.233 xxxx3.back.intxt.net.
60.215.138.233 xxxx3.back.intxt.net.
60.215.138.233 xxxx3.back.intxt.net.
60.215.138.208 xxxx4.back.intxt.net.
60.215.138.208 xxxx4.back.intxt.net.
60.215.138.208 xxxx5.back.intxt.net.
60.215.138.208 xxxx5.back.intxt.net.
60.215.138.208 xxxx5.back.intxt.net.
60.215.138.233 xxxx5.back.intxt.net.
60.215.138.233 xxxx6.back.intxt.net.
60.215.138.208 xxxx6.back.intxt.net.
60.215.138.208 xxxx6.back.intxt.net.
60.215.138.233 xxxx7.back.intxt.net.
60.215.138.208 xxxx6.back.intxt.net.
60.215.138.208 xxxx8.back.intxt.net.
60.215.138.233 xxxx8.back.intxt.net.
60.215.138.233 xxxx8.back.intxt.net.
60.215.138.233 xxxx9.back.intxt.net.
60.215.138.208 xxxx9.back.intxt.net.
60.215.138.208 xxxx10.back.intxt.net.
60.215.138.208 xxxx9.back.intxt.net.
60.215.138.208 xxxx10.back.intxt.net.

可以看出,我向 114.114.114.114 请求,它会把这个请求分发给 60.215.138.* 这一段的后端服务器,这些后端服务器到授权去请求。

dig 命令

  • 默认向系统设置 DNS 请求指定域名的 A 记录

    dig intxt.net
    `

  • 请求指定的域名记录类型

    `dig intxt.net NS
    # 或者
    dig intxt.net -t NS
    `
  • 指定请求 DNS 服务器

    `dig intxt.net @114.114.114.114
    `
  • 简化输出结果

    `dig +noall intxt.net +answer
    `
  • 更简化地输出结果

    `dig intxt.net +short
    `
  • 以 TCP 方式进行 DNS 请求

    `dig intxt.net +tcp
    `
  • 设置请求超时时间,默认是 5 秒

    `dig intxt.net +time=2
    `
  • 设置请求重试次数(包含最开始的一次),默认 3 次

    `# 总共会进行 5 次 请求
    dig intxt.net +tries=5
    `
  • 设置请求重试次数(不包含最开始的一次),默认 2 次

    `# 总共会进行 6 次 请求
    dig intxt.net +retry=5
    `
  • 让 SOA 记录更清晰,以多行的形式输出,而不是在一行上

    `dig intxt.net SOA +multiline
    `
  • 从根开始,跟踪域名解析的整个过程(没有走递归 DNS)

    `dig intxt.net +trace

Unbound 性能优化

安装完 unbound 之后,如果没有负载大量 DNS 请求的需求,一般没必要对 unbound 进行特殊的优化配置。如果有这个需求,或者仅仅是无聊以及其他什么原因,就需要改下配置文件了。下面的内容基本上是从官方文档里扒出来的。

  • num-threads 设置为机器的 CPU 核心数

    例如有一个 CPU,8个核,设置 num-threads: 8

  • msg-cache-slabs, rrset-cache-slabs, infra-cache-slabs, key-cache-slabs 设置为跟 num-threads 相近的 2 的 N 次方,数字设置越大,程序中锁的竞争会越少,但是会产生更多的内存碎片

    例如上面 num-threads: 8, 这几个 *-cache-slabs 也设置为 8 即可。

  • outgoing-range 这个选项指定 unbound 可以打开的端口数目,每个线程能打开的文件描述符个数。一般越大性能越好,要设置超过 1024 (系统默认的最大数目),需要在编译 unbound 时使用 libevent,指定 –with-libevent

  • num-queries-per-thread 指定每个线程能够同时处理的最大请求数,超过这个数目的请求会被丢掉。outgoing-range 设置为 8192,num-queries-per-thread 就设置为 4096。即 outgoing-range 设置为 num-queries-per-thread 的两倍

  • so-rcvbuf, so-sndbuf 设置为 0 则使用系统默认的接收/发送缓冲区大小,在负载比较大的服务器上,视网卡等配置而定,可以设置为 4m, 8m 甚至更大,大的 so-rcvbuf 可以有效减少丢包

  • msg-cache-size 消息缓存的大小,rrset-cache-size DNS 记录缓存大小。为了提高性能,适度增大这两项值,把 rrset-cache-size 设置为 msg-cache-size 的两倍。 unbound 的内存占用会大约稳定在配置文件中指定的各项值的总和的 2 到 2.5 倍。

    如果有 8G 内存供 unbound 使用, 把 rrset-cache-size 和 msg-cache-size 分别设置为 2G, 1G 应该比较合适(除了这两个,还有其他内存占用配置,是与 dnssec 相关的,如果使用 dnssec 的话,这里设置的 2G, 1G 需要重新考虑)

以上需要根据硬件配置适当调整。没必要过分追求“最优”,DNS 服务器遇到的场景本就是多种多样,费时费力得到的“最优参数”不一定适用于所有场景。

另外,有一些配置项,不管是什么硬件配置,都会对 unbound 或多或少产生影响

  • logfile 设置为空("")的话,如果 unbound 不是以 damonize 方式启动,日志输出到 stderr,否则就不输出日志。不为空的话,日志输出到指定文件(注意权限问题)。不输出日志比输出日志性能好。
  • log-time-ascii 设置为 yes,在日志中输出的时间是人一眼能认得的时间格式,设置为 no,则使用 Unix时间戳。设置为 no 要比 yes 性能好一些。
  • log-queries 设置为 yes 会记录 unbound 收到的每一条 DNS 请求,每一条都包括来源 IP、域名、请求类型等内容。设置为 no 则不记录,性能比较高。
  • extended-statistics 设置为 yes,在执行 unbound-control stats 时,会得到更详细的 unbound 的请求量等统计信息,同样会使性能降低。

还有与 unbound 功能模块相关的一些配置

如果不需要 dnssec 的功能,可以在 module-config 这一项中删掉 “validator” 以提升性能

如果不使用 ipv6,可以设置 do-ip6 为 no


为了安全,最好把 hide-identity 和 hide-version 都设置为 yes, 隐藏服务器主机名和 unbound 版本号。

修改完配置可以执行 unbound-control reload 重新加载配置文件,注意它同时也会把缓存,统计信息给清了。

参考资料:

https://unbound.net/documentation/unbound.conf.html
https://unbound.net/documentation/howto_optimise.html