阿里ddns还是需要升级,自己部署的NAS不能用咋办

适用范围:NAS、家庭服务器、专业服务器。

主要修订的内容为优化了获取IPv4与IPV6地址,部分接口受限造成的获取失败。因此建立了一个IP池这样随机轮询,减少被封的概率。

从新设计了IPv4与IPV6逻辑实现了复用。

当然还是支持IPv4与IPV6的。单文件版本,打包容易。

打包命令如下:首先打开cmd,然后cd到代码地址,然后运行这个命令。

pyinstaller --onefile --hidden-import=queue aliddns.py

#!/usr/bin/env python3
# 实现了阿里云ipv4与ipv6同时维护并且不会覆盖。
# 增强了多接口随地检测地址,防止端口超限制。
import json
import logging
import os
import sys
import socket
from urllib.request import urlopen
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ServerException, ClientException
from aliyunsdkalidns.request.v20150109 import (
    DescribeSubDomainRecordsRequest,
    AddDomainRecordRequest,
    UpdateDomainRecordRequest,
    DeleteDomainRecordRequest
)

# 配置日志
def get_log_path():
    if getattr(sys, 'frozen', False):
        # 如果是打包后的可执行文件
        base_dir = os.path.dirname(sys.executable)
    else:
        # 如果是脚本运行
        base_dir = os.path.dirname(os.path.abspath(__file__))
    return os.path.join(base_dir, 'dns_controller.log')

