[Python] Dyn 动态域名更新工具

作者 huhamhire,暂无评论,2014年5月14日 10:26 程序实践

因为设备上的原因,我一直在使用 DynDNS 的动态域名服务(其实家里那只 Cisco 的路由器应该是支持 DynDNS 或者 TZO 服务的,不过 TZO 现在已经被 DynDNS 给拿下了,所以要用路由器上的这个功能的话,没得选 TAT)。虽然从今年开始,Dyn 已经取消了免费使用政策,不过好在 Pro 版在资费上进行了下调,一年的费用也不用花多少美刀。

作为动态域名领域最为知名的国际服务商之一,Dyn 的服务还算不错,感觉付费以后相比于国内的花生壳或者 3322 之流要给力很多。最重要的是有更广泛的设备支持,很多光猫或者路由器都支持更新 Dyn 的动态域名服务,而国内服务商在这一方面确实还比较弱。

虽然 Dyn 的服务很给力,Cisco 的可靠性也不是盖的,不过最近还是偶尔会遇到动态域名更新不及时的情况,导致故障发生时不能在外访问家里资源很是很蛋疼。于是乎,就自己来写一个动态域名的小工具吧。

更新动态域名首先需要获得当前的公网 IP 地址,既然 Dyn 官方提供了一个检测链接,直接用就行。


http://checkip.dyndns.com/

在获取当前机器的公网 IP 之后,就可以与当前 DNS 解析的数据做比对,如果出现变更,则直接向 Dyn 发起更新请求。

与大多数的动态域名服务商一样,DynDNS 的动态域名服务也提供了通过 URL 来更新域名 IP 的接口,并给出了如下的示例。Dyn 提供了三个用于更新的接口,两个使用 http,另一个是 https,出于安全的角度考虑,我用了加密方式来进行数据更新。


https://username:password@members.dyndns.org/nic/update?hostname=yourhostname&myip=ipaddress&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG

因为通过 URL 方式更新 IP 时,用户名密码需要明文填写,问了安全起见,果断要用 https 的方式来发送更新请求。更新时发送的参数主要是 yourhostname (需要更新的域名)以及 myip (需要更新的 IP 地址)两项,其他诸如设置域名通配符以及 MX 记录解析的参数可按需设置。

基于前面提到的这些信息,我最终使用了如下代码实现了一个用于自动检测本地公网 IP 并更新到 Dyn 动态域名上的工具。


 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 __author__ = 'me@huhamhire.com'
 5 
 6 import re
 7 import socket
 8 import urllib
 9 
10 
11 class UpdateDyn(object):
12     def __init__(self, usr, pwd, hostname, **kwargs):
13         socket.setdefaulttimeout(30)
14         self.account = {
15             "usr": usr,
16             "pwd": pwd
17         }
18         self.config = {
19             "hostname": hostname,
20             "myip": kwargs.get("ip"),
21             "wildcard": kwargs.get("wildcard") or "NOCHG",
22             "mx": kwargs.get("mx") or "NOCHG",
23             "backmx": kwargs.get("backmx") or "NOCHG"
24         }
25 
26     def ns_ip(self):
27         if self.config["myip"]:
28             return True
29         try:
30             entries = socket.getaddrinfo(
31                 self.config["hostname"], 0, 0, 0, socket.SOL_TCP)
32             for info in entries:
33                 if info[0] == socket.AF_INET:
34                     self.config["myip"] = info[4][0]
35                     return True
36         except Exception, e:
37             print Exception, e
38         return False
39 
40     def check_ip(self):
41         try:
42             ip_response = urllib.urlopen("http://checkip.dyndns.com/").read()
43             ip_pattern = re.compile("<body>Current IP Address: (.+)</body>")
44             current_ip = ip_pattern.findall(ip_response)[0]
45             if current_ip != self.config["myip"]:
46                 self.config["myip"] = current_ip
47                 return True
48         except Exception, e:
49             print Exception, e
50         return False
51 
52     def update(self):
53         req_url = "https://%(usr)s:%(pwd)s@members.dyndns.org/" \
54                   "nic/update?" % self.account
55         req_data = urllib.urlencode(self.config)
56         try:
57             response_code = urllib.urlopen(req_url + req_data).read().split()[0]
58             return response_code
59         except Exception, e:
60             print Exception, e
61         return
62 
63     def run(self):
64         ns_check = self.ns_ip()
65         ip_check = self.check_ip()
66         if ns_check and ip_check:
67             return self.update()
68         elif not ns_check:
69             return "ns_err"
70         elif not ip_check:
71             return "ip_err"
72         return
73 
74 
75 if __name__ == '__main__':
76     op = UpdateDyn("username", "password", "hostname")
77     print "DDNS Status: [%s]" % op.run()

只需要修改上面代码中的用户名、密码以及域名信息,即可自动执行对相关域名的更新操作。当然也可以通过 crontab 来实现任务的周期定时执行。不过可以看到,当前的方法实现还比较原始,异常处理部分还有待进一步完善。

除了通过 URL 来直接更新 Dyn.com 动态域名服务的 IP 地址以外,Dyn 当前也允许使用 Raw HTTP GET Request 的方式来进行更新,并且支持使用 Base64 方式对用户名与密码进行加密。不过需要注意的是,官方支持说明上已经明确表示将来可能不再提供对这种方法的支持,而且 Base64 本身也只是一种防君子不防小人的方法,安全强度不如 https 方式,所以我就不再使用这个途径来执行相关的更新操作了。

另外,这段代码一般在稍加修改之后,也可以用于其他 DDNS 服务提供商的域名 IP 更新操作。

关键词:DDNS , Python , 工具 DIY
登录后进行评论