├── dns
├── __init__.py
├── nameSilo.py
├── aliyun.py
├── huawei.py
├── qCloud.py
└── core.py
├── requirements.txt
├── .gitignore
├── runNameSilo.py
├── runDnsPod.py
├── runHwDns.py
├── runAliDns.py
├── README.md
└── log.py
/dns/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aliyun-python-sdk-alidns==2.6.19
2 | aliyun-python-sdk-core==2.15.0
3 | huaweicloudsdkcore==3.1.5
4 | huaweicloudsdkdns==3.1.5
5 | requests==2.28.1
6 | tencentcloud-sdk-python==3.0.806
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | __pycache__
3 |
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 | cf2dns.log
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
24 | config.json
25 |
--------------------------------------------------------------------------------
/runNameSilo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | import random
4 | import time
5 | import requests
6 | from dns.nameSilo import NameSiloClient
7 | from dns.core import Core
8 | from log import Logger
9 | import traceback
10 |
11 |
12 | #CM:移动 CU:联通 CT:电信 AB:境外 DEF:默认
13 | #需要动态解析到Cf优选ip的域名和子域名#NameSilo不支持多线解析,只能选DEF默认线路
14 | DOMAINS = {
15 | "vps789.com": {"@": ["DEF"],"www":["DEF"]},
16 | "vpsxxx777.com": {"@": ["DEF"]},
17 | }
18 |
19 | #解析生效条数
20 | AFFECT_NUM = 2
21 |
22 | #解析生效时间,NameSilo最少为3600
23 | TTL = 3600
24 |
25 | #NameSilo后台获取 https://www.namesilo.com/account/api-manager NameSilo只用填SECRETKEY即可
26 | SECRETKEY = '72xxxxxxxxxxxxa332'
27 |
28 | if __name__ == '__main__':
29 | config = {'DOMAINS':DOMAINS,"AFFECT_NUM":AFFECT_NUM,"TTL":TTL,"DNS_SERVER":4}
30 | cloud = NameSiloClient(SECRETKEY)
31 | core = Core(cloud,config)
32 | core.main()
--------------------------------------------------------------------------------
/runDnsPod.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | import random
4 | import time
5 | import requests
6 | from dns.qCloud import QcloudApiv3 # QcloudApiv3 DNSPod 的 API 更新了 By github@z0z0r4
7 | from dns.core import Core
8 | from log import Logger
9 | import traceback
10 |
11 |
12 | #CM:移动 CU:联通 CT:电信 AB:境外 DEF:默认
13 | #需要动态解析到Cf优选ip的域名和子域名
14 | DOMAINS = {
15 | "vps789.com": {"@": ["CM","CU","CT"],"www": ["CM","CU","CT"]},
16 | "vpsxxx777.com": {"@": ["CM","CU","CT"]}
17 | }
18 |
19 | #解析生效条数
20 | #免费的DNSPod相同线路最多支持2条解析
21 | AFFECT_NUM = 2
22 |
23 | #解析生效时间,默认为600秒 如果不是DNS付费版用户 不要修改!!!
24 | TTL = 600
25 |
26 | #腾讯云后台获取 https://console.cloud.tencent.com/cam/capi
27 | SECRETID = 'LTAI5txxxxxxxxxxxxxxxxxcRDH'
28 | SECRETKEY = 'LOixxxxxxxxxxxxxxxxx4mzcGR'
29 |
30 | if __name__ == '__main__':
31 | config = {'DOMAINS':DOMAINS,"AFFECT_NUM":AFFECT_NUM,"TTL":TTL,"DNS_SERVER":1}
32 | cloud = QcloudApiv3(SECRETID, SECRETKEY)
33 | core = Core(cloud,config)
34 | core.main()
--------------------------------------------------------------------------------
/runHwDns.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | import random
4 | import time
5 | import requests
6 | from dns.huawei import HuaWeiApi
7 | from dns.core import Core
8 | from log import Logger
9 | import traceback
10 |
11 |
12 | #CM:移动 CU:联通 CT:电信 AB:境外 DEF:默认
13 | #需要动态解析到Cf优选ip的域名和子域名
14 | DOMAINS = {
15 | "vps789.com": {"@": ["CM","CU","CT"],"www": ["CM","CU","CT"]},
16 | "vpsxxx777.com": {"@": ["CM","CU","CT"]}
17 | }
18 |
19 | #解析生效条数
20 | AFFECT_NUM = 2
21 |
22 | #如果使用华为云解析 需要从API凭证-项目列表中获取
23 | REGION_HW = 'cn-east-3'
24 |
25 | #解析生效时间,默认为600秒
26 | TTL = 600
27 |
28 | #华为云后台获取 https://support.huaweicloud.com/devg-apisign/api-sign-provide-aksk.html
29 | SECRETID = 'LTxxxxxxxxxxxxxxWcRDH'
30 | SECRETKEY = 'LOitxxxxxxxxxxxxxxxxxxmzcGR'
31 |
32 | if __name__ == '__main__':
33 | config = {'DOMAINS':DOMAINS,"AFFECT_NUM":AFFECT_NUM,"TTL":TTL,"DNS_SERVER":3}
34 | cloud = HuaWeiApi(SECRETID, SECRETKEY, REGION_HW)
35 | core = Core(cloud,config)
36 | core.main()
37 |
--------------------------------------------------------------------------------
/runAliDns.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | import random
4 | import time
5 | import requests
6 | from dns.aliyun import AliApi
7 | from dns.core import Core
8 | from log import Logger
9 | import traceback
10 |
11 |
12 | #CM:移动 CU:联通 CT:电信 AB:境外 DEF:默认
13 | #需要动态解析到Cf优选ip的域名和子域名
14 | DOMAINS = {
15 | "vps789.com": {"@": ["CM","CU","CT"],"www": ["CM","CU","CT"]},
16 | "vpsxxx777.com": {"@": ["CM","CU","CT"]}
17 | }
18 |
19 | #解析生效条数
20 | AFFECT_NUM = 2
21 |
22 | #如果使用阿里云解析 REGION出现错误再修改 默认不需要修改 https://help.aliyun.com/document_detail/198326.html
23 | REGION_ALI = 'cn-hongkong'
24 |
25 | #解析生效时间,默认为600秒
26 | TTL = 600
27 |
28 | #阿里云后台获取 https://help.aliyun.com/document_detail/53045.html 注意需要添加DNS控制权限 AliyunDNSFullAccess
29 | SECRETID = 'LTAIxxxxxxxxxxxxxxxxxxxWcRDH'
30 | SECRETKEY = 'LOitxxxxxxxxxxxxxxxxxxxzcGR'
31 |
32 | if __name__ == '__main__':
33 | config = {'DOMAINS':DOMAINS,"AFFECT_NUM":AFFECT_NUM,"TTL":TTL,"DNS_SERVER":2}
34 | cloud = AliApi(SECRETID, SECRETKEY,REGION_ALI)
35 | core = Core(cloud,config)
36 | core.main()
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 功能介绍
2 |
3 | 本项目的主要功能是动态获取cf优选ip,并将域名实时解析到这些优选IP上,以达到网站使用cloudflare CDN并使用国内访问速度更快的CDN节点。
4 |
5 | ★ 动态获取cf优选ip的接口由vps789.com提供
6 |
7 | ★ 网站使用CF优选IP的教程参考:https://www.vpsjxw.com/vps_use/cloudflare_select_ip/
8 |
9 | ★ 本工具主要是将网站域名动态解析到获取到的优选ip上,目前支持阿里云DNS、DNSPod、华为云DNS、NameSilo
10 |
11 |
12 |
13 | ### CF优选IP接口(VPS789.com提供)
14 |
15 | **CF优选IP机制说明:**
16 |
17 | 基于CloudFlareST工具生成一个[初选IP池],vps789固定24小时监测200-500个IP。每天根据网络情况综合评估,淘汰1/3网络较差的IP,然后从[初选IP池]补充到vps789中继续监测。**通过持续补充优选IP和IP优胜略汰机制,保证vps789上的IP都是优中选优。**
18 |
19 | ★ 支持对cloudFlare IP进行延迟、丢包率、下载速度、晚高峰网络状态等多个维度的筛选
20 |
21 | ★ vps789优选IP监控页面地址:https://vps789.com/cfip
22 |
23 | ★ VPS789优选IP动态获取接口:https://vps789.com/public/sum/cfIpApi
24 |
25 |
26 |
27 | ### 使用方法
28 |
29 | 0. 需要python3、pip环境
30 |
31 | 1. 安装运行脚本所需依赖
32 |
33 | pip install -r requirements.txt
34 |
35 |
36 | 2. 登录DNS解析平台,获取 SecretId、SecretKey。如果使用NameSilo只用填SecretKey即可。
37 |
38 | 3. 将脚本下载到本地,修改启动文件runXXX.py中的SecretId、SecretKey
39 |
40 | 4. 修改脚本中域名配置信息,可配置多个域名和多个子域名,注意选择DNS服务商
41 |
42 |
43 | 5. 运行程序,如果能够正常运行可以选择cron定时执行(建议1个小时执行一次)
44 |
45 | ```python
46 | #域名解析在阿里云DNS
47 | python runAliDns.py
48 | #域名解析在DnsPod
49 | python runDnsPod.py
50 | #域名解析在华为云DNS
51 | python runHwDns.py
52 | #域名解析在NameSilo
53 | python runNameSilo.py
54 | ```
55 |
56 |
57 | ### 鸣谢
58 | 感谢以下项目提供技术支撑与实现思路
59 |
60 | [cf2dns](https://github.com/ddgth/cf2dns)@ddgth
61 |
62 | [CloudflareSpeedTest](https://github.com/XIU2/CloudflareSpeedTest)@XIU2
63 |
--------------------------------------------------------------------------------
/log.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from logging import handlers
3 |
4 | class Logger(object):
5 | level_relations = {
6 | 'debug':logging.DEBUG,
7 | 'info':logging.INFO,
8 | 'warning':logging.WARNING,
9 | 'error':logging.ERROR,
10 | 'crit':logging.CRITICAL
11 | }#日志级别关系映射
12 |
13 | def __init__(self,filename,level='info',when='D',backCount=3,fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
14 | self.logger = logging.getLogger(filename)
15 | format_str = logging.Formatter(fmt)#设置日志格式
16 | self.logger.setLevel(self.level_relations.get(level))#设置日志级别
17 | sh = logging.StreamHandler()#往屏幕上输出
18 | sh.setFormatter(format_str) #设置屏幕上显示的格式
19 | th = handlers.TimedRotatingFileHandler(filename=filename,when=when,backupCount=backCount,encoding='utf-8')#往文件里写入#指定间隔时间自动生成文件的处理器
20 | #实例化TimedRotatingFileHandler
21 | #interval是时间间隔,backupCount是备份文件的个数,如果超过这个个数,就会自动删除,when是间隔的时间单位,单位有以下几种:
22 | # S 秒
23 | # M 分
24 | # H 小时、
25 | # D 天、
26 | # W 每星期(interval==0时代表星期一)
27 | # midnight 每天凌晨
28 | th.setFormatter(format_str)#设置文件里写入的格式
29 | self.logger.addHandler(sh) #把对象加到logger里
30 | self.logger.addHandler(th)
31 | if __name__ == '__main__':
32 | log = Logger('monitor.log',level='debug')
33 | log.logger.debug('debug')
34 | log.logger.info('info')
35 | log.logger.warning('警告')
36 | log.logger.error('报错')
37 | log.logger.critical('严重')
38 | Logger('error.log', level='error').logger.error('error')
--------------------------------------------------------------------------------
/dns/nameSilo.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import copy
3 | import sys
4 | import json
5 |
6 | import requests
7 |
8 |
9 | class NameSiloClient:
10 |
11 |
12 | def __init__(self, key) -> None:
13 | self._api_key = key
14 |
15 |
16 | def get_record(self, domain, length, sub_domain, record_type):
17 |
18 | try:
19 | url = f"https://www.namesilo.com/api/dnsListRecords?version=1&type=json&key={self._api_key}&domain={domain}"
20 | response = requests.get(url)
21 | response_body = response.json()
22 | if response.status_code == 200:
23 | if response_body["reply"]["code"] !=300:
24 | raise Exception('namesilo接口错误:'+response_body["reply"]["detail"])
25 | res={}
26 | res["code"]=0
27 | res["data"]={}
28 | res["data"]["records"]=[]
29 | for record in response_body["reply"]["resource_record"]:
30 | if record["type"] == record_type:
31 | if sub_domain == "@":
32 | if record["host"] == domain :
33 | res["data"]["records"].append({'id':record["record_id"],'value':record['value'],'line':'默认'})
34 | else:
35 | if sub_domain+"."+domain == record["host"]:
36 | res["data"]["records"].append({'id':record["record_id"],'value':record['value'],'line':'默认'})
37 | return res
38 | else:
39 | raise Exception('namesilo接口错误')
40 |
41 | except Exception as e:
42 | raise Exception('namesilo接口错误')
43 |
44 | def delete_record(self, domain,recordId):
45 |
46 | try:
47 | url = f"https://www.namesilo.com/api/dnsDeleteRecord?version=1&type=json&key={self._api_key}&domain={domain}&rrid={recordId}"
48 | response = requests.get(url)
49 | if response.status_code == 200:
50 | return response.json()
51 | else:
52 | raise Exception('namesilo接口错误')
53 | except Exception as e:
54 | raise Exception('namesilo接口错误')
55 |
56 | def create_record(self, domain, sub_domain, value, record_type,line, ttl):
57 | try:
58 | url = f"https://www.namesilo.com/api/dnsAddRecord?version=1&type=json&key={self._api_key}&domain={domain}&rrtype={record_type}&rrhost={sub_domain}&rrvalue={value}&rrttl={ttl}"
59 | response = requests.get(url)
60 | if response.status_code == 200:
61 | return response.json()
62 | else:
63 | raise Exception('namesilo接口错误')
64 | except Exception as e:
65 | raise Exception('namesilo接口错误')
66 |
67 | def change_record(self, domain, record_id, sub_domain,value, record_type, line,ttl):
68 | if sub_domain == "@":
69 | sub_domain = ""
70 | try:
71 | url = f"https://www.namesilo.com/api/dnsUpdateRecord?version=1&type=json&key={self._api_key}&domain={domain}&rrid={record_id}&rrhost={sub_domain}&rrvalue={value}&rrttl={ttl}"
72 | response = requests.get(url)
73 | if response.status_code == 200:
74 | return response.json()
75 | else:
76 | raise Exception('namesilo接口错误')
77 | except Exception as e:
78 | raise Exception('namesilo接口错误')
--------------------------------------------------------------------------------
/dns/aliyun.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # Mail: tongdongdong@outlook.com
4 | # Reference: https://help.aliyun.com/document_detail/29776.html?spm=a2c4g.11186623.2.38.3fc33efexrOFkT
5 | # REGION: https://help.aliyun.com/document_detail/198326.html
6 | import json
7 | from aliyunsdkcore import client
8 | from aliyunsdkalidns.request.v20150109 import DescribeDomainRecordsRequest
9 | from aliyunsdkalidns.request.v20150109 import DeleteDomainRecordRequest
10 | from aliyunsdkalidns.request.v20150109 import UpdateDomainRecordRequest
11 | from aliyunsdkalidns.request.v20150109 import AddDomainRecordRequest
12 |
13 |
14 | rc_format = 'json'
15 | class AliApi():
16 | def __init__(self, ACCESSID, SECRETKEY, REGION='cn-hongkong'):
17 | self.access_key_id = ACCESSID
18 | self.access_key_secret = SECRETKEY
19 | self.region = REGION
20 |
21 | def del_record(self, domain, record):
22 | clt = client.AcsClient(self.access_key_id, self.access_key_secret, self.region)
23 | request = DeleteDomainRecordRequest.DeleteDomainRecordRequest()
24 | request.set_RecordId(record)
25 | request.set_accept_format(rc_format)
26 | result = clt.do_action(request).decode('utf-8')
27 | result = json.JSONDecoder().decode(result)
28 | return result
29 |
30 | def get_record(self, domain, length, sub_domain, record_type):
31 | clt = client.AcsClient(self.access_key_id, self.access_key_secret, self.region)
32 | request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
33 | request.set_DomainName(domain)
34 | request.set_PageSize(length)
35 | request.set_RRKeyWord(sub_domain)
36 | request.set_Type(record_type)
37 | request.set_accept_format(rc_format)
38 | result = clt.do_action(request).decode('utf-8').replace('DomainRecords', 'data', 1).replace('Record', 'records', 1).replace('RecordId', 'id').replace('Value', 'value').replace('Line', 'line').replace('telecom', '电信').replace('unicom', '联通').replace('mobile', '移动').replace('oversea', '境外').replace('default', '默认')
39 | result = json.JSONDecoder().decode(result)
40 | return result
41 |
42 | def create_record(self, domain, sub_domain, value, record_type, line, ttl):
43 | clt = client.AcsClient(self.access_key_id, self.access_key_secret, self.region)
44 | request = AddDomainRecordRequest.AddDomainRecordRequest()
45 | request.set_DomainName(domain)
46 | request.set_RR(sub_domain)
47 | if line == "电信":
48 | line = "telecom"
49 | elif line == "联通":
50 | line = "unicom"
51 | elif line == "移动":
52 | line = "mobile"
53 | elif line == "境外":
54 | line = "oversea"
55 | elif line == "默认":
56 | line = "default"
57 | request.set_Line(line)
58 | request.set_Type(record_type)
59 | request.set_Value(value)
60 | request.set_TTL(ttl)
61 | request.set_accept_format(rc_format)
62 | result = clt.do_action(request).decode('utf-8')
63 | result = json.JSONDecoder().decode(result)
64 | return result
65 |
66 | def change_record(self, domain, record_id, sub_domain, value, record_type, line, ttl):
67 | clt = client.AcsClient(self.access_key_id, self.access_key_secret, self.region)
68 | request = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
69 | request.set_RR(sub_domain)
70 | request.set_RecordId(record_id)
71 | if line == "电信":
72 | line = "telecom"
73 | elif line == "联通":
74 | line = "unicom"
75 | elif line == "移动":
76 | line = "mobile"
77 | elif line == "境外":
78 | line = "oversea"
79 | elif line == "默认":
80 | line = "default"
81 | request.set_Line(line)
82 | request.set_Type(record_type)
83 | request.set_Value(value)
84 | request.set_TTL(ttl)
85 | request.set_accept_format(rc_format)
86 | result = clt.do_action(request).decode('utf-8')
87 | result = json.JSONDecoder().decode(result)
88 | return result
89 |
90 |
--------------------------------------------------------------------------------
/dns/huawei.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # Author/Mail: tongdongdong@outlook.com
4 | # Reference1: https://github.com/huaweicloud/huaweicloud-sdk-python-v3/tree/ff7df92d2a496871c7c2d84dfd2a7f4e2467fff5/huaweicloud-sdk-dns/huaweicloudsdkdns/v2/model
5 | # Reference2: https://support.huaweicloud.com/api-dns/dns_api_65003.html
6 | # REGION: https://developer.huaweicloud.com/endpoint
7 |
8 | from re import sub
9 | from huaweicloudsdkcore.auth.credentials import BasicCredentials
10 | from huaweicloudsdkdns.v2 import *
11 | from huaweicloudsdkdns.v2.region.dns_region import DnsRegion
12 | import json
13 |
14 |
15 | class HuaWeiApi():
16 | def __init__(self, ACCESSID, SECRETKEY, REGION = 'cn-east-3'):
17 | self.AK = ACCESSID
18 | self.SK = SECRETKEY
19 | self.region = REGION
20 | self.client = DnsClient.new_builder().with_credentials(BasicCredentials(self.AK, self.SK)).with_region(DnsRegion.value_of(self.region)).build()
21 | self.zone_id = self.get_zones()
22 |
23 | def del_record(self, domain, record):
24 | request = DeleteRecordSetsRequest()
25 | request.zone_id = self.zone_id[domain + '.']
26 | request.recordset_id = record
27 | response = self.client.delete_record_sets(request)
28 | result = json.loads(str(response))
29 | print(result)
30 | return result
31 |
32 | def get_record(self, domain, length, sub_domain, record_type):
33 | request = ListRecordSetsWithLineRequest()
34 | request.limit = length
35 | request.type = record_type
36 | if sub_domain == '@':
37 | request.name = domain + "."
38 | else:
39 | request.name = sub_domain + '.' + domain + "."
40 | response = self.client.list_record_sets_with_line(request)
41 | data = json.loads(str(response))
42 | result = {}
43 | records_temp = []
44 | for record in data['recordsets']:
45 | if (sub_domain == '@' and domain + "." == record['name']) or (sub_domain + '.' + domain + "." == record['name']):
46 | record['line'] = self.line_format(record['line'])
47 | record['value'] = '1.1.1.1'
48 | records_temp.append(record)
49 | result['data'] = {'records': records_temp}
50 | return result
51 |
52 | def create_record(self, domain, sub_domain, value, record_type, line, ttl):
53 | request = CreateRecordSetWithLineRequest()
54 | request.zone_id = self.zone_id[domain + '.']
55 | if sub_domain == '@':
56 | name = domain + "."
57 | else:
58 | name = sub_domain + '.' + domain + "."
59 | request.body = CreateRecordSetWithLineReq(
60 | type = record_type,
61 | name = name,
62 | ttl = ttl,
63 | weight = 1,
64 | records = [value],
65 | line = self.line_format(line)
66 | )
67 | response = self.client.create_record_set_with_line(request)
68 | result = json.loads(str(response))
69 | return result
70 |
71 | def change_record(self, domain, record_id, sub_domain, value, record_type, line, ttl):
72 | request = UpdateRecordSetRequest()
73 | request.zone_id = self.zone_id[domain + '.']
74 | request.recordset_id = record_id
75 | if sub_domain == '@':
76 | name = domain + "."
77 | else:
78 | name = sub_domain + '.' + domain + "."
79 | request.body = UpdateRecordSetReq(
80 | name = name,
81 | type = record_type,
82 | ttl = ttl,
83 | records=[value]
84 | )
85 | response = self.client.update_record_set(request)
86 | result = json.loads(str(response))
87 | return result
88 |
89 | def get_zones(self):
90 | request = ListPublicZonesRequest()
91 | response = self.client.list_public_zones(request)
92 | result = json.loads(str(response))
93 | zone_id = {}
94 | for zone in result['zones']:
95 | zone_id[zone['name']] = zone['id']
96 | return zone_id
97 |
98 | def line_format(self, line):
99 | lines = {
100 | '默认' : 'default_view',
101 | '电信' : 'Dianxin',
102 | '联通' : 'Liantong',
103 | '移动' : 'Yidong',
104 | '境外' : 'Abroad',
105 | 'default_view' : '默认',
106 | 'Dianxin' : '电信',
107 | 'Liantong' : '联通',
108 | 'Yidong' : '移动',
109 | 'Abroad' : '境外',
110 | }
111 | return lines.get(line, None)
112 |
113 | if __name__ == '__main__':
114 | hw_api = HuaWeiApi('WTTCWxxxxxxxxx84O0V', 'GXkG6D4X1Nxxxxxxxxxxxxxxxxxxxxx4lRg6lT')
115 | print(hw_api.get_record('xxxx.com', 100, '@', 'A'))
116 |
--------------------------------------------------------------------------------
/dns/qCloud.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # Mail: tongdongdong@outlook.com
4 | # Reference: https://cloud.tencent.com/document/product/302/8517
5 | # QcloudApiv3 DNSPod 的 API 更新了 By github@z0z0r4
6 |
7 | import json
8 | from tencentcloud.common import credential
9 | from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
10 | from tencentcloud.dnspod.v20210323 import dnspod_client, models
11 |
12 | class QcloudApiv3():
13 | def __init__(self, SECRETID, SECRETKEY):
14 | self.SecretId = SECRETID
15 | self.secretKey = SECRETKEY
16 | self.cred = credential.Credential(SECRETID, SECRETKEY)
17 |
18 | def del_record(self, domain: str, record_id: int):
19 | client = dnspod_client.DnspodClient(self.cred, "")
20 | req_model = models.DeleteRecordRequest()
21 | params = {
22 | "Domain": domain,
23 | "RecordId": record_id
24 | }
25 | req_model.from_json_string(json.dumps(params))
26 |
27 |
28 | resp = client.DeleteRecord(req_model)
29 | resp = json.loads(resp.to_json_string())
30 | resp["code"] = 0
31 | resp["message"] = "None"
32 | return resp
33 |
34 | def get_record(self, domain: str, length: int, sub_domain: str, record_type: str):
35 | def format_record(record: dict):
36 | new_record = {}
37 | record["id"] = record['RecordId']
38 | for key in record:
39 | new_record[key.lower()] = record[key]
40 | return new_record
41 | try:
42 | client = dnspod_client.DnspodClient(self.cred, "")
43 |
44 | req_model = models.DescribeRecordListRequest()
45 | params = {
46 | "Domain": domain,
47 | "Subdomain": sub_domain,
48 | "RecordType": record_type,
49 | "Limit": length
50 | }
51 | req_model.from_json_string(json.dumps(params))
52 |
53 |
54 | resp = client.DescribeRecordList(req_model)
55 | resp = json.loads(resp.to_json_string())
56 | temp_resp = {}
57 | temp_resp["code"] = 0
58 | temp_resp["data"] = {}
59 | temp_resp["data"]["records"] = []
60 | for record in resp['RecordList']:
61 | temp_resp["data"]["records"].append(format_record(record))
62 | temp_resp["data"]["domain"] = {}
63 | temp_resp["data"]["domain"]["grade"] = self.get_domain(domain)["DomainInfo"]["Grade"] # DP_Free
64 | return temp_resp
65 | except TencentCloudSDKException:
66 | # 构造空响应...
67 | temp_resp = {}
68 | temp_resp["code"] = 0
69 | temp_resp["data"] = {}
70 | temp_resp["data"]["records"] = []
71 | temp_resp["data"]["domain"] = {}
72 | temp_resp["data"]["domain"]["grade"] = self.get_domain(domain)["DomainInfo"]["Grade"] # DP_Free
73 | return temp_resp
74 |
75 | def create_record(self, domain: str, sub_domain: str, value: int, record_type: str = "A", line: str = "默认", ttl: int = 600):
76 | client = dnspod_client.DnspodClient(self.cred, "")
77 | req = models.CreateRecordRequest()
78 | params = {
79 | "Domain": domain,
80 | "SubDomain": sub_domain,
81 | "RecordType": record_type,
82 | "RecordLine": line,
83 | "Value": value,
84 | "ttl": ttl
85 | }
86 | req.from_json_string(json.dumps(params))
87 |
88 | # 返回的resp是一个CreateRecordResponse的实例,与请求对象对应
89 | resp = client.CreateRecord(req)
90 | resp = json.loads(resp.to_json_string())
91 | resp["code"] = 0
92 | resp["message"] = "None"
93 | return resp
94 |
95 | def change_record(self, domain: str, record_id: int, sub_domain: str, value: str, record_type: str = "A", line: str = "默认", ttl: int = 600):
96 | client = dnspod_client.DnspodClient(self.cred, "")
97 | req = models.ModifyRecordRequest()
98 | params = {
99 | "Domain": domain,
100 | "SubDomain": sub_domain,
101 | "RecordType": record_type,
102 | "RecordLine": line,
103 | "Value": value,
104 | "TTL": ttl,
105 | "RecordId": record_id
106 | }
107 | req.from_json_string(json.dumps(params))
108 |
109 | # 返回的resp是一个ChangeRecordResponse的实例,与请求对象对应
110 | resp = client.ModifyRecord(req)
111 | resp = json.loads(resp.to_json_string())
112 | resp["code"] = 0
113 | resp["message"] = "None"
114 | return resp
115 |
116 | def get_domain(self, domain: str):
117 | client = dnspod_client.DnspodClient(self.cred, "")
118 |
119 | # 实例化一个请求对象,每个接口都会对应一个request对象
120 | req = models.DescribeDomainRequest()
121 | params = {
122 | "Domain": domain
123 | }
124 | req.from_json_string(json.dumps(params))
125 |
126 | # 返回的resp是一个DescribeDomainResponse的实例,与请求对象对应
127 | resp = client.DescribeDomain(req)
128 | resp = json.loads(resp.to_json_string())
129 | return resp
--------------------------------------------------------------------------------
/dns/core.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | import random
4 | import time
5 | import requests
6 | from dns.qCloud import QcloudApiv3 # QcloudApiv3 DNSPod 的 API 更新了 By github@z0z0r4
7 | from dns.aliyun import AliApi
8 | from dns.huawei import HuaWeiApi
9 | from dns.nameSilo import NameSiloClient
10 | from log import Logger
11 | import traceback
12 |
13 | class Core():
14 |
15 | log_cf2dns = Logger('cf2dns.log', level='debug')
16 |
17 | def __init__(self, cloud,config):
18 | self.cloud = cloud
19 | self.config = config
20 |
21 | def get_optimization_ip(self):
22 | try:
23 | headers = headers = {'Content-Type': 'application/json'}
24 | data = {}
25 | response = requests.post('https://vps789.com/public/sum/cfIpApi', json=data, headers=headers)
26 | if response.status_code == 200:
27 | self.log_cf2dns.logger.info("GET OPTIMIZATION IP SUCCESS" )
28 | return response.json()
29 | else:
30 | self.log_cf2dns.logger.error("GET OPTIMIZATION IP ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: REQUEST STATUS CODE IS NOT 200")
31 | return None
32 | except Exception as e:
33 | self.log_cf2dns.logger.error("GETs OPTIMIZATION IP ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: " + str(e))
34 | return None
35 |
36 | #s_info已存在的ip,c_info 最新的优选cfip
37 | def changeDNS(self,line, s_info, c_info, domain, sub_domain):
38 | recordType = "A"
39 |
40 | lines = {"CM": "移动", "CU": "联通", "CT": "电信", "AB": "境外", "DEF": "默认"}
41 | line = lines[line]
42 | print(s_info)
43 | print(c_info)
44 | try:
45 | create_num = self.config['AFFECT_NUM'] - len(s_info)
46 | # 如果线路能解析的ip已满,就用cfip一个一个去替换原来的ip解析
47 | if create_num == 0:
48 | for info in s_info:
49 | if len(c_info) == 0:
50 | break
51 | cf_ip = c_info.pop(random.randint(0,len(c_info)-1))["ip"]
52 | if cf_ip in str(s_info):
53 | continue
54 | ret = self.cloud.change_record(domain, info["recordId"], sub_domain, cf_ip, recordType, line, self.config['TTL'])
55 | if(self.config['DNS_SERVER'] != 1 or ret["code"] == 0):
56 | self.log_cf2dns.logger.info("CHANGE DNS SUCCESS: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----DOMAIN: " + domain + "----SUBDOMAIN: " + sub_domain + "----RECORDLINE: "+line+"----RECORDID: " + str(info["recordId"]) + "----VALUE: " + cf_ip )
57 | else:
58 | self.log_cf2dns.logger.error("CHANGE DNS ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----DOMAIN: " + domain + "----SUBDOMAIN: " + sub_domain + "----RECORDLINE: "+line+"----RECORDID: " + str(info["recordId"]) + "----VALUE: " + cf_ip + "----MESSAGE: " + ret["message"] )
59 | # 如果线路还可以解析更多的ip,就添加cfip解析,直到用完
60 | elif create_num > 0:
61 | for i in range(create_num):
62 | if len(c_info) == 0:
63 | break
64 | cf_ip = c_info.pop(random.randint(0,len(c_info)-1))["ip"]
65 | if cf_ip in str(s_info):
66 | continue
67 | ret = self.cloud.create_record(domain, sub_domain, cf_ip, recordType, line, self.config['TTL'])
68 | if(self.config['DNS_SERVER'] != 1 or ret["code"] == 0):
69 | self.log_cf2dns.logger.info("CREATE DNS SUCCESS: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----DOMAIN: " + domain + "----SUBDOMAIN: " + sub_domain + "----RECORDLINE: "+line+"----VALUE: " + cf_ip )
70 | else:
71 | self.log_cf2dns.logger.error("CREATE DNS ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----DOMAIN: " + domain + "----SUBDOMAIN: " + sub_domain + "----RECORDLINE: "+line+"----RECORDID: " + str(info["recordId"]) + "----VALUE: " + cf_ip + "----MESSAGE: " + ret["message"] )
72 | # 如果线路能解析的ip已超出配额,就用cfip一个一个去替换原来的ip解析
73 | else:
74 | for info in s_info:
75 | if create_num == 0 or len(c_info) == 0:
76 | break
77 | cf_ip = c_info.pop(random.randint(0,len(c_info)-1))["ip"]
78 | if cf_ip in str(s_info):
79 | create_num += 1
80 | continue
81 | ret = self.cloud.change_record(domain, info["recordId"], sub_domain, cf_ip, recordType, line, self.config['TTL'])
82 | if(self.config['DNS_SERVER'] != 1 or ret["code"] == 0):
83 | self.log_cf2dns.logger.info("CHANGE DNS SUCCESS: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----DOMAIN: " + domain + "----SUBDOMAIN: " + sub_domain + "----RECORDLINE: "+line+"----RECORDID: " + str(info["recordId"]) + "----VALUE: " + cf_ip )
84 | else:
85 | self.log_cf2dns.logger.error("CHANGE DNS ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----DOMAIN: " + domain + "----SUBDOMAIN: " + sub_domain + "----RECORDLINE: "+line+"----RECORDID: " + str(info["recordId"]) + "----VALUE: " + cf_ip + "----MESSAGE: " + ret["message"] )
86 | create_num += 1
87 | except Exception as e:
88 | self.log_cf2dns.logger.error("CHANGE DNS ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: " + str(e))
89 |
90 | def main(self):
91 |
92 | recordType = "A"
93 | if len(self.config['DOMAINS']) > 0:
94 | try:
95 | cfips = self.get_optimization_ip()
96 | if cfips == None or cfips["code"] != 0:
97 | self.log_cf2dns.logger.error("GET CLOUDFLARE IP ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: " + str(cfips["data"]))
98 | return
99 | cf_cmips = cfips["data"]["CM"]
100 | cf_cuips = cfips["data"]["CU"]
101 | cf_ctips = cfips["data"]["CT"]
102 | cf_defips = cfips["data"]["AllAvg"]
103 | for domain, sub_domains in self.config['DOMAINS'].items():
104 | for sub_domain, lines in sub_domains.items():
105 | if self.config['DNS_SERVER'] == 4 and len(lines)!=1:
106 | self.log_cf2dns.logger.info("域名解析为NameSilo时,线路只能填DEF")
107 | return
108 |
109 | #下面5个数组存的是不同线路最新获取的优选IP列表
110 | temp_cf_cmips = cf_cmips.copy()
111 | temp_cf_cuips = cf_cuips.copy()
112 | temp_cf_ctips = cf_ctips.copy()
113 | temp_cf_abips = cf_ctips.copy()
114 | temp_cf_defips = cf_defips.copy()
115 | if self.config['DNS_SERVER'] == 1:
116 | ret = self.cloud.get_record(domain, 20, sub_domain, "CNAME")
117 | if ret["code"] == 0:
118 | for record in ret["data"]["records"]:
119 | if record["line"] == "移动" or record["line"] == "联通" or record["line"] == "电信":
120 | retMsg = self.cloud.del_record(domain, record["id"])
121 | if(retMsg["code"] == 0):
122 | self.log_cf2dns.logger.info("DELETE DNS SUCCESS: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----DOMAIN: " + domain + "----SUBDOMAIN: " + sub_domain + "----RECORDLINE: "+record["line"] )
123 | else:
124 | self.log_cf2dns.logger.error("DELETE DNS ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----DOMAIN: " + domain + "----SUBDOMAIN: " + sub_domain + "----RECORDLINE: "+record["line"] + "----MESSAGE: " + retMsg["message"] )
125 | ret = self.cloud.get_record(domain, 100, sub_domain, recordType)
126 | # ret["code"] == 0 就是self.config['DNS_SERVER'] == 1
127 | if self.config['DNS_SERVER'] != 1 or ret["code"] == 0 :
128 | if self.config['DNS_SERVER'] == 1 and "Free" in ret["data"]["domain"]["grade"] and self.config['AFFECT_NUM'] > 2:
129 | self.config['AFFECT_NUM'] = 2
130 | #下面5个数组存的是已存在的DNS解析列表
131 | cm_info = []
132 | cu_info = []
133 | ct_info = []
134 | ab_info = []
135 | def_info = []
136 | for record in ret["data"]["records"]:
137 | info = {}
138 | info["recordId"] = record["id"]
139 | info["value"] = record["value"]
140 | if record["line"] == "移动":
141 | cm_info.append(info)
142 | elif record["line"] == "联通":
143 | cu_info.append(info)
144 | elif record["line"] == "电信":
145 | ct_info.append(info)
146 | elif record["line"] == "境外":
147 | ab_info.append(info)
148 | elif record["line"] == "默认":
149 | def_info.append(info)
150 | for line in lines:
151 | if line == "CM":
152 | self.changeDNS("CM", cm_info, temp_cf_cmips, domain, sub_domain)
153 | elif line == "CU":
154 | self.changeDNS("CU", cu_info, temp_cf_cuips, domain, sub_domain)
155 | elif line == "CT":
156 | self.changeDNS("CT", ct_info, temp_cf_ctips, domain, sub_domain)
157 | elif line == "AB":
158 | self.changeDNS("AB", ab_info, temp_cf_abips, domain, sub_domain)
159 | elif line == "DEF":
160 | self.changeDNS("DEF", def_info, temp_cf_defips, domain, sub_domain)
161 | except Exception as e:
162 | traceback.print_exc()
163 | self.log_cf2dns.logger.error("CHANGE DNS ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: " + str(e))
--------------------------------------------------------------------------------