Skip to main content

4. check_host() 函数

此描述不是应用程序编程接口定义,而是用于说明算法的函数描述。符合要求的SPF实现必须产生与此描述在语义上等效的结果。

check_host()函数获取SPF记录、解析它们,并评估它们以确定特定主机是否被允许使用给定身份发送邮件。执行此检查的接收ADMD必须按此处所述正确评估check_host()函数。

实现可以使用与此处定义的规范算法不同的算法,只要在所有情况下结果相同即可。

4.1. 参数 (Arguments)

check_host()函数接受这些参数:

  • - 发出邮件的SMTP客户端的IP地址,可以是IPv4或IPv6。

  • - 提供所寻求的授权信息的域;最初是"MAIL FROM"或"HELO"身份的域部分。

  • - "MAIL FROM"或"HELO"身份。

对于递归评估,的域部分可能与最初评估check_host()时的参数不同。在大多数其他情况下它们将相同(参见下文第5.2节)。下文第4.6.4节中描述的SPF术语的整体DNS查询限制必须作为所有评估的单一全局限制来跟踪,而不仅仅是针对递归评估的单个实例。

注意,参数可能不是格式良好的域名。例如,如果反向路径为空,则使用EHLO/HELO域,这会带来相关问题(参见第2.3节)。在这些情况下,check_host()在第4.3节中定义为返回"none"结果。

4.2. 结果 (Results)

check_host()函数可以返回第2.6节中描述的几个结果之一。根据结果,要采取的行动由接收方的本地策略决定。这在第8节中讨论。

4.3. 初始处理 (Initial Processing)

如果格式错误(例如,标签长度超过63个字符、非末尾的零长度标签等)或不是多标签域名,或者如果DNS查询返回"Name Error"(RCODE 3,也称为"NXDOMAIN" [RFC2308]),check_host()立即返回结果"none"。DNS RCODE在[RFC1035]中定义。格式正确的域是[RFC1983]中定义的完全限定域。也就是说,在DNS中它们隐式地相对于根进行限定(参见[RFC1034]的第3.1节)。国际化域名必须编码为A-labels,如[RFC5890]的第2.3节所述。

如果没有local-part,则用字符串"postmaster"替换local-part。

4.4. 记录查找 (Record Lookup)

根据记录的发布方式(参见上文第3节),需要对名称进行DNS查询,仅查询TXT类型。

如果DNS查询返回服务器故障(RCODE 2)或其他错误(RCODE不是0或3),或者如果查询超时,则check_host()立即终止并返回结果"temperror"。

4.5. 选择记录 (Selecting Records)

记录以版本部分开始:

record = version terms *SP
version = "v=spf1"

从查询返回的记录集开始,丢弃不以"v=spf1"版本部分开头的记录。注意,版本部分由SP字符或记录末尾终止。例如,版本部分为"v=spf10"的记录不匹配并被丢弃。

如果结果记录集不包含任何记录,check_host()产生"none"结果。如果结果记录集包含多个记录,check_host()产生"permerror"结果。

4.6. 记录评估 (Record Evaluation)

check_host()函数解析和解释SPF记录以找到当前测试的结果。首先验证记录的语法,如果记录中任何地方有任何语法错误,check_host()立即返回结果"permerror",而不进行进一步的解释或评估。

4.6.1. 术语评估 (Term Evaluation)

有两种类型的术语:机制(mechanisms,在第5节中定义)和修饰符(modifiers,在第6节中定义)。记录包含按以下增强巴科斯-瑙尔范式(ABNF)指定的这些的有序列表。

terms = *( 1*SP ( directive / modifier ) )

directive = [ qualifier ] mechanism
qualifier = "+" / "-" / "?" / "~"
mechanism = ( all / include / a / mx / ptr / ip4 / ip6 / exists )
modifier = redirect / explanation / unknown-modifier
unknown-modifier = name "=" macro-string
; where name is not any known modifier

name = ALPHA *( ALPHA / DIGIT / "-" / "_" / "." )

大多数机制允许在名称后使用":"或"/"字符。

修饰符总是在名称后立即包含等号('=')字符,并在可能是macro-string一部分的任何":"或"/"字符之前。

不包含"="、":"或"/"的术语是机制,如第5节中定义的。

根据[RFC5234]中ABNF符号的定义,机制和修饰符名称不区分大小写。

4.6.2. 机制 (Mechanisms)

每个机制从左到右依次考虑。如果没有更多机制,结果是第4.7节中描述的默认结果。

当评估机制时,可能发生三种情况之一:它可以匹配、不匹配或返回异常。

