commit dba956d25a3ba6cf3fb2ec671efb115199c52c3b Author: D:/Program Files/Git/jaz <1003661230@qq.com> Date: Wed Nov 6 19:56:24 2024 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c2ac33 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +.DS_Store +__pycache__ + + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +cf2dns.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +config.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..191b1a7 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +### 功能介绍 + +本项目的主要功能是动态获取cf优选ip,并将域名实时解析到这些优选IP上,以达到网站使用cloudflare CDN并使用国内访问速度更快的CDN节点。 +★ 动态获取cf优选ip的接口由vps789.com提供 +★ 网站使用CF优选IP的教程参考:https://www.vpsjxw.com/vps_use/cloudflare_select_ip/ +★ 本工具主要是将网站域名动态解析到获取到的优选ip上,目前支持阿里云DNS、DNSPod、华为云DNS、NameSilo + +### VPS789.com提供CF优选IP接口 +1、支持对CF优选IP的三网实时监测延迟、丢包率,支持查看24小时、一个月的监测数据。 + +2、支持对CF优选IP的下载速度进行测试 + +3、基于CloudFlareST工具生成一个优选IP池,vps789的优选IP每天定时淘汰1/3网络不好的IP,从优选IP池补充到vps789中继续监测。通过持续补充优选IP和IP优胜略汰机制,保证vps789上的IP都是优中选优。 + +4、vps789优选IP监控页面地址:https://vps789.com/cfip + +### 使用方法 + +0. 需要python3、pip环境 + +1. 安装运行脚本所需依赖 + +```python +pip install -r requirements.txt +``` + +1. 登录DNS解析平台,获取 SecretId、SecretKey。 + 如果使用阿里云DNS,注意需要添加DNS控制权限**AliyunDNSFullAccess** + 如果使用NameSilo只用填SecretKey即可。 + +2. 将脚本下载到本地修改start.py中的SecretId、SecretKey + +3. 修改脚本中域名配置信息,可配置多个域名和多个子域名,注意选择DNS服务商 + + +4. 运行程序,如果能够正常运行可以选择cron定时执行(建议1个小时执行一次) + +```python +python start.py +``` + +### 鸣谢 +感谢以下项目提供技术支撑与实现思路 +[cf2dns](https://github.com/ddgth/cf2dns)@ddgth +[CloudflareSpeedTest](https://github.com/XIU2/CloudflareSpeedTest)@XIU2 diff --git a/dns/__init__.py b/dns/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dns/aliyun.py b/dns/aliyun.py new file mode 100644 index 0000000..24e0a60 --- /dev/null +++ b/dns/aliyun.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Mail: tongdongdong@outlook.com +# Reference: https://help.aliyun.com/document_detail/29776.html?spm=a2c4g.11186623.2.38.3fc33efexrOFkT +# REGION: https://help.aliyun.com/document_detail/198326.html +import json +from aliyunsdkcore import client +from aliyunsdkalidns.request.v20150109 import DescribeDomainRecordsRequest +from aliyunsdkalidns.request.v20150109 import DeleteDomainRecordRequest +from aliyunsdkalidns.request.v20150109 import UpdateDomainRecordRequest +from aliyunsdkalidns.request.v20150109 import AddDomainRecordRequest + + +rc_format = 'json' +class AliApi(): + def __init__(self, ACCESSID, SECRETKEY, REGION='cn-hongkong'): + self.access_key_id = ACCESSID + self.access_key_secret = SECRETKEY + self.region = REGION + + def del_record(self, domain, record): + clt = client.AcsClient(self.access_key_id, self.access_key_secret, self.region) + request = DeleteDomainRecordRequest.DeleteDomainRecordRequest() + request.set_RecordId(record) + request.set_accept_format(rc_format) + result = clt.do_action(request).decode('utf-8') + result = json.JSONDecoder().decode(result) + return result + + def get_record(self, domain, length, sub_domain, record_type): + clt = client.AcsClient(self.access_key_id, self.access_key_secret, self.region) + request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest() + request.set_DomainName(domain) + request.set_PageSize(length) + request.set_RRKeyWord(sub_domain) + request.set_Type(record_type) + request.set_accept_format(rc_format) + 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', '默认') + result = json.JSONDecoder().decode(result) + return result + + def create_record(self, domain, sub_domain, value, record_type, line, ttl): + clt = client.AcsClient(self.access_key_id, self.access_key_secret, self.region) + request = AddDomainRecordRequest.AddDomainRecordRequest() + request.set_DomainName(domain) + request.set_RR(sub_domain) + if line == "电信": + line = "telecom" + elif line == "联通": + line = "unicom" + elif line == "移动": + line = "mobile" + elif line == "境外": + line = "oversea" + elif line == "默认": + line = "default" + request.set_Line(line) + request.set_Type(record_type) + request.set_Value(value) + request.set_TTL(ttl) + request.set_accept_format(rc_format) + result = clt.do_action(request).decode('utf-8') + result = json.JSONDecoder().decode(result) + return result + + def change_record(self, domain, record_id, sub_domain, value, record_type, line, ttl): + clt = client.AcsClient(self.access_key_id, self.access_key_secret, self.region) + request = UpdateDomainRecordRequest.UpdateDomainRecordRequest() + request.set_RR(sub_domain) + request.set_RecordId(record_id) + if line == "电信": + line = "telecom" + elif line == "联通": + line = "unicom" + elif line == "移动": + line = "mobile" + elif line == "境外": + line = "oversea" + elif line == "默认": + line = "default" + request.set_Line(line) + request.set_Type(record_type) + request.set_Value(value) + request.set_TTL(ttl) + request.set_accept_format(rc_format) + result = clt.do_action(request).decode('utf-8') + result = json.JSONDecoder().decode(result) + return result + diff --git a/dns/huawei.py b/dns/huawei.py new file mode 100644 index 0000000..b1150af --- /dev/null +++ b/dns/huawei.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Author/Mail: tongdongdong@outlook.com +# Reference1: https://github.com/huaweicloud/huaweicloud-sdk-python-v3/tree/ff7df92d2a496871c7c2d84dfd2a7f4e2467fff5/huaweicloud-sdk-dns/huaweicloudsdkdns/v2/model +# Reference2: https://support.huaweicloud.com/api-dns/dns_api_65003.html +# REGION: https://developer.huaweicloud.com/endpoint + +from re import sub +from huaweicloudsdkcore.auth.credentials import BasicCredentials +from huaweicloudsdkdns.v2 import * +from huaweicloudsdkdns.v2.region.dns_region import DnsRegion +import json + + +class HuaWeiApi(): + def __init__(self, ACCESSID, SECRETKEY, REGION = 'cn-east-3'): + self.AK = ACCESSID + self.SK = SECRETKEY + self.region = REGION + self.client = DnsClient.new_builder().with_credentials(BasicCredentials(self.AK, self.SK)).with_region(DnsRegion.value_of(self.region)).build() + self.zone_id = self.get_zones() + + def del_record(self, domain, record): + request = DeleteRecordSetsRequest() + request.zone_id = self.zone_id[domain + '.'] + request.recordset_id = record + response = self.client.delete_record_sets(request) + result = json.loads(str(response)) + print(result) + return result + + def get_record(self, domain, length, sub_domain, record_type): + request = ListRecordSetsWithLineRequest() + request.limit = length + request.type = record_type + if sub_domain == '@': + request.name = domain + "." + else: + request.name = sub_domain + '.' + domain + "." + response = self.client.list_record_sets_with_line(request) + data = json.loads(str(response)) + result = {} + records_temp = [] + for record in data['recordsets']: + if (sub_domain == '@' and domain + "." == record['name']) or (sub_domain + '.' + domain + "." == record['name']): + record['line'] = self.line_format(record['line']) + record['value'] = '1.1.1.1' + records_temp.append(record) + result['data'] = {'records': records_temp} + return result + + def create_record(self, domain, sub_domain, value, record_type, line, ttl): + request = CreateRecordSetWithLineRequest() + request.zone_id = self.zone_id[domain + '.'] + if sub_domain == '@': + name = domain + "." + else: + name = sub_domain + '.' + domain + "." + request.body = CreateRecordSetWithLineReq( + type = record_type, + name = name, + ttl = ttl, + weight = 1, + records = [value], + line = self.line_format(line) + ) + response = self.client.create_record_set_with_line(request) + result = json.loads(str(response)) + return result + + def change_record(self, domain, record_id, sub_domain, value, record_type, line, ttl): + request = UpdateRecordSetRequest() + request.zone_id = self.zone_id[domain + '.'] + request.recordset_id = record_id + if sub_domain == '@': + name = domain + "." + else: + name = sub_domain + '.' + domain + "." + request.body = UpdateRecordSetReq( + name = name, + type = record_type, + ttl = ttl, + records=[value] + ) + response = self.client.update_record_set(request) + result = json.loads(str(response)) + return result + + def get_zones(self): + request = ListPublicZonesRequest() + response = self.client.list_public_zones(request) + result = json.loads(str(response)) + zone_id = {} + for zone in result['zones']: + zone_id[zone['name']] = zone['id'] + return zone_id + + def line_format(self, line): + lines = { + '默认' : 'default_view', + '电信' : 'Dianxin', + '联通' : 'Liantong', + '移动' : 'Yidong', + '境外' : 'Abroad', + 'default_view' : '默认', + 'Dianxin' : '电信', + 'Liantong' : '联通', + 'Yidong' : '移动', + 'Abroad' : '境外', + } + return lines.get(line, None) + +if __name__ == '__main__': + hw_api = HuaWeiApi('WTTCWxxxxxxxxx84O0V', 'GXkG6D4X1Nxxxxxxxxxxxxxxxxxxxxx4lRg6lT') + print(hw_api.get_record('xxxx.com', 100, '@', 'A')) diff --git a/dns/nameSilo.py b/dns/nameSilo.py new file mode 100644 index 0000000..91e0fb6 --- /dev/null +++ b/dns/nameSilo.py @@ -0,0 +1,60 @@ +import logging +import copy +import sys +import json + +import requests + + +class NameSiloClient: + + + def __init__(self, key) -> None: + self._api_key = key + + + def get_record(self, domain): + + try: + url = f"https://www.namesilo.com/api/dnsListRecords?version=1&type=json&key={self._api_key}&domain={domain}" + response = requests.get(url) + if response.status_code == 200: + return response.json() + else: + return None + except Exception as e: + return None + + def delete_record(self, domain,recordId): + + try: + url = f"https://www.namesilo.com/api/dnsDeleteRecord?version=1&type=json&key={self._api_key}&domain={domain}&rrid={recordId}" + response = requests.get(url) + if response.status_code == 200: + return response.json() + else: + return None + except Exception as e: + return None + + def create_record(self, domain, sub_domain, value, record_type, ttl): + try: + 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}" + response = requests.get(url) + if response.status_code == 200: + return response.json() + else: + return None + except Exception as e: + return None + + def change_record(self, domain, record_id, sub_domain, value, ttl): + try: + 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}" + response = requests.get(url) + if response.status_code == 200: + return response.json() + else: + return None + except Exception as e: + return None \ No newline at end of file diff --git a/dns/qCloud.py b/dns/qCloud.py new file mode 100644 index 0000000..d872013 --- /dev/null +++ b/dns/qCloud.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Mail: tongdongdong@outlook.com +# Reference: https://cloud.tencent.com/document/product/302/8517 +# QcloudApiv3 DNSPod 的 API 更新了 By github@z0z0r4 + +import json +from tencentcloud.common import credential +from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException +from tencentcloud.dnspod.v20210323 import dnspod_client, models + +class QcloudApiv3(): + def __init__(self, SECRETID, SECRETKEY): + self.SecretId = SECRETID + self.secretKey = SECRETKEY + self.cred = credential.Credential(SECRETID, SECRETKEY) + + def del_record(self, domain: str, record_id: int): + client = dnspod_client.DnspodClient(self.cred, "") + req_model = models.DeleteRecordRequest() + params = { + "Domain": domain, + "RecordId": record_id + } + req_model.from_json_string(json.dumps(params)) + + + resp = client.DeleteRecord(req_model) + resp = json.loads(resp.to_json_string()) + resp["code"] = 0 + resp["message"] = "None" + return resp + + def get_record(self, domain: str, length: int, sub_domain: str, record_type: str): + def format_record(record: dict): + new_record = {} + record["id"] = record['RecordId'] + for key in record: + new_record[key.lower()] = record[key] + return new_record + try: + client = dnspod_client.DnspodClient(self.cred, "") + + req_model = models.DescribeRecordListRequest() + params = { + "Domain": domain, + "Subdomain": sub_domain, + "RecordType": record_type, + "Limit": length + } + req_model.from_json_string(json.dumps(params)) + + + resp = client.DescribeRecordList(req_model) + resp = json.loads(resp.to_json_string()) + temp_resp = {} + temp_resp["code"] = 0 + temp_resp["data"] = {} + temp_resp["data"]["records"] = [] + for record in resp['RecordList']: + temp_resp["data"]["records"].append(format_record(record)) + temp_resp["data"]["domain"] = {} + temp_resp["data"]["domain"]["grade"] = self.get_domain(domain)["DomainInfo"]["Grade"] # DP_Free + return temp_resp + except TencentCloudSDKException: + # 构造空响应... + temp_resp = {} + temp_resp["code"] = 0 + temp_resp["data"] = {} + temp_resp["data"]["records"] = [] + temp_resp["data"]["domain"] = {} + temp_resp["data"]["domain"]["grade"] = self.get_domain(domain)["DomainInfo"]["Grade"] # DP_Free + return temp_resp + + def create_record(self, domain: str, sub_domain: str, value: int, record_type: str = "A", line: str = "默认", ttl: int = 600): + client = dnspod_client.DnspodClient(self.cred, "") + req = models.CreateRecordRequest() + params = { + "Domain": domain, + "SubDomain": sub_domain, + "RecordType": record_type, + "RecordLine": line, + "Value": value, + "ttl": ttl + } + req.from_json_string(json.dumps(params)) + + # 返回的resp是一个CreateRecordResponse的实例,与请求对象对应 + resp = client.CreateRecord(req) + resp = json.loads(resp.to_json_string()) + resp["code"] = 0 + resp["message"] = "None" + return resp + + def change_record(self, domain: str, record_id: int, sub_domain: str, value: str, record_type: str = "A", line: str = "默认", ttl: int = 600): + client = dnspod_client.DnspodClient(self.cred, "") + req = models.ModifyRecordRequest() + params = { + "Domain": domain, + "SubDomain": sub_domain, + "RecordType": record_type, + "RecordLine": line, + "Value": value, + "TTL": ttl, + "RecordId": record_id + } + req.from_json_string(json.dumps(params)) + + # 返回的resp是一个ChangeRecordResponse的实例,与请求对象对应 + resp = client.ModifyRecord(req) + resp = json.loads(resp.to_json_string()) + resp["code"] = 0 + resp["message"] = "None" + return resp + + def get_domain(self, domain: str): + client = dnspod_client.DnspodClient(self.cred, "") + + # 实例化一个请求对象,每个接口都会对应一个request对象 + req = models.DescribeDomainRequest() + params = { + "Domain": domain + } + req.from_json_string(json.dumps(params)) + + # 返回的resp是一个DescribeDomainResponse的实例,与请求对象对应 + resp = client.DescribeDomain(req) + resp = json.loads(resp.to_json_string()) + return resp \ No newline at end of file diff --git a/log.py b/log.py new file mode 100644 index 0000000..acb3fcd --- /dev/null +++ b/log.py @@ -0,0 +1,38 @@ +import logging +from logging import handlers + +class Logger(object): + level_relations = { + 'debug':logging.DEBUG, + 'info':logging.INFO, + 'warning':logging.WARNING, + 'error':logging.ERROR, + 'crit':logging.CRITICAL + }#日志级别关系映射 + + def __init__(self,filename,level='info',when='D',backCount=3,fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'): + self.logger = logging.getLogger(filename) + format_str = logging.Formatter(fmt)#设置日志格式 + self.logger.setLevel(self.level_relations.get(level))#设置日志级别 + sh = logging.StreamHandler()#往屏幕上输出 + sh.setFormatter(format_str) #设置屏幕上显示的格式 + th = handlers.TimedRotatingFileHandler(filename=filename,when=when,backupCount=backCount,encoding='utf-8')#往文件里写入#指定间隔时间自动生成文件的处理器 + #实例化TimedRotatingFileHandler + #interval是时间间隔,backupCount是备份文件的个数,如果超过这个个数,就会自动删除,when是间隔的时间单位,单位有以下几种: + # S 秒 + # M 分 + # H 小时、 + # D 天、 + # W 每星期(interval==0时代表星期一) + # midnight 每天凌晨 + th.setFormatter(format_str)#设置文件里写入的格式 + self.logger.addHandler(sh) #把对象加到logger里 + self.logger.addHandler(th) +if __name__ == '__main__': + log = Logger('monitor.log',level='debug') + log.logger.debug('debug') + log.logger.info('info') + log.logger.warning('警告') + log.logger.error('报错') + log.logger.critical('严重') + Logger('error.log', level='error').logger.error('error') \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a683288 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +aliyun-python-sdk-alidns==2.6.19 +aliyun-python-sdk-core==2.13.29 +huaweicloudsdkcore==3.1.5 +huaweicloudsdkdns==3.1.5 +requests==2.28.1 +tencentcloud-sdk-python==3.0.806 \ No newline at end of file diff --git a/start.py b/start.py new file mode 100644 index 0000000..85fb71c --- /dev/null +++ b/start.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import random +import time +import requests +from dns.qCloud import QcloudApiv3 # QcloudApiv3 DNSPod 的 API 更新了 By github@z0z0r4 +from dns.aliyun import AliApi +from dns.huawei import HuaWeiApi +from dns.nameSilo import NameSiloClient +from log import Logger +import traceback + + +#CM:移动 CU:联通 CT:电信 AB:境外 DEF:默认 +#修改需要更改的dnspod域名和子域名 +DOMAINS = { + "quanxxxhost.com": {"@": ["CM","CU","CT"]} +} + +#解析生效条数 +#免费的DNSPod相同线路最多支持2条解析 +AFFECT_NUM = 2 + +#DNS解析服务商 DNSPod-1 阿里云-2 如华为云-3 NameSilo-4 +DNS_SERVER = 4 + +#如果使用华为云解析 需要从API凭证-项目列表中获取 +REGION_HW = 'cn-east-3' + +#如果使用阿里云解析 REGION出现错误再修改 默认不需要修改 https://help.aliyun.com/document_detail/198326.html +REGION_ALI = 'cn-hongkong' + +#解析生效时间,默认为600秒 如果不是DNS付费版用户 不要修改!!! +#如果是NameSilo解析最小为3600 +TTL = 600 + +#v4为筛选出IPv4的IP v6为筛选出IPv6的IP +#目前仅支持v4 +TYPE = 'v4' + +#API 密钥 +#腾讯云后台获取 https://console.cloud.tencent.com/cam/capi +#阿里云后台获取 https://help.aliyun.com/document_detail/53045.html?spm=a2c4g.11186623.2.11.2c6a2fbdh13O53 注意需要添加DNS控制权限 AliyunDNSFullAccess +#华为云后台获取 https://support.huaweicloud.com/devg-apisign/api-sign-provide-aksk.html +#NameSilo后台获取 https://www.namesilo.com/account/api-manager NameSilo只用填SECRETKEY即可 +SECRETID = 'LTAI5xxxxxxxxxYWRedp9Y' +SECRETKEY = '7288bxxxxxxxxx23a332' + +log_cf2dns = Logger('cf2dns.log', level='debug') + +def get_optimization_ip(): + try: + headers = headers = {'Content-Type': 'application/json'} + data = {} + response = requests.post('https://vps789.com/public/sum/cfIpApi', json=data, headers=headers) + if response.status_code == 200: + return response.json() + else: + log_cf2dns.logger.error("CHANGE OPTIMIZATION IP ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: REQUEST STATUS CODE IS NOT 200") + return None + except Exception as e: + log_cf2dns.logger.error("CHANGE OPTIMIZATION IP ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: " + str(e)) + return None + +#s_info已存在的ip,c_info 最新的优选cfip +def changeDNS(line, s_info, c_info, domain, sub_domain, cloud): + global AFFECT_NUM, TYPE + if TYPE == 'v6': + recordType = "AAAA" + else: + recordType = "A" + + lines = {"CM": "移动", "CU": "联通", "CT": "电信", "AB": "境外", "DEF": "默认"} + line = lines[line] + + try: + create_num = AFFECT_NUM - len(s_info) + # 如果线路能解析的ip已满,就用cfip一个一个去替换原来的ip解析 + if create_num == 0: + for info in s_info: + if len(c_info) == 0: + break + cf_ip = c_info.pop(random.randint(0,len(c_info)-1))["ip"] + if cf_ip in str(s_info): + continue + ret = cloud.change_record(domain, info["recordId"], sub_domain, cf_ip, recordType, line, TTL) + if(DNS_SERVER != 1 or ret["code"] == 0): + 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 ) + else: + 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"] ) + # 如果线路还可以解析更多的ip,就添加cfip解析,直到用完 + elif create_num > 0: + for i in range(create_num): + if len(c_info) == 0: + break + cf_ip = c_info.pop(random.randint(0,len(c_info)-1))["ip"] + if cf_ip in str(s_info): + continue + ret = cloud.create_record(domain, sub_domain, cf_ip, recordType, line, TTL) + if(DNS_SERVER != 1 or ret["code"] == 0): + 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 ) + else: + 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"] ) + # 如果线路能解析的ip已超出配额,就用cfip一个一个去替换原来的ip解析 + else: + for info in s_info: + if create_num == 0 or len(c_info) == 0: + break + cf_ip = c_info.pop(random.randint(0,len(c_info)-1))["ip"] + if cf_ip in str(s_info): + create_num += 1 + continue + ret = cloud.change_record(domain, info["recordId"], sub_domain, cf_ip, recordType, line, TTL) + if(DNS_SERVER != 1 or ret["code"] == 0): + 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 ) + else: + 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"] ) + create_num += 1 + except Exception as e: + log_cf2dns.logger.error("CHANGE DNS ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: " + str(e)) + +def main(cloud): + global AFFECT_NUM, TYPE + if TYPE == 'v6': + recordType = "AAAA" + else: + recordType = "A" + if len(DOMAINS) > 0: + try: + cfips = get_optimization_ip() + if cfips == None or cfips["code"] != 0: + log_cf2dns.logger.error("GET CLOUDFLARE IP ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: " + str(cfips["data"])) + return + cf_cmips = cfips["data"]["CM"] + cf_cuips = cfips["data"]["CU"] + cf_ctips = cfips["data"]["CT"] + for domain, sub_domains in DOMAINS.items(): + for sub_domain, lines in sub_domains.items(): + #下面5个数组存的是不同线路最新获取的优选IP列表 + temp_cf_cmips = cf_cmips.copy() + temp_cf_cuips = cf_cuips.copy() + temp_cf_ctips = cf_ctips.copy() + temp_cf_abips = cf_ctips.copy() + temp_cf_defips = cf_ctips.copy() + if DNS_SERVER == 1: + ret = cloud.get_record(domain, 20, sub_domain, "CNAME") + if ret["code"] == 0: + for record in ret["data"]["records"]: + if record["line"] == "移动" or record["line"] == "联通" or record["line"] == "电信": + retMsg = cloud.del_record(domain, record["id"]) + if(retMsg["code"] == 0): + 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"] ) + else: + 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"] ) + ret = cloud.get_record(domain, 100, sub_domain, recordType) + # ret["code"] == 0 就是DNS_SERVER == 1 + if DNS_SERVER != 1 or ret["code"] == 0 : + if DNS_SERVER == 1 and "Free" in ret["data"]["domain"]["grade"] and AFFECT_NUM > 2: + AFFECT_NUM = 2 + #下面5个数组存的是已存在的DNS解析列表 + cm_info = [] + cu_info = [] + ct_info = [] + ab_info = [] + def_info = [] + for record in ret["data"]["records"]: + info = {} + info["recordId"] = record["id"] + info["value"] = record["value"] + if record["line"] == "移动": + cm_info.append(info) + elif record["line"] == "联通": + cu_info.append(info) + elif record["line"] == "电信": + ct_info.append(info) + elif record["line"] == "境外": + ab_info.append(info) + elif record["line"] == "默认": + def_info.append(info) + for line in lines: + if line == "CM": + changeDNS("CM", cm_info, temp_cf_cmips, domain, sub_domain, cloud) + elif line == "CU": + changeDNS("CU", cu_info, temp_cf_cuips, domain, sub_domain, cloud) + elif line == "CT": + changeDNS("CT", ct_info, temp_cf_ctips, domain, sub_domain, cloud) + elif line == "AB": + changeDNS("AB", ab_info, temp_cf_abips, domain, sub_domain, cloud) + elif line == "DEF": + changeDNS("DEF", def_info, temp_cf_defips, domain, sub_domain, cloud) + except Exception as e: + traceback.print_exc() + log_cf2dns.logger.error("CHANGE DNS ERROR: ----Time: " + str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "----MESSAGE: " + str(e)) + + +if __name__ == '__main__': + if DNS_SERVER == 1: + cloud = QcloudApiv3(SECRETID, SECRETKEY) + elif DNS_SERVER == 2: + cloud = AliApi(SECRETID, SECRETKEY, REGION_ALI) + elif DNS_SERVER == 3: + cloud = HuaWeiApi(SECRETID, SECRETKEY, REGION_HW) + elif DNS_SERVER == 4: + cloud = NameSiloClient(SECRETKEY) + main(cloud) \ No newline at end of file