通过一个域名,借助IPV6免费远程访问自己家里的设备

方案已实现,可抄作业

前提:

  • 有一个域名(我的是腾讯云的)
  • 毕竟说的再多,也需要有一个可以远程跳转的地方

实现思路:

  • 借助IPV6,可以实现短暂的公网访问
  • 通过代码自动配置腾讯云的域名解析,实现持久的访问

注意:此方案仅用于个人远程访问家用设备,不可用于网站公开访问,不合法合规。

问:获取到IPV6了却在公网无法访问怎么办?

答:进行光猫转桥接

问:不会光猫转桥接怎么办

答:淘宝下单找人帮你搞,几十块钱

好了,现在已经能够在公网进行访问了,下一步,添加代码可以控制的云解析ID和key

右上角,用户信息里面,访问管理

左边,API秘钥管理,创建,保存

然后就是把ID和秘钥粘贴到代码最下面的秘钥的位置,运行,就可以了。

代码如下:

python 复制代码
import requests
import json
import time
import socket
import logging
import os
from typing import Optional, List
from logging.handlers import TimedRotatingFileHandler
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.dnspod.v20210323 import dnspod_client, models

# -------------------------- 日志配置(按天分割+指定目录)--------------------------


def init_logger():
    # 创建log文件夹
    log_dir = "log"
    os.makedirs(log_dir, exist_ok=True)

    # 文件日志处理器
    log_file = os.path.join(log_dir, "ddns.log")
    file_handler = TimedRotatingFileHandler(
        filename=log_file,
        when='D',
        interval=1,
        backupCount=30,
        encoding='utf-8'
    )
    file_handler.suffix = "%Y-%m-%d"

    # 控制台日志处理器
    console_handler = logging.StreamHandler()

    # 日志格式
    log_format = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    file_handler.setFormatter(log_format)
    console_handler.setFormatter(log_format)

    # 初始化日志器
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)

    return logger


# 初始化日志
logger = init_logger()