如果匹配,处理结束,限定符值作为该记录的结果返回。如果不匹配,处理继续下一个机制。如果返回异常,机制处理结束并返回异常值。

可能的限定符及其导致check_host()返回的结果如下:

"+" pass
"-" fail
"~" softfail
"?" neutral

限定符是可选的,默认为"+"。

当机制匹配且限定符为"-"时,返回"fail"结果,并按第6.2节所述计算解释字符串。

具体机制在第5节中描述。

4.6.3. 修饰符 (Modifiers)

修饰符不是机制。它们不返回匹配或不匹配。相反,它们提供附加信息。尽管修饰符不直接影响记录的评估,但"redirect"修饰符在评估所有机制后会产生影响。

4.6.4. DNS 查询限制 (DNS Lookup Limits)

某些机制和修饰符(统称为"术语")在评估时会导致DNS查询,而某些则不会。以下术语会导致DNS查询:"include"、"a"、"mx"、"ptr"和"exists"机制,以及"redirect"修饰符。SPF实现必须在SPF评估期间将这些术语的总数限制为10个,以避免对DNS造成不合理的负载。如果超过此限制,实现必须返回"permerror"。其他术语——"all"、"ip4"和"ip6"机制,以及"exp"修饰符——在SPF评估时不会导致DNS查询("exp"修饰符仅在稍后时间导致查询),并且它们的使用不受此限制。

当评估"mx"机制时,查询的"MX"资源记录数量包含在上述导致DNS查询的机制/修饰符的总限制10个中。除了该限制之外,每个"MX"记录的评估不得导致查询超过10个地址记录——"A"或"AAAA"资源记录。如果超过此限制,"mx"机制必须产生"permerror"结果。

当评估"ptr"机制或%{p}宏时,查询的"PTR"资源记录数量包含在上述导致DNS查询的机制/修饰符的总限制10个中。除了该限制之外,每个"PTR"记录的评估不得导致查询超过10个地址记录——"A"或"AAAA"资源记录。如果超过此限制,除前10个以外的所有记录必须被忽略。

差异的原因是MX记录的集合和内容由发布ADMD控制,而PTR记录的集合和内容由实际建立连接的IP地址的所有者控制。

这些限制是每个记录中每个机制或宏的限制,并且是对上述指定的查询限制的补充。

MTA或其他处理器应该对评估check_host()的最大经过时间施加限制。这样的限制应该允许至少20秒。如果超过这样的限制,授权的结果应该是"temperror"。

如第11.1节末尾所述,在某些情况下,限制DNS查询返回答案计数为0的正面答案(RCODE 0)或"Name Error"(RCODE 3)答案的"术语"数量可能很有用。这些有时统称为"void lookups"(空查询)。SPF实现应该将"void lookups"限制为2个。实现可以选择使这样的限制可配置。在这种情况下,建议默认值为2。超过限制会产生"permerror"结果。

4.7. 默认结果 (Default Result)

如果没有任何机制匹配且没有"redirect"修饰符,则check_host()返回"neutral"结果,就像最后一个指令指定了"?all"一样。如果有"redirect"修饰符,check_host()按第6.1节中定义的方式继续。

最好使用"redirect"修饰符或"all"机制来明确终止处理。尽管在每个未明确终止的记录末尾都有一个隐式的"?all",但明确提供它有助于调试工作。

例如:

v=spf1 +mx -all

v=spf1 +mx redirect=_spf.example.com

4.8. 域规范 (Domain Specification)

这些机制和修饰符中的几个有一个部分。该字符串受宏扩展的约束(参见第7节)。结果字符串是完全限定DNS名称的通用表示形式:由句点分隔的一系列标签。此域在本文档的其余部分称为

注意:宏扩展的结果不受任何进一步的转义。因此,此功能无法产生DNS标签中合法的所有字符(例如,控制字符)。然而,此功能足够强大,可以表达合法的主机名和DNS中使用的常见实用标签(例如"_spf")。

对于几个机制,是可选的。如果未提供,则使用check_host()参数中的(参见第4.1节)作为。在宏扩展后,"domain"和在语法上是相同的。"domain"是check_host()的输入值,而由check_host()计算。

使用语法无效的域评估check_host()的结果是未定义的。

注意:本文档及其前身没有规定如何正确处理语法无效的(这可能是宏扩展的结果),根据[RFC1035]。示例包括具有空标签的名称,例如"foo..example.com",以及长度超过63个字符的标签。一些实现选择将这些错误视为不匹配,因此忽略这些名称,而其他实现返回"permerror"异常。