DNS隧道
前一段时间, 回到家发现电信欠费不能上网了, 赶紧充值的同时, 想到DNS隧道.
host www.baidu.com 发现是可以正常返回的. 但当时手头没有现成的DNS隧道工具, 萌发了自己写一个的想法.
原理应该算是比较简单的, 但是在实现的过程中遇到了一些麻烦, 现在还未完成…
先把这些困难记录一下, 也练练把事情说清楚的能力.
原理
-
原理
我假设你了解DNS, 但不知道什么是DNS隧道, 简单说一下. 一般来说, 如果家里宽带欠费了,或者是连的星巴克网络还未登陆, 这时是不能上网的, 这里说的不能上网包括浏览网页, 发送Email, QQ聊天等. 但是有些人发现, 它会弹出一个让你缴费或者是登陆的页面, 还是域名的页面. 就想到DNS是不是还通的. 果然是通的. 就是说, 虽然HTTP TCP都不通的, 但是到53端口的UDP(DNS使用的端口和协议)是和外界通的, 就通过这条跑把数据发出去.
-
我需要做的分成两部分.
一个叫做proxy-client, 应该可以说, 就是一个代理服务器, 但一般要跑在本机, 毕竟已经断网了, 跑在别有地方又连不过去.. 负责接收我的http请求, 包装成dns请求发出去, 然后等待假的dns回应, 再解成正确的http响应, 交给浏览器.
一个叫做proxy-server, 这个需要部署在外网的机器上, 比如阿里云之类. 负责接收DNS请求, 这应该是一个符合规范的DNS请求, 但它里面是包含了http请求. 解析出来HTTP请求发给真正的HTTP服务器(比如百度), 然后再把百度的响应包装一下发送给proxy-client
-
准备
你还有要一个自己的域名, 然后你需要一台机器做DNS服务器, 在域名系统(比如Goddy)上面把域名托管到你的机器上. 把proxy-server部署在自己的机器上, 所有包装好的DNS请求会发送到proxy-server, 经它处理拿到真实的http数据, 再返回到proxy-client
实现
打算用golang来实现.
思路1
golang已经有web框架了 https://golang.org/doc/articles/wiki/ proxy-client直接拿来用, 拿到http package封装好的request, 就可以拿到header与request body, 就能还原出来原始的http请求(或者说是原始的TCP包内容), 然后包装到DNS请求发出去.
proxy-server收到DNS请求(包含Http请求数据)后, 组装成完成的请求, 以tcp的方式发送给Http server, 拿到数据后再返回给proxy-client.
proxy-client因为是用的现成的web框架, 收到proxy-server的真实的http response之后, 需要解析出来Header和response body, 然后设置Header并写body给浏览器.
困难在于: 如果是chunked, 那proxy-client再写给用户的时候, 编码的后的chunked的内容就错了. 如果你不知道chunked如何编码的, 可以先了解一下.
解决方案就是不管是不是chunked, 都修改成不是. 尚未验证, 心虚.. 待续.
刚刚拿代码试了一下, 果然还是不行. 主要是因为keep-alive的时候, real http server返回给proxy-server的时候, 是不会close链接的, 那proxy-server与realserver只是tcp层面的交互, 它不知道什么时候才算结束. 另外, 在content-encoding: zip 时可能也会有问题?
思路2
路1有点难走, 那在它的基础上换一下思路看. proxy-server拿到proxy-client过来的请求后, 解析出来header与body, 然后用http client库发起http请求, 这样http client库可以帮我判断是不是拿到了完整的respone.
但我还是对http协议不太了解, header里面太多规则了 ,像上面说的content-encoding可能算是一个, 需要replace, 也不确定是不是还有其它的. https如何处理? 让我想一想, 待续.
好像https也没有更特殊之处, 去写代码尝试一下. 待续.
按思路2 实现了一下, 还没有替换特定的header. 在chrome里面试了一下, 对HTTP网站已经可以用了, 效果还不错~~ 但是HTTPS完全不能用. 怪自己之前对https_proxy不了解. 还要得再按下面的思路3再实现一个新版本.
思路3
前面几种思路其实会慢一些(这一句是之前写的, 现在已经不只是慢的问题了), 因为proxy-client到proxy-server以及反过来, 都需要等完整的请求或者是响应收到之后, 才能交给下游.
思路3采用流的方式. proxy-client就是开一个tcp listener, 收到之后直接转给proxy-server, server也同样转发, 大家都不等拿到完整的request/respone.
困难在于: 呃, 我之前以为有一个困难, 但是前两天看golang的http库代码, 发现了如下注释, 感觉没困难了.
// RFC 2616: Must treat
// GET /index.html HTTP/1.1
// Host: www.google.com
// and
// GET http://www.google.com/index.html HTTP/1.1
// Host: doesntmatter
// the same. In the second case, any Host line is ignored.
待续, 我先去把代码再改改..
代码在https://github.com/childe/dns-tunnel, HTTPS还是不行. 对于https__proxy以及HTTPS的原理不太懂. 暂时就这样吧.