这是一款允许你通过 DNS 服务器隧道传输 IPv4 数据的软件。在互联网访问被防火墙拦截、但 DNS 查询被允许的情况下,这会很有用。
Iodine 没有 configure 脚本。Linux 有两个可选特性(SELinux 和 systemd 支持),如果在 /usr/include 中找到相关的头文件,它们会自动启用。(参见 ./src/osflags 中的脚本)
运行 make 来编译服务器和客户端二进制文件。
运行 make install 将二进制文件和手册页复制到目标目录。
运行 make test 来编译并运行单元测试。(需要 check 库)
在你自己的局域网里试试吧!按照以下简单步骤操作:
./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 地址。10.0.0.2,服务器拥有 10.0.0.1。注意:服务器和客户端必须使用完全相同的协议。在大多数情况下,这意味着运行相同版本的 iodine。遗憾的是,实现协议的前向和后向兼容通常并不可行。
要使用这个隧道,你需要控制一个真实域名(如 mydomain.com),并拥有一台具有公网 IP 地址的服务器来运行 iodined。如果这台服务器已经在运行 DNS 程序,请更改其监听端口,然后使用 iodined 的 -b 选项让 iodined 转发 DNS 请求。(注意:不建议在生产环境中采用此做法,因为 iodined 的 DNS 转发并非完全透明,例如区域传送将无法工作。)另一种做法是,将子域从你的 DNS 服务器转发到 iodined,此时 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!
然后重新加载或重启你的域名服务器程序。现在,任何以 t1.mydomain.com 结尾的域名的 DNS 查询都将被发送到你的 iodined 服务器。
最后在你的服务器上启动 iodined。第一个参数是隧道内的 IP 地址,可以来自任何你尚未使用的网段(例如 192.168.99.1),第二个参数是被分配的域名(本例中为 t1.mydomain.com)。使用 -f 选项可以让 iodined 保持在前台运行,这在测试时很有帮助。iodined 将打开一个虚拟接口("tun 设备"),并将开始监听 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 端口(UDP 53)的情况。Iodine 会检测到这一点,并在可能时切换到原始 UDP 隧道。要在任何情况下强制使用 DNS 隧道,请使用 -r 选项(在你自己网络内测试时特别有用)。
客户端的隧道接口将获得一个与服务器端相近的 IP(本例中为 192.168.99.2 或 .3 等)以及合适的 MTU。输入与服务器端相同的密码,可以作为命令行选项输入,也可以在客户端启动后输入。使用 -f 选项可以让 iodine 客户端保持在前台运行。
此示例场景中最终使用的命令行,添加 -r 可强制使用 DNS 隧道,即使原始 UDP 隧道本来可行:
./iodine -f -P secretpassword t1.mydomain.com
从现在开始,从任一端你都应该能够 ping 通隧道另一端的 IP 地址。在本例中,从 iodine 客户端执行 ping 192.168.99.1,从 iodine 服务器执行 ping 192.168.99.2。
隧道内传输的数据仅支持 IPv4。
默认情况下,服务器同时监听 IPv4 和 IPv6 上的传入请求。使用 -4 或 -6 选项可只监听一种协议。原始模式将尝试在与登录所用协议相同的协议上运行。
客户端可以使用 IPv4 或 IPv6 域名服务器连接到 iodined。中继域名服务器会在需要时自动进行协议转换。使用 -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 所使用的域名服务器,并以默认网关作为网关。然后将默认网关替换为 DNS 隧道内 iodined 服务器的 IP 地址,并将服务器配置为执行 NAT。
但是请注意,隧道内的数据流量完全没有加密,外部人员可以相对容易地对其实施读取和篡改。为了获得最大程度的安全,可以在 DNS 隧道中运行 VPN(即双重隧道),或者使用安全外壳(SSH)访问,并可能配合端口转发。如果你在服务器上运行 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 10.6 及更高版本上,iodine 支持操作系统内置的原生 utun 设备 - 使用 -d utunX。
DNS 响应的分片大小通常会被自动探测,以获得最大带宽。要强制指定特定值(并加快速度),请使用 -m 选项。
DNS 主机名通常会使用到其最大长度 255 个字符。已发现有些 DNS 中继在回答全长度查询时相当不可靠,在反复尝试时,分片大小自动探测的结果差异很大(而且大多非常糟糕)。在这些情况下,使用 -M 开关将 DNS 主机名长度减少到例如 200 个字符,这会使这些 DNS 中继稳定得多。这对某些"去优化"DNS 中继也很有用,这类中继会在响应中塞入两份完整的查询副本,留给下行数据的空间非常少(并且不支持 EDNS0)。-M 开关可以用一些上行带宽换取下行带宽。请注意,-M 的最小值约为 100,因为协议只能将数据包(最大 1200 字节)分成 16 个分片,每个分片至少需要 75 个实际数据字节。
上行数据以 gzip 压缩后使用 Base32 编码发送;如果中继服务器支持域名中的混合大小写和 +,则使用 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)查询可能会/将会导致"智能"缓存域名服务器执行额外的查找以获取实际 IP 地址,这可能会减慢速度或完全失败。
对非 NULL/PRIVATE 查询的 DNS 响应可以使用与上行数据相同的编解码器集进行编码。这通常也是自动检测的,但没有进行完全彻底的测试,因此在选择更高级的编解码器时,某些问题可能不会被注意到。在这种情况下,你会在分片大小自动探测中看到失败/损坏。特别是,已发现一些 DNS 中继只在主机名超过约 180 个字符时,才会将返回主机名(SRV、MX、CNAME、A)的回复改为小写。在这些及类似情况下,请使用 -O 选项尝试其他下行编解码器;Base32 应该始终有效。
现在的正常运行方式是,服务器在收到下一个 DNS 请求之前_不_回答当前请求,也就是所谓的"懒惰"模式。这样,当有新的下行数据需要发送时,服务器手头总有一个 DNS 请求可用。这大大改善了(交互式)性能和延迟,并允许将静默期的 ping 请求间隔默认减慢到 4 秒,甚至可能更慢。事实上,现在 ping 的主要目的是强制对前一次 ping 作出回复,并防止 DNS 服务器超时(根据 RFC1035,通常至少为 5-10 秒)。有些 DNS 服务器更不耐烦,在没有隧道数据流量的时段会返回 SERVFAIL 错误(超时)。在这些情况下,所有数据仍然应该能够通过,但 iodine 无论如何都会将 ping 间隔缩短到 1 秒(-I1),以减少错误消息的数量。这对像 dnsadvantage.com(ultradns)这样非常不耐烦、会在 1 秒甚至更短时间内超时的 DNS 中继可能无济于事。但数据仍然能够通过,你可以忽略 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)选项也可以显示收到和发送的查询。
如果你的 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 路由器上运行)时,你可以减小 user.h 中的 USERS 并取消定义 OUTPACKETQ_LEN,这不会产生任何不良后果,前提是任何时候最多只有一个客户端连接。仍然建议使用较小的 DNSCACHE_LEN,最好为 2 或更高;但你也可以取消定义它,以再节省几 KB 内存。
一个 iodine 服务器可以处理多个域名。在同一个域上设置不同的 NS 记录,全部指向同一台主机,并在顶级域名参数的开头使用通配符(例如 *.mydomain.com)。iodine 将接受与该模式匹配的所有域名的隧道流量。通配符必须位于顶级域名参数的开头,并且后面必须跟一个点。
本节以表格形式列出了一些性能测量结果。为了正常查看,请使用 Courier 之类的等宽字体。
测量在协议 00000502 的懒惰模式下完成;上行编码始终为 Base128;iodine -M255;iodined -m1130。网络条件并非极为有利;结果不是基准测试数据,而是在类似情况下可以预期的真实世界性能的现实体现。
上行/下行吞吐量通过 scp 传输一个先前从 /dev/urandom 读取的文件(即不可压缩)来测量,并在另一条非隧道连接上使用 ls -l ; sleep 30 ; ls -l 测量其大小。鉴于 scp 较大的 16 kB 块大小,这提供了 4.3 kbit/s 的分辨率,这也解释了为什么某些值完全相同。ping 往返时间使用 ping -c100 测量,列出的是平均 rtt 和平均偏差(表示围绕平均值的离散程度),单位为毫秒。
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]
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 请求。
iodine 已在 Linux(arm、ia64、x86、AMD64 和 SPARC64)、FreeBSD(ia64、x86)、OpenBSD(x86)、NetBSD(x86)、MacOS X(ppc 和 x86,配合 http://tuntaposx.sourceforge.net/)以及 Windows(使用 OpenVPN TAP32 驱动,参见 win32 自述文件)上测试过。移植到其他支持 TUN/TAP 隧道的类 Unix 系统应该很容易。如果你能让它在其他平台上运行,请告诉我们。
之所以选择 iodine 这个名字,是因为它以 IOD(IP Over DNS,即基于 DNS 的 IP 隧道)开头,而且碘(iodine)的原子序数为 53,恰好是 DNS 的端口号。
版权所有 (c) 2006-2014 Erik Ekman [email protected],2006-2009 Bjorn Andersson [email protected]。此外,Anne Bezemer 做出了重大贡献。
特此授予出于任何目的、无论是否收取费用,使用、复制、修改和/或分发本软件的许可,前提是上述版权声明和本许可声明出现在所有副本中。
本软件按"原样"提供,作者就本软件不承担任何保证,包括所有对适销性和适用性的默示保证。在任何情况下,作者均不对任何特殊的、直接的、间接的或后果性的损害,或因使用、数据或利润损失而产生的任何损害承担责任,无论是基于合同、过失还是其他侵权行为,且无论该等损害是否源于或与本软件的使用或性能有关。
MD5 实现由 L. Peter Deutsch 编写(许可证和源代码位于 src/md5.[ch])
版权所有 (C) 1999, 2000, 2002 Aladdin Enterprises。保留所有权利。