Total Pageviews

Wednesday 28 October 2015

在openwrt 上,通过 pdnsd 和 dnsmasq 解决 dns 污染

以前一直在路由器上改域名 ip 来解决 google 系列服务无法使用的问题, 不过斯巴达以来 google 就没有一个相对稳定的 ip 可以指定了, 经常是好了一天第二天又失效了, 被逼着走代理之类的, 真心的烦躁.
不过浏览器啥的都还好, 走走代理无所谓但对于一些应用比如 Things 或者 nvALT 之类用 GAE 同步的软件就比较麻烦了. 那些软件本身并不能代理设置只有系统全局的, 但全局的又会导致那些国内的软件缓慢.
当然要解决上述问题方法有很多, 但如果可以通过 dns 来解决的话, 只要能在路由器上设置下. 下面的所有设备就自然而然的解决的, 特别是对于那些不能 root 的 android 或者 iphone 来说, 会方便很多很多.
当然修改 dns 之类的只能解决污染问题, 对于 ip 封锁或者 关键字封锁也是没有任何办法的. 所以只有那种全面启用了 https 并且 在全球有很多服务器那种网站才能完美的解决, 典型的例子就是 google .

目前 dns 污染流程

当需要访问某个网站的时候, 系统自动的向 dns 查询当前这个域名对应的 ip 地址, 如果当前 dns 无法回答时候, 它又会向它的上级 dns 取查询直到找到为止. 但那些 dns 服务器如果在 GFW 外的话, 当经过 GFW 时, GFW 就会检测请求的域名. 如果是被屏蔽的话, 就会返回一个错误的 ip 导致网站无法访问.





简单 dns 请求流程 

经过搜索和研究后发现, 这么多年了过去了 dns 污染还是只污染 UDP 协议, 如果可以用 TCP 协议进行 dns 请求的话就可以得到正确的结果.
不过找了很久都没找到一个比较好的既支持 tcp dns 请求, 又可以很好可以根据域名来分别处理的软件. 最终还是把方案定为 dnsmasq + pdnsd 的方案. pdnsd 主要提供一个无污染的 dns, dnsmasq 主要根据域名来切换相应的 dns.

pdnsd

主要用来解决污染问题, 在 openwrt 上直接找到 pdnsd 包安装就可以了.
opkg update
opkg install pdnsd
安装好以后还需要修改 /etc/pdnsd.conf, 其主要配置如下:
global {
 # debug = on;          # 调试模式, 日志会写入 /var/pdnsd/pdnsd.debug
 perm_cache=1024;
 cache_dir="/var/pdnsd";
 run_as="nobody";
 server_port = 1053;    # 使用 1053 作为 dns 端口, 默认是 53
 server_ip = any;
 status_ctl = on;
 query_method=tcp_only; # 最重要的配置, 只使用 tcp 查询上级 dns
 min_ttl=15m;
 max_ttl=1w;
 timeout=10;
}

server {
 label= "wido";           # 这个随便写
 ip = ${UPSTREAM_DNS_IP}; # 这里为上级 dns 的 ip 地址
 root_server = on;        # 设置为 on 后, 就代替系统默认的 dns 了.
 uptest = none;           # 不去检测 dns 是否无效.
}

source {
 owner=localhost;
# serve_aliases=on;
 file="/etc/hosts";
}

rr {
 name=localhost;
 reverse=on;
 a=127.0.0.1;
 owner=localhost;
 soa=localhost,root.localhost,42,86400,900,86400,86400;
}
其中的 ${UPSTREAM_DNS_IP} 需要设置成, 支持 TCP dns 查询的服务器.
可以用 8.8.8.8 这个 google 的 dns 但不是很推荐, 主要是他返回的 ip 虽然没污染, 但这个 ip 很可能也是被封的. 最好是那些 台湾, 日本, 或者美国东部的 dns. 目前大陆默认走的就是香港的 google 服务器, 所以香港的 dns 也不予考虑.
当然最好的方案还是, 如果你自己在海外有 VPS 那就在服务器上装个 pdnsd 来做 dns 服务器吧.
可以用命令
dig @8.8.8.8 +tcp www.google.com
来做测试, 不过也遇到过 +tcp 可以访问但实际无法用 pdnsd 连接的情况具体原因未知. 我这是自己服务器搭了 pdnsd 解决的. 
在找到可用的 dns 后, 接着修改回 pdnsd.conf 文件后输入
/etc/init.d/pdnsd enable
/etc/init.d/pdnsd start
启动路由器上的 pdnsd 服务之后就可以通过命令 (@192.168.2.1 根据自己家的网络情况改)
dig @192.168.2.1 -p 1053 www.google.com
来检测是否已配置好, 还是不行的话, 可以尝试开启 pdnsd.conf 的 dubug 模式看详细的日志, 配合 pdnsd-ctl help 来做进一步处理.

dnsmasq

