yarrick/iodine
GitHub: yarrick/iodine
通过 DNS 协议建立 IPv4 隧道,实现绕过防火墙限制的网络连接工具。
Stars: 7708 | Forks: 580
# iodine -
这是一款软件,允许你通过 DNS 服务器隧道传输 IPv4 数据。这在互联网访问受防火墙限制但允许 DNS 查询的情况下非常有用。
## 编译 (COMPILING)
Iodine 没有 configure 脚本。Linux 有两个可选功能(SELinux 和 systemd 支持),如果相关头文件在 `/usr/include` 中找到,将自动启用。(参见 `./src/osflags` 脚本)
运行 `make` 来编译服务器和客户端二进制文件。
运行 `make install` 将二进制文件和手册页复制到目标目录。
运行 `make test` 编译并运行单元测试。(需要 `check` 库)
## 快速入门 (QUICKSTART)
在你自己的局域网内尝试一下!遵循以下简单步骤:
- 在你的服务器上,运行:`./iodined -f 10.0.0.1 test.com`。
如果你已经使用了 `10.0.0.0` 网络,请使用另一个内部网络,如 `172.16.0.0`。
- 输入密码。
- 在客户端上,运行:`./iodine -f -r 192.168.0.1 test.com`。
将 `192.168.0.1` 替换为你服务器的 IP 地址。
- 输入相同的密码。
- 现在客户端拥有隧道 IP `10.0.0.2`,服务器拥有 `10.0.0.1`。
- 尝试通过隧道互相 ping 对方。
- 完成! :)
要通过中继 nameserver 实际使用它,请参阅下文。
## 使用方法 (HOW TO USE)
注意:服务器和客户端必须使用完全相同的协议。在大多数情况下,这意味着运行相同的 iodine 版本。遗憾的是,实现向后和向前协议兼容性通常是不可行的。
### 服务器端
要使用此隧道,你需要控制一个真实域名(如 `mydomain.com`),以及一台具有公网 IP 地址的服务器来运行 `iodined`。如果此服务器已经运行了 DNS 程序,请更改其监听端口,然后使用 `iodined` 的 `-b` 选项让 `iodined` 转发 DNS 请求。(请注意,不建议在生产环境中使用此过程,因为 `iodined` 的 DNS 转发不是完全透明的,例如区域传送将无法工作。)
或者,你可以将 DNS 服务器的子域名转发到 `iodined`,后者必须运行在不同的端口(`-p`)上。
然后,将一个子域名(例如 `t1.mydomain.com`)委托给 iodined 服务器。
如果你使用 BIND 管理你的域名,请在区域文件中添加如下两行:
```
t1 IN NS t1ns.mydomain.com. ; note the dot!
t1ns IN A 10.15.213.99
```
`NS` 行是将 `t1` 子域名的查询路由到 `t1ns` 服务器所需的全部内容。我们为子域名使用短名称,以便为数据流量保留尽可能多的空间。在 `NS` 行的末尾是你的 `iodined` 服务器的名称。这可以是任何名称,指向任何地方,但在这种情况下,它可以很容易地保存在同一个区域文件中。它必须是一个名称(而不是 IP 地址),并且该名称本身必须有一个 `A` 记录(而不是 `CNAME`)。
如果你的 `iodined` 服务器具有动态 IP,请使用动态 DNS 提供商。只需将 `NS` 行指向它,并省略 `A` 行:
```
t1 IN NS myname.mydyndnsprovider.com. ; note the dot!
```
然后重新加载或重启你的 nameserver 程序。现在,任何针对以 `t1.mydomain.com` 结尾的域名的 DNS 查询都将发送到你的 `iodined` 服务器。
最后在你的服务器上启动 `iodined`。第一个参数是隧道内的 IP 地址,可以来自你尚未使用的任何范围(例如 `192.168.99.1`),第二个参数是分配的域(在本例中为 `t1.mydomain.com`)。使用 `-f` 选项将使 iodined 在前台运行,这有助于测试。iodined 将打开一个虚拟接口("tun device"),并开始监听 UDP 端口 53 上的 DNS 查询。
可以在命令行中(`-P pass`)或在服务器启动后输入密码。现在客户端的一切准备就绪。
如果你有可能在意外的环境中使用 iodine 隧道,请使用 `-c` 选项启动 `iodined`。
在此示例情况下生成的命令行:
```
./iodined -f -c -P secretpassword 192.168.99.1 t1.mydomain.com
```
### 客户端
所有设置已完成,只需启动 `iodine`。它接受一两个参数,第一个是本地中继 DNS 服务器(可选),第二个是你使用的域名(`t1.mydomain.com`)。如果不指定第一个参数,将查询系统当前的 DNS 设置。
如果允许向任何计算机发送 DNS 查询,你可以直接将 `iodined` 服务器的地址作为第一个参数(在示例中:`t1ns.mydomain.com` 或 `10.15.213.99`)。在这种情况下,也可能允许向任何计算机的 DNS 端口(53 UDP)发送任何流量。Iodine 将检测到这种情况,并尽可能切换到 raw UDP 隧道。要在任何情况下强制使用 DNS 隧道,请使用 `-r` 选项(在您自己的网络内测试时特别有用)。
客户端的隧道接口将获得一个接近服务器的 IP(在本例中为 `192.168.99.2` 或 `.3` 等)以及合适的 MTU。输入与服务器相同的密码,可以作为命令行选项或在客户端启动后输入。
使用 `-f` 选项将使 iodine 客户端在前台运行。
在此示例情况下生成的命令行,添加 -r 强制使用 DNS 隧道,即使 raw UDP 隧道可行:
```
./iodine -f -P secretpassword t1.mydomain.com
```
从任意一端,你现在应该能够 ping 隧道另一端的 IP 地址。在这种情况下,从 iodine 客户端 `ping 192.168.99.1`,从 iodine 服务器 `ping 192.168.99.2`。
### 其他信息 (MISC. INFO)
#### IPv6
隧道内的数据仅为 IPv4。
默认情况下,服务器监听 IPv4 和 IPv6 的传入请求。
使用选项 `-4` 或 `-6` 仅监听一种协议。Raw mode 将尝试在与登录相同的协议上进行。
客户端可以使用 IPv4 或 IPv6 nameservers 连接到 iodined。如果需要,中继 nameservers 将自动在协议之间进行转换。使用选项 `-4` 或 `-6` 强制客户端对其 DNS 查询使用特定的 IP 版本。
如果你的服务器监听 IPv6 并且可以访问,请在其 DNS 设置中为其添加 AAAA 记录。扩展示例如下所示:
```
t1 IN NS t1ns.mydomain.com. ; note the dot!
t1ns IN A 10.15.213.99
t1ns IN AAAA 2001:db8::1001:99
```
#### 路由
可以通过 DNS 隧道路由所有流量。为此,首先通过有线/无线接口添加一条通往 iodine 使用的 nameserver 的主机路由,并以默认网关为网关。然后将默认网关替换为 DNS 隧道内 iodined 服务器的 IP 地址,并将服务器配置为执行 NAT。
但是,请注意,隧道传输的数据流量根本没有加密,外部各方可以相对容易地读取和更改。为了获得最大的安全性,请通过 DNS 隧道运行 VPN(= 双重隧道),或使用 secure shell (SSH) 访问,可能带有端口转发。后者也可用于 Web 浏览,当你在服务器上运行 Web 代理(例如 Privoxy)时。
#### 测试
`iodined` 服务器回复为隧道域的子域发送的 `NS` 请求。如果你的 iodined 子域是 `t1.mydomain.com`,请发送针对 `foo123.t1.mydomain.com` 的 `NS` 请求以查看委派是否有效。
`dig` 是一个很好的工具:
```
% dig -t NS foo123.t1.mydomain.com
ns.io.citronna.de.
```
此外,iodined 服务器将回答以 'z' 开头的任何支持请求类型的请求,例如:
```
dig -t TXT z456.t1.mydomain.com
dig -t SRV z456.t1.mydomain.com
dig -t CNAME z456.t1.mydomain.com
```
在所有这些情况下,回复看起来应该像乱码文本。
#### Mac OS X
在 Mac OS X 10.6 及更高版本上,iodine 支持 OS 内置的原生 utun 设备 - 使用 `-d utunX`。
## 操作信息
DNS 响应片段大小通常会自动探测以获得最大带宽。
要强制使用特定值(并加快速度),请使用 `-m` 选项。
DNS 主机名通常使用到其最大长度 255 个字符。
发现某些 DNS 中继对全长查询的回答相当不可靠,在重复尝试时给出差异很大(且大多非常糟糕)的片段大小自动探测结果。在这些情况下,使用 `-M` 开关将 DNS 主机名长度减少到例如 200 个字符,这使得这些 DNS 中继更加稳定。这对于某些“去优化”DNS 中继也很有用,这些中继会用查询的两个完整副本填充响应,留给下游数据的空间非常小(也不支持 EDNS0)。`-M` 开关可以用一些上游带宽换取下游带宽。请注意,最小 `-M` 值约为 100,因为协议最多只能将数据包(最大 1200 字节)分成 16 个片段,每个片段至少需要 75 个真实数据字节。
上游数据使用 Base32 编码的 gzip 压缩发送;或者,如果中继服务器支持域名中的混合大小写和 `+`,则使用 Base64;如果支持 `_`,则使用 Base64u;如果支持高字节值字符,则使用 Base128。
这种上游编码是自动检测的。DNS 协议每个数据包允许一个查询,一个查询最多 256 个字符。每个域名部分最多 63 个字符。因此,你的域名和子域名应尽可能短,以允许最大上游吞吐量。
支持多种 DNS 请求类型,其中 `NULL` 和 `PRIVATE` 类型预计将提供最大的下游带宽。`PRIVATE` 类型在私用范围中使用值 65399。其他可用类型是 `TXT`、`SRV`、`MX`、`CNAME` 和 `A`(返回 `CNAME`),按带宽降序排列。
通常,“最佳”请求类型是自动检测并使用的。但是,DNS 中继可能会对例如 NULL 和 TXT 施加限制,使得 SRV 或 MX 实际上成为最佳选择。这不是自动检测的,但可以使用 `-T` 选项强制执行。
建议尝试各种替代方案,尤其是当自动检测的请求类型提供的下游片段大小小于 200 字节时。
请注意,`SRV`、`MX` 和 `A`(返回 `CNAME`)查询可能/将会导致“智能”缓存 nameservers 进行额外查找以获取实际 IP 地址,这可能会减慢速度或完全失败。
非 `NULL/PRIVATE` 查询的 DNS 响应可以使用与上游数据相同的编解码器集进行编码。这通常也是自动检测的,但不会进行完全详尽的测试,因此在选择更高级的编解码器时可能不会注意到某些问题。在这种情况下,你会在片段大小自动探测中看到失败/损坏。特别是,发现几个 DNS 中继在主机名(`SRV`、`MX`、`CNAME`、`A`)超过约 180 个字符时,会将返回主机名(`SRV`、`MX`、`CNAME`、`A`)的回复更改为仅小写。在这些及类似情况下,使用 `-O` 选项尝试其他下游编解码器;Base32 应该始终有效。
现在的正常操作是服务器在下一个 DNS 请求进入之前_不_回答 DNS 请求,也就是所谓的“懒惰”模式。这样,当必须发送新的下游数据时,服务器将始终有一个 DNS 请求可用。
这极大地提高了(交互式)性能和延迟,并允许将静态 ping 请求默认减慢到 4 秒间隔,甚至可能更慢。事实上,现在 ping 的主要目的是强制回复上一个 ping,并防止 DNS 服务器超时(根据 RFC1035 通常至少 5-10 秒)。一些 DNS 服务器更不耐烦,在没有隧道数据流量的期间会给出 SERVFAIL 错误(超时)。在这些情况下,所有数据仍应通过,但 `iodine` 无论如何都会将 ping 间隔减少到 1 秒(-I1)以减少错误消息的数量。这对于非常不耐烦的 DNS 中继(如 `dnsadvantage.com` (ultradns))可能没有帮助,它们在 1 秒甚至更短的时间内超时。然而数据仍然会通过,你可以忽略 `SERVFAIL` 错误。
如果你在没有任何 DNS 服务器的本地网络上运行,请尝试 `-I 50`(iodine 和 iodined 在 60 秒静默后关闭连接)。
你唯一会注意到变慢的时候是当 DNS 回复数据包丢失时;`iodined` 服务器必须等待新的 ping 来重新发送数据。你可以通过产生一些上游流量(按键、ping)来加快速度。如果这种情况经常发生,请检查你的网络是否存在瓶颈和/或使用 `-I1` 运行。
懒惰模式下的延迟回答将导致一些“运营商级”商业 DNS 中继重复向 iodined 服务器重新发送相同的 DNS 查询。
如果 DNS 中继实际上实现为并行服务器池,则重复请求甚至可能来自多个源。这种效果只会在 `iodined` 服务器的网络流量中可见,不会影响客户端的连接。Iodined 会注意到这些重复项,并将相同的答案(当时机成熟时)发送给原始查询和最新的重复项。之后,完整答案会被缓存一小段时间。
延迟到达服务器的重复项会得到一个 iodine 客户端将忽略的回复(如果它曾经到达那里)。
如果你遇到问题,请尝试使用 tcpdump 或 ethereal/wireshark 等网络工具检查流量,并确保中继 DNS 服务器没有缓存响应。缓存的错误消息可能意味着你在服务器之前启动了客户端。服务器上的 `-D`(和 `-DD`)选项也可以显示接收和发送的查询。
## 技巧与提示 (TIPS & TRICKS)
如果某个接口上的端口 53 被不使用它的应用程序占用,请在 iodined 上使用 `-p` 指定备用端口(如 `-p 5353`),并使用例如 iptables(在 Linux 上)转发流量:
```
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to :5353
```
(由 Tom Schouten 发送)
Iodined 将拒绝来自超过 60 秒未活动(数据/ping)的客户端的数据。同样,当 60 秒内未收到下游数据时,iodine 将退出。在长时间网络中断或类似情况下,只需重启 iodine(重新登录),可能需要多次直到你取回旧 IP 地址。一旦完成,只需等待一会儿,你最终会看到隧道 TCP 流量从中断前的地方继续流动。
随着服务器中下游数据包队列的引入,其内存使用量在默认配置中增加了几兆字节。
用于低内存环境(例如在你的 DSL 路由器上运行),你可以减少 USERS 并在 user.h 中取消定义 OUTPACKETQ_LEN 而没有任何不良后果,假设任何时候最多连接一个客户端。仍然建议使用小的 DNSCACHE_LEN,最好为 2 或更高,但是你也可以取消定义它以节省更多千字节。
一个 iodine 服务器可以处理多个域。在同一域上设置不同的 NS 记录,所有记录都指向同一主机,并在 topdomain 参数的开头使用通配符(例如 `*.mydomain.com`)。iodine 将接受与该模式匹配的所有域的隧道流量。通配符必须位于 topdomain 参数的开头,后跟一个点。
## 性能 (PERFORMANCE)
本节列出了某些性能测量结果。要正确查看,请使用像 Courier 这样的等宽字体。
测量是在懒惰模式下的协议 00000502 中完成的;上游编码始终为 Base128;`iodine -M255`;`iodined -m1130`。网络条件并非极其有利;结果不是基准测试,而是对类似情况下预期现实世界性能的现实指示。
上游/下游吞吐量是通过 `scp` 一个先前从 `/dev/urandom` 读取的文件(即不可压缩)来测量的,并在单独的非隧道连接上使用 `ls -l ; sleep 30 ; ls -l` 测量大小。鉴于 16 kB 的巨大 `scp` 块大小,这给出了 4.3 kbit/s 的分辨率,这解释了为什么某些值完全相等。
Ping 往返时间使用 `ping -c100` 测量,显示的是平均 rtt 和平均偏差(表示围绕平均值的分布),以毫秒为单位。
### 情况 1: `Laptop -> Wifi AP -> Home server -> DSL provider -> Datacenter`
```
iodine DNS "relay" bind9 DNS cache iodined
downstr. upstream downstr. ping-up ping-down
fragsize kbit/s kbit/s avg +/-mdev avg +/-mdev
-----------------------------------------------------------------------------
iodine -> Wifi AP :53
-Tnull (= -Oraw) 982 43.6 131.0 28.0 4.6 26.8 3.4
iodine -> Home server :53
-Tnull (= -Oraw) 1174 48.0 305.8 26.6 5.0 26.9 8.4
iodine -> DSL provider :53
-Tnull (= -Oraw) 1174 56.7 367.0 20.6 3.1 21.2 4.4
-Ttxt -Obase32 730 56.7 174.7*
-Ttxt -Obase64 874 56.7 174.7
-Ttxt -Obase128 1018 56.7 174.7
-Ttxt -Oraw 1162 56.7 358.2
-Tsrv -Obase128 910 56.7 174.7
-Tcname -Obase32 151 56.7 43.6
-Tcname -Obase128 212 56.7 52.4
iodine -> DSL provider :53
wired (no Wifi) -Tnull 1174 74.2 585.4 20.2 5.6 19.6 3.4
[174.7* : these all have 2frag/packet]
```
### 情况 2: `Laptop -> Wifi+vpn / wired -> Home server`
```
iodine iodined
downstr. upstream downstr. ping-up ping-down
fragsize kbit/s kbit/s avg +/-mdev avg +/-mdev
-----------------------------------------------------------------------------
wifi + openvpn -Tnull 1186 166.0 1022.3 6.3 1.3 6.6 1.6
wired -Tnull 1186 677.2 2464.1 1.3 0.2 1.3 0.1
```
### 注意
性能与低 ping 时间密切相关,因为 iodine 在继续下一个数据片段之前需要确认。像 TCP 那样允许多个片段在传输中可能会提高性能,但这可能会导致中间 DNS 服务器严重过载。
当前协议的性能随 DNS 响应能力而变化,因为 DNS 服务器平均每个客户端最多处理一个 DNS 请求。
## 可移植性 (PORTABILITY)
iodine 已在 Linux (arm, ia64, x86, AMD64 和 SPARC64)、FreeBSD (ia64, x86)、OpenBSD (x86)、NetBSD (x86)、MacOS X (ppc 和 x86,使用 ) 和 Windows (使用 OpenVPN TAP32 驱动程序,参见 win32 readme 文件) 上进行了测试。移植到其他支持 TUN/TAP 隧道的类 unix 系统应该很容易。如果你让它在其他平台上运行,请告诉我们。
## 名称由来 (THE NAME)
选择 iodine 这个名字是因为它以 IOD (IP Over DNS) 开头,并且 iodine 的原子序数是 53,这恰好是 DNS 端口号。
## 致谢 (THANKS)
- To kuxien for FreeBSD and OS X testing
- To poplix for code audit
## 作者与许可 (AUTHORS & LICENSE)
Copyright (c) 2006-2014 Erik Ekman , 2006-2009 Bjorn
Andersson . Also major contributions by Anne Bezemer.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
MD5 implementation by L. Peter Deutsch (license and source in `src/md5.[ch]`)
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
标签:DNS隐蔽通道, DNS隧道, iodine, IPv4隧道, IP 地址批量处理, Linux工具, socks代理支持, 域名系统, 客户端加密, 客户端加密, 数据渗漏, 本体建模, 流量伪装, 端口探测, 绕过防火墙, 网络安全, 网络隧道, 边界穿透, 隐私保护, 隧道代理