SPF Toolbox v2.2.0 常用 CLI 命令详解

用 curl/jq 调用 SPF Toolbox v2.2.0 的 11 个 JSON 查询端点实现命令行邮件 DNS 体检,并与 dig/nslookup/host/whois/nc 建立命令矩阵对照:全部回显实测,含 RFC8482 占位、TTL 差异与黑名单误报解读。

SPF ToolboxSPF Toolbox邮件服务域名服务2026/10/26

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 版本实测):curl 7.81.0、dig 9.18.39(bind9-dnsutils 包,含 nslookup/host)、whois 5.5.13、nc(netcat-openbsd 1.218-4)、jq 1.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=mxdig X MX +shortJSON 对象数组 vs 缩进文本
查 SPF(TXT)?domain=X&request=txtdig X TXT +short同上,需过滤 v=spf1
查 DMARC?domain=X&request=dmarcdig _dmarc.X TXT +short应用自动拼 _dmarc.
查 A/AAAA?domain=X&request=a / aaaadig X A +shortJSON vs 文本
全量记录?domain=X&request=alldig X ANYANY 常被 RFC 8482 拒绝(见 4.1 之 ⑧)
黑名单反查?domain=X&request=blacklistdig 反转IP.区域名(50 次查询)应用一次聚合 50 个 DNSBL
反向解析?domain=IP&request=reverseLookupdig -x IP +shortJSON 简化映射 vs 文本
whois?domain=X&request=whoiswhois X应用返回 JSON 包装的原始报文行
端口探测?domain=X&request=port&port=Nnc -zv X NJSON open/closed vs 退出码
HINFO?domain=X&request=hinfodig X HINFO +short解析器常以 RFC8482 占位应答
结果解析| jqgrep/sed/awkjq 面向 JSON,awk 面向文本

选型逻辑:巡检脚本/监控管道用 SPF Toolbox 端点(一次调用多类结果、输出稳定 JSON);临时单点排查用 dig 等原生命令(零依赖);两者输出可互相印证(见五、注意事项)。

三、前置条件

  1. 已有可访问的 SPF Toolbox 部署(部署步骤见本专栏部署篇:《Ubuntu 22.04 安装 SPF Toolbox v2.2.0 指南》);
  2. CLI 工具已安装(一、环境信息的 apt 命令);
  3. 服务器可出站 DNS(UDP 53);whois 与端口探测另需相应出站 TCP 权限;
  4. 若部署篇加了 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)。

五、注意事项

  1. TTL 数值不要跨工具硬比:实测同一 gmail.com MX,dig 显示 TTL 3600、应用 JSON 显示 ttl: 5——应用经系统解析器递归链路取数,链路上的解析器会改写/压低 TTL;定性判断(是否可缓存、是否在倒计时)才有意义。
  2. whois/all/hinfo 的输出结构特殊:whois 是 JSON 行数组且首元素为字面量 [、各行带 \r;all/hinfo 在 RFC 8482 解析器下返回合成占位记录(见 4.1 之 ⑧)。对这三类别套用按 pri/txt 字段写的 jq 表达式。
  3. 黑名单查询慢且可能误报:50 区域串行,冷缓存实测约 1 分半、热缓存 53~68 秒,脚本必须设长超时;FAIL 判定不校验 127 网段,dnsrbl.org 对 Google IP 实测即误报(4.3 手工反查法可复核)。
  4. 端点无鉴权时注意暴露面:应用响应头实测含 Access-Control-Allow-Origin: *,任何网页可跨域调用该端点;公网部署务必加访问控制(部署篇第七章)。
  5. 查询实时无缓存:响应头 Cache-Control: no-store,巡检脚本的查询频率会直接传导到 DNS 出口,批量巡检建议控制间隔或自建本地递归缓存。
  6. DKIM 查询走变通路径:把 选择器._domainkey.域名 传给 txt 端点(实测 ebay 的 dkim._domainkey.ebay.com 返回完整 DKIM RSA 公钥记录,见配置篇 5.1)。
  7. 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' 确认数据结构再写选择器。

七、参考链接