本来指望 pdnsd 把 dnsmasq 的任务也一并解决的, 不过研究后发现他并不能将特殊的域名交给特殊的 dns 处理, 所以这部分功能还需要交给 dnsmasq. 
对于 openwrt 来说默认就安装好了 dnsmasq, 所以这里只需要简单的编辑 /etc/dnsmasq.conf, 再最后加入
conf-dir=/etc/dnsmasq.d
让 dnsmasq 会去加载 /etc/dnsmasq.d 目录下所有的配置. 
然后在建一个 /etc/dnsmasq.d/gfw.conf 这样的文件, 把需要进化的域名代理给 pdnsd 就可以了, 例如:
# google
server=/.google.com/127.0.0.1#1053
server=/.gstatic.com/127.0.0.1#1053
server=/.googleusercontent.com/127.0.0.1#1053
server=/.appspot.com/127.0.0.1#1053
server=/.googlecode.com/127.0.0.1#1053
server=/.googleapis.com/127.0.0.1#1053
server=/.gmail.com/127.0.0.1#1053
server=/.google-analytics.com/127.0.0.1#1053
server=/.youtube.com/127.0.0.1#1053
server=/.blogspot.com/127.0.0.1#1053
server=/.blogger.com/127.0.0.1#1053
之后重启 dnsmasq 服务
/etc/init.d/dnsmasq restart
一般来说这样折腾以后 google 系列的服务应该就可以访问了, 当然也可以按这个写法自己加其他的域名. 比如 .facebook.com, 具体的还得是满足 ip 没被封且关键字不被过滤(https) 的网站才行.

本来指望 pdnsd 把 dnsmasq 的任务也一并解决的, 不过研究后发现他并不能将特殊的域名交给特殊的 dns 处理, 所以这部分功能还需要交给 dnsmasq.
这个可以吧,
server {
label= "wido"; # 这个随便写
ip = ${UPSTREAM_DNS_IP}; # 这里为上级 dns 的 ip 地址
root_server = on; # 设置为 on 后, 就代替系统默认的 dns 了.
uptest = none; # 不去检测 dns 是否无效.
policy=excluded; #默认排除
include=".appspot.com",#允许列表
".blogger.com",
".google.com",
".nicovideo.jp",
".torproject.org",
".wikipedia.org",
".wordpress.com";
reject= 0.0.0.0/255.0.0.0,
10.0.0.0/255.0.0.0,
127.0.0.0/255.0.0.0,
172.16.0.0/255.240.0.0,
192.168.0.0/255.255.0.0,
224.0.0.0/224.0.0.0,
4.36.66.178;#如果查询结果为此范围ip则拒绝
reject_policy=fail;#拒绝视同访问失败,查询下个server
}  )
相关帖子:http://briteming.blogspot.com/2012/04/pdnsd-google-dns-dns.html
http://briteming.blogspot.com/2012/10/dnsdns.html
---------------------

pdnsd.conf,文件配置分为数个区域,主要修改global区域和server区域,其他区域保留默认值即可。具体的配置文件详情请查阅官方文档:pdnsd Documents:
http://members.home.nl/p.a.rombouts/pdnsd/doc.html

# 这里是全局设置区域,酌情修改
global {
perm_cache=2048;   # 缓存文件大小,单位KB
cache_dir="/var/pdnsd";  # 缓存文件位置,保留默认
# pid_file = /var/run/pdnsd.pid;  # pid文件,不用管
run_as="nobody";          # runas,保留默认
server_ip = 0.0.0.0;  # 监听所有
server_port = 25252;  # pdnsd 监听端口
status_ctl = on;   # 保留on
# paranoid=on;       # This option reduces the chance of cache poisoning
                   # but may make pdnsd less efficient, unfortunately.
query_method=tcp_only;  # 只使用TCP
min_ttl=6h;       # 最小TTL时间,自己酌情往上加,默认是15分钟
max_ttl=1w;        # 最长TTL时间,默认一周
timeout=10;        # 全局超时时间,默认10秒,酌情修改
neg_domain_pol=on; # 用到neg domain的话可以试试,不用就不用管
udpbufsize=1024;   # 默认
}
# 这个是server区域,可以定义多组,定义pdnsd的上游dns
server {
label= "mydns";  # 为这组server起一个名字
ip = 3.4.5.6;    # 填写DNS的IP地址,多个地址逗号分隔,可以换行,分号结尾
port = 5353;     # 填写DNS的端口,我这里上一级是3.4.5.6,监听的5353
                         # 如果使用GoogleDNS等公共 DNS 这里端口号应该是53,或者删除该行也行
# proxy_only=on;     # Do not query any name servers beside your ISP's.
                   # This may be necessary if you are behind some
                   # kind of firewall and cannot receive replies
                   # from outside name servers.
timeout=4;         # 超时值,酌情修改
uptest=none;       # 是否进行可用性检查,none=不检查,具体方法看官方文档
interface=eth0;    # 可用性检查所用的interface名
interval=10m;      # 检查频率
purge_cache=off;   # 如果off,pdnsd不会主动清除缓存中过期的项目 除非缓存满了
edns_query=on;    # 是否使用edns
}