3. Server Behavior (服务器行为)
3. 服务器行为 (Server Behavior)
服务器收到 UPDATE 请求后, 如果不识别 UPDATE opcode, 或者识别但尚未实现, 将向请求者发出 NOTIMP 信号. 否则, 处理按如下方式继续.
3.1 处理 ZONE Section (Process Zone Section)
3.1.1. 检查 Zone Section, 确认其中恰好有一个 RR, 且该 RR 的 ZTYPE 为 SOA, 否则向请求者发出 FORMERR 信号. 接着检查 ZNAME 和 ZCLASS, 确认如此命名的 zone 是此服务器的权威 zone 之一, 否则向请求者发出 NOTAUTH 信号. 如果服务器是 zone slave, 请求将被朝 primary master 转发.
3.1.2 - ZONE Section 处理伪代码 (Pseudocode For Zone Section Processing)
if (zcount != 1 || ztype != SOA)
return (FORMERR)
if (zone_type(zname, zclass) == SLAVE)
return forward()
if (zone_type(zname, zclass) == MASTER)
return update()
return (NOTAUTH)
第 3.2 到 3.8 节描述 primary master 的行为, 而第 6 节描述 forwarder 的行为.
3.2 处理 PREREQ Section (Process Prerequisite Section)
接下来检查 Prerequisite Section, 确认所有 prerequisite 都由 zone 的当前状态满足. 使用第 1.2 节表达的定义, 如果任何 RR 的 NAME 不在 Zone Section 指定的 zone 内, 则向请求者发出 NOTZONE 信号.
3.2.1. 对于此 section 中 CLASS 为 ANY 的 RR, 测试 TTL 和 RDLENGTH 是否均为零 (0), 否则向请求者发出 FORMERR 信号. 如果 TYPE 为 ANY, 测试 zone 中是否至少有一个 RR 的 NAME 与 Prerequisite RR 的 NAME 相同, 否则向请求者发出 NXDOMAIN 信号. 如果 TYPE 不是 ANY, 测试 zone 中是否至少有一个 RR 的 NAME 和 TYPE 与 Prerequisite RR 的 NAME 和 TYPE 相同, 否则向请求者发出 NXRRSET 信号.
3.2.2. 对于此 section 中 CLASS 为 NONE 的 RR, 测试 TTL 和 RDLENGTH 是否均为零 (0), 否则向请求者发出 FORMERR 信号. 如果 TYPE 为 ANY, 测试 zone 中是否不存在 NAME 与 Prerequisite RR 的 NAME 相同的 RR, 否则向请求者发出 YXDOMAIN 信号. 如果 TYPE 不是 ANY, 测试 zone 中是否不存在 NAME 和 TYPE 与 Prerequisite RR 的 NAME 和 TYPE 相同的 RR, 否则向请求者发出 YXRRSET 信号.
3.2.3. 对于此 section 中 CLASS 与 ZCLASS 相同的 RR, 测试 TTL 是否为零 (0), 否则向请求者发出 FORMERR 信号. 然后, 为每个唯一的 <NAME,TYPE> 构建一个 RRset, 并将每个所得 RRset 与 zone 中的 RRset 做集合相等性比较 (成员相同, 不多也不少). 如果任何 Prerequisite RRset 未被某个 zone RRset 完全且精确匹配, 则向请求者发出 NXRRSET 信号. 如果此 section 中任何 RR 的 CLASS 不是 ZCLASS, NONE 或 ANY, 则向请求者发出 FORMERR 信号.
3.2.4 - PREREQ Section 中使用的元值表 (Table Of Metavalues Used In Prerequisite Section)
| CLASS | TYPE | RDATA | 含义 |
|---|---|---|---|
| ANY | ANY | empty | 名称正在使用 |
| ANY | rrset | empty | RRset 存在 (与值无关) |
| NONE | ANY | empty | 名称未在使用 |
| NONE | rrset | empty | RRset 不存在 |
| zone | rrset | rr | RRset 存在 (与值相关) |
3.2.5 - PREREQ Section 处理伪代码 (Pseudocode for Prerequisite Section Processing)
for rr in prerequisites
if (rr.ttl != 0)
return (FORMERR)
if (zone_of(rr.name) != ZNAME)
return (NOTZONE);
if (rr.class == ANY)
if (rr.rdlength != 0)
return (FORMERR)
if (rr.type == ANY)
if (!zone_name<rr.name>)
return (NXDOMAIN)
else
if (!zone_rrset<rr.name, rr.type>)
return (NXRRSET)
if (rr.class == NONE)
if (rr.rdlength != 0)
return (FORMERR)
if (rr.type == ANY)
if (zone_name<rr.name>)
return (YXDOMAIN)
else
if (zone_rrset<rr.name, rr.type>)
return (YXRRSET)
if (rr.class == zclass)
temp<rr.name, rr.type> += rr
else
return (FORMERR)
for rrset in temp
if (zone_rrset<rrset.name, rrset.type> != rrset)
return (NXRRSET)
3.3 检查请求者权限 (Check Requestor's Permissions)
3.3.1. 接下来, 可以用实现相关方式, 或用后续 Secure DNS Update 协议指定的机制, 测试请求者是否有权更新 Update Section 中命名的 RR. 如果请求者无权执行这些更新, 服务器可以在其操作日志中写入一条警告消息, 并且可以向请求者发出 REFUSED 信号, 或忽略该权限问题并继续更新.
3.3.2. 虽然确切处理由实现定义, 但如果要执行这些验证活动, 就应在服务器处理流程的这一点执行. 因为如果在某个更新已部分应用后才遇到 REFUSED 条件, 则必须先撤销该部分更新并将 zone 恢复到原始状态, 然后才能答复请求者.
3.3.3 - 权限检查伪代码 (Pseudocode for Permission Checking)
if (security policy exists)
if (this update is not permitted)
if (local option)
log a message about permission problem
if (local option)
return (REFUSED)
3.4 处理 UPDATE Section (Process Update Section)
接下来按如下方式处理 Update Section.
3.4.1 - 预扫描 (Prescan)
Update Section 被解析为 RR, 并检查每个 RR 的 CLASS 是否为 ANY, NONE 或与 Zone Class 相同, 否则向请求者发出 FORMERR 信号. 使用第 1.2 节中的定义, 每个 RR 的 NAME 必须位于 Zone Section 指定的 zone 内, 否则向请求者发出 NOTZONE 信号.
3.4.1.2. 对于 CLASS 不是 ANY 的 RR, 检查 TYPE. 如果它是 ANY, AXFR, MAILA, MAILB, 任何其他 QUERY metatype, 或任何未识别的类型, 则向请求者发出 FORMERR 信号. 对于 CLASS 为 ANY 或 NONE 的 RR, 检查 TTL 是否为零 (0), 否则向请求者发出 FORMERR 信号. 对于任何 CLASS 为 ANY 的 RR, 检查 RDLENGTH 以确保其为零 (0) (即 RDATA 字段为空), 并确保 TYPE 不是 AXFR, MAILA, MAILB, 除 ANY 之外的任何其他 QUERY metatype, 或任何未识别的类型, 否则向请求者发出 FORMERR 信号.
3.4.1.3 - UPDATE Section 预扫描伪代码 (Pseudocode For Update Section Prescan)
[rr] for rr in updates
if (zone_of(rr.name) != ZNAME)
return (NOTZONE);
if (rr.class == zclass)
if (rr.type & ANY|AXFR|MAILA|MAILB)
return (FORMERR)
elsif (rr.class == ANY)
if (rr.ttl != 0 || rr.rdlength != 0
|| rr.type & AXFR|MAILA|MAILB)
return (FORMERR)
elsif (rr.class == NONE)
if (rr.ttl != 0 || rr.type & ANY|AXFR|MAILA|MAILB)
return (FORMERR)
else
return (FORMERR)
3.4.2 - 更新 (Update)
Update Section 被解析为 RR, 这些 RR 按顺序处理.
3.4.2.1. 如果在处理此 section 期间发生任何系统故障 (例如内存不足条件, 或持久存储中的硬件错误), 则向请求者发出 SERVFAIL 信号, 并撤销此事务期间已应用到 zone 的所有更新.
3.4.2.2. 任何 CLASS 与 ZCLASS 相同的 Update RR 都被添加到 zone. 如果存在重复的 RDATA (这对 SOA RR 总是成立, 对 WKS RR 则在 ADDRESS 和 PROTOCOL 字段均匹配时成立), Zone RR 将由 Update RR 替换. 如果 TYPE 为 SOA 且不存在 Zone SOA RR, 或者新的 SOA.SERIAL 低于 (按照 [RFC1982]) 或等于当前 Zone SOA RR 的 SOA.SERIAL, 则忽略该 Update RR. 对于 CNAME Update RR 与非 CNAME Zone RRset 并存的情况, 或反过来的情况, 忽略该 CNAME Update RR, 否则用该 CNAME Update RR 替换 CNAME Zone RR.
3.4.2.3. 对于任何 CLASS 为 ANY 且 TYPE 为 ANY 的 Update RR, 删除所有 NAME 相同的 Zone RR, 除非 NAME 与 ZNAME 相同, 在这种情况下只删除 TYPE 不是 SOA 或 NS 的 RR. 对于任何 CLASS 为 ANY 且 TYPE 不是 ANY 的 Update RR, 删除所有 NAME 和 TYPE 相同的 Zone RR, 除非 NAME 与 ZNAME 相同, 在这种情况下既不删除 SOA RR 也不删除 NS RR.
3.4.2.4. 对于任何 CLASS 为 NONE 的 Update RR, 删除任何 NAME, TYPE, RDATA 和 RDLENGTH 均与该 Update RR 相等的 Zone RR, 除非 NAME 与 ZNAME 相同, 且 TYPE 为 SOA, 或 TYPE 为 NS 且匹配的 Zone RR 是该 RRset 中剩余的唯一 NS. 在这些情况下, 忽略该 Update RR.
3.4.2.5. 向请求者发出 NOERROR 信号.
3.4.2.6 - UPDATE Section 中使用的元值表 (Table Of Metavalues Used In Update Section)
| CLASS | TYPE | RDATA | 含义 |
|---|---|---|---|
| ANY | ANY | empty | 从一个名称删除所有 RRset |
| ANY | rrset | empty | 删除一个 RRset |
| NONE | rrset | rr | 从一个 RRset 删除一个 RR |
| zone | rrset | rr | 添加到一个 RRset |
3.4.2.7 - UPDATE Section 处理伪代码 (Pseudocode For Update Section Processing)
[rr] for rr in updates
if (rr.class == zclass)
if (rr.type == CNAME)
if (zone_rrset<rr.name, ~CNAME>)
next [rr]
elsif (zone_rrset<rr.name, CNAME>)
next [rr]
if (rr.type == SOA)
if (!zone_rrset<rr.name, SOA> ||
zone_rr<rr.name, SOA>.serial > rr.soa.serial)
zone_rr<rr.name, SOA> = rr
elsif (rr.type == WKS)
if (zone_rrset<rr.name, WKS, rr.proto, rr.address>)
zone_rr<rr.name, WKS, rr.proto, rr.address> = rr
else
zone_rrset<rr.name, WKS> += rr
else
zone_rrset<rr.name, rr.type> += rr
elsif (rr.class == ANY)
if (rr.type == ANY)
if (rr.name == zname)
zone_rrset<rr.name, ~(SOA|NS)> = Nil
else
zone_rrset<rr.name, *> = Nil
elsif (rr.name == zname &&
(rr.type == SOA || rr.type == NS))
next [rr]
else
zone_rrset<rr.name, rr.type> = Nil
elsif (rr.class == NONE)
if (rr.type == SOA)
next [rr]
if (rr.type == NS && zone_rrset<rr.name, NS> == rr)
next [rr]
zone_rr<rr.name, rr.type, rr.data> = Nil
return (NOERROR)
3.5 处理 ADDITIONAL Section (Process Additional Data Section)
本文档不描述 Additional Data 处理. 服务器应静默忽略 Additional Data Section 中与 UPDATE 处理相关的 RR. 但是, 如果 Additional Data Section 包含服务器为了使更新有用而需要的 RR (例如新 NS RR 目标的 glue A RR), 则应处理此 section.
3.6 将变更追加到事务日志 (Append Changes to Transaction Log)
如果 zone 有事务日志, 对 zone 所做的变更 MAY 被追加到其中, 或许采用一条最小 UPDATE 消息的形式. 注意, 只有当 SOA RR 是 UPDATE 消息的一部分, 或服务器策略规定 SOA SERIAL 自动递增时, SOA SERIAL 才会被更新.
3.7 向请求者发送响应 (Send Response to Requestor)
此时, 必须组装响应消息并发送回请求者. 头部不包含特殊选项, 且 Zone Section (Query 中的 Question Section) 从请求的相应 section 复制. PRCOUNT, UPCOUNT 和 ADCOUNT 均设置为零 (0). 响应代码为 NOERROR, 除非发生某些处理错误或权限错误, 在这种情况下发送适当的 RCODE. 应在响应的 Answer Section 中发送 zone 当前 SOA RR 的副本 (即使发生错误也如此), 以便那些使用 UPDATE 改变 SOA 参数且需要关于实际状态反馈的请求者受益.
3.8 通知 slave (Signal Slaves)
primary master 应使用 DNS NOTIFY 等机制 (参见 [RFC1996]) 通知任何已知 slave 该 zone 已更新. 注意, 即使 SOA SERIAL 因策略或 UPDATE 内容而未递增, 也应发送此 notify.