From 7c67c07a0dff70f9b4aee6cc8f3cb919853aa781 Mon Sep 17 00:00:00 2001 From: sinspired Date: Thu, 5 Dec 2024 14:33:14 +0800 Subject: [PATCH] =?UTF-8?q?pref:=20=E4=BC=98=E5=8C=96=E5=BB=B6=E8=BF=9F?= =?UTF-8?q?=E6=B5=8B=E9=80=9F=EF=BC=8C=E5=A4=A7=E5=B9=85=E6=8F=90=E5=8D=87?= =?UTF-8?q?=E6=80=A7=E8=83=BD=EF=BC=9Bconfig:=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=9B=B4=E5=A4=9ADNS=E6=9C=8D=E5=8A=A1=E5=99=A8=EF=BC=8C?= =?UTF-8?q?=E5=89=94=E9=99=A4=E8=A7=A3=E6=9E=90=E7=BB=93=E6=9E=9C=E8=BE=83?= =?UTF-8?q?=E5=B7=AE=E7=9A=84=E6=9C=8D=E5=8A=A1=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 114 +++++++-- hosts | 120 ++++++--- setHosts_Classic.py | 577 +++++++++++++++++++++++++++++++++----------- 3 files changed, 611 insertions(+), 200 deletions(-) diff --git a/README.md b/README.md index cd6c91a..32d3a81 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,20 @@ ```bash +# cnNetTool Start in 2024-12-05 11:37:05 +08:00 +140.82.113.26 alive.github.com +20.205.243.168 api.github.com +140.82.113.22 central.github.com +20.205.243.165 codeload.github.com +140.82.113.22 collector.github.com +140.82.112.3 gist.github.com +20.205.243.166 github.com +140.82.113.18 github.community +151.101.129.194 github.global.ssl.fastly.net +2a03:2880:f131:83:face:b00c:0:25de github.global.ssl.fastly.net +16.182.37.73 github-com.s3.amazonaws.com +3.5.21.112 github-production-release-asset-2e65be.s3.amazonaws.com +140.82.112.25 live.github.com # cnNetTool Start in 2024-11-29 20:52:50 +08:00 140.82.112.26 alive.github.com 140.82.113.21 central.github.com @@ -41,56 +55,108 @@ 3.5.25.118 github-production-release-asset-2e65be.s3.amazonaws.com 140.82.114.25 live.github.com 13.107.42.16 pipelines.actions.githubusercontent.com -185.199.110.154 github.githubassets.com -185.199.108.153 github.io +185.199.108.154 github.githubassets.com 185.199.108.153 githubstatus.com +2606:50c0:8001::153 githubstatus.com 185.199.108.153 assets-cdn.github.com +2606:50c0:8001::153 assets-cdn.github.com +185.199.108.153 github.io +2606:50c0:8001::153 github.io 185.199.108.133 avatars.githubusercontent.com +2606:50c0:8002::154 avatars.githubusercontent.com 185.199.108.133 avatars0.githubusercontent.com +2606:50c0:8002::154 avatars0.githubusercontent.com 185.199.108.133 avatars1.githubusercontent.com +2606:50c0:8002::154 avatars1.githubusercontent.com 185.199.108.133 avatars2.githubusercontent.com +2606:50c0:8002::154 avatars2.githubusercontent.com 185.199.108.133 avatars3.githubusercontent.com +2606:50c0:8002::154 avatars3.githubusercontent.com 185.199.108.133 avatars4.githubusercontent.com +2606:50c0:8002::154 avatars4.githubusercontent.com 185.199.108.133 avatars5.githubusercontent.com +2606:50c0:8002::154 avatars5.githubusercontent.com 185.199.108.133 camo.githubusercontent.com +2606:50c0:8002::154 camo.githubusercontent.com 185.199.108.133 cloud.githubusercontent.com +2606:50c0:8002::154 cloud.githubusercontent.com 185.199.108.133 desktop.githubusercontent.com +2606:50c0:8002::154 desktop.githubusercontent.com 185.199.108.133 favicons.githubusercontent.com +2606:50c0:8002::154 favicons.githubusercontent.com 185.199.108.133 github.map.fastly.net +2606:50c0:8002::154 github.map.fastly.net 185.199.108.133 media.githubusercontent.com +2606:50c0:8002::154 media.githubusercontent.com 185.199.108.133 objects.githubusercontent.com +2606:50c0:8002::154 objects.githubusercontent.com 185.199.108.133 private-user-images.githubusercontent.com +2606:50c0:8002::154 private-user-images.githubusercontent.com 185.199.108.133 raw.githubusercontent.com +2606:50c0:8002::154 raw.githubusercontent.com 185.199.108.133 user-images.githubusercontent.com -99.84.108.50 tmdb.org -99.84.108.50 api.tmdb.org -99.84.108.50 files.tmdb.org -52.85.151.48 themoviedb.org -52.85.151.48 api.themoviedb.org -52.85.151.48 www.themoviedb.org -52.85.151.48 auth.themoviedb.org -52.94.225.248 imdb.com -52.94.228.167 secure.imdb.com -18.67.59.121 s.media-imdb.com +2606:50c0:8002::154 user-images.githubusercontent.com +54.230.71.49 tmdb.org +2600:9000:2014:8a00:5:da10:7440:93a1 tmdb.org +54.230.71.49 api.tmdb.org +2600:9000:2014:8a00:5:da10:7440:93a1 api.tmdb.org +54.230.71.49 files.tmdb.org +2600:9000:2014:8a00:5:da10:7440:93a1 files.tmdb.org +13.226.61.96 themoviedb.org +2600:9000:2816:5000:e:5373:440:93a1 themoviedb.org +13.226.61.96 api.themoviedb.org +2600:9000:2816:5000:e:5373:440:93a1 api.themoviedb.org +13.226.61.96 www.themoviedb.org +2600:9000:2816:5000:e:5373:440:93a1 www.themoviedb.org +13.226.61.96 auth.themoviedb.org +2600:9000:2816:5000:e:5373:440:93a1 auth.themoviedb.org +109.61.83.98 image.tmdb.org +2400:52e0:1500::1092:1 image.tmdb.org +109.61.83.98 images.tmdb.org +2400:52e0:1500::1092:1 images.tmdb.org +52.94.228.167 imdb.com +13.35.217.192 www.imdb.com +52.94.237.74 secure.imdb.com +18.172.46.173 s.media-imdb.com 52.94.225.248 us.dd.imdb.com -151.101.65.16 m.media-amazon.com +13.35.217.192 www.imdb.to +52.94.228.167 imdb-webservice.amazon.com +44.215.137.99 origin-www.imdb.com +23.49.104.166 m.media-amazon.com +2600:9000:2078:f800:1d:d7f6:39d4:e6e1 m.media-amazon.com 151.101.65.16 Images-na.ssl-images-amazon.com -13.32.177.205 images-fe.ssl-images-amazon.com -54.192.199.207 images-eu.ssl-images-amazon.com -172.217.31.174 translate.google.com -172.217.31.174 translate.googleapis.com -172.217.31.174 translate-pa.googleapis.com -13.32.151.9 plugins.jetbrains.com -13.32.151.9 download.jetbrains.com -13.32.151.9 cache-redirector.jetbrains.com - -# Update time: 2024-11-29 20:52:50 +08:00 +2600:1417:4400:12::1721:b8e7 Images-na.ssl-images-amazon.com +23.49.104.166 images-fe.ssl-images-amazon.com +2600:9000:2079:fa00:1d:d7f6:39d4:e6e1 images-fe.ssl-images-amazon.com +151.101.129.16 images-eu.ssl-images-amazon.com +2600:9000:2751:1a00:1d:d7f6:39d4:e6e1 images-eu.ssl-images-amazon.com +23.49.104.166 ia.media-imdb.com +2600:1417:4400:12::1721:b8e7 ia.media-imdb.com +151.101.129.16 f.media-amazon.com +2a04:4e42:48::272 f.media-amazon.com +13.35.186.103 imdb-video.media-imdb.com +13.32.32.190 dqpnq362acqdi.cloudfront.net +2600:9000:27af:7600:5:ce70:a180:21 dqpnq362acqdi.cloudfront.net +34.105.140.105 translate.google.com +2404:6800:4005:823::200a translate.google.com +34.105.140.105 translate.googleapis.com +2404:6800:4005:823::200a translate.googleapis.com +34.105.140.105 translate-pa.googleapis.com +2404:6800:4005:823::200a translate-pa.googleapis.com +13.33.183.20 plugins.jetbrains.com +2600:9000:2014:f600:12:7c44:15c0:93a1 plugins.jetbrains.com +13.33.183.20 download.jetbrains.com +2600:9000:2014:f600:12:7c44:15c0:93a1 download.jetbrains.com +13.33.183.20 cache-redirector.jetbrains.com +2600:9000:2014:f600:12:7c44:15c0:93a1 cache-redirector.jetbrains.com + +# Update time: 2024-12-05 11:37:05 +08:00 # GitHub仓库: https://github.com/sinspired/cnNetTool # cnNetTool End ``` -以上内容会自动定时更新, 数据更新时间:2024-11-29 20:52:50 +08:00 +以上内容会自动定时更新, 数据更新时间:2024-12-05 11:37:05 +08:00 #### 1.2.2 修改 hosts 文件 diff --git a/hosts b/hosts index 8dc7608..f903722 100644 --- a/hosts +++ b/hosts @@ -1,58 +1,114 @@ -# cnNetTool Start in 2024-11-29 20:52:50 +08:00 -140.82.112.26 alive.github.com -140.82.113.21 central.github.com +# cnNetTool Start in 2024-12-05 11:37:05 +08:00 +140.82.113.26 alive.github.com +20.205.243.168 api.github.com +140.82.113.22 central.github.com +20.205.243.165 codeload.github.com +140.82.113.22 collector.github.com 140.82.112.3 gist.github.com -140.82.113.3 github.com -140.82.114.18 github.community -151.101.193.194 github.global.ssl.fastly.net -52.217.197.113 github-com.s3.amazonaws.com -3.5.25.118 github-production-release-asset-2e65be.s3.amazonaws.com -140.82.114.25 live.github.com +20.205.243.166 github.com +140.82.113.18 github.community +151.101.129.194 github.global.ssl.fastly.net +2a03:2880:f131:83:face:b00c:0:25de github.global.ssl.fastly.net +16.182.37.73 github-com.s3.amazonaws.com +3.5.21.112 github-production-release-asset-2e65be.s3.amazonaws.com +140.82.112.25 live.github.com 13.107.42.16 pipelines.actions.githubusercontent.com -185.199.110.154 github.githubassets.com -185.199.108.153 github.io +185.199.108.154 github.githubassets.com 185.199.108.153 githubstatus.com +2606:50c0:8001::153 githubstatus.com 185.199.108.153 assets-cdn.github.com +2606:50c0:8001::153 assets-cdn.github.com +185.199.108.153 github.io +2606:50c0:8001::153 github.io 185.199.108.133 avatars.githubusercontent.com +2606:50c0:8002::154 avatars.githubusercontent.com 185.199.108.133 avatars0.githubusercontent.com +2606:50c0:8002::154 avatars0.githubusercontent.com 185.199.108.133 avatars1.githubusercontent.com +2606:50c0:8002::154 avatars1.githubusercontent.com 185.199.108.133 avatars2.githubusercontent.com +2606:50c0:8002::154 avatars2.githubusercontent.com 185.199.108.133 avatars3.githubusercontent.com +2606:50c0:8002::154 avatars3.githubusercontent.com 185.199.108.133 avatars4.githubusercontent.com +2606:50c0:8002::154 avatars4.githubusercontent.com 185.199.108.133 avatars5.githubusercontent.com +2606:50c0:8002::154 avatars5.githubusercontent.com 185.199.108.133 camo.githubusercontent.com +2606:50c0:8002::154 camo.githubusercontent.com 185.199.108.133 cloud.githubusercontent.com +2606:50c0:8002::154 cloud.githubusercontent.com 185.199.108.133 desktop.githubusercontent.com +2606:50c0:8002::154 desktop.githubusercontent.com 185.199.108.133 favicons.githubusercontent.com +2606:50c0:8002::154 favicons.githubusercontent.com 185.199.108.133 github.map.fastly.net +2606:50c0:8002::154 github.map.fastly.net 185.199.108.133 media.githubusercontent.com +2606:50c0:8002::154 media.githubusercontent.com 185.199.108.133 objects.githubusercontent.com +2606:50c0:8002::154 objects.githubusercontent.com 185.199.108.133 private-user-images.githubusercontent.com +2606:50c0:8002::154 private-user-images.githubusercontent.com 185.199.108.133 raw.githubusercontent.com +2606:50c0:8002::154 raw.githubusercontent.com 185.199.108.133 user-images.githubusercontent.com -99.84.108.50 tmdb.org -99.84.108.50 api.tmdb.org -99.84.108.50 files.tmdb.org -52.85.151.48 themoviedb.org -52.85.151.48 api.themoviedb.org -52.85.151.48 www.themoviedb.org -52.85.151.48 auth.themoviedb.org -52.94.225.248 imdb.com -52.94.228.167 secure.imdb.com -18.67.59.121 s.media-imdb.com +2606:50c0:8002::154 user-images.githubusercontent.com +54.230.71.49 tmdb.org +2600:9000:2014:8a00:5:da10:7440:93a1 tmdb.org +54.230.71.49 api.tmdb.org +2600:9000:2014:8a00:5:da10:7440:93a1 api.tmdb.org +54.230.71.49 files.tmdb.org +2600:9000:2014:8a00:5:da10:7440:93a1 files.tmdb.org +13.226.61.96 themoviedb.org +2600:9000:2816:5000:e:5373:440:93a1 themoviedb.org +13.226.61.96 api.themoviedb.org +2600:9000:2816:5000:e:5373:440:93a1 api.themoviedb.org +13.226.61.96 www.themoviedb.org +2600:9000:2816:5000:e:5373:440:93a1 www.themoviedb.org +13.226.61.96 auth.themoviedb.org +2600:9000:2816:5000:e:5373:440:93a1 auth.themoviedb.org +109.61.83.98 image.tmdb.org +2400:52e0:1500::1092:1 image.tmdb.org +109.61.83.98 images.tmdb.org +2400:52e0:1500::1092:1 images.tmdb.org +52.94.228.167 imdb.com +13.35.217.192 www.imdb.com +52.94.237.74 secure.imdb.com +18.172.46.173 s.media-imdb.com 52.94.225.248 us.dd.imdb.com -151.101.65.16 m.media-amazon.com +13.35.217.192 www.imdb.to +52.94.228.167 imdb-webservice.amazon.com +44.215.137.99 origin-www.imdb.com +23.49.104.166 m.media-amazon.com +2600:9000:2078:f800:1d:d7f6:39d4:e6e1 m.media-amazon.com 151.101.65.16 Images-na.ssl-images-amazon.com -13.32.177.205 images-fe.ssl-images-amazon.com -54.192.199.207 images-eu.ssl-images-amazon.com -172.217.31.174 translate.google.com -172.217.31.174 translate.googleapis.com -172.217.31.174 translate-pa.googleapis.com -13.32.151.9 plugins.jetbrains.com -13.32.151.9 download.jetbrains.com -13.32.151.9 cache-redirector.jetbrains.com +2600:1417:4400:12::1721:b8e7 Images-na.ssl-images-amazon.com +23.49.104.166 images-fe.ssl-images-amazon.com +2600:9000:2079:fa00:1d:d7f6:39d4:e6e1 images-fe.ssl-images-amazon.com +151.101.129.16 images-eu.ssl-images-amazon.com +2600:9000:2751:1a00:1d:d7f6:39d4:e6e1 images-eu.ssl-images-amazon.com +23.49.104.166 ia.media-imdb.com +2600:1417:4400:12::1721:b8e7 ia.media-imdb.com +151.101.129.16 f.media-amazon.com +2a04:4e42:48::272 f.media-amazon.com +13.35.186.103 imdb-video.media-imdb.com +13.32.32.190 dqpnq362acqdi.cloudfront.net +2600:9000:27af:7600:5:ce70:a180:21 dqpnq362acqdi.cloudfront.net +34.105.140.105 translate.google.com +2404:6800:4005:823::200a translate.google.com +34.105.140.105 translate.googleapis.com +2404:6800:4005:823::200a translate.googleapis.com +34.105.140.105 translate-pa.googleapis.com +2404:6800:4005:823::200a translate-pa.googleapis.com +13.33.183.20 plugins.jetbrains.com +2600:9000:2014:f600:12:7c44:15c0:93a1 plugins.jetbrains.com +13.33.183.20 download.jetbrains.com +2600:9000:2014:f600:12:7c44:15c0:93a1 download.jetbrains.com +13.33.183.20 cache-redirector.jetbrains.com +2600:9000:2014:f600:12:7c44:15c0:93a1 cache-redirector.jetbrains.com -# Update time: 2024-11-29 20:52:50 +08:00 -# GitHubֿ: https://github.com/sinspired/cnNetTool +# Update time: 2024-12-05 11:37:05 +08:00 +# GitHub�ֿ�: https://github.com/sinspired/cnNetTool # cnNetTool End diff --git a/setHosts_Classic.py b/setHosts_Classic.py index 23d672e..6ab7633 100644 --- a/setHosts_Classic.py +++ b/setHosts_Classic.py @@ -1,7 +1,9 @@ +from math import floor import os import ssl import sys from pathlib import Path +import concurrent import dns.resolver import json import shutil @@ -21,6 +23,8 @@ from rich.progress import Progress, SpinnerColumn, TextColumn import wcwidth +# if sys.platform == "win32": +# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # -------------------- 常量设置 -------------------- # RESOLVER_TIMEOUT = 0.1 # DNS 解析超时时间 秒 HOSTS_NUM = 1 # 每个域名限定Hosts主机 ipv4 数量 @@ -336,16 +340,20 @@ async def resolve_domain(self, domain: str) -> Set[str]: ipv6_ips = domain_hosts.get("ipv6", []) ips.update(ipv4_ips + ipv6_ips) - logging.debug(f"成功通过缓存文件解析 {domain}, 发现 {len( - ipv4_ips)+len(ipv6_ips)} 个 DNS 主机:\n{ipv4_ips}\n{ipv6_ips if ipv6_ips else ''}\n") + logging.debug( + f"成功通过缓存文件解析 {domain}, 发现 {len( + ipv4_ips)+len(ipv6_ips)} 个 DNS 主机:\n{ipv4_ips}\n{ipv6_ips if ipv6_ips else ''}\n" + ) else: ipaddress_ips = await self._resolve_via_ipaddress(domain) if ipaddress_ips: ips.update(ipaddress_ips) if ips: - logging.debug(f"成功通过 DNS服务器 和 DNS记录 解析 {domain}, 发现 { - len(ips)} 个 唯一 DNS 主机\n{ips}\n") + logging.debug( + f"成功通过 DNS服务器 和 DNS记录 解析 {domain}, 发现 { + len(ips)} 个 唯一 DNS 主机\n{ips}\n" + ) else: logging.debug(f"警告: 无法解析 {domain}") @@ -371,10 +379,11 @@ async def _resolve_via_dns(self, domain: str, dns_type: str = "all") -> Set[str] - "international": 仅使用国际 DNS :return: 解析得到的 IP 集合 """ + async def resolve_with_dns_server(dns_server_info: dict) -> Set[str]: """单个DNS服务器的解析协程""" - dns_server = dns_server_info['ip'] - dns_provider = dns_server_info['provider'] + dns_server = dns_server_info["ip"] + dns_provider = dns_server_info["provider"] ips = set() resolver = dns.resolver.Resolver(configure=False) resolver.nameservers = [dns_server] @@ -395,8 +404,10 @@ async def resolve_with_dns_server(dns_server_info: dict) -> Set[str]: logging.debug(f"DNS 查询异常 ({qtype}, {dns_server}): {e}") if ips: - logging.debug(f"成功使用 {dns_provider} : {dns_server} 解析 { - domain},共 {len(ips)} 个主机: {ips}") + logging.debug( + f"成功使用 {dns_provider} : {dns_server} 解析 { + domain},共 {len(ips)} 个主机: {ips}" + ) return ips @@ -405,29 +416,36 @@ async def resolve_with_dns_server(dns_server_info: dict) -> Set[str]: return set() # 根据 dns_type 选择要使用的 DNS 服务器 - if dns_type == "all": - dns_servers = self.dns_servers['china_mainland'] + \ - self.dns_servers['international'] - elif dns_type == "china": - dns_servers = self.dns_servers['china_mainland'] - elif dns_type == "international": - dns_servers = self.dns_servers['international'] + if dns_type.lower() == "all": + dns_servers = ( + self.dns_servers["china_mainland"] + + self.dns_servers["international"] + ) + elif dns_type.lower() == "china": + dns_servers = self.dns_servers["china_mainland"] + elif dns_type.lower() == "global" or dns_type.lower() == "international": + dns_servers = self.dns_servers["international"] else: - dns_servers = self.dns_servers['china_mainland'] + \ - self.dns_servers['international'] + dns_servers = ( + self.dns_servers["china_mainland"] + + self.dns_servers["international"] + ) # raise ValueError(f"无效的 DNS 类型:{dns_type}") + with concurrent.futures.ThreadPoolExecutor(max_workers=10): + # 并发解析所有选定的 DNS 服务器,并保留非空结果 + tasks = [resolve_with_dns_server(dns_server) + for dns_server in dns_servers] + results = await asyncio.gather(*tasks) - # 并发解析所有选定的 DNS 服务器,并保留非空结果 - tasks = [resolve_with_dns_server(dns_server) - for dns_server in dns_servers] - results = await asyncio.gather(*tasks) + # 合并所有非空的解析结果 + ips = set(ip for result in results for ip in result if ip) - # 合并所有非空的解析结果 - ips = set(ip for result in results for ip in result if ip) if ips: - logging.debug(f"成功使用多个 DNS 服务器解析 {domain},共 { - len(ips)} 个主机:\n{ips}\n") - # input("按任意键继续") + logging.debug( + f"成功使用多个 DNS 服务器解析 {domain},共 { + len(ips)} 个主机:\n{ips}\n" + ) + return ips def retry_async(tries=3, delay=0.1): @@ -563,7 +581,7 @@ async def _resolve_via_ipaddress(self, domain: str) -> Set[str]: class LatencyTester: - def __init__(self, hosts_num: int): + def __init__(self, hosts_num: int, max_workers: int = 200): self.hosts_num = hosts_num self.max_workers = max_workers @@ -587,15 +605,16 @@ async def get_lowest_latency_hosts( rprint(f"[bright_black]- 检测主机延迟...[/bright_black]") # 使用线程池来并发处理SSL证书验证 - with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor: + with concurrent.futures.ThreadPoolExecutor( + max_workers=self.max_workers + ) as executor: # 第一步:并发获取IP延迟 ping_tasks = [self.get_host_average_latency(ip) for ip in all_ips] latency_results = await asyncio.gather(*ping_tasks) # 筛选有效延迟的IP valid_latency_results = [ - result for result in latency_results - if result[1] != float("inf") + result for result in latency_results if result[1] != float("inf") ] if valid_latency_results: if len(valid_latency_results) < len(all_ips): @@ -604,15 +623,21 @@ async def get_lowest_latency_hosts( len(valid_latency_results)}[/bold bright_green] 个有效IP地址[/bright_black]" ) valid_latency_ips = [ - result for result in valid_latency_results if result[1] < latency_limit] + result + for result in valid_latency_results + if result[1] < latency_limit + ] if not valid_latency_ips: logging.warning(f"未发现延迟小于 {latency_limit}ms 的IP。") min_result = [ min(valid_latency_results, key=lambda x: x[1])] - latency_limit = min_result[0][1]*2 + latency_limit = min_result[0][1] * 2 logging.debug(f"主机IP最低延迟 {latency_limit:.0f}ms") valid_latency_ips = [ - result for result in valid_latency_results if result[1] <= latency_limit] + result + for result in valid_latency_results + if result[1] <= latency_limit + ] else: rprint("[red]延迟检测没有获得有效IP[/red]") @@ -633,11 +658,28 @@ async def get_lowest_latency_hosts( ipv6_results = [ r for r in valid_latency_ips if Utils.is_ipv6(r[0])] # 第二步:使用线程池并发验证SSL证书 + # loop = asyncio.get_running_loop() + # ssl_verification_tasks = [] + # if "github" in group_name.lower(): - if len(valid_latency_ips) > 1 and any(keyword in group_name.lower() for keyword in ["github", "google"]): - rprint( - f"[bright_black]- 验证SSL证书...[/bright_black]" - ) + if len(valid_latency_ips) > 1 and any( + keyword in group_name.lower() for keyword in ["github", "google"] + ): + rprint(f"[bright_black]- 验证SSL证书...[/bright_black]") + # batch_size = args.batch_size + # total_results = len(valid_latency_ips) + + # for i in range(0, total_results, batch_size): + # min_len = min(total_results, batch_size) + # batch = valid_latency_ips[i:i + min_len] + # valid_results = await self.process_hosts(domains, batch, executor, valid_latency_ips) + # if valid_results: + # break + # if not valid_results: + # logging.warning(f"未发现延迟小于 {latency_limit}ms 且证书有效的IP。") + # return [] + + # 方案0 ipv4_count = 0 ipv6_count = 0 batch_size = args.batch_size @@ -648,7 +690,7 @@ async def get_lowest_latency_hosts( for i in range(0, total_results, batch_size): min_len = min(total_results, batch_size) - batch = valid_latency_ips[i:i + min_len] + batch = valid_latency_ips[i: i + min_len] ssl_verification_tasks = [ loop.run_in_executor( executor, @@ -657,11 +699,12 @@ async def get_lowest_latency_hosts( # self._sync_is_cert_valid_dict_average, # domains, ip, - latency + latency, ) for ip, latency in batch ] + # 方案0-1 for future in asyncio.as_completed(ssl_verification_tasks): ip, latency, ssl_valid = await future if ssl_valid: @@ -680,6 +723,34 @@ async def get_lowest_latency_hosts( else: if ipv4_count >= self.hosts_num: break + + # # 方案0-2 better + # done, pending = await asyncio.wait( + # ssl_verification_tasks + # ) + + # for task in done: + # (ip, latency, ssl_valid) = await task + # if ssl_valid: + # valid_results.append((ip, latency)) + # if Utils.is_ipv6(ip): + # ipv6_count += 1 + # else: + # ipv4_count += 1 + # if ipv6_results: + # if ipv4_results: + # if ipv6_count >= 1 and ipv4_count >= 1: + # break + # else: + # if ipv6_count >= 1: + # break + # else: + # if ipv4_count >= self.hosts_num: + # break + # # 显式取消剩余任务 + # for task in pending: + # task.cancel() + if ipv6_results: if ipv4_results: if ipv6_count >= 1 and ipv4_count >= 1: @@ -690,14 +761,130 @@ async def get_lowest_latency_hosts( else: if ipv4_count >= self.hosts_num: break + + # loop = asyncio.get_running_loop() + # ssl_verification_tasks = [] + + # for ip, latency in valid_latency_ips: + # ssl_verification_tasks.append( + # loop.run_in_executor( + # executor, + # self._sync_is_cert_valid_dict, + # domains[0], + # ip, + # latency + # ) + # ) + + # # 方案1 + # # 等待SSL证书验证结果 + # ssl_results = await asyncio.gather(*ssl_verification_tasks) + # valid_results = [] + # for ip,latency,ssl_valid in ssl_results: + # if ssl_valid: + # print(ip,latency,ssl_valid) + # valid_results.append((ip,latency)) + + # 结合延迟和SSL验证结果 + + # # 方案2 + # valid_results = [] + # ipv4_count = 0 + # ipv6_count = 0 + # ssl_verification_tasks = [ + # asyncio.ensure_future(task) for task in ssl_verification_tasks + # ] + # for future in asyncio.as_completed(ssl_verification_tasks): + # ip, latency, ssl_valid = await future + # if ssl_valid: + # valid_results.append((ip, latency)) + # if Utils.is_ipv6(ip): + # ipv6_count += 1 + # else: + # ipv4_count += 1 + # if ipv6_results: + # if ipv4_results: + # if ipv6_count >= 1 and ipv4_count >= 1: + # break + # else: + # if ipv6_count >= 1: + # break + # else: + # if ipv4_count >= self.hosts_num: + # break + # # 取消所有未完成任务 + # for task in ssl_verification_tasks: + # if not task.done(): + # task.cancel() + + # # 方案3 + # valid_results = [] + # ipv4_count = 0 + # ipv6_count = 0 + # done, pending = await asyncio.wait( + # ssl_verification_tasks + # ) + + # for task in done: + # (ip, latency, ssl_valid) = await task + # if ssl_valid: + # print(ip) + # valid_results.append((ip, latency)) + # if Utils.is_ipv6(ip): + # ipv6_count += 1 + # else: + # ipv4_count += 1 + # if ipv6_results: + # if ipv4_results: + # if ipv6_count >= 1 and ipv4_count >= 1: + # break + # else: + # if ipv6_count >= 1: + # break + # else: + # if ipv4_count >= self.hosts_num: + # break + # # 显式取消剩余任务 + # for task in pending: + # task.cancel() + + # break + # print(valid_results) + # input("暂停1") else: valid_results = valid_latency_ips + # for ip, latency in valid_latency_ips: + # ssl_verification_tasks.append( + # loop.run_in_executor( + # executor, + # self._sync_is_cert_valid, + # domains[0], + # ip + # ) + # ) + + # # 等待SSL证书验证结果 + # ssl_results = await asyncio.gather(*ssl_verification_tasks) + # for (ip, latency), ssl_valid in zip(valid_latency_ips, ssl_results): + # print((ip,latency),ssl_valid) + # # input("暂停1") + # # 结合延迟和SSL验证结果 + # valid_results = [ + # (ip, latency) for (ip, latency), ssl_valid in zip(valid_latency_ips, ssl_results) + # if ssl_valid + # ] + + # print(valid_results) + # input("暂停") + # 按延迟排序并选择最佳主机 valid_results = sorted(valid_results, key=lambda x: x[1]) if not valid_results: rprint(f"[red]未发现延迟小于 {latency_limit}ms 且证书有效的IP。[/red]") + # input("按任意键继续") + # exit("主动退出") # 选择最佳主机(支持IPv4和IPv6) best_hosts = self._select_best_hosts(valid_results) @@ -731,56 +918,63 @@ async def get_host_average_latency( logging.debug(f"ping {ip} 时出错: {e}") return ip, float("inf") - # 异步重试装饰器 - def retry_async(tries=3, delay=0): - def decorator(func): - @wraps(func) - async def wrapper(*args, **kwargs): - for attempt in range(tries): - try: - return await func(*args, **kwargs) - except ssl.SSLError as e: - # 如果是 SSL 错误,不重试,直接返回 False - # print(f"SSL 错误(第 {attempt + 1} 次尝试): {e}") - return False - except socket.timeout as e: - # 对超时错误进行重试 - if attempt < tries - 1: - # print(f"证书验证(超时错误),第 {attempt + 2} 次尝试:") - logging.debug(f"证书验证(超时错误) {args[1]} | { - args[2]},第 {attempt + 2} 次尝试:") - if attempt == tries - 1: - # print( - # f"证书验证 {args[1]} | {args[2]}, {tries} 次尝试后终止!" - # ) - return False - await asyncio.sleep(delay) - except ConnectionError as e: - # 如果是连接错误,直接返回 True - return True - except Exception as e: - # 捕获其他异常,进行重试 - if attempt < tries - 1: - print(f"证书验证(其他错误),第 {attempt + 2} 次尝试:") - # logging.debug(f"证书验证(其他错误) {args[1]} | {args[2]},第 {attempt + 2} 次尝试:") - if attempt == tries - 1: - print( - f"证书验证 {args[1]} | {args[2]}, {tries} 次尝试后终止!" - ) - return False - await asyncio.sleep(delay) - return None - - return wrapper + async def get_latency(self, ip: str, port: int = 443) -> float: + try: + # 使用 getaddrinfo 来获取正确的地址格式 + addrinfo = await asyncio.get_event_loop().getaddrinfo( + ip, port, family=socket.AF_UNSPEC, type=socket.SOCK_STREAM + ) - return decorator + for family, type, proto, canonname, sockaddr in addrinfo: + try: + start = asyncio.get_event_loop().time() + _, writer = await asyncio.wait_for( + asyncio.open_connection(sockaddr[0], sockaddr[1]), + timeout=PING_TIMEOUT, + ) + end = asyncio.get_event_loop().time() + writer.close() + await writer.wait_closed() + return (end - start) * 1000 + except asyncio.TimeoutError: + continue + except Exception as e: + logging.debug(f"连接测试失败 {ip} (sockaddr: {sockaddr}): {e}") + continue - @retry_async(tries=1) - async def is_cert_valid(self, domain: str, ip: str, port: int = 443) -> bool: - # 设置SSL上下文,用于证书验证 - context = ssl.create_default_context() - context.verify_mode = ssl.CERT_REQUIRED # 验证服务器证书 - context.check_hostname = True # 确保证书主机名匹配 + return float("inf") + except Exception as e: + logging.error(f"获取地址信息失败 {ip}: {e}") + return float("inf") + + async def process_hosts(self, domains, batch, executor, valid_latency_ips): + loop = asyncio.get_running_loop() + ssl_verification_tasks = [] + for ip, latency in batch: + ssl_verification_tasks.append( + loop.run_in_executor( + executor, self._sync_is_cert_valid, domains[0], ip) + ) + # 等待SSL证书验证结果 + ssl_results = await asyncio.gather(*ssl_verification_tasks) + + # 结合延迟和SSL验证结果 + valid_results = [ + (ip, latency) + for (ip, latency), ssl_valid in zip(valid_latency_ips, ssl_results) + if ssl_valid + ] + return valid_results + + def _sync_is_cert_valid(self, domain: str, ip: str, port: int = 443) -> bool: + """ + 同步版本的证书验证方法,用于在线程池中执行 + """ + try: + # 复制原is_cert_valid方法的同步实现 + context = ssl.create_default_context() + context.verify_mode = ssl.CERT_REQUIRED + context.check_hostname = True try: # 1. 尝试与IP地址建立SSL连接 @@ -817,7 +1011,9 @@ async def is_cert_valid(self, domain: str, ip: str, port: int = 443) -> bool: raise return False - def _sync_is_cert_valid_dict(self, domain: str, ip: str, latency: float, port: int = 443) -> Tuple[str, float, bool]: + def _sync_is_cert_valid_dict( + self, domain: str, ip: str, latency: float, port: int = 443 + ) -> Tuple[str, float, bool]: """ 同步版本的证书验证方法,用于在线程池中执行 """ @@ -830,25 +1026,34 @@ def _sync_is_cert_valid_dict(self, domain: str, ip: str, latency: float, port: i with context.wrap_socket(sock, server_hostname=domain) as ssock: cert = ssock.getpeercert() not_after = datetime.strptime( - cert["notAfter"], "%b %d %H:%M:%S %Y %Z") + cert["notAfter"], "%b %d %H:%M:%S %Y %Z" + ) if not_after < datetime.now(): - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: 证书已过期") + logging.debug( + f"{domain} ({ip}) { + latency:.0f}ms: 证书已过期" + ) return (ip, latency, False) - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: SSL证书有效,截止日期为 {not_after}") + logging.debug( + f"{domain} ({ip}) { + latency:.0f}ms: SSL证书有效,截止日期为 {not_after}" + ) return (ip, latency, True) except ConnectionError as e: - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: 连接被强迫关闭,ip有效 - {e}") + logging.debug( + f"{domain} ({ip}) { + latency:.0f}ms: 连接被强迫关闭,ip有效 - {e}" + ) return (ip, latency, True) except Exception as e: logging.debug(f"{domain} ({ip}) {latency:.0f}ms: 证书验证失败 - {e}") return (ip, latency, False) - def _sync_is_cert_valid_dict_average(self, domains: List[str], ip: str, latency: float, port: int = 443) -> Tuple[str, float, bool]: + def _sync_is_cert_valid_dict_average( + self, domains: List[str], ip: str, latency: float, port: int = 443 + ) -> Tuple[str, float, bool]: """ 同步版本的证书验证方法,用于在线程池中执行。 任意一个 domain 验证通过就视为通过。 @@ -863,19 +1068,26 @@ def _sync_is_cert_valid_dict_average(self, domains: List[str], ip: str, latency: with context.wrap_socket(sock, server_hostname=domain) as ssock: cert = ssock.getpeercert() not_after = datetime.strptime( - cert["notAfter"], "%b %d %H:%M:%S %Y %Z") + cert["notAfter"], "%b %d %H:%M:%S %Y %Z" + ) if not_after < datetime.now(): - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: 证书已过期") + logging.debug( + f"{domain} ({ip}) { + latency:.0f}ms: 证书已过期" + ) continue # 检查下一个 domain - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: SSL证书有效,截止日期为 {not_after}") + logging.debug( + f"{domain} ({ip}) { + latency:.0f}ms: SSL证书有效,截止日期为 {not_after}" + ) return (ip, latency, True) # 任意一个验证通过即返回成功 except ConnectionError as e: - logging.debug(f"{domain} ({ip}) { - latency:.0f}ms: 连接被强迫关闭,ip有效 - {e}") + logging.debug( + f"{domain} ({ip}) { + latency:.0f}ms: 连接被强迫关闭,ip有效 - {e}" + ) return (ip, latency, True) except Exception as e: logging.debug(f"{domain} ({ip}) {latency:.0f}ms: 证书验证失败 - {e}") @@ -884,7 +1096,9 @@ def _sync_is_cert_valid_dict_average(self, domains: List[str], ip: str, latency: # 如果所有 domain 都验证失败 return (ip, latency, False) - def _select_best_hosts(self, valid_results: List[Tuple[str, float]]) -> List[Tuple[str, float]]: + def _select_best_hosts( + self, valid_results: List[Tuple[str, float]] + ) -> List[Tuple[str, float]]: """ 选择最佳主机,优先考虑IPv4和IPv6 """ @@ -901,19 +1115,8 @@ def _select_best_hosts(self, valid_results: List[Tuple[str, float]]) -> List[Tup if ipv4_results: logging.debug(f"有效IPv4:\n{ipv4_results}\n") for ip, latency in ipv4_results: - if group_name == "Google 翻译": - if await self.is_cert_valid( - domains[0], ip - ): # shareGroup会传入多个域名,只需检测第一个就行 - best_hosts.append((ip, latency)) - selected_count += 1 - else: - if await self.is_cert_valid( - domains[0], ip - ): # shareGroup会传入多个域名,只需检测第一个就行 - best_hosts.append((ip, latency)) - selected_count += 1 - + best_hosts.append((ip, latency)) + selected_count += 1 if ( ipv6_results and selected_count >= 1 ) or selected_count >= self.hosts_num: @@ -922,19 +1125,22 @@ def _select_best_hosts(self, valid_results: List[Tuple[str, float]]) -> List[Tup if ipv6_results: logging.debug(f"有效IPv6:\n{ipv6_results}\n") for ip, latency in ipv6_results: - if group_name in "Google 翻译": - if await self.is_cert_valid( - domains[0], ip - ): # shareGroup会传入多个域名,只需检测第一个就行 - best_hosts.append((ip, latency)) - break - else: - if await self.is_cert_valid( - domains[0], ip - ): # shareGroup会传入多个域名,只需检测第一个就行 - best_hosts.append((ip, latency)) - break + best_hosts.append((ip, latency)) + break + best_hosts.append((ip, latency)) + break + return best_hosts + + def _print_results( + self, + best_hosts: List[Tuple[str, float]], + latency_limit: int, + start_time: datetime, + ): + """ + 打印结果的方法 + """ rprint( f"[bold yellow]最快的 DNS主机 IP(优先选择 IPv6) 丨 延迟 < { latency_limit:.0f}ms :[/bold yellow]" @@ -944,10 +1150,16 @@ def _select_best_hosts(self, valid_results: List[Tuple[str, float]]) -> List[Tup f" [green]{ ip}[/green] [bright_black]{time:.2f} ms[/bright_black]" ) + rprint( + f" [green]{ + ip}[/green] [bright_black]{time:.2f} ms[/bright_black]" + ) end_time = datetime.now() total_time = end_time - start_time rprint( - f"[bright_black]- 运行时间:[/bright_black] [cyan]{total_time.total_seconds():.2f} 秒[/cyan]") + f"[bold]运行时间:[/bold] [cyan]{total_time.total_seconds() :.2f} 秒[/cyan]" + ) + return best_hosts # -------------------- Hosts文件管理 -------------------- # @@ -1118,6 +1330,7 @@ async def update_hosts(self): for domain in group.domains: rprint(f"\n为域名 {domain} 设置 DNS 映射主机") all_ips = set() # 重置初始ip,否则会混淆 + all_ips = set() # 重置初始ip,否则会混淆 resolved_ips = await self.resolver.resolve_domain(domain) all_ips.update(resolved_ips) @@ -1162,6 +1375,8 @@ async def update_hosts(self): self.resolver.max_latency, ) + # input("继续") + # input("继续") if not fastest_ips: logging.warning(f"组 {group.name} 未找到延迟满足要求的IP。") continue @@ -1246,9 +1461,10 @@ class Config: name="GitHub Asset", group_type=GroupType.SHARED, domains=[ - "github.io", "githubstatus.com", "assets-cdn.github.com", + "github.io", + "github.io", ], ips={}, ), @@ -1677,25 +1893,66 @@ class Config: DNS_SERVERS = { "international": [ # 国际 DNS 服务器 - # { - # "ip": "8.8.8.8", # Google Public DNS - # "provider": "Google", - # "type": "ipv4" - # }, + # 第 1 梯队: 延迟较低 + {"ip": "208.67.222.222", "provider": "OpenDNS", + "type": "ipv4"}, # Open DNS + {"ip": "2620:0:ccc::2", "provider": "OpenDNS", + "type": "ipv6"}, # Open DNS { "ip": "2001:4860:4860::8888", # Google Public DNS "provider": "Google", - "type": "ipv6" + "type": "ipv6", }, + { + "ip": "2001:4860:4860::8844", # Google Public DNS + "provider": "Google", + "type": "ipv6", + }, + {"ip": "210.184.24.65", "provider": "CPC HK", "type": "ipv4"}, + {"ip": "118.201.189.90", "provider": "SingNet", "type": "ipv4"}, # 新加坡 + {"ip": "1.228.180.5", "provider": "SK Broadband ", "type": "ipv4"}, # 韩国 + {"ip": "183.99.33.6", "provider": "Korea Telecom ", "type": "ipv4"}, # 韩国 + {"ip": "203.248.252.2", "provider": "LG DACOM ", "type": "ipv4"}, # 韩国 + # 第 2 梯队:延迟适中 # { - # "ip": "1.1.1.1", # CloudFlare DNS - # "provider": "CloudFlare", + # "ip": "129.250.35.250", + # "provider": "NTT Communications", # 日本 # "type": "ipv4" # }, # { - # "ip": "2606:4700:4700::1111", # CloudFlare DNS - # "provider": "CloudFlare", - # "type": "ipv6" + # "ip": "168.126.63.1", + # "provider": "KT DNS", # 韩国 + # "type": "ipv4" + # }, + # { + # "ip": "101.110.50.106", + # "provider": "Soft Bank", + # "type": "ipv4" + # }, + # { + # "ip": "202.175.86.206", + # "provider": "Telecomunicacoes de Macau", #澳门 + # "type": "ipv4" + # }, + # { + # "ip": "45.123.201.235", + # "provider": "Informacoes Tecnologia de Macau", #澳门 + # "type": "ipv4" + # }, + # { + # "ip": "2400:6180:0:d0::5f6e:4001", + # "provider": "DigitalOcean", # 新加坡 + # "type": "ipv6" + # }, + # { + # "ip": "2a09::", # DNS.SB 德国 2a11:: + # "provider": "DNS.SB", + # "type": "ipv6" + # }, + # { + # "ip": "185.222.222.222", # DNS.SB 德国45.11.45.11 + # "provider": "DNS.SB", + # "type": "ipv4" # }, # { # "ip": "9.9.9.9", # Quad9 DNS @@ -1726,10 +1983,41 @@ class Config: # "ip": "2620:fe::9", # Quad9 # "provider": "Quad9", # "type": "ipv6" - # } + # }, + # { + # "ip": "77.88.8.1", + # "provider": "Yandex DNS",# 俄国 + # "type": "ipv4" + # }, + # { + # "ip": "2a02:6b8::feed:0ff",# 俄国 + # "provider": "Yandex DNS", + # "type": "ipv6" + # }, ], "china_mainland": [ # 国内 DNS 服务器 + # 第 1 梯队:正确解析Google翻译 + # 首选:延迟较低,相对稳定: + {"ip": "114.114.114.114", "provider": "114DNS", "type": "ipv4"}, # 114 DNS + {"ip": "1.1.8.8", "provider": "China Unicom", "type": "ipv4"}, # 中国联通 + # 备选:延迟一般: + # { + # "ip": "180.76.76.76", # 百度 + # "provider": "Baidu", + # "type": "ipv4" + # }, + # { + # "ip": "202.46.33.250", # 上海通讯 + # "provider": "Shanghai Communications", + # "type": "ipv4" + # }, + # { + # "ip": "202.46.34.75", # 上海通讯 + # "provider": "Shanghai Communications", + # "type": "ipv4" + # },240c::6644 + # 第 2 梯队:无法正确解析Google翻译 # { # "ip": "223.5.5.5", # 阿里云 DNS # "provider": "Alibaba", @@ -1813,7 +2101,8 @@ async def main(): ) # 2.延迟检测 - tester = LatencyTester(hosts_num=args.hosts_num) + tester = LatencyTester(hosts_num=args.hosts_num, max_workers=200) + tester = LatencyTester(hosts_num=args.hosts_num, max_workers=200) # 3.Hosts文件操作 hosts_manager = HostsManager()