前言
办公室的网络是光猫拨号上网,每次拨号都动态分配一个新的IP地址。
公司内网做了一个erp系统,想外网能使用,买了百度域名和AiPage,无奈域名绑定的事成立一个问题。
花生壳能解决这个问题,但是要收费,还不低,每年大几百。作为白嫖党,岂能白白交这份钱。
借鉴代码
csdn拔了一圈,这位大神给了解决思路 :百度域名解析API+python实现百度云DDNS功能绑定动态公网ip_百度ddns-优快云博客
程序员节礼物
白嫖完,刚好看到csdn程序员节老大推得insCode,马上现学现用,解决了上面代码获取公网IP的bug,在包装一下,加了配置文件,变成下面的样子:
本着不白嫖的精神,同样把代码分享出来,以示对原作者 A欢笑与泪滴 的谢意。
源码
直接贴代码
import hmac
import time
import requests
import socket
import os
import urllib.request
import threading
from tkinter import *
from tkinter import font
from tkinter import PhotoImage
import json
from sympy import false
class DDNS:
def __init__(self,url,AK,SK,Domain):
self.url=url
self.AK=AK
self.SK=SK
self.Domain=Domain
def getTime(self):#获取网络时间戳,用于鉴权
url="http://acs.m.taobao.com/gw/mtop.common.getTimestamp/"
res=requests.get(url,timeout=5).text
res=json.loads(res)["data"]["t"]
return int(res)/1000
def getPublicIP(self): # 获取公网地址的函数
ip1 = requests.get("https://httpbin.org/ip").json()["origin"]
ip2 = requests.get("https://ifconfig.me/ip").text.strip()
if ip1 == ip2:
return ip1
else:
return None
def enc(self,key,message):
h=hmac.new(key,message,digestmod="SHA256")
return h.hexdigest()
def post(self,URI,_data):
#获取UTC时间
utc = time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime(self.getTime()))
#POST字典1
data=json.dumps(_data)
#创建请求header字典
_headers={
"host":"bcd.baidubce.com",
"x-bce-date":utc,
"Content-Type":"application/json;charset=utf-8",
"Content-Length":str(len(data))
}
#格式化字典为http头标准格式
headers=""
for a,b in _headers.items():
headers+=a+":"+b+"\r\n"
#生成CanonicalHeaders
method = "POST"
CanonicalQueryString=""
CanonicalURI = urllib.parse.quote(URI)
result = []
for key,value in _headers.items():
tempStr = str(urllib.parse.quote(key.lower(),safe="")) + ":" + str(urllib.parse.quote(value,safe=""))
result.append(tempStr)
result.sort()
CanonicalHeaders = "\n".join(result)
#拼接得到CanonicalRequest
CanonicalRequest = method + "\n" + CanonicalURI + "\n" + CanonicalQueryString +"\n" + CanonicalHeaders
#计算signingKey
signingKey=self.enc(self.SK.encode(),b"bce-auth-v1/%b/%b/1800"%(self.AK.encode(),utc.encode()))
#计算signature
signature=self.enc(signingKey.encode(),CanonicalRequest.encode())
#生成认证Authorization
Authorization="bce-auth-v1/%s/%s/1800/content-length;content-type;host;x-bce-date/%s"%(self.AK,utc,signature)
http="POST %s HTTP/1.1\r\n%sAuthorization:%s\r\n\r\n"%(URI,headers,Authorization)+data
#建立socket链接,发送http数据
s=socket.socket()
s.connect(("bcd.baidubce.com",80))
s.send(http.encode())
res=b""
while True:
_res=s.recv(10240)
res+=_res
if b"chunked" not in res: #如果不是chunked,一次性读完退出
break
if _res[-5:]==b"0\r\n\r\n": #chunked标志,连续读取到标识符退出
break
s.close()
return res
def getID(self):
data={
'domain':self.url,
'pageNo':1,
'pageSize':100
}
url="/v1/domain/resolve/list"
res=self.post(url,data)
res=res.decode().split("\r\n{")[1].split("}\r\n")[0]
res="{"+res+"}"
res=json.loads(res)["result"]
recordId={}
for i in res:
recordId[i["domain"]]=(i["recordId"],i["rdata"])
return recordId
def SET(self,domain,ip):
recordId=self.getID()
self.urlIP=recordId[domain][1]
if ip!=recordId[domain][1]:
url="/v1/domain/resolve/edit"
data={
"domain" : domain,
"rdType" : "A",
"rdata" : ip,
"ttl" : 600,
"zoneName" : self.url,
"recordId" : recordId[domain][0]
}
res=self.post(url,data)
print(res)
class DDNSApp:
def __init__(self, root):
self.root = root
self.root.title("百度DDNS动态域名解析工具")
self.root.geometry("500x500")
# Set background color
self.root.configure(bg="#f0f0f0")
# Define font
bold_font = font.Font(weight="bold")
default_text = """#参数设置\nurl="baidu.com" #填写你的域名,如http://www.baidu.com,仅填写baidu.com\nDomain="bcd"\nAK="2d132b22XXXXXXXXXXXXXXXXXXXXXXXX" #Access Key 百度云控制台申请\nSK="05422f2XXXXXXXXXXXXXXXXXXXXXXXXX" #Secret Key 百度云控制台申请\nsync=3600 #检测间隔时间,3600秒"""
self.url_label = Label(root, text="URL:", bg="#f0f0f0", font=bold_font, anchor="e")
self.url_label.grid(row=0, column=0, padx=10, pady=10, sticky="e")
self.url_entry = Entry(root)
self.url_entry.grid(row=0, column=1, padx=10, pady=10, ipadx=50)
self.domain_label = Label(root, text="Domain:", bg="#f0f0f0", font=bold_font, anchor="e")
self.domain_label.grid(row=1, column=0, padx=10, pady=10, sticky="e")
self.domain_entry = Entry(root)
self.domain_entry.grid(row=1, column=1, padx=10, pady=10, ipadx=50)
self.ak_label = Label(root, text="AK:", bg="#f0f0f0", font=bold_font, anchor="e")
self.ak_label.grid(row=2, column=0, padx=10, pady=10, sticky="e")
self.ak_entry = Entry(root)
self.ak_entry.grid(row=2, column=1, padx=10, pady=10, ipadx=50)
self.sk_label = Label(root, text="SK:", bg="#f0f0f0", font=bold_font, anchor="e")
self.sk_label.grid(row=3, column=0, padx=10, pady=10, sticky="e")
self.sk_entry = Entry(root)
self.sk_entry.grid(row=3, column=1, padx=10, pady=10, ipadx=50)
self.sync_label = Label(root, text="间隔时间(秒):", bg="#f0f0f0", font=bold_font, anchor="e")
self.sync_label.grid(row=4, column=0, padx=10, pady=10, sticky="e")
self.sync_entry = Entry(root)
self.sync_entry.grid(row=4, column=1, padx=10, pady=10, ipadx=50)
self.start_button = Button(root, text="开始(&S)", command=self.start_ddns)
self.start_button.grid(row=5, column=0, padx=10, pady=10)
self.stop_button = Button(root, text="停止(&P)", command=self.stop_ddns)
self.stop_button.grid(row=5, column=1, padx=10, pady=10)
self.result_text = Text(root, height=15, width=50)
self.result_text.grid(row=6, column=0, columnspan=2, padx=10, pady=10)
self.result_text.insert(END, default_text)
self.running = False
self.thread = None
self.config_file = "ddns_config.json"
self.load_config()
self.stoped=False
def save_config(self):
config = {
"url": self.url_entry.get(),
"domain": self.domain_entry.get(),
"ak": self.ak_entry.get(),
"sk": self.sk_entry.get(),
"sync": self.sync_entry.get()
}
with open(self.config_file, "w") as f:
json.dump(config, f)
def load_config(self):
if os.path.exists(self.config_file):
with open(self.config_file, "r") as f:
config = json.load(f)
self.url_entry.insert(0, config.get("url", ""))
self.domain_entry.insert(0, config.get("domain", ""))
self.ak_entry.insert(0, config.get("ak", ""))
self.sk_entry.insert(0, config.get("sk", ""))
self.sync_entry.insert(0, config.get("sync", ""))
def start_ddns(self):
self.save_config()
if not self.running:
self.running = True
self.stoped=False
self.thread = threading.Thread(target=self.run_ddns)
self.thread.start()
def stop_ddns(self):
self.running = False
self.stoped=True
if self.thread is not None:
self.thread.join()
self.thread = None
def run_ddns(self):
url = self.url_entry.get()
domain = self.domain_entry.get()
ak = self.ak_entry.get()
sk = self.sk_entry.get()
sync = int(self.sync_entry.get())
ddns = DDNS(url, ak, sk, domain)
first = True
jsq = sync
while self.running:
if jsq == 0 or first:
try:
ddns.SET(domain, ddns.getPublicIP())
now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(ddns.getTime()))
result_text = f"---------同步成功---------\n\n---------百度域名DDNS信息---------\n\n域名:{domain}.{url}\n-----\n域名解析IP:{ddns.urlIP}\n本地公网IP:{ddns.getPublicIP()}\n更新时间:{now}\n"
self.result_text.insert(END, result_text)
self.result_text.see(END)
except Exception as e:
error_text = f"Error: {e}\n"
self.result_text.insert(END, error_text)
self.result_text.see(END)
jsq = sync
first = False
if self.stoped==False:
time.sleep(1)
jsq -= 1
self.update_countdown(jsq)
def update_countdown(self, seconds):
self.root.title(f"百度DDNS动态域名解析工具 - 倒计时: {seconds}秒")
if __name__ == "__main__":
root = Tk()
app = DDNSApp(root)
root.mainloop()
下载
直接运行可能会报错,需要完整版的可以到这里下载,或Gitee下载
csdn:百度域名动态解析DDns资源-优快云文库