# 配置日志
log_file = get_log_path()
logging.basicConfig(filename=log_file, level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')

class DnsController:
    def __init__(self):
        # 读取配置文件或环境变量
        self.access_key_id = os.getenv('ALIYUN_ACCESS_KEY_ID', "你自己的")
        self.access_key_secret = os.getenv('ALIYUN_ACCESS_KEY_SECRET', "你自己的")
        self.region = "cn-shanghai"
        self.domain = "你自己的.xyz"
        self.name_ipv4 = ["你自己的二级", "你自己的二级"]
        self.name_ipv6 = ["你自己的二级", "你自己的二级"]
        self.record_type_ipv4 = "A"
        self.record_type_ipv6 = "AAAA"
        
        # IPv4 API列表
        self.ipv4_api_list = [
            ('https://api.ipify.org?format=json', 'ip'),
            ('https://httpbin.org/ip', 'origin'),
            ('https://v4.ident.me/.json', 'address'),
            ('http://ip-api.com/json/?fields=query', 'query')
        ]
        
        # IPv6 API列表
        self.ipv6_api_list = [
            ('https://ipv6.jsonip.com/', 'ip'),
            ('https://v6.ident.me/.json', 'address'),
            ('https://ifconfig.me/all.json', 'ip_addr'),
            ('https://wtfismyip.com/json', 'YourFuckingIPAddress')
        ]
        
        self.timeout = 10  # 请求超时时间(秒)
        self.client = AcsClient(
            self.access_key_id,
            self.access_key_secret,
            self.region
        )

    def _validate_ipv4(self, ip):
        """简单验证IPv4格式"""
        parts = ip.split('.')
        if len(parts) != 4:
            return False
        return all(part.isdigit() and 0 <= int(part) <= 255 for part in parts)

    def _validate_ipv6(self, ip):
        """简单验证IPv6格式"""
        try:
            socket.inet_pton(socket.AF_INET6, ip)
            return True
        except socket.error:
            return False

    def _get_ip(self, api_list, ip_type='ipv4'):
        """通过多个API获取IP地址,自动故障转移"""
        errors = []
        for url, key in api_list:
            try:
                with urlopen(url, timeout=self.timeout) as response:
                    data = json.loads(response.read().decode())
                
                # 处理特殊响应格式
                if key == 'origin':  # httpbin.org
                    ip = data[key].split(', ')[0]
                else:
                    ip = data[key]
                
                # 验证IP
                if (ip_type == 'ipv4' and self._validate_ipv4(ip)) or \
                   (ip_type == 'ipv6' and self._validate_ipv6(ip)):
                    logging.info(f"成功通过 {url} 获取{ip_type.upper()}地址: {ip}")
                    return ip
                
            except Exception as e:
                error_message = f"API {url} 请求失败: {str(e)}"
                logging.warning(error_message)
                errors.append(error_message)
                continue
        
        raise RuntimeError(f"所有{ip_type.upper()}查询API均不可用\n" + "\n".join(errors))

    def add(self, DomainName, RR, Type, Value):
        """添加新的域名解析记录"""
        try:
            request = AddDomainRecordRequest.AddDomainRecordRequest()
            request.set_accept_format('json')
            request.set_DomainName(DomainName)
            request.set_RR(RR)
            request.set_Type(Type)
            request.set_Value(Value)
            response = self.client.do_action_with_exception(request)
            logging.info(f"成功添加记录: {RR}.{DomainName} ({Type}) -> {Value}")
            return True
        except Exception as e:
            logging.error(f"添加域名解析记录失败: {e}", exc_info=True)
            return False

    def update(self, RecordId, RR, Type, Value):
        """修改域名解析记录"""
        try:
            request = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
            request.set_accept_format('json')
            request.set_RecordId(RecordId)
            request.set_RR(RR)
            request.set_Type(Type)
            request.set_Value(Value)
            response = self.client.do_action_with_exception(request)
            logging.info(f"成功更新记录: {RR}.{self.domain} ({Type}) -> {Value}")
            return True
        except Exception as e:
            logging.error(f"修改域名解析记录失败: {e}", exc_info=True)
            return False

    def delete(self, RecordId):
        """删除域名解析记录"""
        try:
            delete_request = DeleteDomainRecordRequest.DeleteDomainRecordRequest()
            delete_request.set_accept_format('json')
            delete_request.set_RecordId(RecordId)
            self.client.do_action_with_exception(delete_request)
            logging.info(f"成功删除记录: {RecordId}")
            return True
        except Exception as e:
            logging.error(f"删除域名解析记录失败: {e}", exc_info=True)
            return False

    def process_dns_records(self, ip_type='ipv4'):
        """处理DNS记录的主逻辑"""
        try:
            # 获取IP地址
            ip = self._get_ip(getattr(self, f'{ip_type}_api_list'), ip_type)
            record_type = getattr(self, f'record_type_{ip_type}')
            subdomains = getattr(self, f'name_{ip_type}')

            print(f"当前公网{ip_type.upper()}地址: {ip}")

            # 初始化请求对象
            request = DescribeSubDomainRecordsRequest.DescribeSubDomainRecordsRequest()
            request.set_accept_format('json')
            request.set_DomainName(self.domain)

            # 处理每个子域名
            for subdomain in subdomains:
                full_subdomain = f"{subdomain}.{self.domain}"
                print(f"正在处理子域名: {full_subdomain}")
                
                # 查询记录
                request.set_SubDomain(full_subdomain)
                response = self.client.do_action_with_exception(request)
                records = json.loads(response)

                # 记录处理逻辑
                if records['TotalCount'] == 0:
                    self.add(self.domain, subdomain, record_type, ip)
                    print(f"创建新记录: {full_subdomain} → {ip}")
                else:
                    type_records = [
                        r for r in records['DomainRecords']['Record']
                        if r['Type'] == record_type
                    ]

                    if not type_records:
                        self.add(self.domain, subdomain, record_type, ip)
                        print(f"添加{record_type}记录: {full_subdomain} → {ip}")
                    else:
                        need_update = False
                        for record in type_records:
                            if record['Value'].strip() != ip:
                                self.update(record['RecordId'], subdomain, record_type, ip)
                                print(f"更新记录: {full_subdomain} ({record['RecordId']}) → {ip}")
                                need_update = True
                        
                        if not need_update:
                            print(f"记录无需更新: {full_subdomain}")

        except (ServerException, ClientException) as e:
            logging.error(f"阿里云API错误: {e.get_error_code()} - {e.get_error_msg()}")
            print(f"阿里云API错误: {e.get_error_code()} - {e.get_error_msg()}")
        except Exception as e:
            logging.error(f"处理过程中发生未预期错误: {str(e)}", exc_info=True)
            print(f"处理过程中发生未预期错误: {str(e)}")

if __name__ == "__main__":
    controller = DnsController()
    controller.process_dns_records('ipv4')
    controller.process_dns_records('ipv6')



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值