class TencentCloudDDNS:
    def __init__(self, secret_id: str, secret_key: str, domain: str):
        """初始化腾讯云DDNS客户端"""
        self.secret_id = secret_id
        self.secret_key = secret_key
        self.domain = domain
        self.last_ipv6 = None

        # 初始化SDK客户端
        cred = credential.Credential(secret_id, secret_key)
        httpProfile = HttpProfile()
        httpProfile.endpoint = "dnspod.tencentcloudapi.com"

        clientProfile = ClientProfile()
        clientProfile.httpProfile = httpProfile
        self.client = dnspod_client.DnspodClient(cred, "", clientProfile)

    def get_public_ipv6(self) -> Optional[str]:
        """获取本地公网IPv6地址"""
        try:
            # 方法1: 通过icanhazip获取
            response = requests.get('https://icanhazip.com', timeout=5)
            ipv6 = response.text.strip()

            # 验证IPv6格式
            try:
                socket.inet_pton(socket.AF_INET6, ipv6)
                return ipv6
            except socket.error:
                logger.warning(f"获取的IPv6格式无效: {ipv6},尝试备用接口")

                # 方法2: 通过ipify获取
                response = requests.get('https://api64.ipify.org', timeout=5)
                ipv6 = response.text.strip()
                socket.inet_pton(socket.AF_INET6, ipv6)
                return ipv6
        except socket.error as e:
            logger.error(f"获取的IPv6格式无效: {e}")
            return None
        except requests.exceptions.RequestException as e:
            logger.error(f"网络请求失败: {e}")
            return None
        except Exception as e:
            logger.error(f"获取IPv6地址失败: {str(e)}")
            return None

    def get_all_aaaa_records(self) -> List[dict]:
        """获取域名的所有AAAA记录"""
        try:
            req = models.DescribeRecordListRequest()
            params = {
                "Domain": self.domain,
                "RecordType": "AAAA"
            }
            req.from_json_string(json.dumps(params))

            resp = self.client.DescribeRecordList(req)
            records = json.loads(resp.to_json_string()).get("RecordList", [])
            logger.info(f"获取到 {len(records)} 条AAAA记录")
            return records
        except Exception as e:
            logger.error(f"获取AAAA记录失败: {str(e)}")
            return []

    def update_record(self, sub_domain: str, record_id: int, ipv6: str) -> bool:
        """更新单条域名记录"""
        try:
            req = models.ModifyRecordRequest()
            params = {
                "Domain": self.domain,
                "RecordId": record_id,
                "SubDomain": sub_domain,
                "RecordType": "AAAA",
                "RecordLine": "默认",
                "Value": ipv6,
                "TTL": 600
            }
            req.from_json_string(json.dumps(params))

            self.client.ModifyRecord(req)
            logger.info(f"成功更新记录: {sub_domain}.{self.domain} -> {ipv6}")
            return True
        except Exception as e:
            logger.error(f"更新记录 {sub_domain}.{self.domain} 失败: {str(e)}")
            return False

    def check_and_update(self) -> bool:
        """检查IPv6变化并更新"""
        current_ipv6 = self.get_public_ipv6()
        if not current_ipv6:
            logger.error("无法获取有效公网IPv6地址,跳过本次检查")
            return False

        logger.info(f"当前公网IPv6地址: {current_ipv6}")

        if self.last_ipv6 and current_ipv6 == self.last_ipv6:
            logger.info("IPv6地址未变化,无需更新")
            return True

        records = self.get_all_aaaa_records()
        if not records:
            logger.error("未获取到任何AAAA记录,无法更新")
            return False

        update_results = []
        for record in records:
            sub_domain = record.get("Name")
            record_id = record.get("RecordId")

            if not sub_domain or record_id is None:
                logger.warning(f"跳过无效记录: {record}")
                continue

            try:
                record_id = int(record_id)
            except (ValueError, TypeError):
                logger.warning(f"无效的 RecordId: {record_id},跳过该记录")
                continue

            logger.info(
                f"开始处理记录: {sub_domain}.{self.domain} (ID: {record_id})")
            result = self.update_record(sub_domain, record_id, current_ipv6)
            update_results.append(result)

        if all(update_results):
            logger.info("所有AAAA记录更新成功,同步last_ipv6记录")
            self.last_ipv6 = current_ipv6
            return True
        else:
            logger.error("部分/全部记录更新失败,保留原last_ipv6记录(下次将重试)")
            return False

    def monitor(self, interval: int = 60) -> None:
        """持续监控"""
        logger.info(f"DDNS监控启动,检查间隔: {interval}秒")

        # 首次运行立即检查
        if not self.check_and_update():
            logger.warning("首次检查更新失败,将在下次间隔后重试")

        # 循环监控
        try:
            while True:
                time.sleep(interval)
                self.check_and_update()
        except KeyboardInterrupt:
            logger.info("程序被用户手动中断")
        except Exception as e:
            logger.error(f"监控循环异常终止: {str(e)}")


if __name__ == "__main__":
    # -------------------------- 配置信息(请替换为你的实际信息)--------------------------
    SECRET_ID = ""  # 你的腾讯云SECRET_ID
    SECRET_KEY = ""    # 你的腾讯云SECRET_KEY
    DOMAIN = ""                                # 你的主域名
    CHECK_INTERVAL = 60                                 # 检查间隔(秒)

    # 启动DDNS监控
    ddns_client = TencentCloudDDNS(SECRET_ID, SECRET_KEY, DOMAIN)
    ddns_client.monitor(interval=CHECK_INTERVAL)
相关推荐
鹏程十八少几秒前
破解Android悬浮窗遮挡无障碍服务难题:我在可见即可说上踩过的坑
android·前端·面试
@zulnger几秒前
python 学习笔记(闭包)
笔记·python·学习
SHolmes18542 分钟前
Python all函数 判断是否同时满足多个条件
java·服务器·python
inksci2 分钟前
Python 中使用 SQL 连接池
服务器·数据库·python
Kapaseker4 分钟前
前端已死...了吗
android·前端·javascript
子午6 分钟前
【2026原创】中草药识别系统实现~Python+深度学习+模型训练+人工智能
人工智能·python·深度学习
洛克大航海7 分钟前
Python 在系统 Windows 和 Ubuntu 中创建虚拟环境
windows·python·ubuntu·虚拟环境
m0_471199638 分钟前
【自动化】前端开发,如何将 Jenkins 与 Gitee 结合实现自动化的持续集成(构建)和持续部署(发布)
前端·gitee·自动化·jenkins
ZEERO~9 分钟前
@dataclass的作用
开发语言·windows·python
w***95499 分钟前
spring-boot-starter和spring-boot-starter-web的关联
前端