From 164b6cb0b3f3975f63828e8023b4172b727092a4 Mon Sep 17 00:00:00 2001 From: sinspired Date: Thu, 5 Dec 2024 12:19:16 +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 --- .github/workflows/build_release.yml | 16 +- README.md | 169 ++++-- hosts | 167 ++++-- setDNS.py | 47 +- setHosts_Classic.py | 892 ++++++++++++++++++++-------- 5 files changed, 879 insertions(+), 412 deletions(-) diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml index 8e263d4..8298a51 100644 --- a/.github/workflows/build_release.yml +++ b/.github/workflows/build_release.yml @@ -26,22 +26,22 @@ jobs: with: fetch-depth: 0 - - name: Set up Python 3.11 + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt - pip install pyinstaller + pip install -U pyinstaller - name: Build executables (Linux) if: matrix.os == 'ubuntu-latest' run: | for script in setDNS.py setHosts.py setHosts_Classic.py; do - pyinstaller --onefile "$script" + pyinstaller --clean --onefile "$script" mv "dist/${script%.py}" "dist/${script%.py}-Linux-x64" done zip -j "cnNetTool-Linux-x64.zip" dist/*-Linux-x64 @@ -57,9 +57,9 @@ jobs: $icoName = $script -replace '\.py$', '.ico' if (Test-Path -Path $icoName) { - pyinstaller --onefile $script --uac-admin --icon $icoName + pyinstaller --clean --onefile $script --uac-admin --icon $icoName } else { - pyinstaller --onefile $script --uac-admin + pyinstaller --clean --onefile $script --uac-admin } # pyinstaller --onefile $script --uac-admin @@ -73,7 +73,7 @@ jobs: if: matrix.os == 'macos-latest' run: | for script in setDNS.py setHosts.py setHosts_Classic.py; do - pyinstaller --onefile "$script" + pyinstaller --clean --onefile "$script" mv "dist/${script%.py}" "dist/${script%.py}-macOS-x64" done zip -j "cnNetTool-macOS-x64.zip" dist/*-macOS-x64 @@ -144,4 +144,4 @@ jobs: **/cnNetTool-Linux-x64.zip \ **/cnNetTool-Windows-x64.zip \ **/cnNetTool-macOS-x64.zip - shell: bash \ No newline at end of file + shell: bash diff --git a/README.md b/README.md index 49ffb0f..09b7bae 100644 --- a/README.md +++ b/README.md @@ -30,80 +30,123 @@ ```bash -# cnNetTool Start in 2024-11-30 12:24:58 +08:00 +# cnNetTool Start in 2024-12-05 11:37:05 +08:00 140.82.113.26 alive.github.com -140.82.112.5 api.github.com -140.82.112.21 central.github.com -140.82.113.9 codeload.github.com -140.82.114.22 collector.github.com -140.82.113.4 gist.github.com -140.82.114.3 github.com -140.82.114.18 github.community -146.75.29.194 github.global.ssl.fastly.net -16.182.38.33 github-com.s3.amazonaws.com -16.15.185.16 github-production-release-asset-2e65be.s3.amazonaws.com -140.82.114.26 live.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 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 -185.199.109.133 avatars.githubusercontent.com -185.199.109.133 avatars0.githubusercontent.com -185.199.109.133 avatars1.githubusercontent.com -185.199.109.133 avatars2.githubusercontent.com -185.199.109.133 avatars3.githubusercontent.com -185.199.109.133 avatars4.githubusercontent.com -185.199.109.133 avatars5.githubusercontent.com -185.199.109.133 camo.githubusercontent.com -185.199.109.133 cloud.githubusercontent.com -185.199.109.133 desktop.githubusercontent.com -185.199.109.133 favicons.githubusercontent.com -185.199.109.133 github.map.fastly.net -185.199.109.133 media.githubusercontent.com -185.199.109.133 objects.githubusercontent.com -185.199.109.133 private-user-images.githubusercontent.com -185.199.109.133 raw.githubusercontent.com -185.199.109.133 user-images.githubusercontent.com -18.160.200.13 tmdb.org -18.160.200.13 api.tmdb.org -18.160.200.13 files.tmdb.org -52.85.247.30 themoviedb.org -52.85.247.30 api.themoviedb.org -52.85.247.30 www.themoviedb.org -52.85.247.30 auth.themoviedb.org -185.93.1.244 image.tmdb.org -185.93.1.244 images.tmdb.org -52.94.225.248 imdb.com -3.168.35.144 www.imdb.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 +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 -3.168.35.144 s.media-imdb.com -52.94.237.74 us.dd.imdb.com -3.168.35.144 www.imdb.to -52.94.225.248 imdb-webservice.amazon.com +18.172.46.173 s.media-imdb.com +52.94.225.248 us.dd.imdb.com +13.35.217.192 www.imdb.to +52.94.228.167 imdb-webservice.amazon.com 44.215.137.99 origin-www.imdb.com -146.75.29.16 m.media-amazon.com -146.75.29.16 Images-na.ssl-images-amazon.com -104.123.159.75 images-fe.ssl-images-amazon.com -3.167.162.195 images-eu.ssl-images-amazon.com -146.75.29.16 ia.media-imdb.com -146.75.29.16 f.media-amazon.com -52.84.18.90 imdb-video.media-imdb.com -3.167.180.205 dqpnq362acqdi.cloudfront.net -142.250.190.74 translate.google.com -142.250.190.74 translate.googleapis.com -142.250.190.74 translate-pa.googleapis.com -3.167.138.84 plugins.jetbrains.com -3.167.138.84 download.jetbrains.com -3.167.138.84 cache-redirector.jetbrains.com - -# Update time: 2024-11-30 12:24:58 +08:00 +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 +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-30 12:24:58 +08:00 +以上内容会自动定时更新, 数据更新时间:2024-12-05 11:37:05 +08:00 #### 1.2.2 修改 hosts 文件 diff --git a/hosts b/hosts index 5f9e059..2a35e94 100644 --- a/hosts +++ b/hosts @@ -1,71 +1,114 @@ -# cnNetTool Start in 2024-11-30 12:24:58 +08:00 +# cnNetTool Start in 2024-12-05 11:37:05 +08:00 140.82.113.26 alive.github.com -140.82.112.5 api.github.com -140.82.112.21 central.github.com -140.82.113.9 codeload.github.com -140.82.114.22 collector.github.com -140.82.113.4 gist.github.com -140.82.114.3 github.com -140.82.114.18 github.community -146.75.29.194 github.global.ssl.fastly.net -16.182.38.33 github-com.s3.amazonaws.com -16.15.185.16 github-production-release-asset-2e65be.s3.amazonaws.com -140.82.114.26 live.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 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 -185.199.109.133 avatars.githubusercontent.com -185.199.109.133 avatars0.githubusercontent.com -185.199.109.133 avatars1.githubusercontent.com -185.199.109.133 avatars2.githubusercontent.com -185.199.109.133 avatars3.githubusercontent.com -185.199.109.133 avatars4.githubusercontent.com -185.199.109.133 avatars5.githubusercontent.com -185.199.109.133 camo.githubusercontent.com -185.199.109.133 cloud.githubusercontent.com -185.199.109.133 desktop.githubusercontent.com -185.199.109.133 favicons.githubusercontent.com -185.199.109.133 github.map.fastly.net -185.199.109.133 media.githubusercontent.com -185.199.109.133 objects.githubusercontent.com -185.199.109.133 private-user-images.githubusercontent.com -185.199.109.133 raw.githubusercontent.com -185.199.109.133 user-images.githubusercontent.com -18.160.200.13 tmdb.org -18.160.200.13 api.tmdb.org -18.160.200.13 files.tmdb.org -52.85.247.30 themoviedb.org -52.85.247.30 api.themoviedb.org -52.85.247.30 www.themoviedb.org -52.85.247.30 auth.themoviedb.org -185.93.1.244 image.tmdb.org -185.93.1.244 images.tmdb.org -52.94.225.248 imdb.com -3.168.35.144 www.imdb.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 +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 -3.168.35.144 s.media-imdb.com -52.94.237.74 us.dd.imdb.com -3.168.35.144 www.imdb.to -52.94.225.248 imdb-webservice.amazon.com +18.172.46.173 s.media-imdb.com +52.94.225.248 us.dd.imdb.com +13.35.217.192 www.imdb.to +52.94.228.167 imdb-webservice.amazon.com 44.215.137.99 origin-www.imdb.com -146.75.29.16 m.media-amazon.com -146.75.29.16 Images-na.ssl-images-amazon.com -104.123.159.75 images-fe.ssl-images-amazon.com -3.167.162.195 images-eu.ssl-images-amazon.com -146.75.29.16 ia.media-imdb.com -146.75.29.16 f.media-amazon.com -52.84.18.90 imdb-video.media-imdb.com -3.167.180.205 dqpnq362acqdi.cloudfront.net -142.250.190.74 translate.google.com -142.250.190.74 translate.googleapis.com -142.250.190.74 translate-pa.googleapis.com -3.167.138.84 plugins.jetbrains.com -3.167.138.84 download.jetbrains.com -3.167.138.84 cache-redirector.jetbrains.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 +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-30 12:24:58 +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/setDNS.py b/setDNS.py index 1bb3158..d22dc8e 100644 --- a/setDNS.py +++ b/setDNS.py @@ -58,34 +58,13 @@ "ipv4": ["64.6.64.6", "64.6.65.6"], "ipv6": ["2620:74:1b::1:1", "2620:74:1c::2:2"], }, - "NTT Communications DNS": { - "ipv4": ["129.250.35.250"], - "ipv6": [] - }, - "KT DNS": { - "ipv4": ["168.126.63.1"], - "ipv6": [] - }, - "CPC HK": { - "ipv4": ["210.184.24.65"], - "ipv6": [] - }, - "Soft Bank": { - "ipv4": ["101.110.50.106"], - "ipv6": [] - }, - "SingNet": { - "ipv4": ["118.201.189.90"], - "ipv6": [] - }, - "SK Broadband": { - "ipv4": ["1.228.180.5"], - "ipv6": [] - }, - "Korea Telecom": { - "ipv4": ["183.99.33.6"], - "ipv6": [] - }, + "NTT Communications DNS": {"ipv4": ["129.250.35.250"], "ipv6": []}, + "KT DNS": {"ipv4": ["168.126.63.1"], "ipv6": []}, + "CPC HK": {"ipv4": ["210.184.24.65"], "ipv6": []}, + "Soft Bank": {"ipv4": ["101.110.50.106"], "ipv6": []}, + "SingNet": {"ipv4": ["118.201.189.90"], "ipv6": []}, + "SK Broadband": {"ipv4": ["1.228.180.5"], "ipv6": []}, + "Korea Telecom": {"ipv4": ["183.99.33.6"], "ipv6": []}, }, "中国大陆": { "114DNS": { @@ -309,8 +288,8 @@ def set_dns_servers(ipv4_dns_list: list[str], ipv6_dns_list: list[str]): continue if ipv4_dns_list: logger.debug( - f"设置IPv4 DNS for {interface}: { - ', '.join(ipv4_dns_list)}" + f"设置IPv4 DNS for {interface}:{ + ', '.join(ipv4_dns_list)}" ) try: subprocess.run( @@ -462,8 +441,7 @@ def print_recommended_dns_table(dns_list: list, ip_version: str, available_dns: if dns: # 在best_dns列表中查找正确的服务器信息 server_info = next( - (info for server, - info in available_dns[ip_version] if server == dns), + (info for server, info in available_dns[ip_version] if server == dns), None, ) if server_info: @@ -573,8 +551,7 @@ def main(): print_domain_resolutions(domain_resolutions, dns_performance) # 防止 best_dns_num 数值超过数组长度 - num_servers = min( - len(available_dns["ipv4"]), len(available_dns["ipv6"])) + num_servers = min(len(available_dns["ipv4"]), len(available_dns["ipv6"])) if args.best_dns_num > num_servers: args.best_dns_num = num_servers @@ -598,7 +575,7 @@ def main(): set_dns_servers(recommended_dns["ipv4"], recommended_dns["ipv6"]) logger.info("DNS服务器已更新") if thread.is_alive(): # 确认输入线程是否仍在运行 - thread.join() # 等待线程完成 + thread.join() # 等待线程完成 input("任务执行完毕,按任意键退出!") else: logger.info("操作已取消") diff --git a/setHosts_Classic.py b/setHosts_Classic.py index 3726eaa..29b1262 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,10 +23,12 @@ from rich.progress import Progress, SpinnerColumn, TextColumn import wcwidth +# if sys.platform == "win32": +# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # -------------------- 常量设置 -------------------- # RESOLVER_TIMEOUT = 1 # DNS 解析超时时间 秒 HOSTS_NUM = 1 # 每个域名限定Hosts主机 ipv4 数量 -MAX_LATENCY = 500 # 允许的最大延迟 +MAX_LATENCY = 300 # 允许的最大延迟 PING_TIMEOUT = 1 # ping 超时时间 NUM_PINGS = 4 # ping次数 @@ -76,6 +80,20 @@ def parse_args(): action="store_true", help="打印运行信息", ) + parser.add_argument( + "-size", + "--batch-size", + default=5, + type=int, + help="SSL证书验证批次", + ) + parser.add_argument( + "-policy", + "--dns-resolve-policy", + default="all", + type=str, + help="SSL证书验证批次,[all、global、china]", + ) return parser.parse_args() @@ -320,7 +338,7 @@ async def resolve_domain(self, domain: str) -> Set[str]: ips = set() # 1. 首先通过常规DNS服务器解析 - dns_ips = await self._resolve_via_dns(domain, "all") + dns_ips = await self._resolve_via_dns(domain, args.dns_resolve_policy) ips.update(dns_ips) # 2. 然后通过DNS_records解析 @@ -331,15 +349,19 @@ 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) 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}") @@ -356,10 +378,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] @@ -380,8 +403,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 @@ -390,27 +415,35 @@ 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: - raise ValueError(f"无效的 DNS 类型:{dns_type}") - - # 并发解析所有选定的 DNS 服务器,并保留非空结果 - tasks = [resolve_with_dns_server(dns_server) - for dns_server in dns_servers] - results = await asyncio.gather(*tasks) + 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) - # 合并所有非空的解析结果 - 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") + logging.debug( + f"成功使用多个 DNS 服务器解析 {domain},共 { + len(ips)} 个主机:\n{ips}\n" + ) return ips @@ -501,37 +534,318 @@ 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 - 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 - ) + async def get_lowest_latency_hosts( + self, + group_name: str, + domains: List[str], + file_ips: Set[str], + latency_limit: int, + ) -> List[Tuple[str, float]]: + """ + 使用线程池和异步操作优化IP延迟和SSL证书验证 + """ + all_ips = list(file_ips) + start_time = datetime.now() + rprint( + f"[bright_black]- 获取到 [bold bright_green]{ + len(all_ips)}[/bold bright_green] 个唯一IP地址[/bright_black]" + ) + if all_ips: + rprint(f"[bright_black]- 检测主机延迟...[/bright_black]") - 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, + # 使用线程池来并发处理SSL证书验证 + 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") + ] + if valid_latency_results: + if len(valid_latency_results) < len(all_ips): + rprint( + f"[bright_black]- 检测到 [bold bright_green]{ + len(valid_latency_results)}[/bold bright_green] 个有效IP地址[/bright_black]" ) - 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 + valid_latency_ips = [ + 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 + logging.debug(f"主机IP最低延迟 {latency_limit:.0f}ms") + valid_latency_ips = [ + result + for result in valid_latency_results + if result[1] <= latency_limit + ] - return float("inf") - except Exception as e: - logging.error(f"获取地址信息失败 {ip}: {e}") - return float("inf") + else: + rprint("[red]延迟检测没有获得有效IP[/red]") + # if "google" in group_name.lower(): + # input("按任意键继续...") + return [] + # 排序结果 + valid_latency_ips = sorted(valid_latency_ips, key=lambda x: x[1]) + + if len(valid_latency_ips) < len(valid_latency_results): + rprint( + f"[bright_black]- 检测到 [bold bright_green]{ + len(valid_latency_ips)}[/bold bright_green] 个延迟小于 {latency_limit}ms 的有效IP地址[/bright_black]" + ) + + ipv4_results = [ + r for r in valid_latency_ips if not Utils.is_ipv6(r[0])] + 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]") + # 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 + total_results = len(valid_latency_ips) + valid_results = [] + + loop = asyncio.get_running_loop() + + for i in range(0, total_results, batch_size): + min_len = min(total_results, batch_size) + batch = valid_latency_ips[i: i + min_len] + ssl_verification_tasks = [ + loop.run_in_executor( + executor, + self._sync_is_cert_valid_dict, + domains[0], + # self._sync_is_cert_valid_dict_average, + # domains, + ip, + 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: + 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 + + # # 方案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: + break + else: + if ipv6_count >= 1: + break + 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) + + # 打印结果(可以根据需要保留或修改原有的打印逻辑) + self._print_results(best_hosts, latency_limit, start_time) + + return best_hosts async def get_host_average_latency( self, ip: str, port: int = 443 @@ -557,63 +871,67 @@ 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: - # 1. 尝试与IP地址建立SSL连接 - with socket.create_connection((ip, port), timeout=1) as sock: + # 复制原is_cert_valid方法的同步实现 + context = ssl.create_default_context() + context.verify_mode = ssl.CERT_REQUIRED + context.check_hostname = True + + with socket.create_connection((ip, port), timeout=2) as sock: 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" ) @@ -621,142 +939,142 @@ async def is_cert_valid(self, domain: str, ip: str, port: int = 443) -> bool: logging.debug(f"{domain} ({ip}): 证书已过期") return False - # 验证证书域名(由context自动完成),同时获取连接状态 logging.debug( f"{domain} ({ip}): SSL证书有效,截止日期为 {not_after}" ) return True - except ssl.SSLError as e: - logging.debug(f"{domain} ({ip}): SSL错误 - {e}") - return False - except socket.timeout as e: - logging.debug(f"{domain} ({ip}): 连接超时 - {e}") - raise - return False except ConnectionError as e: logging.debug(f"{domain} ({ip}): 连接被强迫关闭,ip有效 - {e}") return True except Exception as e: - logging.error(f"{domain} ({ip}): 其他错误 - {e}") - raise + logging.debug(f"{domain} ({ip}): 证书验证失败 - {e}") return False - async def get_lowest_latency_hosts( - self, - group_name: str, - domains: List[str], - file_ips: Set[str], - latency_limit: int, - ) -> List[Tuple[str, float]]: - all_ips = file_ips - start_time = datetime.now() # 记录程序开始运行时间 - rprint( - f"[bright_black]- 获取到 [bold bright_green]{ - len(all_ips)}[/bold bright_green] 个唯一IP地址[/bright_black]" - ) - if all_ips: - rprint(f"[bright_black]- 检测主机延迟...[/bright_black]") - - if args.log.upper() == "INFO": - with Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - transient=True, - ) as progress: - task = progress.add_task( - f"正在 ping {domains[0] if len(domains) == 1 else f'[{group_name}] { - len(domains)} 域名'} 所有IP地址...", - total=len(all_ips), - ) - ping_tasks = [self.get_host_average_latency( - ip) for ip in all_ips] - results = [] - for result in await asyncio.gather(*ping_tasks): - # rprint(f"[green]{result[0]} {result[1]:.0f}ms ,进行ssl证书验证...[/green]") - if result[1] != float("inf"): - results.append(result) - progress.update(task, advance=1) - - progress.stop_task - else: - ping_tasks = [self.get_host_average_latency(ip) for ip in all_ips] - results = [] - for result in await asyncio.gather(*ping_tasks): - if result[1] != float("inf"): - results.append(result) - - valid_results = [] - - if results: - valid_results = [ - result for result in results if result[1] < latency_limit] - if not valid_results: - logging.warning(f"未发现延迟小于 {latency_limit}ms 的IP。") - valid_results = [min(results, key=lambda x: x[1])] - latency_limit = valid_results[0][1] - logging.debug(f"主机IP最低延迟 {latency_limit:.0f}ms") + def _sync_is_cert_valid_dict( + self, domain: str, ip: str, latency: float, port: int = 443 + ) -> Tuple[str, float, bool]: + """ + 同步版本的证书验证方法,用于在线程池中执行 + """ + try: + context = ssl.create_default_context() + context.verify_mode = ssl.CERT_REQUIRED + context.check_hostname = True - else: - rprint("[red]延迟检测没有获得有效IP[/red]") - return [] + with socket.create_connection((ip, port), timeout=2) as sock: + 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" + ) + if not_after < datetime.now(): + logging.debug( + f"{domain} ({ip}) { + latency:.0f}ms: 证书已过期" + ) + return (ip, latency, False) - # 排序结果 - valid_results = sorted(valid_results, key=lambda x: x[1]) + logging.debug( + f"{domain} ({ip}) { + latency:.0f}ms: SSL证书有效,截止日期为 {not_after}" + ) + return (ip, latency, True) - if len(valid_results) < len(all_ips): - rprint( - f"[bright_black]- 检测到 [bold bright_green]{ - len(valid_results)}[/bold bright_green] 个有效IP地址[/bright_black]" + except ConnectionError as 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]: + """ + 同步版本的证书验证方法,用于在线程池中执行。 + 任意一个 domain 验证通过就视为通过。 + """ + for domain in domains: + try: + context = ssl.create_default_context() + context.verify_mode = ssl.CERT_REQUIRED + context.check_hostname = True + + with socket.create_connection((ip, port), timeout=2) as sock: + 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" + ) + if not_after < datetime.now(): + logging.debug( + f"{domain} ({ip}) { + latency:.0f}ms: 证书已过期" + ) + continue # 检查下一个 domain + 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}" + ) + return (ip, latency, True) + except Exception as e: + logging.debug(f"{domain} ({ip}) {latency:.0f}ms: 证书验证失败 - {e}") + continue # 检查下一个 domain + + # 如果所有 domain 都验证失败 + return (ip, latency, False) + + def _select_best_hosts( + self, valid_results: List[Tuple[str, float]] + ) -> List[Tuple[str, float]]: + """ + 选择最佳主机,优先考虑IPv4和IPv6 + """ ipv4_results = [r for r in valid_results if not Utils.is_ipv6(r[0])] ipv6_results = [r for r in valid_results if Utils.is_ipv6(r[0])] best_hosts = [] selected_count = 0 - if ipv4_results or ipv6_results: - rprint(f"[bright_black]- 验证SSL证书...[/bright_black]") - - # 检测 IPv4 证书有效性 + # 先选择IPv4 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: break - # 检测 IPv6 证书有效性 + + # 再选择IPv6 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 + + 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]" @@ -766,16 +1084,16 @@ async def get_lowest_latency_hosts( f" [green]{ ip}[/green] [bright_black]{time:.2f} ms[/bright_black]" ) + end_time = datetime.now() total_time = end_time - start_time rprint( - f"[bold]运行时间:[/bold] [cyan]{total_time.total_seconds() :.2f} 秒[/cyan]" + f"[bright_black]- 运行时间:[/bright_black] [cyan]{ + total_time.total_seconds():.2f} 秒[/cyan]" ) - return best_hosts - -# -------------------- Hosts文件管理 -------------------- # +# -------------------- Hosts文件管理 -------------------- # class HostsManager: def __init__(self): # 自动根据操作系统获取hosts文件路径 @@ -938,7 +1256,7 @@ async def update_hosts(self): if group.group_type == GroupType.SEPARATE: 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) @@ -970,8 +1288,6 @@ async def update_hosts(self): logging.warning(f"组 {group.name} 未找到任何可用IP。跳过该组。") continue - # rprint(f" 找到 {len(all_ips)} 个 DNS 主机记录") - fastest_ips = await self.tester.get_lowest_latency_hosts( group.name, # [group.domains[0]], # 只需传入一个域名,因为只是用来测试IP @@ -980,6 +1296,7 @@ async def update_hosts(self): self.resolver.max_latency, ) + # input("继续") if not fastest_ips: logging.warning(f"组 {group.name} 未找到延迟满足要求的IP。") continue @@ -1057,9 +1374,9 @@ class Config: name="GitHub Asset", group_type=GroupType.SHARED, domains=[ - "github.io", "githubstatus.com", "assets-cdn.github.com", + "github.io", ], ips={}, ), @@ -1487,25 +1804,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 @@ -1518,16 +1876,6 @@ class Config: # "type": "ipv4" # }, # { - # "ip": "208.67.222.222", # Open DNS - # "provider": "OpenDNS", - # "type": "ipv4" - # }, - # { - # "ip": "2620:0:ccc::2", # Open DNS - # "provider": "OpenDNS", - # "type": "ipv6" - # }, - # { # "ip": "2620:fe::fe", # Quad9 # "provider": "Quad9", # "type": "ipv6" @@ -1537,9 +1885,70 @@ class Config: # "provider": "Quad9", # "type": "ipv6" # } + # { + # "ip": "194.25.0.68", + # "provider": "German Telekom DNS ",# 德国 + # "type": "ipv4" + # }, + # {26 + # "ip": "46.182.19.48", + # "provider": "Digitalcourage DNS",# 德国 + # "type": "ipv4" + # }, + # { + # "ip": "2a02:2970:1002::18", + # "provider": "Digitalcourage DNS",# 德国 + # "type": "ipv6" + # }, + # { + # "ip": "2001:1608:10:25::1c04:b12f",# 德国 + # "provider": "DNS.Watch", + # "type": "ipv6" + # }, + # { + # "ip": "149.112.121.20", + # "provider": "CIRA Canadian Shield",# 加拿大 + # "type": "ipv4" + # }, + # { + # "ip": "2620:10a:80bb::20", + # "provider": "CIRA Canadian Shield",# 加拿大 + # "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", @@ -1560,11 +1969,6 @@ class Config: # "provider": "Tencent", # "type": "ipv6" # }, - { - "ip": "114.114.114.114", # 114 DNS - "provider": "114DNS", - "type": "ipv4" - }, # { # "ip": "101.226.4.6", # 未360dns # "provider": "360dns", @@ -1623,7 +2027,7 @@ async def main(): ) # 2.延迟检测 - tester = LatencyTester(hosts_num=args.hosts_num) + tester = LatencyTester(hosts_num=args.hosts_num, max_workers=200) # 3.Hosts文件操作 hosts_manager = HostsManager()