SPF Toolbox v2.2.0 常用 CLI 命令详解
用 curl/jq 调用 SPF Toolbox v2.2.0 的 11 个 JSON 查询端点实现命令行邮件 DNS 体检,并与 dig/nslookup/host/whois/nc 建立命令矩阵对照:全部回显实测,含 RFC8482 占位、TTL 差异与黑名单误报解读。
SPF Toolbox v2.2.0 常用 CLI 命令详解
目标:把 SPF Toolbox 的 Web 界面搬进命令行——用 curl/jq 调用其 11 个 JSON 查询端点,并与 dig、nslookup、host、whois、nc 等原生工具做命令矩阵对照与结果解读差异分析,实现邮件 DNS 体检的完全脚本化。全部命令与回显实测自同一台 Ubuntu 22.04 服务器。
一、环境信息
本文所有命令与回显均在以下真实服务器环境实测:
- 操作系统:Ubuntu 22.04.5 LTS
- 应用:SPF Toolbox v2.2.0(部署于
/var/www/html/spftoolbox,Apache 2.4.52 + PHP 8.1.2 托管,80 端口) - CLI 工具集(Ubuntu 22.04 apt 版本实测):
curl7.81.0、dig9.18.39(bind9-dnsutils 包,含nslookup/host)、whois5.5.13、nc(netcat-openbsd 1.218-4)、jq1.6 - 验证域名/IP:gmail.com、qq.com、ebay.com、8.8.8.8;本地模拟域 lab.example(dnsmasq 自建,见 4.1 之 AAAA 演示)
工具安装(部署篇已装 apache/php,此处补 CLI 工具):
apt-get install -y -qq curl jq dnsutils whois netcat-openbsd
输出节选(实测,末段):
Setting up netcat-openbsd (1.218-4ubuntu1) ...
Processing triggers for man-db (2.10.1-1) ...
二、工具概览
SPF Toolbox 的后端是一个 GET JSON 端点,Web 界面上的每次查询在命令行下都等价于一条 curl 命令。工具与命令矩阵如下:
| 体检需求 | SPF Toolbox 端点(curl) | 等价原生命令 | 输出形态差异 |
|---|---|---|---|
| 查 MX 收信服务器 | ?domain=X&request=mx | dig X MX +short | JSON 对象数组 vs 缩进文本 |
| 查 SPF(TXT) | ?domain=X&request=txt | dig X TXT +short | 同上,需过滤 v=spf1 |
| 查 DMARC | ?domain=X&request=dmarc | dig _dmarc.X TXT +short | 应用自动拼 _dmarc. |
| 查 A/AAAA | ?domain=X&request=a / aaaa | dig X A +short | JSON vs 文本 |
| 全量记录 | ?domain=X&request=all | dig X ANY | ANY 常被 RFC 8482 拒绝(见 4.1 之 ⑧) |
| 黑名单反查 | ?domain=X&request=blacklist | dig 反转IP.区域名(50 次查询) | 应用一次聚合 50 个 DNSBL |
| 反向解析 | ?domain=IP&request=reverseLookup | dig -x IP +short | JSON 简化映射 vs 文本 |
| whois | ?domain=X&request=whois | whois X | 应用返回 JSON 包装的原始报文行 |
| 端口探测 | ?domain=X&request=port&port=N | nc -zv X N | JSON open/closed vs 退出码 |
| HINFO | ?domain=X&request=hinfo | dig X HINFO +short | 解析器常以 RFC8482 占位应答 |
| 结果解析 | | jq | grep/sed/awk | jq 面向 JSON,awk 面向文本 |
选型逻辑:巡检脚本/监控管道用 SPF Toolbox 端点(一次调用多类结果、输出稳定 JSON);临时单点排查用 dig 等原生命令(零依赖);两者输出可互相印证(见五、注意事项)。
三、前置条件
- 已有可访问的 SPF Toolbox 部署(部署步骤见本专栏部署篇:《Ubuntu 22.04 安装 SPF Toolbox v2.2.0 指南》);
- CLI 工具已安装(一、环境信息的 apt 命令);
- 服务器可出站 DNS(UDP 53);whois 与端口探测另需相应出站 TCP 权限;
- 若部署篇加了 Apache Basic Auth,curl 需带
-u admin:口令参数。
四、各工具详解
4.1 curl:调用 11 个查询端点
端点统一格式:http://<服务器>/spftoolbox/operations/?domain=<域名或IP>&request=<类型>[&port=<端口>]。后端入口 operations/index.php 对所有类型统一执行 print_r($object->getOutput($domain)),多数查询类的 getOutput 内部返回 json_encode 后的字符串,因此输出为 JSON。以下回显均为实测。
(1)MX 查询——排查「对方收不到信」第一步:
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=gmail.com&request=mx'
实测回显(完整 JSON):
[
{
"host": "gmail.com",
"class": "IN",
"ttl": 5,
"type": "MX",
"pri": 20,
"target": "alt2.gmail-smtp-in.l.google.com"
},
{
"host": "gmail.com",
"class": "IN",
"ttl": 5,
"type": "MX",
"pri": 30,
"target": "alt3.gmail-smtp-in.l.google.com"
},
{
"host": "gmail.com",
"class": "IN",
"ttl": 5,
"type": "MX",
"pri": 40,
"target": "alt4.gmail-smtp-in.l.google.com"
},
{
"host": "gmail.com",
"class": "IN",
"ttl": 5,
"type": "MX",
"pri": 10,
"target": "alt1.gmail-smtp-in.l.google.com"
},
{
"host": "gmail.com",
"class": "IN",
"ttl": 5,
"type": "MX",
"pri": 5,
"target": "gmail-smtp-in.l.google.com"
}
]
解读:5 条 MX 按 pri 升序使用(5 优先),gmail-smtp-in.l.google.com 及 4 个 alt 备援;记录顺序由解析器轮转决定,解读时按 pri 排序而非出现顺序。
(2)TXT/SPF 查询——返回全部 TXT 记录,SPF 是其中 v=spf1 开头的条目:
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=gmail.com&request=txt' | head -32
实测回显(输出节选):
[
{
"host": "gmail.com",
"class": "IN",
"ttl": 300,
"type": "TXT",
"txt": "yahoo-verification-key=+eZwIPSgRxkAUzDtqT8Qhk+j4A1JF6V\/wtGoyGTkELY=",
"entries": [
"yahoo-verification-key=+eZwIPSgRxkAUzDtqT8Qhk+j4A1JF6V\/wtGoyGTkELY="
]
},
{
"host": "gmail.com",
"class": "IN",
"ttl": 300,
"type": "TXT",
"txt": "v=spf1 redirect=_spf.google.com",
"entries": [
"v=spf1 redirect=_spf.google.com"
]
},
{
"host": "gmail.com",
"class": "IN",
"ttl": 300,
"type": "TXT",
"txt": "globalsign-smime-dv=CDYX+XFHUw2wml6\/Gb8+59BsH31KzUr6c1l2BPvqKX8=",
"entries": [
"globalsign-smime-dv=CDYX+XFHUw2wml6\/Gb8+59BsH31KzUr6c1l2BPvqKX8="
]
},
解读:v=spf1 redirect=_spf.google.com 即 gmail.com 的 SPF 授权声明(redirect 指向 _spf.google.com 的具体 IP 集);每条记录同时带 txt 与 entries 字段(PHP dns_get_record 对多段 TXT 的标准结构),脚本解析取 txt 即可。
(3)DMARC 查询——自动拼 _dmarc. 前缀:
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=gmail.com&request=dmarc'
实测回显(完整 JSON):
[
{
"host": "_dmarc.gmail.com",
"class": "IN",
"ttl": 600,
"type": "TXT",
"txt": "v=DMARC1; p=none; sp=quarantine; rua=mailto:mailauth-reports@google.com",
"entries": [
"v=DMARC1; p=none; sp=quarantine; rua=mailto:mailauth-reports@google.com"
]
}
]
解读:p=none 对主域只监控不处置,sp=quarantine 对子域转隔离,报告发往 rua 邮箱。
(4)A / AAAA 查询:
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=gmail.com&request=a'
实测回显(完整 JSON,4 条 A 记录轮转返回):
[
{
"host": "gmail.com",
"class": "IN",
"ttl": 184,
"type": "A",
"ip": "74.125.199.83"
},
{
"host": "gmail.com",
"class": "IN",
"ttl": 184,
"type": "A",
"ip": "74.125.199.18"
},
{
"host": "gmail.com",
"class": "IN",
"ttl": 184,
"type": "A",
"ip": "74.125.199.19"
},
{
"host": "gmail.com",
"class": "IN",
"ttl": 184,
"type": "A",
"ip": "74.125.199.17"
}
]
AAAA 演示用本地模拟域(公网域名的 AAAA 受本环境解析链路限制返回 [],本地 dnsmasq 加一条 AAAA 记录即可完整验证,配置篇 5.6 有搭建过程):
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=lab.example&request=aaaa'
实测回显:
[
{
"host": "lab.example",
"class": "IN",
"ttl": 0,
"type": "AAAA",
"ipv6": "fd00::10"
}
]
(5)反向解析——domain 传 IP,输出为「IP: 主机名」简化映射(非完整 PTR 结构):
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=8.8.8.8&request=reverseLookup'
实测回显:
[{"8.8.8.8": "dns.google"}]
(6)端口探测——配 port 参数探测单端口:
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=127.0.0.1&request=port&port=80'
实测回显:
[{
"80": "Is open"
}]
不传 port 则按内置默认数组(25/53/80/443/465/587/993)逐个探测,行为与自定义方式见配置篇。对出站受限地址返回 closed 并随超时参数等长等待(排查思路见部署篇 FAQ)。
(7)whois 查询——内置 phpwhois 库,输出为 JSON 数组包装的 whois 原始报文行(每行一个字符串元素,\r 为报文原始换行):
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=gmail.com&request=whois' | head -c 320
实测回显:
[
[
"Domain Name: gmail.com\r",
"Registry Domain ID: 4667231_DOMAIN_COM-VRSN\r",
"Registrar WHOIS Server: whois.markmonitor.com\r",
"Registrar Registration Expiration Date: 2027-08-12T07:00:00.000+00:00\r",
"Registry Expiry Date: 2027-08-12T00:00:00.000+00:00\r",
"Creation Date: 1995-08-13T07:00
解读要点:Creation Date(注册时间 1995-08-13)、Registry Expiry Date(到期 2027-08-12)与 Registrar WHOIS Server(markmonitor);注意数组首元素是字面量 [(应用输出细节),用 jq 取值时按下标跳过即可。
(8)全部记录与 HINFO——本环境解析器对 ANY/HINFO 类查询统一以 RFC 8482 合成记录占位应答:
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=qq.com&request=all'
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=qq.com&request=hinfo'
实测回显(all 与 hinfo 输出结构相同,仅 TTL 差 1):
[
{
"host": "qq.com",
"class": "IN",
"ttl": 3600,
"type": "HINFO",
"cpu": "RFC8482",
"os": ""
}
]
机制说明(PHP 层实测):All.php 先调 dns_get_record($host, DNS_ALL)——本环境实测该调用触发 PHP Warning「A temporary server error occurred」并返回失败,随后回退 dns_get_record($host, DNS_ANY),后者拿到解析器的 RFC8482 合成 HINFO。hinfo 端点对无真实 HINFO 记录的域名同样返回该占位结构。需要完整记录清单时,改用 a/mx/txt 等单项查询逐个拉取。
(9)黑名单聚合查询——先解析域名 IP,再对 50 个 DNSBL 区域逐一反查,输出为手工拼接的紧凑 JSON 串(以 "end of list": "end of list" 结尾哨兵):
time curl -s -m 150 'http://127.0.0.1/spftoolbox/operations/?domain=gmail.com&request=blacklist' | head -c 200
实测回显(输出节选,实测耗时):
[{"babl.rbl.webiron.net": "OK","cabl.rbl.webiron.net": "OK","stabl.rbl.webiron.net": "OK","all.rbl.webiron.net": "OK","crawler.rbl.webiron.net": "OK","truncate.gbudb.net": "OK","dnsbl.sorbs.net": "OK"
real 0m53.263s
user 0m0.003s
sys 0m0.003s
gmail.com 的 IP 在 dnsrbl.org 一项上返回 FAIL(其余 49 项 OK;该区域对 Google 段存在大量非命中误报,解读警示与 50 区域完整清单见配置篇)。整体耗时随解析器缓存状态波动:冷缓存首跑实测约 1 分半,热缓存约 53~68 秒,脚本化使用必须设长超时(如 curl -m 150)。
4.2 jq:结果解析
端点输出是标准 JSON(whois/all 结构特殊,见上文),jq 是最顺手的解析器:
# MX 按 pri 升序输出「优先级 -> 目标」
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=qq.com&request=mx' | jq -r '.[] | "pri=\(.pri) -> \(.target)"' | sort -t= -k2 -n
实测回显:
pri=10 -> mx3.qq.com
pri=20 -> mx2.qq.com
pri=30 -> mx1.qq.com
# 从 TXT 中提取 SPF 记录
curl -s 'http://127.0.0.1/spftoolbox/operations/?domain=qq.com&request=txt' | jq -r '.[] | select(.txt | startswith("v=spf1")) | .txt'
实测回显:
v=spf1 include:spf.mail.qq.com ~all
# 黑名单结果中只列 FAIL 项(无 FAIL 则无输出)
curl -s -m 150 'http://127.0.0.1/spftoolbox/operations/?domain=gmail.com&request=blacklist' | jq -r '.[0] | to_entries[] | select(.value | startswith("FAIL")) | "\(.key): \(.value)"'
实测回显:
dnsrbl.org: FAIL. The domain is listed on a blacklist
4.3 dig:DNS 交叉验证
dig 是判断「应用返回异常」还是「解析器本身异常」的第一对照工具(应用查询全部走系统解析器,dig 亦然,两者可直接对照)。
# MX 全量(含 TTL)
dig gmail.com MX +noall +answer
实测回显:
gmail.com. 3600 IN MX 20 alt2.gmail-smtp-in.l.google.com.
gmail.com. 3600 IN MX 5 gmail-smtp-in.l.google.com.
gmail.com. 3600 IN MX 30 alt3.gmail-smtp-in.l.google.com.
gmail.com. 3600 IN MX 10 alt1.gmail-smtp-in.l.google.com.
gmail.com. 3600 IN MX 40 alt4.gmail-smtp-in.l.google.com.
# DMARC 记录
dig _dmarc.gmail.com TXT +noall +answer
实测回显:
_dmarc.gmail.com. 3600 IN TXT "v=DMARC1; p=none; sp=quarantine; rua=mailto:mailauth-reports@google.com"
# 反向解析与 RFC 8482 复核
dig -x 8.8.8.8 +short
dig gmail.com ANY +noall +answer
实测回显:
dns.google.
gmail.com. 3600 IN HINFO "RFC8482" ""
解读:本环境解析器对 ANY 一律按 RFC 8482 返回合成 HINFO 占位——与应用 request=all/hinfo 的占位输出同源(4.1 之 ⑧),dig 侧复核是定位这类「占位应答」的最快手段。
黑名单手工反查——把域名 IP 反转后拼区域名,与应用 blacklist 结果对照(gmail.com 解析出的 IP 为 142.250.99.83 时):
dig +short 83.99.250.142.dnsrbl.org
dig +short 83.99.250.142.zen.spamhaus.org
实测回显:
103.224.182.253
(无输出,即 zen 上未命中)
解读:dnsrbl.org 有返回值(应用即判 FAIL),zen.spamhaus.org 无输出(判 OK)。反查返回非 127.0.0.x 网段时必须人工核对该 DNSBL 的编码规范,不能见「有值」就定性(配置篇 5.2/7.4)。
4.4 nslookup 与 host:快速对照
nslookup -type=mx qq.com
实测回显(完整输出):
Server: 127.0.0.11
Address: 127.0.0.11#53
Non-authoritative answer:
qq.com mail exchanger = 10 mx3.qq.com.
qq.com mail exchanger = 30 mx1.qq.com.
qq.com mail exchanger = 20 mx2.qq.com.
Authoritative answers can be found from:
host -t txt qq.com
实测回显(完整输出):
qq.com descriptive text "v=spf1 include:spf.mail.qq.com ~all"
三者分工:dig 信息最全(TTL/类别/权威段),host/nslookup 输出最短适合人眼快速确认,脚本里优先 dig 或 SPF Toolbox 端点。
4.5 whois 原生命令对照
whois qq.com 2>/dev/null | head -6
实测回显:
Domain Name: QQ.COM
Registry Domain ID: 2895300_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.markmonitor.com
Registrar URL: http://markmonitor.com
Updated Date: 2026-10-22T04:00:07Z
Creation Date: 1995-05-04T04:00:00Z
与 4.1 的 request=whois 对照:原生命令输出纯文本(head/grep 即可解析),应用输出 JSON 行数组但内容同为注册商原始报文;排障时两者可互证。
4.6 nc:端口探测对照
nc -zv 127.0.0.1 80 2>&1
实测回显:
Connection to 127.0.0.1 80 port [tcp/http] succeeded!
与 request=port 的差异:nc 单端口一次调用、以退出码(0/1)可直接进 shell 分支;应用端点一次可探测默认 7 端口并返回 open/closed 文本。判空出的 closed 结果时,先 nc 交叉验证是否为出站防火墙所致(部署篇 8.2)。
五、注意事项
- TTL 数值不要跨工具硬比:实测同一 gmail.com MX,dig 显示 TTL 3600、应用 JSON 显示
ttl: 5——应用经系统解析器递归链路取数,链路上的解析器会改写/压低 TTL;定性判断(是否可缓存、是否在倒计时)才有意义。 whois/all/hinfo的输出结构特殊:whois是 JSON 行数组且首元素为字面量[、各行带\r;all/hinfo在 RFC 8482 解析器下返回合成占位记录(见 4.1 之 ⑧)。对这三类别套用按pri/txt字段写的 jq 表达式。- 黑名单查询慢且可能误报:50 区域串行,冷缓存实测约 1 分半、热缓存 53~68 秒,脚本必须设长超时;FAIL 判定不校验 127 网段,dnsrbl.org 对 Google IP 实测即误报(4.3 手工反查法可复核)。
- 端点无鉴权时注意暴露面:应用响应头实测含
Access-Control-Allow-Origin: *,任何网页可跨域调用该端点;公网部署务必加访问控制(部署篇第七章)。 - 查询实时无缓存:响应头
Cache-Control: no-store,巡检脚本的查询频率会直接传导到 DNS 出口,批量巡检建议控制间隔或自建本地递归缓存。 - DKIM 查询走变通路径:把
选择器._domainkey.域名传给txt端点(实测 ebay 的dkim._domainkey.ebay.com返回完整 DKIM RSA 公钥记录,见配置篇 5.1)。 - A 记录轮转导致结果顺序不稳定:同一域名多次查询的记录条目顺序与 TTL 倒计时不同(实测 gmail.com 两次查询分别返回不同 IP 顺序、TTL 184/275),比对结果请按值集合而非顺序。
六、常见问题(FAQ)
6.1 curl 返回空(无输出)
domain 参数传空时应用整个处理分支不执行,响应体为空(实测行为,配置篇 5.1)。检查 URL 里 domain= 是否真的带上了值;带 -G --data-urlencode 传参可避免手拼 URL 的转义问题:
curl -s -G 'http://127.0.0.1/spftoolbox/operations/' --data-urlencode 'domain=gmail.com' --data-urlencode 'request=mx'
6.2 返回 Please check a valid DNS type
request 值拼写不在 11 个合法取值内(须精确匹配,含大小写,如 reverseLookup)。实测非法值回显:
[{"error": "Please check a valid DNS type"}]
6.3 应用查到了记录,dig 查不到(或反之)
先确认两者走的解析器一致:cat /etc/resolv.conf 对照 nslookup 输出的 Server: 行。应用与 dig 均按 /etc/resolv.conf 代发,若服务器近期改过 resolv.conf(如配置篇 5.6 的本地 dnsmasq 实验),先恢复再复测;仍不一致时用 dig @<指定上游> 逐级对比,定位改写的解析器节点。
6.4 jq 报错 Cannot index string with "pri"
多半是把 whois/all/hinfo 的输出喂给了按 mx 结构写的 jq 表达式(见五、注意事项第 2 条)。先 jq 'type' 确认数据结构再写选择器。
七、参考链接
- 官方仓库(端点与 request 取值的源码依据):https://github.com/charlesabarnes/SPFtoolbox (检索日期 2026-10-26)
- 本专栏部署篇:《Ubuntu 22.04 安装 SPF Toolbox v2.2.0 指南》
- 本专栏配置篇(端点参数与黑名单清单详解):《SPF Toolbox v2.2.0 配置参数详解》
- RFC 8482(Refusing Any Queries):https://www.rfc-editor.org/rfc/rfc8482