在 阮一峰周刊 369 期 中这样一篇文章 破解加拿大航空的飞机上网 当时看了之后觉得很有意思,但没有额外作什么思考。过了两周的某一天早上在上班路上突然想到了这个文章,因为是上班路上有点无聊,所以就思考了一下这个问题:如果在某一网络中,只有 DNS 协议的请求能成功,那么在这个网络中的人们能否自由彼此通信?
先说结论,肯定是可以的,只需要单建一个特殊的 DNS 服务器就可以了。
核心思想:把文本消息伪装成 DNS 查询,放到 QNAME 中发给服务器,服务器收到特殊的 DNS 查询,将结果按照 TXT 查询格式返回内容。
上行数据包 (消息)
<group>.<sid>.u.<seq>.<nonce>.<payload1>.<payload2>....[.<TLD>]. ^------^-----^-^-----^-------^--------------------------^------^ | | | | | | | | | | | | | | | | | | | | └── 可选域 (权威模式) | | | | | | | | | | | └── 密文分片(Base64,≤30/段) | | | | | | | | | └── 随机数 (Base32) | | | | | | | └── 分片号 (从0递增) | | | | | └── 数据包方向 (u = 上行) | | | └── 会话 ID (Base32) | └── 组 ID (Base32)
|
上行数据包 (轮询)
<group>.<sid>.p.0.<nonce>[.<TLD>]. ^------^-----^-^-^--------^------^ | | | | | | | | | | | └── 可选域 (权威模式) | | | | | | | | | └── 随机数 (Base32) | | | | | | | └── 固定 0 (简单轮询) | | | | | └── 数据包方向 (p = 下行) | | | └── 会话 ID (Base32) | └── 组 ID (Base32)
|
服务端决策
服务端收到DNS查询 │ ├─ 查询类型TXT? ──否──> TXT "ok" │ │ │ |是 │ │ ├─ 域名格式正确? ──否──> TXT "ok" │ │ │ |是 │ │ ├─ 解析域名标签 │ ├─ 提取: group, sid, dir, seq, nonce, payload │ │ │ └─ Base32解码成功? ──否──> TXT "ok" │ │ │ |是 │ │ ├─ 更新会话(touch) │ └─ 记录: sid, group, 最后活跃时间 │ ├─ 判断方向标识 (dir) │ │ │ ├─ dir = "u" (上行消息) │ │ │ │ │ ├─ 解密payload │ │ │ │ │ │ │ ├─ 成功? ──否──> TXT "bad" │ │ │ │ │ │ │ │ │ |是 │ │ │ │ │ │ │ │ ├─ 广播到同组其他会话 │ │ │ │ └─ 放入各会话的下行队列 │ │ │ │ │ │ │ └─ 构造ACK响应 │ │ │ ├─ 明文: "ok" + 4字节序列号 │ │ │ ├─ 加密: AES-GCM │ │ │ └─ 返回: TXT "<encrypted_ack>" │ │ │ │ │ └──> [上行确认包] │ │ │ ├─ dir = "p" (轮询请求) │ │ │ │ │ ├─ 获取会话(sid) │ │ │ │ │ ├─ 检查下行队列 │ │ │ │ │ │ │ ├─ 队列非空? │ │ │ │ │ │ │ │ │ ├─ 是: 取出第一条消息 │ │ │ │ │ ├─ 加密消息 │ │ │ │ │ ├─ Base64URL编码 │ │ │ │ │ ├─ 按maxLength分片 │ │ │ │ │ └─ 返回: TXT "<片段1>" "<片段2>" ... │ │ │ │ │ └──> 【下行消息包】 │ │ │ │ │ │ │ │ │ └─ 否: 返回空内容 │ │ │ │ ├─ 加密空字节数组 │ │ │ │ └─ 返回: TXT "<encrypted_empty>" │ │ │ │ └──> 【空响应包】 │ │ │ │ │ │ │ └─ 加密失败? ──> TXT "bad" │ │ │ │ │ └──> 根据队列状态返回 │ │ │ └─ dir = 其他 │ └──> TXT "noop" │ └─ 发送DNS响应
|
最后就是实现
bob-zebedy/dnsay - GitHub