Ubuntu 22.04 安装 djbdns 1.05 指南

Ubuntu 22.04 安装 djbdns 1.05 全流程指南:apt 发行版包安装与官方源码编译(含 errno/TLS 补丁排障)双方案,tinydns 权威服务器与 dnscache 递归缓存的配置、启动与 dig 验证,附常见问题与卸载清理。

djbdns域名服务网络DNS服务器2026/10/25

Ubuntu 22.04 安装 djbdns 1.05 指南

目标:从零开始在 Ubuntu 22.04 服务器上安装 djbdns 1.05,完成 tinydns 权威服务器与 dnscache 递归缓存的配置、启动与 dig 验证。本篇已在 Ubuntu 22.04(glibc 2.35)环境实测通过,覆盖发行版包安装与源码编译两种方案,并记录了现代 Linux 下编译 errno/TLS 报错的排障过程。

一、环境信息

  • 操作系统:Ubuntu 22.04 LTS(glibc 2.35,内核 5.15+)
  • 架构:x86_64
  • djbdns 版本:1.05(Ubuntu 打包版 1:1.05-15fakesync1)
  • 依赖:daemontools 0.76、ucspi-tcp
  • tinydns 监听:10.0.0.1:53(UDP+TCP)
  • dnscache 监听:127.0.0.1:53(UDP+TCP)
  • 测试域名:example.test
  • 查询日期:2026-10-25

二、前置条件

  1. 一台 Ubuntu 22.04 服务器,具有 root 权限
  2. 服务器已配置好网络,能访问 Ubuntu 软件源或官方源码站点
  3. 53 端口(UDP+TCP)未被其他 DNS 服务占用(如 systemd-resolved 占用 127.0.0.53:53 时,需将 djbdns 绑定到其他 IP)
  4. 已创建专用运行用户(安装脚本会自动创建,也可手动创建)

⚠️ 注意:如果服务器上已有 systemd-resolved 或其他 DNS 服务监听 53 端口,需要为 tinydns/dnscache 指定不同的监听 IP,避免端口冲突。Ubuntu 22.04 默认 systemd-resolved 监听 127.0.0.53:53,因此 dnscache 可以绑定 127.0.0.1:53(不冲突),tinydns 绑定对外网卡 IP。

三、版本选型说明

djbdns 上游最后一个版本是 1.05(2001 年发布),此后无更新。各发行版通过打包补丁维护,主要修复现代 Linux 下的编译问题和安全补丁。

方案版本来源适用场景
发行版包安装1:1.05-15fakesync1Ubuntu universe 源快速部署、生产环境
源码编译1.05cr.yp.to/djbdns/djbdns-1.05.tar.gz需要定制、学习排障

💡 说明:Ubuntu 的打包版已经包含 errno 编译补丁和 2008 年 DNS 缓存投毒修复补丁(源端口随机化),开箱即用。源码编译需要手动打 errno 补丁。

四、在线安装方案(A:apt 发行版包)

4.1 方案说明

Ubuntu 22.04 的 universe 仓库提供了 djbdns 的拆分包:tinydns(权威服务器二进制)、dnscache(递归缓存二进制)、djbdns-conf(*-conf 配置工具)、djbdns-utils(客户端 CLI 工具)。同时需要安装 daemontools 和 daemontools-run 用于进程监管。

4.2 安装软件包

apt update
apt install -y tinydns dnscache djbdns-conf djbdns-utils daemontools daemontools-run ucspi-tcp

安装完成后,关键二进制位于:

ls -la /usr/sbin/tinydns /usr/sbin/dnscache /usr/sbin/tinydns-data /usr/sbin/tinydns-conf /usr/sbin/dnscache-conf
ls -la /usr/bin/dnsq /usr/bin/dnstrace /usr/bin/dnsfilter /usr/bin/random-ip

实测安装后二进制路径:

/usr/sbin/tinydns
/usr/sbin/dnscache
/usr/sbin/tinydns-data
/usr/sbin/tinydns-conf
/usr/sbin/dnscache-conf
/usr/bin/dnsq
/usr/bin/dnstrace
/usr/bin/dnsfilter
/usr/bin/random-ip

4.3 配置 tinydns 权威服务器

首先创建专用运行用户:

useradd -r -s /usr/sbin/nologin tinydns
useradd -r -s /usr/sbin/nologin dnslog

使用 tinydns-conf 生成服务目录:

tinydns-conf tinydns dnslog /etc/tinydns 10.0.0.1

参数说明:

  • tinydns:运行 tinydns 的用户
  • dnslog:运行日志进程的用户
  • /etc/tinydns:服务目录
  • 10.0.0.1:tinydns 监听的 IP 地址

生成的目录结构:

