跳到主要内容

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"异常.