返回首页
当前位置: 主页 > 路由器设置 >

翻墙路由器的原理与实现(13)

时间:2016-02-01 13:38点击:

        subprocess.call('iptables -I INPUT %s' % rule, shell=True)  

    print('running..')  

    sys.stdin.readline()  

except KeyboardInterrupt:  

    print('bye')  

finally:  

    for rule in reversed(rules):  

        subprocess.call('iptables -D INPUT %s' % rule, shell=True

本地有了这些iptables规则之后就可以丢弃掉GFW发回来的错误答案,从而得到正确的解析结果。这个脚本用到了两个iptables模块一个是u32一个是string。这两个内核模块不是所有的linux机器都有的。比如大部分的Android手机都没有这两个内核模块。所以上面的脚本适合内核模块很容易安装的场景,比如你的ubuntu pc。因为linux的内核模块与内核版本(每次编译基本都不同)是一一对应的,所以不同的linux机器是无法共享同样的内核模块的。所以基于内核模块的方案天然地具有安装困难的缺陷。

使用nfqueue过滤

对于没有办法自己安装或者编译内核模块的场景,比如最常见的Android手机,厂家不告诉你内核的具体版本以及编译参数,普通用户是没有办法重新编译linux内核的。对于这样的情况,iptables提供了nfqueue,我们可以把内核模块做的ip过滤的工作交给用户态(也就是普通的应用程序)来完成。

CLEAN_DNS = '8.8.8.8'
RULES = []
for iface in network_interface.list_data_network_interfaces():
# this rule make sure we always query from the "CLEAN" dns
RULE_REDIRECT_TO_CLEAN_DNS = (
{'target': 'DNAT', 'iface_out': iface, 'extra': 'udp dpt:53 to:%s:53' % CLEAN_DNS},
('nat', 'OUTPUT', '-o %s -p udp --dport 53 -j DNAT --to-destination %s:53' % (iface, CLEAN_DNS))
)
RULES.append(RULE_REDIRECT_TO_CLEAN_DNS)
RULE_DROP_PACKET = (
{'target': 'NFQUEUE', 'iface_in': iface, 'extra': 'udp spt:53 NFQUEUE num 1'},
('filter', 'INPUT', '-i %s -p udp --sport 53 -j NFQUEUE --queue-num 1' % iface)
)
RULES.append(RULE_DROP_PACKET)
# source %E5%9F%9F%E5%90%8D%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BC%93%E5%AD%98%E6%B1%A1%E6%9F%93
WRONG_ANSWERS = {
'4.36.66.178',
'8.7.198.45',
'37.61.54.158',
'46.82.174.68',
'59.24.3.173',
'64.33.88.161',
'64.33.99.47',
'64.66.163.251',
'65.104.202.252',
'65.160.219.113',
'66.45.252.237',
'72.14.205.99',
'72.14.205.104',
'78.16.49.15',
'93.46.8.89',
'128.121.126.139',
'159.106.121.75',
'169.132.13.103',
'192.67.198.6',
'202.106.1.2',
'202.181.7.85',
'203.161.230.171',
'203.98.7.65',
'207.12.88.98',
'208.56.31.43',
'209.36.73.33',
'209.145.54.50',
'209.220.30.174',
'211.94.66.147',
'213.169.251.35',
'216.221.188.182',
'216.234.179.13',
'243.185.187.39'
}
def handle_nfqueue():
try:
nfqueue = NetfilterQueue()
nfqueue.bind(1, handle_packet)
nfqueue.run()
except:
LOGGER.exception('stopped handling nfqueue')
dns_service_status.error = traceback.format_exc()
def handle_packet(nfqueue_element):
try:
ip_packet = dpkt.ip.IP(nfqueue_element.get_payload())
dns_packet = dpkt.dns.DNS(ip_packet.udp.data)
if contains_wrong_answer(dns_packet):
# after the fake packet dropped, the real answer can be accepted by the client
LOGGER.debug('drop fake dns packet: %s' % repr(dns_packet))
nfqueue_element.drop()
return
nfqueue_element.accept()
dns_service_status.last_activity_at = time.time()
except:
LOGGER.exception('failed to handle packet')
nfqueue_element.accept()
def contains_wrong_answer(dns_packet):
if dpkt.dns.DNS_A not in [question.type for question in dns_packet.qd]:
return False # not answer to A question, might be PTR
for answer in dns_packet.an:
if dpkt.dns.DNS_A == answer.type:
resolved_ip = socket.inet_ntoa(answer['rdata'])
if resolved_ip in WRONG_ANSWERS:
return True # to find wrong answer
else:
LOGGER.info('dns resolve: %s => %s' % (dns_packet.qd[0].name, resolved_ip))
return False # if the blacklist is incomplete, we will think it is right answer
return True # to find empty answer

其原理是一样的,过滤所有的DNS应答,如果发现是错误的答案就丢弃。因为是基于nfqueue的,所以只要linux内核支持nfqueue,而且iptables可以添加nfqueue的target,就可以使用以上方式来丢弃DNS错误答案。目前已经成功在主流的android手机上运行该程序,并获得正确的DNS解析结果。另外,上面的实现利用iptables的重定向能力,达到了更换本机dns服务器的目的。无论机器设置的dns服务器是什么,通过上面的iptables规则,统统给你重定向到干净的DNS(8.8.8.8)。

------分隔线----------------------------
推荐内容