ls -la /etc/tinydns/
drwxr-sr-t 5 root root 4096 Oct 25 11:53 .
drwxr-xr-x 1 root root 4096 Oct 25 11:53 ..
drwxr-sr-x 2 root root 4096 Oct 25 11:53 env
drwxr-xr-x 3 root root 4096 Oct 25 11:53 log
drwxr-sr-x 2 root root 4096 Oct 25 11:53 root
-rwxr-xr-x 1 root root   93 Oct 25 11:53 run

环境变量文件:

cat /etc/tinydns/env/IP
cat /etc/tinydns/env/ROOT
10.0.0.1
/etc/tinydns/root

run 脚本内容:

cat /etc/tinydns/run
#!/bin/sh
exec 2>&1
exec envuidgid tinydns envdir ./env softlimit -d400000 /usr/sbin/tinydns

4.4 添加 DNS 记录

进入数据目录,使用辅助脚本添加记录:

cd /etc/tinydns/root
./add-host ns1.example.test 10.0.0.1
./add-alias www.example.test 10.0.0.1
./add-alias mail.example.test 10.0.0.1
./add-mx example.test 10.0.0.1 10
./add-ns example.test 10.0.0.1

手动添加 CNAME、TXT 和反向域 NS 记录:

echo "Cftp.example.test:www.example.test:3600" >> data
echo "'txt.example.test:v=spf1 include\072example.test ~all:3600" >> data
echo ".1.0.0.10.in-addr.arpa:10.0.0.1:a:259200" >> data

⚠️ 注意:TXT 记录中的冒号必须用八进制转义 \072,否则 tinydns-data 会把冒号当作字段分隔符截断文本。这是实测中踩到的坑:直接写 v=spf1 include:example.test ~all 会被解析成 TTL 为 example.test ~all:3600(无效),TXT 内容只剩 v=spf1 include。

查看生成的 data 文件:

cat data
=ns1.example.test:10.0.0.1:86400
+www.example.test:10.0.0.1:86400
+mail.example.test:10.0.0.1:86400
@example.test:10.0.0.1:a::86400
.example.test:10.0.0.1:a:259200
Cftp.example.test:www.example.test:3600
'txt.example.test:v=spf1 include\072example.test ~all:3600
.1.0.0.10.in-addr.arpa:10.0.0.1:a:259200

4.5 编译数据并启动 tinydns

编译 data 文件为 data.cdb:

cd /etc/tinydns/root
make
/usr/sbin/tinydns-data

确认 data.cdb 已生成:

ls -la data.cdb
-rw-r--r-- 1 root root 2848 Oct 25 11:54 data.cdb

启动 tinydns(使用 daemontools supervise 或直接后台运行):

cd /etc/tinydns
setsid envuidgid tinydns envdir ./env softlimit -d400000 /usr/sbin/tinydns </dev/null >/var/log/tinydns.log 2>&1 &

确认进程运行:

ps aux | grep "[t]inydns"
tinydns  3448  0.0  0.0   2776  1420 ?        Ss   11:54   0:00 /usr/sbin/tinydns

4.6 验证 tinydns

使用 dig 查询各类型记录:

dig @10.0.0.1 www.example.test A +short
dig @10.0.0.1 example.test MX +short
dig @10.0.0.1 ftp.example.test CNAME +short
dig @10.0.0.1 txt.example.test TXT +short
dig @10.0.0.1 example.test NS +short
dig @10.0.0.1 -x 10.0.0.1 +short

实测回显:

=== dig A www.example.test ===
10.0.0.1
=== dig MX example.test ===
0 a.mx.example.test.
=== dig CNAME ftp.example.test ===
www.example.test.
=== dig TXT txt.example.test ===
"v=spf1 include:example.test ~all"
=== dig NS example.test ===
a.ns.example.test.
=== dig PTR 10.0.0.1 ===
ns1.example.test.

完整 A 查询回显:

dig @10.0.0.1 www.example.test A
; <<>> DiG 9.18.39-0ubuntu0.22.04.7-Ubuntu <<>> @10.0.0.1 www.example.test A
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18855
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;www.example.test.		IN	A

;; ANSWER SECTION:
www.example.test.	86400	IN	A	10.0.0.1

;; AUTHORITY SECTION:
example.test.		259200	IN	NS	a.ns.example.test.

;; ADDITIONAL SECTION:
a.ns.example.test.	259200	IN	A	10.0.0.1

;; Query time: 0 msec
;; SERVER: 10.0.0.1#53(10.0.0.1) (UDP)

💡 说明:WARNING: recursion requested but not available 是正常的,因为 tinydns 是纯权威服务器,不提供递归服务。响应中的 aa(authoritative answer)标志表示这是权威应答。

4.7 配置 dnscache 递归缓存

创建 dnscache 用户并生成服务目录:

useradd -r -s /usr/sbin/nologin dnscache
dnscache-conf dnscache dnslog /etc/dnscache 127.0.0.1

⚠️ 注意:dnscache 默认监听 0.0.0.0:53,会与 tinydns(监听 10.0.0.1:53)冲突。这里改为监听 127.0.0.1,避免端口冲突。

修改监听 IP:

echo "127.0.0.1" > /etc/dnscache/env/IP

允许内网客户端递归查询(创建以 IP 前缀命名的空文件):

touch /etc/dnscache/root/ip/10.0.0
touch /etc/dnscache/root/ip/127.0.0.1

查看 dnscache 环境变量:

for f in /etc/dnscache/env/*; do echo "$(basename $f): $(cat $f)"; done
CACHESIZE: 1000000
DATALIMIT: 4000000
IP: 127.0.0.1
IPSEND: 0.0.0.0
ROOT: /etc/dnscache/root

根服务器列表(内置 13 个根服务器 IP):

cat /etc/dnscache/root/servers/@
198.41.0.4
192.228.79.201
192.33.4.12
128.8.10.90
192.203.230.10
192.5.5.241
192.112.36.4
128.63.2.53
192.36.148.17
192.58.128.30
193.0.14.129
199.7.83.42
202.12.27.33

启动 dnscache:

cd /etc/dnscache
setsid envdir ./env sh -c "exec envuidgid dnscache softlimit -o250 -d 4000000 /usr/sbin/dnscache" </dev/null >/var/log/dnscache.log 2>&1 &

验证递归解析:

dig @127.0.0.1 google.com A +short
142.251.46.78

五、源码编译方案(B:官方源码 + errno 补丁)

5.1 方案说明

当发行版不提供 djbdns 包,或需要定制编译时,从官方站点下载源码编译。现代 Linux(glibc 2.34+)下必须应用 errno 补丁,否则链接失败。

5.2 安装编译依赖

apt install -y gcc make wget

5.3 下载源码

cd /usr/local/src
wget https://cr.yp.to/djbdns/djbdns-1.05.tar.gz
tar xzf djbdns-1.05.tar.gz
cd djbdns-1.05

5.4 直接编译(复现 errno 报错)

echo "gcc -O2" > conf-cc
make

实测报错(关键输出):

/usr/bin/ld: errno: TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches non-TLS reference in buffer.a(buffer_put.o)
/usr/bin/ld: /lib/x86_64-linux-gnu/libc.so.6: error adding symbols: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:26: auto-str] Error 1

⚠️ 排障实录:这个报错是 djbdns 在现代 glibc 上的经典问题。原因是 djbdns 源码中 error.c 定义了全局变量 errno,而现代 glibc 把 errno 改为线程局部存储(TLS)宏,两者冲突导致链接器报错。官方安装文档给出的解决方案是在编译时强制包含 <errno.h>,让 glibc 的 errno 宏覆盖源码中的定义。

5.5 应用 errno 补丁并重新编译

按照官方安装文档的说明,修改 conf-cc 加入 -include /usr/include/errno.h:

echo "gcc -O2 -include /usr/include/errno.h" > conf-cc
cat conf-cc
gcc -O2 -include /usr/include/errno.h

清理并重新编译:

make clean
make

编译成功后检查生成的二进制:

ls -la tinydns dnscache tinydns-data dnsq dnstrace dnsfilter random-ip
-rwxr-xr-x 1 root root 71936 Oct 25 12:00 dnscache
-rwxr-xr-x 1 root root 39264 Oct 25 12:00 dnsfilter
-rwxr-xr-x 1 root root 47424 Oct 25 12:00 dnsq
-rwxr-xr-x 1 root root 51520 Oct 25 12:00 dnstrace
-rwxr-xr-x 1 root root 18688 Oct 25 12:00 random-ip
-rwxr-xr-x 1 root root 35104 Oct 25 12:00 tinydns
-rwxr-xr-x 1 root root 35072 Oct 25 12:00 tinydns-data

5.6 安装到系统

make setup check

此命令会把二进制安装到 /usr/local/bin 和 /usr/local/sbin,并安装 dnsroots.global 到 /etc/dnsroots.global。后续配置步骤与方案 A 相同(tinydns-conf、dnscache-conf、添加记录、编译、启动、验证)。

六、方案对比与选择建议

维度方案 A:apt 发行版包方案 B:源码编译
版本1:1.05-15fakesync1(含维护补丁)1.05(原始上游)
是否需联网是(apt 源)是(下载源码)
编译复杂度无需编译需 gcc + make + errno 补丁
安全补丁已包含源端口随机化等补丁需自行评估补丁
安装路径/usr/sbin、/usr/bin/usr/local/sbin、/usr/local/bin
适合场景生产环境、快速部署定制需求、学习排障

选择建议:生产环境优先选方案 A(apt 包),已包含必要补丁且便于后续升级;只有在发行版无包或需要深度定制时才选方案 B。

七、配置说明

7.1 tinydns 关键配置

  • /etc/tinydns/env/IP:tinydns 监听的 IP 地址,由 tinydns-conf 生成
  • /etc/tinydns/env/ROOT:数据目录(含 data 和 data.cdb)
  • /etc/tinydns/root/data:文本格式的 DNS 记录源文件
  • /etc/tinydns/root/data.cdb:编译后的二进制数据库,tinydns 实际读取此文件

7.2 dnscache 关键配置

  • /etc/dnscache/env/IP:dnscache 监听的 IP
  • /etc/dnscache/env/CACHESIZE:缓存大小(字节),默认 1000000
  • /etc/dnscache/env/DATALIMIT:进程数据段上限(字节),默认 4000000
  • /etc/dnscache/root/servers/@:根服务器 IP 列表
  • /etc/dnscache/root/ip/<前缀>:允许递归的客户端 IP 前缀

7.3 进程监管

djbdns 设计上由 daemontools 监管。生产环境建议通过 daemontools 的 svscan 启动服务,将服务目录软链接到 /etc/service/:

ln -s /etc/tinydns /etc/service/tinydns
ln -s /etc/dnscache /etc/service/dnscache

daemontools 会自动拉起 run 脚本,并在进程退出时自动重启。

八、常见问题

8.1 编译报错 errno: TLS definition mismatches

现象:make 时报 errno: TLS definition in libc.so.6 section .tbss mismatches non-TLS reference。

原因:djbdns 源码中 error.c 定义了全局 errno,与现代 glibc 的 TLS 版 errno 冲突。

解决:在 conf-cc 中加 -include /usr/include/errno.h:

echo "gcc -O2 -include /usr/include/errno.h" > conf-cc
make clean && make

8.2 tinydns 启动失败 fatal: $GID not set

现象:直接运行 tinydns 报 fatal: $GID not set。

原因:tinydns 需要通过 envuidgid 设置 $UID 和 $GID 环境变量来降权运行,不能直接以 root 运行。

解决:使用 run 脚本中的完整命令启动:

envuidgid tinydns envdir ./env softlimit -d400000 /usr/sbin/tinydns

8.3 PTR 查询超时或无响应

现象:A 记录查询正常,但 PTR 反向查询超时。

原因:tinydns 虽然在 data 中通过 = 记录生成了 PTR 记录,但如果没有为反向域(如 1.0.0.10.in-addr.arpa)声明 NS 记录(. 行),tinydns 不认为自己对该反向域有权威,因而不响应 PTR 查询。

解决:在 data 文件中添加反向域的 NS 记录:

.1.0.0.10.in-addr.arpa:10.0.0.1:a:259200

重新 make 编译后,PTR 查询即可正常返回。

8.4 TXT 记录内容被截断

现象:TXT 记录中的内容在冒号处被截断,如 v=spf1 include:example.test 变成 v=spf1 include。

原因:tinydns-data 以冒号为字段分隔符,TXT 文本中的冒号被误解析。

解决:TXT 文本中的冒号用八进制转义 \072:

'txt.example.test:v=spf1 include\072example.test ~all:3600

8.5 53 端口冲突

现象:启动时提示 address already in use。

原因:服务器上已有其他 DNS 服务(如 systemd-resolved)占用 53 端口。

解决:将 tinydns/dnscache 绑定到不同 IP。例如 tinydns 绑定对外网卡 IP,dnscache 绑定 127.0.0.1。确保两者不监听同一 IP 的 53 端口。

九、卸载清理

9.1 停止服务

svc -d /etc/service/tinydns
svc -d /etc/service/dnscache

9.2 移除 daemontools 服务链接

rm -f /etc/service/tinydns
rm -f /etc/service/dnscache

9.3 卸载软件包(方案 A)

apt remove -y tinydns dnscache djbdns-conf djbdns-utils daemontools daemontools-run ucspi-tcp
apt autoremove -y

9.4 删除服务目录和用户

rm -rf /etc/tinydns /etc/dnscache
userdel tinydns 2>/dev/null
userdel dnscache 2>/dev/null
userdel dnslog 2>/dev/null

9.5 源码编译安装的清理(方案 B)

cd /usr/local/src/djbdns-1.05
make clean
rm -rf /usr/local/src/djbdns-1.05
rm -f /usr/local/sbin/tinydns /usr/local/sbin/dnscache /usr/local/sbin/tinydns-data
rm -f /usr/local/bin/dnsq /usr/local/bin/dnstrace /usr/local/bin/dnsfilter

十、参考链接