<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>CLI — mizore / notes</title>
    <link>https://mizore.blog/tags/cli/</link>
    <link xmlns="http://www.w3.org/2005/Atom" href="https://mizore.blog/tags/cli/feed.xml" rel="self" type="application/rss+xml"></link>
    <description>1 篇</description>
    <language>zh-CN</language>
    <lastBuildDate>Mon, 26 Oct 2026 00:00:00 +0000</lastBuildDate>
    <item>
      <title>gdirect：终端直连 Google 云盘的原理与配置指南</title>
      <link>https://mizore.blog/posts/gdirect/</link>
      <description>&lt;p&gt;平时从 Google Drive 拖几十 GB 的数据集或备份，走代理往往既烧流量又受限于节点带宽；用浏览器下载还经常在中途断流后从头再来。为此我写了 &lt;a href=&#34;https://github.com/mizorewww/gdirect&#34;&gt;gdirect&lt;/a&gt;：一个在终端里浏览、下载、上传和挂载 Google 云盘的工具。&lt;/p&gt;&#xA;&lt;p&gt;这篇文章按三个部分组织：&lt;strong&gt;先用几分钟讲清楚它是怎么“伪装”并直连 Google 的（TL;DR）&lt;/strong&gt;，接着是&lt;strong&gt;开箱即用的安装与 10 分钟图文配置指南&lt;/strong&gt;，最后再深入源码做&lt;strong&gt;底层技术解析&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;2026-10-26 勘误&lt;/strong&gt;：本文初版说 &lt;code&gt;183.56.143.147&lt;/code&gt; 是 Google 在广州电信的 &lt;code&gt;google-cn&lt;/code&gt; 国内节点、云盘流量“物理上不过墙”。读者 Ken 来信指出这是错的，我们复核后确认：它是一台把流量转发到香港、再进入 Google 的&lt;strong&gt;第三方中转（反向代理）&lt;/strong&gt;；国内真正的 Google 节点也并不提供云盘服务。gdirect 0.7.0 起改为扫描 Google 自己公布的地址段，找出恰好能直连的真实 Google 前端；0.8.0 默认先试这台中转（快且稳定），不通再用扫描结果，并新增 &lt;code&gt;--google-only&lt;/code&gt; 彻底不用中转。下文已全部改正，验证过程见&lt;a href=&#34;#%E5%8B%98%E8%AF%AF%E4%B8%8E%E6%94%B9%E8%BF%9B18356143147-%E4%B8%8D%E6%98%AF-google&#34;&gt;勘误与改进&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;tldr我们是怎么伪装直连的&#34;&gt;TL;DR：我们是怎么“伪装”直连的？&lt;/h2&gt;&#xA;&lt;p&gt;在大陆网络下，Google Drive 的域名（&lt;code&gt;drive.google.com&lt;/code&gt;、&lt;code&gt;www.googleapis.com&lt;/code&gt;、&lt;code&gt;drive.usercontent.google.com&lt;/code&gt;）被封锁：&lt;code&gt;drive.google.com&lt;/code&gt; 的 DNS 被污染成别人的 IP，另外两个域名虽然能解析出真实的 Google IP，但这些 IP 在 TCP 层就被黑洞，连不上。&lt;/p&gt;&#xA;&lt;p&gt;但 Google 的 IP 远不止 DNS 会给出的那些。Google 公布了自己全部的地址段（&lt;a href=&#34;https://www.gstatic.com/ipranges/goog.json&#34;&gt;goog.json&lt;/a&gt;），其中减去租给 Google Cloud 客户的部分（cloud.json），剩下的就是 Google 自家服务的地址。这些地址上的服务器都接入 Google 的前端服务层（GFE，Google Front End：Google 安全白皮书里负责 TLS 终止、DDoS 防护和负载均衡的那一层），&lt;strong&gt;按 HTTP &lt;code&gt;Host&lt;/code&gt; 头把请求路由到对应的后端服务&lt;/strong&gt;，并且持有 &lt;code&gt;*.googleapis.com&lt;/code&gt;、&lt;code&gt;*.googleusercontent.com&lt;/code&gt; 的合法证书。我们实测发现，&lt;strong&gt;其中有一部分前端从国内可以直接连通&lt;/strong&gt;——大概因为国内 DNS 从来不会把它们解析给用户，它们还没进封锁名单。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;gdirect&lt;/code&gt; 就是利用这一点，用&lt;strong&gt;“TCP 连向一个能连通的 Google 前端 IP，TLS 与 HTTP 照常发 Google Drive 的原始请求（域名与证书）”&lt;/strong&gt;完成直连的：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;底层 TCP 指向扫描出的端点&lt;/strong&gt;：绕过被污染的系统 DNS，直接连目标 IP 的 443 端口。候选只来自 Google 公布的自有地址段：先试 TCP，只对连得上的做完整验证（Google 官方证书 + Drive v3 API 响应 + 测试文件下载），凑够 8 个就停，结果存入本地 SQLite。从 0.8.0 起，默认会&lt;strong&gt;先试第三方中转 &lt;code&gt;183.56.143.147&lt;/code&gt;&lt;/strong&gt;（实测快且稳定），不通再用扫描出的 Google 前端；不想让任何第三方经手的话，加 &lt;code&gt;--google-only&lt;/code&gt;（或设置 &lt;code&gt;GDIRECT_GOOGLE_ONLY=1&lt;/code&gt;）就只用 Google 地址段内的端点。在底层套接字建立阶段（Rust 的 &lt;code&gt;reqwest::resolve&lt;/code&gt; 和定制 C++ &lt;code&gt;libaria2&lt;/code&gt; 的 &lt;code&gt;getaddrinfo&lt;/code&gt; 钩子），哪怕下载中途发生多次 302 跨域名重定向，所有连接的目标 IP 都会被锁死在这个端点上。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;上层 TLS / HTTP 保持真实云盘域名与证书校验；被重置时自动换“伪造身份”重试&lt;/strong&gt;：默认发送真实域名。一旦审查设备把携带真实 SNI 的连接重置（表现为连接错误或超时），&lt;code&gt;gdirect&lt;/code&gt; 会自动改用&lt;strong&gt;伪造身份&lt;/strong&gt;再试一次——TLS 以 &lt;code&gt;g.cn&lt;/code&gt;（Google 自有、前端会出示配套证书的域名）连接并按 &lt;code&gt;g.cn&lt;/code&gt; 校验证书，而 HTTP &lt;code&gt;Host&lt;/code&gt; 头依然携带 &lt;code&gt;www.googleapis.com&lt;/code&gt; 等真实云盘主机名。GFE 按 &lt;code&gt;Host&lt;/code&gt; 路由，云盘请求照常工作；由于 TLS 连接名与证书校验名始终一致，&lt;strong&gt;证书校验从头到尾没有被破坏&lt;/strong&gt;——这也是它与“裸改 SNI”（伪造后证书就对不上了）的本质区别。能通过的身份会被记进 SQLite，之后直接用能通的那条。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;证书校验只锚定 Google 自家的 GTS 根证书&lt;/strong&gt;：不再信任通用 CA 包。Google 把我们用到的每个端点都签在自己的 Google Trust Services 根下，所以校验只认这几张内置根证书——即使某家第三方 CA 被攻破、中间 CA 私钥泄露（如 2026 年 10 月公开的 eMudhra 泄钥事件），也无法对 &lt;code&gt;gdirect&lt;/code&gt; 冒充 Google 做中间人。这一点对中转同样成立：TLS 原样穿过中转，它读不到令牌和文件内容。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;端点失效自动切换&lt;/strong&gt;：当前端点连不上时，先换另一个 TLS 身份，再依次换到过去一天内验证过的其他端点；失效的端点在数据库里标死，下载会在下一个端点上接着已下载的部分续传。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;控制面与数据面分离&lt;/strong&gt;：只有几 KB 的登录、账号核验与刷新令牌（&lt;code&gt;accounts.google.com&lt;/code&gt;、&lt;code&gt;oauth2.googleapis.com&lt;/code&gt;、&lt;code&gt;www.googleapis.com&lt;/code&gt;）照常走系统网络（翻墙代理；0.8.0 起在 macOS / Windows 上会读取系统 HTTP/HTTPS 代理，Linux 用 &lt;code&gt;HTTPS_PROXY&lt;/code&gt;），保证随时能登得上；真正吃带宽的目录浏览、多连接并发下载、32 MiB 分块上传全部走上述直连端点，不耗代理流量。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;流量类型&lt;/th&gt;&#xA;&lt;th&gt;涉及命令 / 域名&lt;/th&gt;&#xA;&lt;th&gt;走向&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;云盘 API、下载、上传&lt;/td&gt;&#xA;&lt;td&gt;&lt;code&gt;browse&lt;/code&gt;、&lt;code&gt;ls&lt;/code&gt;、&lt;code&gt;download&lt;/code&gt;、&lt;code&gt;upload&lt;/code&gt;（&lt;code&gt;www.googleapis.com&lt;/code&gt;、&lt;code&gt;drive.usercontent.google.com&lt;/code&gt;、&lt;code&gt;*.googleusercontent.com&lt;/code&gt;）&lt;/td&gt;&#xA;&lt;td&gt;默认先试中转 &lt;code&gt;183.56.143.147&lt;/code&gt;，不通再用扫描到的 Google 前端（&lt;code&gt;--google-only&lt;/code&gt; 只用后者）。默认用真实 SNI，被重置时自动回退为 &lt;code&gt;g.cn&lt;/code&gt; 伪造身份（&lt;code&gt;Host&lt;/code&gt; 头保持真实）；端点失效时切换到下一个已验证端点。也可用 &lt;code&gt;--ip&lt;/code&gt;、&lt;code&gt;--sni&lt;/code&gt; / &lt;code&gt;--no-sni&lt;/code&gt; 手动指定&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;登录、账号核验与刷新令牌&lt;/td&gt;&#xA;&lt;td&gt;&lt;code&gt;login&lt;/code&gt; 及自动刷新（&lt;code&gt;accounts.google.com&lt;/code&gt;、&lt;code&gt;oauth2.googleapis.com&lt;/code&gt;、&lt;code&gt;www.googleapis.com&lt;/code&gt;）&lt;/td&gt;&#xA;&lt;td&gt;系统网络（系统 DNS 与代理；读取系统 HTTP/HTTPS 代理或 &lt;code&gt;HTTPS_PROXY&lt;/code&gt;）&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;磁盘挂载&lt;/td&gt;&#xA;&lt;td&gt;&lt;code&gt;mount&lt;/code&gt;（底层调用 &lt;code&gt;rclone&lt;/code&gt;）&lt;/td&gt;&#xA;&lt;td&gt;系统网络&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;大陆网络环境特别提示：&lt;/strong&gt;&#xA;登录类请求要走代理，传输类请求要直连。0.8.0 起最省事的配法是：代理软件用&lt;strong&gt;规则模式、打开系统代理、关闭 TUN&lt;/strong&gt;——macOS / Windows 上 &lt;code&gt;gdirect&lt;/code&gt; 登录时会自动使用系统代理（Linux 设置 &lt;code&gt;HTTPS_PROXY&lt;/code&gt;），扫描和传输则绕过代理直连，不需要任何直连规则。&#xA;如果开着 &lt;strong&gt;TUN 模式&lt;/strong&gt;，流量&lt;strong&gt;很可能被重新送进代理&lt;/strong&gt;：一是 &lt;code&gt;gdirect&lt;/code&gt; 连的是 Google 自己的海外 IP（如 &lt;code&gt;142.250.x.x&lt;/code&gt;、&lt;code&gt;192.178.x.x&lt;/code&gt;），按 &lt;code&gt;GEOIP&lt;/code&gt; / Google IP 段分流的规则会把它们判给代理；二是 TLS 握手里的 SNI 是云盘域名，翻墙软件嗅探（Sniff）出域名后会按 &lt;code&gt;google&lt;/code&gt; 规则把连接截胡；TUN 还会替所有地址“接通” TCP，扫描测到的就成了代理而不是你的网络（&lt;code&gt;gdirect scan&lt;/code&gt; 发现几乎所有地址都能连上时会提示）。TUN 必须常开时，要让 &lt;code&gt;gdirect&lt;/code&gt; 进程的传输走直连，详见下文&lt;a href=&#34;#%E5%A4%A7%E9%99%86%E7%BD%91%E7%BB%9C%E4%B8%8E%E4%BB%A3%E7%90%86%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97&#34;&gt;大陆网络与代理配置指南&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;安装指南&#34;&gt;安装指南&lt;/h2&gt;&#xA;&lt;p&gt;预编译二进制支持 &lt;strong&gt;macOS 15+&lt;/strong&gt;（Apple Silicon / Intel）、&lt;strong&gt;Linux x86_64&lt;/strong&gt;（glibc 2.35+，如 Ubuntu 22.04+、Debian 12+、Fedora、Arch）和 &lt;strong&gt;Windows x86_64&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;h3 id=&#34;方式一一键安装脚本&#34;&gt;方式一：一键安装脚本&lt;/h3&gt;&#xA;&lt;p&gt;macOS / Linux：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;curl -fsSL https://raw.githubusercontent.com/mizorewww/gdirect/main/scripts/install.sh &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; bash&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;sh&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Windows PowerShell：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;scriptblock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Create&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Invoke-RestMethod&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;https://raw.githubusercontent.com/mizorewww/gdirect/main/scripts/install.ps1&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;powershell&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;安装脚本会把 &lt;code&gt;gdirect&lt;/code&gt; 放到 &lt;code&gt;~/.local/bin&lt;/code&gt;（Windows 为 &lt;code&gt;%LOCALAPPDATA%\gdirect\bin&lt;/code&gt;）。Google 证书校验所需的 GTS 根证书已内置在二进制里，不需要再放置任何 CA 文件。&lt;/p&gt;&#xA;&lt;p&gt;如果你还想用 &lt;code&gt;gdirect mount&lt;/code&gt; 把云盘挂载成本地磁盘，在安装命令末尾加上 &lt;code&gt;--with-mount&lt;/code&gt;（PowerShell 用 &lt;code&gt;-WithMount&lt;/code&gt;），脚本会顺带装好依赖（&lt;code&gt;rclone 1.75.2+&lt;/code&gt;，Linux 额外安装 &lt;code&gt;fuse3&lt;/code&gt;，Windows 通过 &lt;code&gt;winget&lt;/code&gt; 安装 &lt;code&gt;WinFsp&lt;/code&gt;）：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;curl -fsSL https://raw.githubusercontent.com/mizorewww/gdirect/main/scripts/install.sh &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; bash -s -- --with-mount&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;sh&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;方式二homebrewmacos--linux&#34;&gt;方式二：Homebrew（macOS / Linux）&lt;/h3&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;brew tap mizorewww/gdirect https://github.com/mizorewww/gdirect&#xA;brew install mizorewww/gdirect/gdirect&#xA;brew install rclone   &lt;span class=&#34;c1&#34;&gt;# 仅挂载功能需要；Linux 另需用系统包管理器安装 fuse3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;sh&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;后续升级执行 &lt;code&gt;brew update &amp;amp;&amp;amp; brew upgrade gdirect&lt;/code&gt; 即可。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;google-cloud-一次性配置指南约-10-分钟&#34;&gt;Google Cloud 一次性配置指南（约 10 分钟）&lt;/h2&gt;&#xA;&lt;p&gt;为了避免共用 OAuth 客户端导致全员配额耗尽（429 限流）或被封禁，&lt;code&gt;gdirect&lt;/code&gt; 不内置公共客户端，而是让你在自己的 Google Cloud 项目里创建一个 &lt;strong&gt;Desktop app（桌面应用）&lt;/strong&gt; 客户端。只需操作一次，Google 完全免费。&lt;/p&gt;&#xA;&lt;h3 id=&#34;1-新建项目并启用-drive-api&#34;&gt;1. 新建项目并启用 Drive API&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;打开 &lt;a href=&#34;https://console.cloud.google.com/&#34;&gt;Google Cloud 控制台&lt;/a&gt;，点击页面顶部的项目选择器，再点击右上角的 &lt;strong&gt;New project（新建项目）&lt;/strong&gt;。名称随意（例如 &lt;code&gt;gdirect&lt;/code&gt;），个人账号的 &lt;strong&gt;Location（位置）&lt;/strong&gt; 保持 &lt;strong&gt;No organization（无组织）&lt;/strong&gt; 即可。点击 &lt;strong&gt;Create（创建）&lt;/strong&gt; 后，确认顶部已经切换到这个新项目。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/227da6b0853f1e9f1bd3d6804f483597ce7217450ec04377447a2b6c51eb51f6.webp&#34; alt=&#34;在 Google Cloud 控制台中新建项目&#34; decoding=&#34;async&#34; fetchpriority=&#34;high&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/25e786a94dff774300f684fbe675525a7f82a90a4f3b8578bdba3ddf1863ec7a.webp 720w, https://mizore.blog/images/227da6b0853f1e9f1bd3d6804f483597ce7217450ec04377447a2b6c51eb51f6.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;打开 &lt;a href=&#34;https://console.cloud.google.com/apis/library/drive.googleapis.com&#34;&gt;Google Drive API&lt;/a&gt; 页面，确认顶部是刚才新建的项目，点击 &lt;strong&gt;Enable（启用）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/b84f0601e820434a99963946f57ff312f6110e50881f0d7c1347e5c6836b9990.webp&#34; alt=&#34;启用 Google Drive API&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/d2d2e7c8c0eb550cd9975ae7df86bb2e549b4d5cb69577b9e4f207c38136ac89.webp 720w, https://mizore.blog/images/b84f0601e820434a99963946f57ff312f6110e50881f0d7c1347e5c6836b9990.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;完成后，页面会显示服务名称 &lt;code&gt;drive.googleapis.com&lt;/code&gt;，状态变为 &lt;strong&gt;Enabled（已启用）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/5fa0e312eeb0c5ce4708715c0655f507d6f1dd8ffc9a1b68e7076f8bcee35ae5.webp&#34; alt=&#34;确认 Google Drive API 已启用&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/5aa003706de18fec8763a7f1f1416adca22b86b1743de5ee1d1c4bd0f9cc63b2.webp 720w, https://mizore.blog/images/5fa0e312eeb0c5ce4708715c0655f507d6f1dd8ffc9a1b68e7076f8bcee35ae5.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;2-配置-oauth-应用&#34;&gt;2. 配置 OAuth 应用&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;打开 &lt;a href=&#34;https://console.cloud.google.com/auth/overview&#34;&gt;Google Auth Platform&lt;/a&gt;，点击 &lt;strong&gt;Get started（开始）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;App information（应用信息）&lt;/strong&gt;：填写应用名称（比如 &lt;code&gt;gdirect&lt;/code&gt;，登录授权时会显示），用户支持邮箱选择你自己的邮箱，点击 &lt;strong&gt;Next（下一步）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/af8d860d5859c321ae318dab7d90038bfbe88e59dc1acf358da250843f599fe4.webp&#34; alt=&#34;填写 OAuth 应用名称与支持邮箱&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/868cf435178d0450318cc0d4956be7eb2de154bacb68cdba2595af118853e3e7.webp 720w, https://mizore.blog/images/af8d860d5859c321ae318dab7d90038bfbe88e59dc1acf358da250843f599fe4.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Audience（受众群体）&lt;/strong&gt;：选择 &lt;strong&gt;External（外部）&lt;/strong&gt;，点击 &lt;strong&gt;Next（下一步）&lt;/strong&gt;（个人账号无法选 Internal）。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/2d099de3d6f1255b437cbc02c7d2d32abc06f3ae9e4de4afbd07d2244469e395.webp&#34; alt=&#34;受众群体选择外部&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/a4a128070612087c09608ca332e65ce1e44ade5851d37de2563181f254d94e8b.webp 720w, https://mizore.blog/images/2d099de3d6f1255b437cbc02c7d2d32abc06f3ae9e4de4afbd07d2244469e395.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Contact information（联系信息）&lt;/strong&gt;：填写你的邮箱，点击 &lt;strong&gt;Next（下一步）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Finish（完成）&lt;/strong&gt;：勾选同意 Google API 服务用户数据政策，点击 &lt;strong&gt;Continue（继续）&lt;/strong&gt;，再点击 &lt;strong&gt;Create（创建）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/1f8b2061017b9ec81fdbf3adb251dc75de414bbc9b3499220dfe593c0abff777.webp&#34; alt=&#34;勾选同意数据政策并创建应用&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/01a49b478f3b483adb4d7ce4ca05a48a229302970ff8546ed3b423e7ddd9be42.webp 720w, https://mizore.blog/images/1f8b2061017b9ec81fdbf3adb251dc75de414bbc9b3499220dfe593c0abff777.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;3-添加-drive-权限范围scope&#34;&gt;3. 添加 Drive 权限范围（Scope）&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在左侧菜单打开 &lt;strong&gt;Data Access（数据访问）&lt;/strong&gt;，点击 &lt;strong&gt;Add or remove scopes（添加或移除范围）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在弹出的面板中勾选 &lt;code&gt;.../auth/drive&lt;/code&gt;（说明为“查看、修改、创建和删除您的所有 Google 云端硬盘文件”）。如果列表里找不到，直接把下面的地址粘贴到 &lt;strong&gt;Manually add scopes（手动添加范围）&lt;/strong&gt; 中，点击 &lt;strong&gt;Add to table（添加到表）&lt;/strong&gt;，再点击 &lt;strong&gt;Update（更新）&lt;/strong&gt;：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;https://www.googleapis.com/auth/drive&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/9ec301365079ddf7b5f38239fec7f012ca291d9dc2f5410cd4a1a55205dd653c.webp&#34; alt=&#34;添加并勾选 auth/drive 权限范围&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/32a60fdf56b7adacce5b4c410c766404bca94ccb029865c830ddb93df091188a.webp 720w, https://mizore.blog/images/9ec301365079ddf7b5f38239fec7f012ca291d9dc2f5410cd4a1a55205dd653c.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：回到数据访问页面后，一定要点击底部的 &lt;strong&gt;Save（保存）&lt;/strong&gt;。只点上一步的 Update 并不会真正保存。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/6cc1c4e76ebd5f981820b1ea892b01e967e8abeef3454594081d06967ce8b71d.webp&#34; alt=&#34;点击页面底部的 Save 保存权限范围&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/d921003eacdd0301478aa6a5ea63019d50bc9e7310d2528a9bf597665ce1de39.webp 720w, https://mizore.blog/images/6cc1c4e76ebd5f981820b1ea892b01e967e8abeef3454594081d06967ce8b71d.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;4-创建桌面客户端并下载-json&#34;&gt;4. 创建桌面客户端并下载 JSON&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在左侧菜单打开 &lt;strong&gt;Clients（客户端）&lt;/strong&gt;，点击 &lt;strong&gt;Create client（创建客户端）&lt;/strong&gt;。应用类型必须选择 &lt;strong&gt;Desktop app（桌面应用）&lt;/strong&gt;（网页客户端、API Key 和服务账号 JSON 都不能用），名称随意（如 &lt;code&gt;gdirect Desktop&lt;/code&gt;），点击 &lt;strong&gt;Create（创建）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/7245cd5c4fb7c5fb6e2382604ec69cf7559c64ed80ad7c5ad560907940406c17.webp&#34; alt=&#34;创建 Desktop app 类型的 OAuth 客户端&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/c2a71a1ebbdc0e129577a8aa2b4a6eeabcfaf85f0dec5be4cc58974d27d8175b.webp 720w, https://mizore.blog/images/7245cd5c4fb7c5fb6e2382604ec69cf7559c64ed80ad7c5ad560907940406c17.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在弹出的窗口中，&lt;strong&gt;关闭之前&lt;/strong&gt;点击 &lt;strong&gt;Download JSON（下载 JSON）&lt;/strong&gt;。窗口关闭后就无法再次下载这个文件；如果不小心关掉了，重新创建一个客户端即可。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/6ef9a135c4a4fd44828da2070ef67b9953b0147a32b507f9e670179b8659ac1a.webp&#34; alt=&#34;下载客户端 JSON 文件&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/485f1069d4436e8f7a5fac4f24ccbe6f5be31322ea4372f073b1cbf27d642e4f.webp 720w, https://mizore.blog/images/6ef9a135c4a4fd44828da2070ef67b9953b0147a32b507f9e670179b8659ac1a.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;5-允许你的账号登录解决-7-天过期问题&#34;&gt;5. 允许你的账号登录（解决 7 天过期问题）&lt;/h3&gt;&#xA;&lt;p&gt;新建的应用默认处于 &lt;strong&gt;Testing（测试）&lt;/strong&gt; 状态，只有测试用户列表中的账号可以登录：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在左侧菜单打开 &lt;strong&gt;Audience（目标对象）&lt;/strong&gt;，在 &lt;strong&gt;Test users（测试用户）&lt;/strong&gt; 下点击 &lt;strong&gt;Add users&lt;/strong&gt;，填入你要在 &lt;code&gt;gdirect&lt;/code&gt; 中使用的 Google 账号邮箱，点击 &lt;strong&gt;Save（保存）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/3dce73deeb5b0257abdb91f13eb51c98d55b87e301b0b905701731dec6dfeba3.webp&#34; alt=&#34;将自己的 Google 账号添加为测试用户&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/599f697dd1e4de390d1093d6e2843b18679aea141211a155d72182222bb59be0.webp 720w, https://mizore.blog/images/3dce73deeb5b0257abdb91f13eb51c98d55b87e301b0b905701731dec6dfeba3.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;测试状态下，登录凭证会在 &lt;strong&gt;7 天&lt;/strong&gt;后失效，届时执行 &lt;code&gt;gdirect login --reauth&lt;/code&gt; 重新授权即可。&lt;/p&gt;&#xA;&lt;p&gt;如果不想每周重新登录，可以直接在 &lt;strong&gt;Audience（目标对象）&lt;/strong&gt; 页面顶部点击 &lt;strong&gt;Publish app（发布应用）&lt;/strong&gt;，把状态改为 &lt;strong&gt;In production（正式版）&lt;/strong&gt;。自己使用的应用不需要经过 Google 审核，切到正式版后就不会再有 7 天过期的限制。&lt;/p&gt;&#xA;&lt;h3 id=&#34;6-在终端登录&#34;&gt;6. 在终端登录&lt;/h3&gt;&#xA;&lt;p&gt;在终端执行下面的命令，把路径换成你下载的 JSON 文件：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;gdirect login --client-secret ~/Downloads/client_secret.json&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;sh&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;程序会自动打开浏览器（如果没有自动打开，请把终端打印的链接复制到&lt;strong&gt;同一台电脑&lt;/strong&gt;的浏览器中打开，因为授权完成后要回调本机的临时端口）。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;选择你的 Google 账号，Google 会提示“此应用未经验证”——这是正常的，因为应用是你自己刚刚创建的：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;测试状态&lt;/strong&gt;：点击 &lt;strong&gt;Continue（继续）&lt;/strong&gt;，不要点“返回到安全网页”。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;正式版（In production）&lt;/strong&gt;：点击左下角 &lt;strong&gt;Advanced（高级）&lt;/strong&gt;，再点击 &lt;strong&gt;Go to 你的应用名 (unsafe)（转至……（不安全））&lt;/strong&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/16d0ca2ca0226f0282eaddc0e7523496d63c8f4f108b2c118226665f6e2a94f1.webp&#34; alt=&#34;在未验证应用提示页点击继续&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/c75581419f26c9fc6343c43a9323ff4ff7227a6261dcf0245a0952b202eeb05e.webp 720w, https://mizore.blog/images/16d0ca2ca0226f0282eaddc0e7523496d63c8f4f108b2c118226665f6e2a94f1.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;核对应用名称和 Google 云端硬盘权限，确认无误后点击 &lt;strong&gt;Continue（继续）&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://mizore.blog/images/2990236874548513a7d3cf18e351ff26d82f5fd485d9d39c70efcdfb38c2885d.webp&#34; alt=&#34;勾选并确认授权访问 Google 云端硬盘&#34; decoding=&#34;async&#34; loading=&#34;lazy&#34; width=&#34;1600&#34; height=&#34;1197&#34; srcset=&#34;https://mizore.blog/images/451c0035c2dd1d54bd2d6fa769390ca0b25378206a7c71a3332a77e5d67675a1.webp 720w, https://mizore.blog/images/2990236874548513a7d3cf18e351ff26d82f5fd485d9d39c70efcdfb38c2885d.webp 1600w&#34; sizes=&#34;(max-width: 760px) calc(100vw - 64px), 704px&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;回到终端，看到 &lt;code&gt;Logged in&lt;/code&gt; 即大功告成。此时可以删掉下载的 JSON 文件（客户端信息已保存在本地 SQLite 中，以后切换账号或重登只需执行 &lt;code&gt;gdirect login --reauth&lt;/code&gt;）。&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;无界面服务器（Headless）提示&lt;/strong&gt;：如果在没有浏览器的远程服务器上使用，可以先在桌面电脑上完成登录，或者用 &lt;code&gt;gdirect login --credentials 路径&lt;/code&gt; 导入已有的 &lt;code&gt;authorized_user&lt;/code&gt; 凭据 JSON；写脚本时也可以直接通过环境变量 &lt;code&gt;GDIRECT_ACCESS_TOKEN&lt;/code&gt; 传入访问令牌。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;大陆网络与代理配置指南&#34;&gt;大陆网络与代理配置指南&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;gdirect&lt;/code&gt; 的流量分两类：登录、账号核验和刷新令牌（&lt;code&gt;accounts.google.com&lt;/code&gt;、&lt;code&gt;oauth2.googleapis.com&lt;/code&gt;、&lt;code&gt;www.googleapis.com&lt;/code&gt;）必须走代理；扫描、云盘 API、上传和下载则要直连扫描出的端点，&lt;strong&gt;不能&lt;/strong&gt;被代理接走。代理软件怎么配，决定了这两类流量能不能各走各的路。&lt;/p&gt;&#xA;&lt;p&gt;0.8.0 起（感谢 &lt;a href=&#34;https://github.com/Liuycer&#34;&gt;@Liuycer&lt;/a&gt; 贡献的 &lt;a href=&#34;https://github.com/mizorewww/gdirect/pull/1&#34;&gt;#1&lt;/a&gt;），最省事的做法是&lt;strong&gt;规则模式 + 系统代理，关掉 TUN&lt;/strong&gt;：登录类请求会读取系统代理设置，传输类请求本来就绕过一切应用层代理，两者天然分开，什么直连规则都不用加。下面先讲这种做法，再讲 TUN 常开时会出什么问题、该怎么配。&lt;/p&gt;&#xA;&lt;h3 id=&#34;做法一推荐规则模式--系统代理关闭-tun&#34;&gt;做法一（推荐）：规则模式 + 系统代理，关闭 TUN&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;代理软件（sing-box / Mihomo / Clash 等）保持规则模式，打开“系统代理”，关闭 TUN。&lt;/strong&gt; 确认 &lt;code&gt;accounts.google.com&lt;/code&gt;、&lt;code&gt;oauth2.googleapis.com&lt;/code&gt; 和 &lt;code&gt;www.googleapis.com&lt;/code&gt; 会走代理（常见规则集的 &lt;code&gt;google&lt;/code&gt; 分组已经包含它们）。不需要添加任何 &lt;code&gt;DIRECT&lt;/code&gt; 规则。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;macOS 和 Windows&lt;/strong&gt;：&lt;code&gt;gdirect&lt;/code&gt; 的登录、账号核验与刷新令牌会自动使用系统里手动配置的 HTTP/HTTPS 代理，直接用即可。&lt;strong&gt;Linux，或者系统代理用的是 PAC&lt;/strong&gt;：给每条命令设置 &lt;code&gt;HTTPS_PROXY&lt;/code&gt;，指向代理软件的 HTTP 或 mixed 端口（&lt;code&gt;HTTP_PROXY&lt;/code&gt; / &lt;code&gt;HTTPS_PROXY&lt;/code&gt; 优先于系统设置，&lt;code&gt;mount&lt;/code&gt; 也会把它们传给 rclone；&lt;code&gt;NO_PROXY&lt;/code&gt; 不要把上面三个域名排除掉）。传输命令里也可能刷新令牌，所以最简单是直接 &lt;code&gt;export&lt;/code&gt; 到 shell 里。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;扫描和传输不受影响&lt;/strong&gt;：扫描、固定 IP 的 API 请求、上传和下载都显式绕过应用层代理，直连扫描出的端点。没有 TUN，代理软件根本看不到这些连接，也就谈不上截胡。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;gdirect login --client-secret ~/Downloads/client_secret.json   &lt;span class=&#34;c1&#34;&gt;# macOS / Windows：自动走系统代理&lt;/span&gt;&#xA;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;HTTPS_PROXY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;http://127.0.0.1:7890                        &lt;span class=&#34;c1&#34;&gt;# Linux / PAC：换成你的代理端口&lt;/span&gt;&#xA;gdirect scan&#xA;gdirect download FILE_ID&#xA;gdirect --google-only download FILE_ID   &lt;span class=&#34;c1&#34;&gt;# 只用 Google 自己的前端，不经过第三方中转&lt;/span&gt;&#xA;&lt;span class=&#34;c1&#34;&gt;# 手动干预仍可用：--sni 强制指定 TLS 身份，--no-sni 干脆不发 SNI（仅下载）&lt;/span&gt;&#xA;gdirect download FILE_ID --no-sni&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;sh&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;&lt;code&gt;gdirect scan --list&lt;/code&gt; 会列出每个端点的延迟、需要哪种 TLS 身份，并把 Google 地址段之外的标为 &lt;code&gt;[third-party relay]&lt;/code&gt;。即使运营商重置携带真实 SNI 的连接也不用手动处理：&lt;code&gt;gdirect&lt;/code&gt; 会自动改用 &lt;code&gt;g.cn&lt;/code&gt; 伪造身份重试，当前端点彻底不通时再换下一个已验证端点。&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;也可以登录后&lt;strong&gt;完全退出&lt;/strong&gt;代理软件，只靠 &lt;code&gt;gdirect&lt;/code&gt; 直连传输。但访问令牌大约 1 小时过期，之后刷新令牌仍需要代理，所以长时间传输时最好让系统代理保持开启。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h3 id=&#34;tun-模式下翻墙软件是怎么把直连流量截胡的&#34;&gt;TUN 模式下，翻墙软件是怎么把直连流量截胡的？&lt;/h3&gt;&#xA;&lt;p&gt;如果你开着 &lt;strong&gt;TUN 虚拟网卡模式&lt;/strong&gt;，所有流量都会先经过翻墙软件的路由引擎，&lt;code&gt;gdirect&lt;/code&gt; 在应用层绕过代理也没用。顺着匹配逻辑看一遍：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;目标 IP 是 Google 自己的海外前端&lt;/strong&gt;：&#xA;&lt;code&gt;gdirect&lt;/code&gt; 在底层把连接目标换成扫描出的端点——通常是 Google 自有地址段里的前端（比如 &lt;code&gt;142.250.x.x&lt;/code&gt;、&lt;code&gt;192.178.x.x&lt;/code&gt;），默认情况下也可能是先试的中转 &lt;code&gt;183.56.143.147&lt;/code&gt;。为了让 Google 前端按主机名正确路由并通过本机 TLS 证书校验，它在 TLS 握手（&lt;code&gt;ClientHello&lt;/code&gt; 的 SNI）和 HTTP &lt;code&gt;Host&lt;/code&gt; 头里发出的依然是 &lt;code&gt;www.googleapis.com&lt;/code&gt;、&lt;code&gt;drive.usercontent.google.com&lt;/code&gt; 和 &lt;code&gt;*.googleusercontent.com&lt;/code&gt;（启用伪造身份时 SNI 是 &lt;code&gt;g.cn&lt;/code&gt;）。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;按 IP 分流：Google 的 IP 默认就走代理&lt;/strong&gt;：&#xA;常见规则模板里有 &lt;code&gt;GEOIP,google&lt;/code&gt; / &lt;code&gt;RULE-SET,google-ip&lt;/code&gt; 之类的 IP 规则，Google 的地址段整体被判给代理。这对浏览器是对的（大部分 Google IP 在国内确实不通），但 &lt;code&gt;gdirect&lt;/code&gt; 好不容易扫出来的“恰好能直连”的前端也会被一并送进代理。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;按域名分流：TUN 流量嗅探（&lt;code&gt;sniff&lt;/code&gt;）把连接截胡&lt;/strong&gt;：&#xA;当 &lt;code&gt;gdirect&lt;/code&gt; 向某个前端 IP 的 443 端口发起连接时，TUN 网卡拦下数据包，通过 &lt;code&gt;sniff&lt;/code&gt;（TLS 域名嗅探）读出 SNI 是 &lt;code&gt;drive.usercontent.google.com&lt;/code&gt; 或 &lt;code&gt;www.googleapis.com&lt;/code&gt;。这些域名在 &lt;a href=&#34;https://github.com/DustinWin/ruleset_geodata&#34;&gt;DustinWin/ruleset_geodata&lt;/a&gt; 等规则集里属于需要翻墙的 &lt;code&gt;google&lt;/code&gt;，而不是国内可直连的 &lt;code&gt;google-cn&lt;/code&gt;（后者只收录 &lt;code&gt;dl.google.com&lt;/code&gt;、&lt;code&gt;g.cn&lt;/code&gt;、&lt;code&gt;fonts.googleapis.com&lt;/code&gt; 这类域名）：&#xA;&lt;ul&gt;&#xA;&lt;li&gt;经过 &lt;code&gt;rule_set: google-cn&lt;/code&gt;（直连）&lt;span class=&#34;math-inline&#34;&gt;&lt;math display=&#34;inline&#34; style=&#34;font-feature-settings: &#39;dtls&#39; off;&#34; xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\rightarrow&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; &lt;strong&gt;未命中&lt;/strong&gt;；&lt;/li&gt;&#xA;&lt;li&gt;走到 &lt;code&gt;rule_set: google&lt;/code&gt;（代理）&lt;span class=&#34;math-inline&#34;&gt;&lt;math display=&#34;inline&#34; style=&#34;font-feature-settings: &#39;dtls&#39; off;&#34; xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&#34;application/x-tex&#34;&gt;\rightarrow&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; &lt;strong&gt;直接命中！&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;即使目标 IP 本来会被某条直连规则放行（例如中转 &lt;code&gt;183.56.143.147&lt;/code&gt; 是国内 IP，会命中 &lt;code&gt;GEOIP,CN&lt;/code&gt;），在主流模板里域名规则优先于 IP 规则，嗅探出的云盘域名照样把它拉回代理。登录要求 &lt;code&gt;www.googleapis.com&lt;/code&gt; 走代理，偏偏传输用的也是这个域名，这就是 TUN 模式下两类流量难以分开的根源。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;TUN 会替所有地址“接通” TCP&lt;/strong&gt;：&#xA;TUN 模式下，翻墙软件会先在本地接受连接，再自己去连目标。于是 &lt;code&gt;gdirect scan&lt;/code&gt; 的第一步 TCP 探测几乎全部“成功”，测到的其实是代理而不是你的网络。&lt;code&gt;scan&lt;/code&gt; 看到一批地址里 90% 以上都能连上时会打印警告，提示你把 Google IP 设为直连后再扫。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;做法二tun-必须常开时让传输绕开嗅探与-ip-规则&#34;&gt;做法二：TUN 必须常开时，让传输绕开嗅探与 IP 规则&lt;/h3&gt;&#xA;&lt;p&gt;如果 TUN 必须 24 小时开着，目标是：&lt;strong&gt;登录类请求照常按域名走代理，&lt;code&gt;gdirect&lt;/code&gt; 的传输连接则直连。&lt;/strong&gt; 有两种配法：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;按进程直连（桌面端最简单）&lt;/strong&gt;：在 &lt;code&gt;GEOSITE,google&lt;/code&gt;、&lt;code&gt;GEOIP,google&lt;/code&gt; 等规则之前，先放登录与刷新令牌的代理规则，再放 &lt;code&gt;PROCESS-NAME,gdirect,DIRECT&lt;/code&gt;。代价是 &lt;code&gt;gdirect login&lt;/code&gt; 时核验账号的那次 &lt;code&gt;www.googleapis.com&lt;/code&gt; 请求也会被这条进程规则判为直连而失败，所以&lt;strong&gt;登录那一下&lt;/strong&gt;要临时切回做法一（关 TUN、开系统代理），或暂时停用这条进程规则。登录很少发生（正式版应用只需一次），之后的刷新令牌只用 &lt;code&gt;oauth2.googleapis.com&lt;/code&gt;，排在进程规则前面，照常走代理。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;对扫描出的 IP 关闭嗅探&lt;/strong&gt;：在代理软件的嗅探设置里把 &lt;code&gt;gdirect scan --list&lt;/code&gt; 列出的端点排除掉，再让这些 IP 直连（或者干脆让 Google 的 IP 段直连）。这样传输连接不带域名，&lt;code&gt;www.googleapis.com&lt;/code&gt; 的代理规则就只会命中登录请求。缺点是端点会随扫描变化，IP 段直连还会让浏览器里的 Google 服务断掉。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：普通浏览器访问 &lt;code&gt;www.googleapis.com&lt;/code&gt; 时连的是 DNS 给出的、在国内被黑洞的 IP，不会像 &lt;code&gt;gdirect&lt;/code&gt; 这样换成扫描出的可用前端。所以不要为了 &lt;code&gt;gdirect&lt;/code&gt; 把云盘域名或 Google 的 IP 段整体设为 &lt;code&gt;DIRECT&lt;/code&gt;，那会让浏览器里依赖它们的网页和 Google 服务全部断掉。fake-ip 也不影响传输：&lt;code&gt;gdirect&lt;/code&gt; 直接连 IP、不查 DNS，只是 &lt;code&gt;scan&lt;/code&gt; 时从 DNS 拿到的 &lt;code&gt;198.18.x.x&lt;/code&gt; 假地址会被丢弃，候选少几个而已。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;以 &lt;strong&gt;Mihomo（Clash）&lt;/strong&gt; 为例，按进程直连的配法是在配置文件的 &lt;code&gt;GEOSITE,google&lt;/code&gt;、&lt;code&gt;GEOIP,google&lt;/code&gt; 等规则之前加入：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;nt&#34;&gt;rules&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;  &lt;span class=&#34;c&#34;&gt;# 1. 登录与刷新令牌走代理（必须排在进程规则之前）&lt;/span&gt;&#xA;  - &lt;span class=&#34;l&#34;&gt;DOMAIN,accounts.google.com,PROXY&lt;/span&gt;&#xA;  - &lt;span class=&#34;l&#34;&gt;DOMAIN,oauth2.googleapis.com,PROXY&lt;/span&gt;&#xA;&#xA;  &lt;span class=&#34;c&#34;&gt;# 2. gdirect 进程的其余流量（扫描、API、下载、上传）强制直连，&lt;/span&gt;&#xA;  &lt;span class=&#34;c&#34;&gt;#    不被 sniff 出的域名规则或 Google IP 规则截胡。&lt;/span&gt;&#xA;  &lt;span class=&#34;c&#34;&gt;#    gdirect login 时请临时关 TUN 改用系统代理，或停用这一条&lt;/span&gt;&#xA;  - &lt;span class=&#34;l&#34;&gt;PROCESS-NAME,gdirect,DIRECT&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;yaml&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;如果你用的是 &lt;strong&gt;sing-box&lt;/strong&gt;，同样可以在 &lt;code&gt;route.rules&lt;/code&gt; 的 &lt;code&gt;rule_set: [&amp;quot;google&amp;quot;]&lt;/code&gt; 和 Google IP 规则之前，先放 &lt;code&gt;oauth2.googleapis.com&lt;/code&gt; 的代理规则，再放一条 &lt;code&gt;process_name: gdirect&lt;/code&gt; 的直连规则。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;日常使用与排障速查&#34;&gt;日常使用与排障速查&lt;/h2&gt;&#xA;&lt;h3 id=&#34;常用命令&#34;&gt;常用命令&lt;/h3&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;gdirect                                   &lt;span class=&#34;c1&#34;&gt;# 等同于 gdirect browse，打开交互式 TUI&lt;/span&gt;&#xA;gdirect browse -o ~/Downloads             &lt;span class=&#34;c1&#34;&gt;# 指定下载目录（默认 ./downloads）&lt;/span&gt;&#xA;gdirect ls root --json                    &lt;span class=&#34;c1&#34;&gt;# 列出目录内容（支持 JSON 输出）&lt;/span&gt;&#xA;gdirect download 文件ID或分享链接          &lt;span class=&#34;c1&#34;&gt;# 下载文件，断流重跑同一命令即可续传&lt;/span&gt;&#xA;gdirect download FILE_ID -o file.zip&#xA;gdirect download DOC_ID --export application/pdf -o doc.pdf&#xA;gdirect upload ./file.zip                 &lt;span class=&#34;c1&#34;&gt;# 32 MiB 分块上传，支持断点续传&lt;/span&gt;&#xA;gdirect upload ./file.zip --parent FOLDER_ID --name backup.zip&#xA;gdirect mount                             &lt;span class=&#34;c1&#34;&gt;# 挂载到 ~/GoogleDrive（macOS 走原生 NFS 免 FUSE）&lt;/span&gt;&#xA;gdirect unmount                           &lt;span class=&#34;c1&#34;&gt;# 卸载并清理临时导出的凭据&lt;/span&gt;&#xA;gdirect scan                              &lt;span class=&#34;c1&#34;&gt;# 重新扫描；scan --list 查看结果&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;sh&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;0.6.0 起支持多个 Google 账号，共用同一个 OAuth 客户端：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;gdirect login --add                       &lt;span class=&#34;c1&#34;&gt;# 在浏览器里登录另一个账号&lt;/span&gt;&#xA;gdirect accounts                          &lt;span class=&#34;c1&#34;&gt;# 列出账号，* 为默认账号&lt;/span&gt;&#xA;gdirect switch work                       &lt;span class=&#34;c1&#34;&gt;# 把 work@… 设为默认&lt;/span&gt;&#xA;gdirect -a me@gmail.com browse            &lt;span class=&#34;c1&#34;&gt;# 只在这一条命令里用某个账号（也可设 GDIRECT_ACCOUNT）&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;span class=&#34;lang&#34;&gt;sh&lt;/span&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figure&gt;&#xA;&lt;p&gt;新版 &lt;code&gt;browse&lt;/code&gt; 仿照 &lt;a href=&#34;https://github.com/sxyazi/yazi&#34;&gt;yazi&lt;/a&gt; 的三栏布局，并排显示上级文件夹、当前文件夹和预览，支持多选标记、整目录下载和云端文件操作。按 &lt;code&gt;?&lt;/code&gt; 可查看全部按键：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;按键&lt;/th&gt;&#xA;&lt;th&gt;操作&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;j / k&lt;/code&gt;、&lt;code&gt;↑ / ↓&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;选择&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;g / G&lt;/code&gt;、Home/End&lt;/td&gt;&#xA;&lt;td&gt;跳到第一项 / 最后一项&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;Ctrl-d / Ctrl-u&lt;/code&gt;、PgDn/PgUp&lt;/td&gt;&#xA;&lt;td&gt;向下或向上翻半页 / 一页&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;Enter、&lt;code&gt;l / →&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;进入文件夹&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;h / ←&lt;/code&gt;、Backspace&lt;/td&gt;&#xA;&lt;td&gt;返回上级&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;按名称筛选当前文件夹。Enter 保留筛选，Esc 清除&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;Ctrl-r&lt;/code&gt;、F5&lt;/td&gt;&#xA;&lt;td&gt;刷新&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;空格&lt;/td&gt;&#xA;&lt;td&gt;标记或取消标记；切换文件夹后标记仍保留&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;Ctrl-a&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;标记当前显示的所有项目（再按一次取消）&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;Esc&lt;/td&gt;&#xA;&lt;td&gt;依次清除筛选、标记、剪贴板&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;a&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;新建空文件；名称以 &lt;code&gt;/&lt;/code&gt; 结尾则新建文件夹&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;重命名&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;y&lt;/code&gt; / &lt;code&gt;x&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;复制 / 剪切已标记的项目（没有标记时为当前选中的项目）&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;粘贴到当前文件夹。剪切的项目会被移动，复制的文件会生成副本&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;D&lt;/code&gt;、Delete&lt;/td&gt;&#xA;&lt;td&gt;把已标记的项目移到 Drive 回收站（会先确认）&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;u&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;上传本地文件到当前文件夹&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;d&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;把已标记的项目加入下载队列。文件夹会连同所有子文件夹一起下载；Google 文档、表格、幻灯片导出为 PDF&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;+ / -&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;调整下一个下载的并发连接数（&lt;code&gt;1–16&lt;/code&gt;），并保存为默认值&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;c&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;取消当前下载并保留已下载部分&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;R&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;重试失败或已取消的下载&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;X&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;移除尚未开始的下载&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;q&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;退出&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;下载逐个进行，文件列表会显示每个文件的状态（排队、下载中、已保存、失败），选中失败的文件可以看到完整错误。&lt;/p&gt;&#xA;&lt;p&gt;如果遇到特殊网络环境，还支持手动干预传输层参数：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;--google-only&lt;/code&gt;（全局选项，也可设 &lt;code&gt;GDIRECT_GOOGLE_ONLY=1&lt;/code&gt;）：不使用任何第三方中转，扫描和传输只用 Google 公布的自有地址段内的端点；&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;--ip IP&lt;/code&gt;：强制使用指定的 IP，跳过自动扫描结果，也不做端点切换；&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;--sni HOST&lt;/code&gt; / &lt;code&gt;--no-sni&lt;/code&gt;：手动指定 TLS 身份，或干脆不发 SNI（&lt;code&gt;--sni&lt;/code&gt; 也适用于 &lt;code&gt;ls&lt;/code&gt;、&lt;code&gt;upload&lt;/code&gt;；&lt;code&gt;--no-sni&lt;/code&gt; 仅下载）。不指定时自动使用扫描出的身份，失败自动回退；&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;--ca-cert FILE&lt;/code&gt;：用指定的 CA 证书包代替内置的 Google GTS 根证书；&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;-j N&lt;/code&gt;：仅对当前命令设置并发连接数（&lt;code&gt;1–16&lt;/code&gt;）；&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;gdirect scan --ips FILE&lt;/code&gt;：探测自定义候选 IP 列表（每行一个公网 IP，&lt;code&gt;#&lt;/code&gt; 后为注释）。列表里的地址不受 Google 地址段限制，但不在段内的会被标为 &lt;code&gt;[third-party relay]&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;常见报错速查表&#34;&gt;常见报错速查表&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;现象&lt;/th&gt;&#xA;&lt;th&gt;处理方法&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;access_denied&lt;/code&gt; 或提示应用正在测试&lt;/td&gt;&#xA;&lt;td&gt;把当前账号加为测试用户，或发布应用（见配置指南第 5 步）&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;redirect_uri_mismatch&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;下载的 JSON 不是 &lt;strong&gt;Desktop app&lt;/strong&gt; 客户端，按第 4 步重新创建&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;Drive API 403 … insufficient permission&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;在客户端所在的&lt;strong&gt;同一个项目&lt;/strong&gt;中启用 Drive API（第 1 步），并保存 Drive 权限范围（第 3 步底部点 Save）；然后执行 &lt;code&gt;gdirect login --reauth&lt;/code&gt;&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;refresh credential expired or revoked&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;执行 &lt;code&gt;gdirect login --reauth&lt;/code&gt;。如果每 7 天出现一次，把应用从测试状态发布为正式版（第 5 步）&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;scan&lt;/code&gt; 提示 &lt;code&gt;almost every address answered TCP&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;TUN 代理在替所有地址接通连接，扫描测的是代理。按上文关闭 TUN，或让 &lt;code&gt;gdirect&lt;/code&gt; 进程 / Google IP 走直连后重扫&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;no usable Google front end found&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;当前网络没扫到能用的端点。先排除 TUN 代理；仍不行可过段时间重扫（可直连的前端会变化），或用 &lt;code&gt;gdirect scan --ips FILE&lt;/code&gt; 提供自己的候选&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;下载中途报 &lt;code&gt;libaria2 code 1&lt;/code&gt;（连接或 TLS 失败）&lt;/td&gt;&#xA;&lt;td&gt;真实 SNI 被运营商重置或端点失效。&lt;code&gt;gdirect&lt;/code&gt; 会先换 &lt;code&gt;g.cn&lt;/code&gt; 身份重试，再换到下一个已验证端点续传，并把失效端点标死；所有端点都失败时才报错，重跑同一命令会重新扫描。也可手动 &lt;code&gt;--sni g.cn&lt;/code&gt; / &lt;code&gt;--no-sni&lt;/code&gt;&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;登录时报 &lt;code&gt;cannot verify the account&lt;/code&gt; 或 &lt;code&gt;cannot reach Google&#39;s token endpoint&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;登录、账号核验和刷新令牌没能走代理。确认代理软件开了系统代理且 &lt;code&gt;accounts.google.com&lt;/code&gt;、&lt;code&gt;oauth2.googleapis.com&lt;/code&gt;、&lt;code&gt;www.googleapis.com&lt;/code&gt; 走代理；Linux 或 PAC 下设置 &lt;code&gt;HTTPS_PROXY&lt;/code&gt;；TUN 下按进程直连时，登录那一下请临时关 TUN（见做法二）&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;scan --list&lt;/code&gt; 里某个 IP 带 &lt;code&gt;[third-party relay]&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;这个地址不在 Google 公布的地址段里，是第三方中转（如默认先试的 &lt;code&gt;183.56.143.147&lt;/code&gt;）。它读不到流量内容，但能看到你的 IP；介意就用 &lt;code&gt;--google-only&lt;/code&gt;&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;勘误与改进18356143147-不是-google&#34;&gt;勘误与改进：183.56.143.147 不是 Google&lt;/h2&gt;&#xA;&lt;p&gt;本文初版发出后，读者 Ken 来信指出了几处事实错误：&lt;code&gt;183.56.143.147&lt;/code&gt; 并不是 Google 的服务器；国内的 &lt;code&gt;google-cn&lt;/code&gt; 节点不会替你访问云盘；“GFE”也不是一类 IP 或一种服务器。我们花了一天逐条复核，他说的都对。这一节把复核过程、新的做法和它的局限写清楚。感谢 Ken 认真读完并写信。&lt;/p&gt;&#xA;&lt;h3 id=&#34;初版错在哪里&#34;&gt;初版错在哪里&lt;/h3&gt;&#xA;&lt;p&gt;初版的说法是：&lt;code&gt;183.56.143.147&lt;/code&gt; 是 Google 在广州电信的 &lt;code&gt;google-cn&lt;/code&gt; 边缘节点；这些国内节点跑着 Google 的前端（GFE），经 Google 内网连到云盘后端；所以 &lt;code&gt;gdirect&lt;/code&gt; 连它，流量“物理上不过墙”。扫描也以它为内置种子。&lt;/p&gt;&#xA;&lt;p&gt;这个 IP 来自 &lt;a href=&#34;https://github.com/SpaceTimee/Sheas-Cealer&#34;&gt;Sheas-Cealer&lt;/a&gt; 使用的 Cealing-Host 列表，那里所有 Google 条目都指向同一个 IP。它出示的是 Google 的真证书，Drive API 也能正常返回，我们当时据此认定它就是 Google，没有再查它到底是谁。&lt;/p&gt;&#xA;&lt;h3 id=&#34;验证一google-看到的客户端是谁&#34;&gt;验证一：Google 看到的“客户端”是谁&lt;/h3&gt;&#xA;&lt;p&gt;Google 有个诊断接口 &lt;code&gt;https://redirector.googlevideo.com/report_mapping?di=no&lt;/code&gt;，会返回它看到的客户端 IP。我们在中国联通上海的宽带上测试（绑定物理网卡，绕开本机 VPN）：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;直接请求时，Google 报告的是我们自己的 IP；&lt;/li&gt;&#xA;&lt;li&gt;经 &lt;code&gt;183.56.143.147&lt;/code&gt; 请求时，Google 报告的是 &lt;code&gt;122.10.103.81&lt;/code&gt;——一个香港的地址（AS136897，Enjoyvc）。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;也就是说，连接是从香港进入 Google 的。再查归属：whois 显示 &lt;code&gt;183.56.143.147&lt;/code&gt; 属于 CHINANET 广东；它和 &lt;code&gt;122.10.103.81&lt;/code&gt; 都不在 Google 公布的地址段（goog.json）里。结论：&lt;strong&gt;它是一台第三方 TCP 中转（反向代理），把流量转发到香港，再从那里连到 Google。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;它不碰 TLS，只是原样转发字节流，所以客户端看到的是 Google 真正的 GTS 证书，&lt;code&gt;gdirect&lt;/code&gt; 也照常按 Google 的 GTS 根校验通过。这意味着：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;中转读不到&lt;/strong&gt;：访问令牌、文件内容、请求路径，全部在端到端的 TLS 里；它也无法伪造证书做中间人。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;中转看得到&lt;/strong&gt;：你的 IP、流量大小和时间；以及在不用伪造身份时，TLS SNI 里的主机名（比如 &lt;code&gt;www.googleapis.com&lt;/code&gt;）。用 &lt;code&gt;g.cn&lt;/code&gt; 伪造身份时，它只看到一个连往 &lt;code&gt;g.cn&lt;/code&gt; 的连接。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;验证二国内真正的-google-节点能不能用&#34;&gt;验证二：国内真正的 Google 节点能不能用&lt;/h3&gt;&#xA;&lt;p&gt;Google 在国内确实有节点，但它们不是初版说的那样。我们测了两类：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;203.208.40.x&lt;/code&gt; / &lt;code&gt;203.208.41.x&lt;/code&gt;，登记在 GOOGLECN（北京谷翔，Google 的中国实体）名下；&lt;/li&gt;&#xA;&lt;li&gt;运营商网内的 Google 缓存节点，比如联通上海网内的 &lt;code&gt;211.95.34.x&lt;/code&gt;——国内 DNS 给 &lt;code&gt;dl.google.com&lt;/code&gt;、&lt;code&gt;fonts.googleapis.com&lt;/code&gt;、&lt;code&gt;g.cn&lt;/code&gt;、&lt;code&gt;www.gstatic.com&lt;/code&gt; 返回的就是它们。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;它们都能建立 TCP 和 TLS，出示的也是 Google 证书。但按 &lt;code&gt;Host&lt;/code&gt; 头分别请求各个服务，结果一目了然：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&lt;thead&gt;&#xA;&lt;tr&gt;&#xA;&lt;th&gt;请求的主机&lt;/th&gt;&#xA;&lt;th&gt;结果&lt;/th&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/thead&gt;&#xA;&lt;tbody&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;www.google.cn&lt;/code&gt;、&lt;code&gt;fonts.googleapis.com&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;200&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;www.gstatic.com/ipranges/goog.json&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;200&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;www.googleapis.com/drive/v3/…&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;404&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;oauth2.googleapis.com&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;404&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;tr&gt;&#xA;&lt;td&gt;&lt;code&gt;drive.usercontent.google.com&lt;/code&gt;&lt;/td&gt;&#xA;&lt;td&gt;404&lt;/td&gt;&#xA;&lt;/tr&gt;&#xA;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;它们只服务一份白名单，&lt;strong&gt;云盘不在其中&lt;/strong&gt;。所以“国内节点经 Google 内网连到云盘”这条路并不存在。（不过它们能返回 Google 的 IP 段列表，后面会用到。）&lt;/p&gt;&#xA;&lt;h3 id=&#34;验证三其他直觉上的候选&#34;&gt;验证三：其他直觉上的候选&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;国内 DNS 给 &lt;code&gt;www.googleapis.com&lt;/code&gt;、&lt;code&gt;drive.usercontent.google.com&lt;/code&gt; 返回的是真的 Google IP（&lt;code&gt;74.125.x&lt;/code&gt;、&lt;code&gt;172.217.x&lt;/code&gt;、&lt;code&gt;142.250.157.x&lt;/code&gt; 等），但 TCP 直接超时：被黑洞了。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;drive.google.com&lt;/code&gt; 的 DNS 被污染，返回的 &lt;code&gt;69.63.184.30&lt;/code&gt;、&lt;code&gt;128.242.240.59&lt;/code&gt; 根本不是 Google。&lt;/li&gt;&#xA;&lt;li&gt;Firebase Hosting 和 Google Cloud 负载均衡的 IP 出示的是客户自己的证书，不能用来访问 Google API。&lt;/li&gt;&#xA;&lt;li&gt;一个 Cloud Storage 的 IP（&lt;code&gt;142.250.73.123&lt;/code&gt;）能连通，但它是 GCS 专用前端：不管 &lt;code&gt;Host&lt;/code&gt; 写什么，都按 GCS 回答。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;验证四扫描-google-自己的前端&#34;&gt;验证四：扫描 Google 自己的前端&lt;/h3&gt;&#xA;&lt;p&gt;既然 DNS 给出的 IP 都被封了，那 DNS 不给的呢？Google 公布了自己全部的地址段（goog.json）和租给 Cloud 客户的部分（cloud.json），两者相减就是 Google 自家服务的地址。我们扫描了其中前端最集中的几块（&lt;code&gt;142.250.0.0/15&lt;/code&gt;、&lt;code&gt;172.217.0.0/16&lt;/code&gt;、&lt;code&gt;172.253.0.0/16&lt;/code&gt;、&lt;code&gt;173.194.0.0/16&lt;/code&gt;、&lt;code&gt;74.125.0.0/16&lt;/code&gt;、&lt;code&gt;192.178.0.0/15&lt;/code&gt;、&lt;code&gt;64.233.160.0/19&lt;/code&gt;、&lt;code&gt;66.102.0.0/20&lt;/code&gt;、&lt;code&gt;108.177.0.0/17&lt;/code&gt;、&lt;code&gt;209.85.128.0/17&lt;/code&gt;、&lt;code&gt;216.58.192.0/19&lt;/code&gt;、&lt;code&gt;216.239.32.0/19&lt;/code&gt;、&lt;code&gt;108.170.192.0/18&lt;/code&gt;、&lt;code&gt;72.14.192.0/18&lt;/code&gt;），约 2500 个 /24，每个 /24 试 12 个主机号（&lt;code&gt;.4 .14 .91 .100 .113 .123 .132 .138 .155 .187 .207 .219&lt;/code&gt;）。对每个地址：先试 TCP 443；连得上的，用真实 SNI 和 &lt;code&gt;g.cn&lt;/code&gt; 两种身份各做一次完整验证——无凭证请求 Drive API 必须返回预期的 401 JSON，公开测试文件必须下载正确。&lt;/p&gt;&#xA;&lt;figure class=&#34;stats&#34;&gt;&lt;figcaption&gt;&lt;span&gt;Google 自有前端扫描结果&lt;/span&gt;&lt;span class=&#34;key&#34;&gt;中国联通上海 · 2026-10-26&lt;/span&gt;&lt;/figcaption&gt;&lt;p class=&#34;stats-group&#34;&gt;&lt;b&gt;漏斗&lt;/b&gt;约 2500 个 /24 × 12 个主机号&lt;/p&gt;&lt;dl class=&#34;kpi&#34;&gt;&lt;div&gt;&lt;dt&gt;探测地址&lt;/dt&gt;&lt;dd&gt;30528&lt;/dd&gt;&lt;/div&gt;&lt;div&gt;&lt;dt&gt;TCP 443 有应答&lt;/dt&gt;&lt;dd&gt;1423&lt;/dd&gt;&lt;dd class=&#34;s&#34;&gt;4.7%&lt;/dd&gt;&lt;/div&gt;&lt;div&gt;&lt;dt&gt;完全可用&lt;/dt&gt;&lt;dd class=&#34;up&#34;&gt;+104&lt;/dd&gt;&lt;dd class=&#34;s&#34;&gt;Drive API + 测试下载均通过&lt;/dd&gt;&lt;/div&gt;&lt;div&gt;&lt;dt&gt;分布&lt;/dt&gt;&lt;dd&gt;约 65 个 /24&lt;/dd&gt;&lt;dd class=&#34;s&#34;&gt;按 /24 成簇，192.179.50/60/61 各有 7 个&lt;/dd&gt;&lt;/div&gt;&lt;/dl&gt;&lt;p class=&#34;stats-group&#34;&gt;&lt;b&gt;性能&lt;/b&gt;从同一条联通线路测得&lt;/p&gt;&lt;dl class=&#34;kpi&#34;&gt;&lt;div&gt;&lt;dt&gt;TCP 建连&lt;/dt&gt;&lt;dd&gt;268 ms&lt;/dd&gt;&lt;dd class=&#34;s&#34;&gt;p50；p90 为 511 ms&lt;/dd&gt;&lt;/div&gt;&lt;div&gt;&lt;dt&gt;单连接下载&lt;/dt&gt;&lt;dd&gt;4–6 MB/s&lt;/dd&gt;&lt;dd class=&#34;s&#34;&gt;27 MB 文件，Host 为 dl.google.com&lt;/dd&gt;&lt;/div&gt;&lt;/dl&gt;&lt;p class=&#34;data-note&#34;&gt;每个可用地址都同时通过了真实 SNI 和 g.cn 伪造身份两种验证。&lt;/p&gt;&lt;/figure&gt;&#xA;&lt;figure class=&#34;data&#34;&gt;&lt;figcaption&gt;TCP 能连上但不能用的 1319 个地址&lt;/figcaption&gt;&lt;div class=&#34;data-scroll&#34; tabindex=&#34;0&#34;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;原因&lt;/th&gt;&lt;th class=&#34;num&#34;&gt;数量&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;证书不匹配&lt;/td&gt;&lt;td class=&#34;num bar&#34; style=&#34;--a:0.0%;--b:100.0%&#34;&gt;约 1030&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TLS 超时或重置&lt;/td&gt;&lt;td class=&#34;num bar&#34; style=&#34;--a:0.0%;--b:28.2%&#34;&gt;约 290&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p class=&#34;data-note&#34;&gt;证书不匹配的大多是只服务 Cloud Storage 的前端；TLS 超时 / 重置可能是按 SNI 的干扰。&lt;/p&gt;&lt;/figure&gt;&#xA;&lt;p&gt;主机号的规律也很明显：&lt;code&gt;.187&lt;/code&gt;、&lt;code&gt;.207&lt;/code&gt;、&lt;code&gt;.219&lt;/code&gt; 一个都没成功过（都是 Cloud Storage 专用前端）；&lt;code&gt;.100&lt;/code&gt;、&lt;code&gt;.113&lt;/code&gt;、&lt;code&gt;.155&lt;/code&gt;、&lt;code&gt;.132&lt;/code&gt;、&lt;code&gt;.138&lt;/code&gt;、&lt;code&gt;.91&lt;/code&gt; 成功率最高。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;它们真的是 Google，而且是直连吗？&lt;/strong&gt; 三个证据：地址都在 goog.json 里；whois 归属 Google LLC；从联通线路 traceroute，路径直接进入 Google 骨干网（&lt;code&gt;142.251.x&lt;/code&gt;、&lt;code&gt;192.178.x&lt;/code&gt;、&lt;code&gt;172.253.x&lt;/code&gt;），中间没有任何第三方。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;为什么这些前端没被封？&lt;/strong&gt; 我们的推测是：国内 DNS 从来不会把它们解析给用户，所以它们还没进封锁名单。这也意味着用的人多了，它们随时可能被封——这正是 &lt;code&gt;gdirect&lt;/code&gt; 不内置固定 IP 列表、而是每次动态扫描的原因。&lt;/p&gt;&#xA;&lt;h3 id=&#34;顺带澄清gfe-是什么&#34;&gt;顺带澄清：GFE 是什么&lt;/h3&gt;&#xA;&lt;p&gt;初版把“GFE”当成一类节点在用，这不准确。Google 的服务器有 GWS（Google Web Server）、GVS（视频服务器）、GGC（运营商网内的缓存节点）等类别；而 &lt;strong&gt;Google Front End（GFE）是 Google 基础设施里的一个组件&lt;/strong&gt;，&lt;a href=&#34;https://docs.cloud.google.com/docs/security/infrastructure/design#google-frontend-service&#34;&gt;Google 安全白皮书&lt;/a&gt;里描述它负责 TLS 终止、DDoS 防护和负载均衡，再把请求转给后端服务。本文现在只在这个意义上使用“GFE”：它是 Google 的前端服务层，按 &lt;code&gt;Host&lt;/code&gt; 路由请求；它不是一个 IP 类别。文中说“Google 前端”时，指的是 Google 自有地址段里由这一层对外服务的服务器。&lt;/p&gt;&#xA;&lt;h3 id=&#34;gdirect-070--080-改了什么&#34;&gt;gdirect 0.7.0 / 0.8.0 改了什么&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Google 地址段内置并每周更新&lt;/strong&gt;（0.7.0，&lt;code&gt;src/ranges.rs&lt;/code&gt;、&lt;code&gt;src/google-ranges.txt&lt;/code&gt;、&lt;code&gt;scripts/update-ranges.py&lt;/code&gt;）：内置一份 goog.json 减去 cloud.json 的快照，扫描时每周更新一次。更新先走系统网络——在国内，&lt;code&gt;www.gstatic.com&lt;/code&gt; 解析到的正是上面那种运营商网内的 Google 缓存，它会返回 IP 段列表——不通再经一个已验证的端点去取；明显不完整的列表会被拒绝。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;扫描改为 Google 前端扫描&lt;/strong&gt;（0.7.0，&lt;code&gt;src/scan.rs&lt;/code&gt;）：候选只来自 Google 地址段内：之前验证过的端点、DNS 结果（被污染的自然落在段外、被丢弃）、我们这次扫出可用前端的 /24（&lt;code&gt;src/frontend-seeds.txt&lt;/code&gt;），再是前端集中的核心地址块，最后是其余地址，每部分为每个用户打乱顺序。只试成功率高的主机号。每批 2048 个地址先做 TCP 探测，再对有应答的做完整验证；某个地址可用，就优先试它所在 /24 的其他地址；凑够 8 个已验证端点即停。几乎所有地址都能连上时提示 TUN 代理在拦截。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;端点切换&lt;/strong&gt;（0.7.0，&lt;code&gt;Api::deliver&lt;/code&gt; 与下载）：当前端点连不上时，先换 TLS 身份，再依次换到一天内验证过的其他端点；下载在新端点上续传。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;中转的处理&lt;/strong&gt;：0.7.0 曾把 &lt;code&gt;183.56.143.147&lt;/code&gt; 从扫描里去掉，并从已有数据库里清除一次。实际用下来，这台中转速度快、也很稳定，所以 &lt;strong&gt;0.8.0 改为默认先试它，不通再用扫描出的 Google 前端&lt;/strong&gt;，并取消了 0.7.0 的清除。同时新增全局选项 &lt;strong&gt;&lt;code&gt;--google-only&lt;/code&gt;&lt;/strong&gt;（环境变量 &lt;code&gt;GDIRECT_GOOGLE_ONLY=1&lt;/code&gt;）：扫描跳过中转，传输只用 Google 地址段内的端点。无论哪种模式，Google 地址段之外的端点在 &lt;code&gt;scan&lt;/code&gt; 和 &lt;code&gt;scan --list&lt;/code&gt; 里都会标为 &lt;code&gt;[third-party relay]&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;登录走系统代理&lt;/strong&gt;（0.8.0，&lt;a href=&#34;https://github.com/Liuycer&#34;&gt;@Liuycer&lt;/a&gt; 贡献的 &lt;a href=&#34;https://github.com/mizorewww/gdirect/pull/1&#34;&gt;#1&lt;/a&gt;）：登录、账号核验和刷新令牌会使用 macOS / Windows 的 HTTP/HTTPS 系统代理（以及 &lt;code&gt;HTTPS_PROXY&lt;/code&gt;），关掉 TUN、开着规则模式的系统代理就能登录；扫描、固定 IP 的 API 请求、上传和下载仍然直连。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;怎么选：中转读不到你的令牌和文件，但它的运营者能看到你的 IP、流量规模和时间，以及（不用伪造身份时）你连的是哪个 Google 主机，而且我们不知道运营者是谁。介意这些，就用 &lt;code&gt;--google-only&lt;/code&gt;；需要时也可以用 &lt;code&gt;--sni g.cn&lt;/code&gt; 强制伪造身份，让中转连主机名也看不到。&lt;/p&gt;&#xA;&lt;h3 id=&#34;为什么不内置网上搜到的反向代理&#34;&gt;为什么不内置网上搜到的反向代理&lt;/h3&gt;&#xA;&lt;p&gt;用 fofa 这类网络空间搜索引擎，能搜出不少出示 Google 证书的反向代理，有人建议直接内置一批。我们没有这么做：它们多半是别人配置失误、无意中对外开放的服务器，未经授权批量使用是在蹭别人的机器和带宽；运营者能看到每个用户的 IP 和访问时间；而且这类服务器往往很快就会被关掉或被封，内置进去很快就成了死地址。&lt;code&gt;183.56.143.147&lt;/code&gt; 是一个公开列表里长期使用的中转，我们把它明确标为第三方、默认先试、并提供 &lt;code&gt;--google-only&lt;/code&gt; 关掉；除此之外，&lt;code&gt;gdirect&lt;/code&gt; 不会主动去用任何第三方服务器。你自己找到的候选仍然可以用 &lt;code&gt;gdirect scan --ips FILE&lt;/code&gt; 测试，段外的同样会被标出来。&lt;/p&gt;&#xA;&lt;h3 id=&#34;局限&#34;&gt;局限&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;以上全部结果来自&lt;strong&gt;一条线路（中国联通上海）、一个地点、一天（2026-10-26）&lt;/strong&gt;。电信、移动和其他地区没有验证过，可直连的前端可能完全不同，也可能一个都没有。&lt;/li&gt;&#xA;&lt;li&gt;这些前端能直连，很可能只是因为还没被注意到。它们可能随时被封，被封后 &lt;code&gt;gdirect&lt;/code&gt; 只能重新扫描、换别的地址；扫不到时就只能回到中转或代理。&lt;/li&gt;&#xA;&lt;li&gt;单连接 4–6 MB/s、建连 p50 268 ms，是跨境直连的水平，谈不上“跑满宽带”；多连接并发会快一些，但取决于线路。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;如果你在其他运营商或地区测出了结果（不管能不能用），欢迎告诉我。&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;技术解析底层是怎么实现的&#34;&gt;技术解析：底层是怎么实现的？&lt;/h2&gt;&#xA;&lt;p&gt;如果你对 &lt;code&gt;gdirect&lt;/code&gt; 内部是如何用 Rust 配合定制 &lt;code&gt;libaria2&lt;/code&gt; 实现上述机制感兴趣，下面结合仓库源码逐一拆解。&lt;/p&gt;&#xA;&lt;h3 id=&#34;1-候选从哪来google-自有地址段与扫描顺序&#34;&gt;1. 候选从哪来：Google 自有地址段与扫描顺序&lt;/h3&gt;&#xA;&lt;p&gt;第一次运行传输类命令时，如果本地 SQLite 里没有 24 小时内验证过的可用端点，&lt;code&gt;src/scan.rs&lt;/code&gt; 就会启动扫描。0.8.0 默认先探测已知的中转 &lt;code&gt;183.56.143.147&lt;/code&gt;（&lt;code&gt;--google-only&lt;/code&gt; 时跳过），然后照样扫描 Google 前端，作为中转失效时的备用：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/scan.rs&lt;/b&gt;&lt;span&gt;0.8.0：扫描前先验证已知中转，--google-only 时跳过&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/1099f77c614fbd6de246b67ca21da4a0ffab06eb/src/scan.rs#L336-L346&#34;&gt;L336–346 · 1099f77 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;336&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;google_only&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;337&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;relays&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;RELAYS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;collect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;338&lt;/span&gt;            &lt;span class=&#34;fm&#34;&gt;eprintln!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Checking known relays (not Google; use --google-only to skip)…&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;339&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;record&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;340&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;341&lt;/span&gt;                &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;google&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;342&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;verify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;relays&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;usize&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;MAX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;343&lt;/span&gt;                &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;344&lt;/span&gt;                &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;warned&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;345&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;346&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;中转只有一个，写死在 &lt;code&gt;RELAYS&lt;/code&gt; 里，并在注释里写明它不是 Google。传输时，&lt;code&gt;usable&lt;/code&gt; 决定端点的使用顺序：中转排第一，其余按延迟；&lt;code&gt;--google-only&lt;/code&gt; 则直接丢掉 Google 地址段之外的所有端点，包括你用 &lt;code&gt;scan --ips&lt;/code&gt; 手动加进来的：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/scan.rs&lt;/b&gt;&lt;span&gt;0.8.0：已知中转与端点排序&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/1099f77c614fbd6de246b67ca21da4a0ffab06eb/src/scan.rs#L32-L54&#34;&gt;L32–54 · 1099f77 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Known third-party relays that forward to Google. Not Google: the operator&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;33&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// sees client IPs and, without the forged identity, the hostname, but TLS&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;34&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// stays end to end and is verified against Google&amp;#39;s roots. They are fast and&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;35&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// stable in mainland China, so they are tried first unless --google-only.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;36&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;RELAYS&lt;/span&gt;: &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;183.56.143.147&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;37&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;relay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;38&lt;/span&gt;    &lt;span class=&#34;no&#34;&gt;RELAYS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;any&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;39&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;40&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Endpoints in the order transfers should use them: known relays first&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;41&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// (unless `google_only`, which drops everything outside Google&amp;#39;s space),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;42&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// then the rest as given (fastest first).&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;43&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;pub&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;usable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;44&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Route&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;45&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;google&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;Ranges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;46&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;google_only&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;47&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Route&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;48&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;49&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into_iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;50&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;google_only&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;google&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;51&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;collect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;52&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sort_by_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;relay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;53&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;54&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/main.rs&lt;/b&gt;&lt;span&gt;0.8.0：选出当前端点与备用端点；--ip 固定端点时不做切换&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/1099f77c614fbd6de246b67ca21da4a0ffab06eb/src/main.rs#L194-L220&#34;&gt;L194–220 · 1099f77 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;194&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;direct_route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;store&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;route&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;Route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;google_only&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nb&#34;&gt;Result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Direct&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;195&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;explicit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;196&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;explicit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;197&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;198&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Direct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;199&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;200&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;explicit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;201&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;202&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;backups&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;203&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;204&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;205&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;google&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ranges&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;current&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;206&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scan&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;usable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;google&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;google_only&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;207&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;208&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;scan&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;google_only&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;209&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;scan&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;usable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;google&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;google_only&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;210&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;211&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identity&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;then_some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;212&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into_iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)));&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;213&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stored&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;routes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;no verified endpoint&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;214&lt;/span&gt;    &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Direct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;215&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;216&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;explicit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;or&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stored&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;217&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;218&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;backups&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;routes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;take&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;collect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;219&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;220&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;下面关于 Google 前端扫描的代码摘自 0.7.0，这部分在 0.8.0 里没有变。&lt;/p&gt;&#xA;&lt;p&gt;扫描的边界是 &lt;code&gt;src/ranges.rs&lt;/code&gt; 里的 Google 自有地址段：goog.json 是 Google 拥有的全部地址，cloud.json 是其中可以分配给 Google Cloud 客户的部分，两者相减，剩下的就是 Google 自家服务的地址。程序内置一份快照，每周从 &lt;code&gt;www.gstatic.com&lt;/code&gt; 更新一次；如果下载到的列表少得不像话（被截断或被篡改），宁可沿用旧的也不接受：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/ranges.rs&lt;/b&gt;&lt;span&gt;Google 自有地址段：goog.json 减去 cloud.json&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/ranges.rs#L128-L150&#34;&gt;L128–150 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;128&lt;/span&gt;    &lt;span class=&#34;sd&#34;&gt;/// Google&amp;#39;s own space from the published feeds: everything Google owns&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;129&lt;/span&gt;    &lt;span class=&#34;sd&#34;&gt;/// minus what Google Cloud customers can be assigned.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;130&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;pub&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;from_feeds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;goog&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cloud&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nb&#34;&gt;Result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;131&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;list&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nb&#34;&gt;Result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;Self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;132&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;serde_json&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;serde_json&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from_str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;133&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;prefixes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;prefixes&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;134&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;135&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;IP range feed has no prefixes&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;136&lt;/span&gt;            &lt;span class=&#34;bp&#34;&gt;Self&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from_prefixes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;137&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;prefixes&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;138&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;139&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ipv4Prefix&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;or&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ipv6Prefix&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;].&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;140&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;141&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;142&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;goog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cloud&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;goog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cloud&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;143&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ranges&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;Self&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;144&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;v4&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;subtract&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;goog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cloud&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;145&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;v6&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;subtract&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;goog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cloud&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;146&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;147&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// A truncated or hostile feed must not shrink the trusted space to nothing.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;148&lt;/span&gt;        &lt;span class=&#34;fm&#34;&gt;ensure!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ranges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;IP range feed looks incomplete&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;149&lt;/span&gt;        &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ranges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;150&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;候选按固定的优先级排队，而且只收 Google 地址段内的地址：之前验证过的端点和 DNS 结果排最前（被污染的 DNS 结果落在段外，自动丢弃），然后是我们扫出过可用前端的 /24（&lt;code&gt;src/frontend-seeds.txt&lt;/code&gt;，67 个），再是前端最集中的核心地址块，最后是其余地址。每一部分都为每个用户随机打乱，免得所有人都挤在同几个前端上：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/scan.rs&lt;/b&gt;&lt;span&gt;扫描顺序：只在 Google 地址段内，按优先级排队&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/scan.rs#L96-L131&#34;&gt;L96–131 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;96&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Sweep order: earlier verified endpoints, DNS answers, seed /24s, then&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;97&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Google&amp;#39;s core front-end blocks and finally the rest of its space, each&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;98&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// shuffled. Only addresses inside `google` are ever considered.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;99&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;plan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;100&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;google&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;Ranges&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;101&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;known&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;102&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;dns&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;103&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;shuffle&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Shuffle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;104&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nc&#34;&gt;VecDeque&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;105&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;core&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ranges&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;CORE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sc&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;core blocks must parse&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;106&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seeds&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Ranges&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;SEEDS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;seed list must parse&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;107&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seen&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;HashSet&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;108&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;VecDeque&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;109&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;VecDeque&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;110&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;google&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;111&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;push_back&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;112&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;113&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;114&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;known&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dns&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;115&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;116&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;117&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seed_nets&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;u32&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seeds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slash24s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;collect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;118&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;shuffle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seed_nets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;119&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;core_nets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rest_nets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;: &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;u32&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;u32&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;google&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;120&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;slash24s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;121&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;seeds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;V4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Ipv4Addr&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;122&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;partition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;core&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;V4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Ipv4Addr&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))));&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;123&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;shuffle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;core_nets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;124&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;shuffle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;apply&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rest_nets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;125&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;net&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;seed_nets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into_iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;core_nets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rest_nets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;126&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hosts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;net&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;127&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;128&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;129&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;130&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;order&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;131&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;每个 /24 只试 7 个主机号。这来自前面那次扫描的统计：&lt;code&gt;.187&lt;/code&gt;、&lt;code&gt;.207&lt;/code&gt;、&lt;code&gt;.219&lt;/code&gt; 从没成功过（都是 Cloud Storage 专用前端），所以直接不试：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/scan.rs&lt;/b&gt;&lt;span&gt;扫描参数：目标数、批大小、主机号与核心地址块&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/scan.rs#L16-L31&#34;&gt;L16–31 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;LIMIT&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;usize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;256&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Verified endpoints to collect before a sweep stops.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;18&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;TARGET&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;usize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Upper bound on TCP probes in one sweep.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;MAX_PROBES&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;usize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;60_000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Addresses swept per batch: TCP first, then full verification of what answered.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;BATCH&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;usize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2048&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;TCP_WORKERS&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;usize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;256&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;TCP_TIMEOUT&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;Duration&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Duration&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from_secs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Host numbers inside a /24 where Google&amp;#39;s general front ends answer. .187,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;26&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// .207 and .219 are Cloud Storage-only front ends and never serve Drive.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;27&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;OFFSETS&lt;/span&gt;: &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;u8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;113&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;155&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;132&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;138&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;91&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;123&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Blocks that hold most of Google&amp;#39;s general front ends; swept before the&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;29&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// rest of Google&amp;#39;s space.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;CORE&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;142.250.0.0/15 172.217.0.0/16 172.253.0.0/16 173.194.0.0/16 74.125.0.0/16 192.178.0.0/15 64.233.160.0/19 66.102.0.0/20 108.177.0.0/17 209.85.128.0/17 216.58.192.0/19 216.239.32.0/19 108.170.192.0/18 72.14.192.0/18&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;31&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;SEEDS&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;include_str!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;frontend-seeds.txt&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;在进入探测前，&lt;code&gt;public(ip)&lt;/code&gt; 会先把私网、环回、链路本地地址以及 &lt;code&gt;198.18.0.0/15&lt;/code&gt;（Clash / Mihomo &lt;code&gt;fake-ip&lt;/code&gt; 默认网段）全部过滤掉。&lt;/p&gt;&#xA;&lt;h3 id=&#34;2-两段式扫描先-tcp再完整验证&#34;&gt;2. 两段式扫描：先 TCP，再完整验证&lt;/h3&gt;&#xA;&lt;p&gt;大部分 Google 地址在国内连 TCP 都不通，对每个地址都做完整的 TLS + API 验证太慢。所以扫描分两段：每批 2048 个地址，先用 256 个线程各做一次 2 秒超时的 TCP 连接；只有连得上的，才交给 8 个线程做完整验证。某个地址验证通过后，它所在 /24 的其他主机号会被插到队首——可用的前端往往成簇出现。凑够 8 个已验证端点就停：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/scan.rs&lt;/b&gt;&lt;span&gt;扫描主循环：TCP 探测、完整验证、邻居优先&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/scan.rs#L321-L352&#34;&gt;L321–352 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;321&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;good&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;TARGET&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;probed&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;MAX_PROBES&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;queue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;322&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;batch&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;iter&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from_fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;queue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pop_front&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;323&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tried&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;insert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;324&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;take&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;BATCH&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;325&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;collect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;326&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;probed&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;batch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;327&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;answered&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tcp_sweep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;batch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;328&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;open&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;answered&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;329&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// Real networks block most of Google; a TUN proxy accepts every connect.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;330&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;batch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;256&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;answered&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;batch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;9&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;intercepted&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;331&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;intercepted&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;332&lt;/span&gt;                &lt;span class=&#34;fm&#34;&gt;eprintln!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;333&lt;/span&gt;                    &lt;span class=&#34;s&#34;&gt;&amp;#34;warning: almost every address answered TCP, so a TUN proxy or VPN is probably intercepting; results reflect the proxy, not your direct network. Route Google IPs DIRECT for a real scan&amp;#34;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;334&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;335&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;336&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;candidates&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;answered&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into_iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;collect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;337&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;reached&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;candidates&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;338&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;verdicts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;verify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;candidates&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;TARGET&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;good&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;339&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;working&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;record&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;google&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;verdicts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;known&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;warned&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;340&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;good&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;working&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;341&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// Front ends come in groups: the rest of a working /24 goes next.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;342&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;working&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;343&lt;/span&gt;                &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;IpAddr&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;V4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;344&lt;/span&gt;                    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;neighbour&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hosts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;u32&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;v4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;mh&#34;&gt;0xff&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;345&lt;/span&gt;                        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tried&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbour&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;346&lt;/span&gt;                            &lt;span class=&#34;n&#34;&gt;queue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;push_front&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;neighbour&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;347&lt;/span&gt;                        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;348&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;349&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;350&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;351&lt;/span&gt;            &lt;span class=&#34;fm&#34;&gt;eprintln!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;  … probed &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{probed}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;, &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{open}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; answered TCP, &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{good}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; verified&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;352&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;注意循环里那条 TUN 警告：真实网络会挡掉绝大多数 Google 地址，如果一批里 90% 以上都能连上，几乎可以肯定是 TUN 代理在本地替所有地址接通了连接。&lt;/p&gt;&#xA;&lt;p&gt;完整验证对每个地址最多探两遍：先真实 SNI，失败再换 &lt;code&gt;g.cn&lt;/code&gt; 伪造身份：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/scan.rs&lt;/b&gt;&lt;span&gt;完整验证：真实 SNI 先探，失败再试伪造身份&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/scan.rs#L172-L178&#34;&gt;L172–178 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;172&lt;/span&gt;                    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;probe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;173&lt;/span&gt;                        &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;174&lt;/span&gt;                        &lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;real&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;probe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;175&lt;/span&gt;                            &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;FORGED_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;176&lt;/span&gt;                            &lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;format!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{real:#}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; | forged: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{forged:#}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;177&lt;/span&gt;                        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;178&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/scan.rs&lt;/b&gt;&lt;span&gt;单次探测：TLS 证书 + 无凭证 Drive API + 测试文件&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/scan.rs#L189-L243&#34;&gt;L189–243 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;189&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// A missing or changed public test file is reported as a warning, not as a dead IP.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;190&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;191&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// `forged` selects the fallback identity: the URL (and therefore SNI and&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;192&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// certificate name) is g.cn while the Host header still carries the real&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;193&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Google host. Google&amp;#39;s front end routes by Host, so the same probes work.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;194&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;probe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;IpAddr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nb&#34;&gt;Result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;i64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Option&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;195&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;::&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;API_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;FORGED_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;196&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;FILE_HOST&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;drive.usercontent.google.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;197&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api_url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file_url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;198&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;FORGED_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;FORGED_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;199&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;200&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;API_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;FILE_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;201&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;202&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;client_builder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;203&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;no_proxy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;204&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;redirect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;redirect&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Policy&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;205&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connect_timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Duration&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from_secs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;206&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Duration&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from_secs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;207&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;api_url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SocketAddr&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;443&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;208&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;resolve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;file_url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SocketAddr&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;443&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;209&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;210&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Instant&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;211&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;212&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;format!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{api_url}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;/drive/v3/about?fields=user&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;213&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;API_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;214&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;215&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map_err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;without_url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;216&lt;/span&gt;    &lt;span class=&#34;fm&#34;&gt;ensure!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;217&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;StatusCode&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;UNAUTHORIZED&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;218&lt;/span&gt;        &lt;span class=&#34;s&#34;&gt;&amp;#34;unexpected Drive API status {}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;219&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;220&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;221&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;serde_json&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;222&lt;/span&gt;    &lt;span class=&#34;fm&#34;&gt;ensure!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;401&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;invalid Drive API response&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;223&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;224&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;format!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;225&lt;/span&gt;            &lt;span class=&#34;s&#34;&gt;&amp;#34;https://&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{file_url}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;/download?id=0B9P1L--7Wd2vU3VUVlFnbTgtS2c&amp;amp;export=download&amp;amp;confirm=t&amp;#34;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;226&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;227&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;FILE_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;228&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;229&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map_err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;without_url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;230&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;231&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;warning&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;matches!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;232&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;233&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;StatusCode&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;FORBIDDEN&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;StatusCode&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;NOT_FOUND&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;234&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;235&lt;/span&gt;        &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;format!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;HTTP &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{status}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;236&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;237&lt;/span&gt;        &lt;span class=&#34;fm&#34;&gt;ensure!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_success&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Drive download probe HTTP {status}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;238&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bytes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;239&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;take&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read_to_end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;240&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bytes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;spam&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;then&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;unexpected content&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;241&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;242&lt;/span&gt;    &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;elapsed&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_millis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;i64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;warning&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;243&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;这段探测逻辑有几个关键设计：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;.no_proxy()&lt;/code&gt; + &lt;code&gt;.resolve()&lt;/code&gt;&lt;/strong&gt;：强制绕过环境变量代理，在客户端内部把连接直接绑定到待测的 &lt;code&gt;ip:443&lt;/code&gt;，同时按内置的 GTS 根走完整的 TLS 证书校验（杜绝连上运营商拦截页、客户证书的 Cloud 负载均衡或中间人）。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;无凭证 API 校验&lt;/strong&gt;：不携带任何用户 Token 请求 &lt;code&gt;GET /drive/v3/about?fields=user&lt;/code&gt;。真正服务 Drive API 的前端一定会返回 HTTP &lt;code&gt;401 Unauthorized&lt;/code&gt; 且 JSON &lt;code&gt;error.code == 401&lt;/code&gt;；国内那些只服务白名单的 Google 节点返回 404，Cloud Storage 专用前端则过不了证书校验。既验证了应用层路由，又不会在探测未知 IP 时泄露用户的 Bearer Token。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;下载链路探针&lt;/strong&gt;：接着请求一个公开的小测试文件。如果测试文件本身因 Google 限流返回 403/404，只要前一步 Drive API 校验通过，该 IP 依然会被标记为可用并打印警告，避免因为公开测试文件失效而把正常 IP 全部误杀。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;双身份探测&lt;/strong&gt;：伪造身份下，连接 URL、TLS SNI 与证书校验名全部变成 &lt;code&gt;g.cn&lt;/code&gt;，但 &lt;code&gt;Host&lt;/code&gt; 头通过 &lt;code&gt;.header(HOST, API_HOST)&lt;/code&gt; 显式设回真实主机名。GFE 按 &lt;code&gt;Host&lt;/code&gt; 路由，所以同一套 API/测试文件探针可以端到端地验证伪造路径真的能跑通——能通过的身份会被存进 &lt;code&gt;endpoints&lt;/code&gt; 表的 &lt;code&gt;sni&lt;/code&gt; 列，&lt;code&gt;gdirect scan --list&lt;/code&gt; 也会标注每个 IP 需要哪种身份。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;归属标注&lt;/strong&gt;：扫描结果打印时会再查一次地址是否在 Google 地址段内，不在的标为 &lt;code&gt;[third-party relay]&lt;/code&gt;。默认扫描碰到的段外地址只有内置的中转 &lt;code&gt;183.56.143.147&lt;/code&gt;；&lt;code&gt;gdirect scan --ips FILE&lt;/code&gt; 则会照单探测你给的任何地址，段外的同样标出来。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;想自己提供候选 IP？&lt;/strong&gt;&#xA;把 IP 每行一个存成 &lt;code&gt;ips.txt&lt;/code&gt;，执行 &lt;code&gt;gdirect scan --ips ips.txt&lt;/code&gt;，探测仪会逐个验证证书、Drive API 和测试下载。注意：国内 DNS 给 &lt;code&gt;dl.google.com&lt;/code&gt;、&lt;code&gt;g.cn&lt;/code&gt; 等 &lt;code&gt;google-cn&lt;/code&gt; 域名返回的运营商网内 Google 节点&lt;strong&gt;不服务云盘&lt;/strong&gt;，放进去也会全部失败；更有希望的是 Google 地址段内、DNS 平时不会给出的前端地址。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h3 id=&#34;3-伪造身份回退与端点切换&#34;&gt;3. 伪造身份回退与端点切换&lt;/h3&gt;&#xA;&lt;p&gt;扫描解决的是“连哪个 IP”，但实际网络里还有两个变数：&lt;strong&gt;用什么身份连&lt;/strong&gt;，以及&lt;strong&gt;这个 IP 明天还通不通&lt;/strong&gt;。有些审查设备不封 IP 本身，而是精确重置 TLS &lt;code&gt;ClientHello&lt;/code&gt; 里携带真实 Google 域名（SNI）的连接——这是下载中途“连接突然被掐断”的典型成因，单纯换 IP 解决不了；而我们扫出来的前端本身也可能随时被封。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;gdirect&lt;/code&gt; 的应对是把所有固定 IP 的请求路径统一套上 &lt;code&gt;src/api.rs&lt;/code&gt; 里的 &lt;code&gt;deliver&lt;/code&gt; 包装：遇到连接错误或超时，先在当前端点上换另一个 TLS 身份重试；还不行，就把当前端点在数据库里标死，依次换到过去一天内验证过的其他端点（每个端点先用它记录的身份，再试另一个）。哪条路走通了，就在当前进程内保留下来，并把能用的身份持久化到数据库里该 IP 的 &lt;code&gt;sni&lt;/code&gt; 列：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/api.rs&lt;/b&gt;&lt;span&gt;deliver：换身份，再换端点&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/api.rs#L145-L218&#34;&gt;L145–218 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;145&lt;/span&gt;    &lt;span class=&#34;sd&#34;&gt;/// Sends one request. On a connect/timeout failure it is retried under&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;146&lt;/span&gt;    &lt;span class=&#34;sd&#34;&gt;/// the other TLS identity (real &amp;lt;-&amp;gt; forged g.cn), then on each backup&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;147&lt;/span&gt;    &lt;span class=&#34;sd&#34;&gt;/// endpoint in turn; dead endpoints are marked in SQLite. Whatever works&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;148&lt;/span&gt;    &lt;span class=&#34;sd&#34;&gt;/// is kept for this process and the working identity is persisted.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;149&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;pub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;deliver&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;150&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;151&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;: &lt;span class=&#34;nc&#34;&gt;impl&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Option&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nc&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;blocking&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;RequestBuilder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;152&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nb&#34;&gt;Result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;blocking&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;153&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;154&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;live&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;live&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unwrap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;155&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;live&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;live&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;live&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;156&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;157&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;send&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;Client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Option&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;158&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;159&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;API_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;160&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;161&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map_err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;without_url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;162&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;163&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;transport&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Error&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;164&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;165&lt;/span&gt;            &lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;transport&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;166&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;167&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;168&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Store&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;169&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;endpoint&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()));&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;170&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tried_current&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;171&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;first&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;172&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;preferred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;endpoint&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;173&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// The identity already tried on the current endpoint is skipped.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;174&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identities&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;175&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tried_current&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;176&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;identities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;preferred&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;177&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;178&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;179&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;identities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;preferred&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;180&lt;/span&gt;                    &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;181&lt;/span&gt;                    &lt;span class=&#34;nb&#34;&gt;None&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;FORGED_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_owned&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;182&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;183&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;184&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identity&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identities&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;185&lt;/span&gt;                &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client_for&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;186&lt;/span&gt;                &lt;span class=&#34;k&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;send&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;187&lt;/span&gt;                    &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;188&lt;/span&gt;                        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;live&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;live&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unwrap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;189&lt;/span&gt;                        &lt;span class=&#34;n&#34;&gt;live&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;190&lt;/span&gt;                        &lt;span class=&#34;n&#34;&gt;live&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;191&lt;/span&gt;                        &lt;span class=&#34;n&#34;&gt;live&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;192&lt;/span&gt;                        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;193&lt;/span&gt;                            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;194&lt;/span&gt;                                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set_sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;identity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unwrap_or_default&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;195&lt;/span&gt;                        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;196&lt;/span&gt;                        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;197&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;198&lt;/span&gt;                    &lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;transport&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;199&lt;/span&gt;                    &lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;200&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;201&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;202&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;203&lt;/span&gt;                &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endpoint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;connection failed&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;204&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;205&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;tried_current&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;206&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;endpoint&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;207&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;live&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;208&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;209&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unwrap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;210&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;backups&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;211&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pop_front&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;212&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stored&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;213&lt;/span&gt;                    &lt;span class=&#34;c1&#34;&gt;// An explicit --sni applies to every endpoint.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;214&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stored&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;215&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;216&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;217&lt;/span&gt;        &lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;218&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;几个设计点：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;只有传输层错误才触发&lt;/strong&gt;：连接错误和超时才会换身份、换端点；HTTP 层错误（如 403）直接返回，不会被误判成审查。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;手动指定优先&lt;/strong&gt;：显式传了 &lt;code&gt;--sni&lt;/code&gt; 时（&lt;code&gt;auto&lt;/code&gt; 为假）不会悄悄换身份，换端点时也沿用你指定的身份；传了 &lt;code&gt;--ip&lt;/code&gt; 时只用这一个端点，不做切换。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;记忆分两级&lt;/strong&gt;：进程内的 &lt;code&gt;Live&lt;/code&gt; 结构让后续请求直接用能通的端点和身份；&lt;code&gt;store::set_sni&lt;/code&gt; 把结果写进 SQLite，换一条命令也记得：&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/store.rs&lt;/b&gt;&lt;span&gt;把验证可用的身份持久化到该 IP&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/store.rs#L107-L114&#34;&gt;L107–114 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;107&lt;/span&gt;    &lt;span class=&#34;sd&#34;&gt;/// The fallback identity proved itself: make it the stored one for this IP.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;108&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;pub&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;set_sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nb&#34;&gt;Result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;109&lt;/span&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;execute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;110&lt;/span&gt;            &lt;span class=&#34;s&#34;&gt;&amp;#34;UPDATE endpoints SET sni=?,available=1,verified_at=? WHERE ip=?&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;111&lt;/span&gt;            &lt;span class=&#34;fm&#34;&gt;params!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;112&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;113&lt;/span&gt;        &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(())&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;114&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;下载（libaria2 路径）同样两级回退。aria2 错误码 1/2/6/19 分别是 TLS/连接失败、超时、网络问题、域名解析失败——正是 SNI 被重置或端点被封的直接表现。命中这些码时，先换身份重下一次；仍不行就把该端点标死，换到下一个已验证端点，在已下载的部分上继续（aria2 的 &lt;code&gt;.aria2&lt;/code&gt; 控制文件让续传天然成立）；所有端点都用完才报错，下一条命令自动重新扫描：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/download.rs&lt;/b&gt;&lt;span&gt;传输层错误判定与端点标死&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/download.rs#L156-L172&#34;&gt;L156–172 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;156&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// TLS handshake, connect and DNS failures: exactly what the fallback route is for.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;157&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;transport&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;i32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; -&amp;gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;158&lt;/span&gt;    &lt;span class=&#34;fm&#34;&gt;matches!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;159&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;160&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mark_dead&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;Args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;161&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;162&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;163&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;164&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Store&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;165&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endpoint&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;166&lt;/span&gt;            &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;167&lt;/span&gt;            &lt;span class=&#34;nb&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;168&lt;/span&gt;            &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;download transport failure&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;169&lt;/span&gt;            &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;170&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;171&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;172&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/download.rs&lt;/b&gt;&lt;span&gt;下载失败：先换身份，再换端点续传&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/download.rs#L233-L289&#34;&gt;L233–289 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;233&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;234&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tried_alt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;235&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attempt&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;236&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;download&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;237&lt;/span&gt;            &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;238&lt;/span&gt;            &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;239&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;240&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;241&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;progress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref_mut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;242&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;243&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;loop&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;244&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tried_alt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;transport&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;245&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;tried_alt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;246&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;247&lt;/span&gt;                    &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;248&lt;/span&gt;                    &lt;span class=&#34;nb&#34;&gt;None&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;FORGED_HOST&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_owned&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;249&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;250&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;download&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;251&lt;/span&gt;                    &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;252&lt;/span&gt;                    &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;253&lt;/span&gt;                    &lt;span class=&#34;n&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;254&lt;/span&gt;                    &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;255&lt;/span&gt;                    &lt;span class=&#34;n&#34;&gt;progress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref_mut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;256&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;257&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;258&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transport&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;backups&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;259&lt;/span&gt;                &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;260&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;261&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// This endpoint is gone: resume the partial file on the next one.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;262&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;mark_dead&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;263&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stored&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;backups&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;remove&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;264&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;progress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;265&lt;/span&gt;                &lt;span class=&#34;fm&#34;&gt;eprintln!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;266&lt;/span&gt;                    &lt;span class=&#34;s&#34;&gt;&amp;#34;Endpoint &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt; failed; resuming via &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{ip}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;267&lt;/span&gt;                    &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unwrap_or_default&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;268&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;269&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;270&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;271&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stored&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;clone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;272&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;tried_alt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;273&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;download&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;274&lt;/span&gt;                &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;275&lt;/span&gt;                &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;276&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;277&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;278&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;progress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref_mut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;279&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;280&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;281&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;282&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;transport&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;283&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;mark_dead&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;284&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;285&lt;/span&gt;            &lt;span class=&#34;fm&#34;&gt;bail!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;286&lt;/span&gt;                &lt;span class=&#34;s&#34;&gt;&amp;#34;download failed: {} (libaria2 code {code}); partial data is kept, rerun to resume&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;287&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;aria2_error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;288&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;289&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;上传走的也是 &lt;code&gt;deliver&lt;/code&gt;（可恢复会话的创建与每个分块的发送都包着同样的回退），于是 API、下载、上传三条传输路径全部具备身份回退和端点切换能力。&lt;/p&gt;&#xA;&lt;h3 id=&#34;4-定制-libaria2为什么必须给-c-源码打补丁&#34;&gt;4. 定制 libaria2：为什么必须给 C++ 源码打补丁&lt;/h3&gt;&#xA;&lt;p&gt;对于 &lt;code&gt;ls&lt;/code&gt; 和 &lt;code&gt;upload&lt;/code&gt;，Rust 层的 &lt;code&gt;reqwest&lt;/code&gt; 配合 &lt;code&gt;.resolve()&lt;/code&gt; 就够用了；但下载大文件需要多连接分段并发和断点续传，&lt;code&gt;gdirect&lt;/code&gt; 选择把 &lt;code&gt;aria2 1.37.0&lt;/code&gt; 裁剪成纯 HTTP/HTTPS + OpenSSL 3 的静态库 &lt;code&gt;libaria2&lt;/code&gt; 嵌进二进制。&lt;/p&gt;&#xA;&lt;p&gt;问题在于：&lt;strong&gt;Google Drive 的下载请求经常会发生跨域名 302 重定向&lt;/strong&gt;（例如从 &lt;code&gt;www.googleapis.com&lt;/code&gt; 或 &lt;code&gt;drive.google.com&lt;/code&gt; 重定向到 &lt;code&gt;drive.usercontent.google.com&lt;/code&gt; 乃至动态生成的 &lt;code&gt;*.googleusercontent.com&lt;/code&gt;）。在原版 &lt;code&gt;aria2&lt;/code&gt; 里，如果你只是修改初始 URL，一旦跟随 302 跳转到新域名，&lt;code&gt;aria2&lt;/code&gt; 又会去查系统 DNS；而如果你把 URL 里的域名硬改成 IP，TLS 证书校验又会立刻因为主机名不匹配而失败。&lt;/p&gt;&#xA;&lt;p&gt;为此，&lt;code&gt;gdirect&lt;/code&gt; 在构建 &lt;code&gt;libaria2&lt;/code&gt; 时打了一个约 120 行的补丁（&lt;code&gt;vendor/aria2-1.37.0.patch&lt;/code&gt; 配合 &lt;code&gt;vendor/gdirect_route.h&lt;/code&gt;），在 C++ 底层做了五处改动：&lt;/p&gt;&#xA;&lt;h4 id=&#34;1google-drive-传输域名白名单&#34;&gt;（1）Google Drive 传输域名白名单&lt;/h4&gt;&#xA;&lt;p&gt;先在 &lt;code&gt;vendor/gdirect_route.h&lt;/code&gt; 中收拢所有受控的 Google Drive 域名，并定义三个路由钩子变量：&lt;code&gt;gdirect_ip&lt;/code&gt; 是扫出的直连 IP；&lt;code&gt;gdirect_sni&lt;/code&gt; 是 TLS 身份（空指针 = 真实 SNI、空字符串 = 不发 SNI、主机名 = 伪造身份）；&lt;code&gt;gdirect_hosthdr&lt;/code&gt; 记住伪造时真实的 Google 主机名，专供 HTTP &lt;code&gt;Host&lt;/code&gt; 头使用：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;vendor/gdirect_route.h&lt;/b&gt;&lt;span&gt;路由钩子与 Google Drive 域名白名单&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/vendor/gdirect_route.h#L1-L17&#34;&gt;L1–17 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// gdirect&amp;#39;s narrow routing hook for the pinned aria2 1.37.0 build.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;#pragma once&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;cstring&amp;gt;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gdirect_ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Empty means no override. &amp;#34;&amp;#34; as a set value means &amp;#34;send no SNI&amp;#34;; a hostname&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;7&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// means the forged identity: connect and verify as that name while the HTTP&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Host header carries gdirect_hosthdr, the real Google host.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;9&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gdirect_sni&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;extern&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gdirect_hosthdr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;11&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;inline&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;gdirect_host&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;12&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;suffix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;.googleusercontent.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;13&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;drive.google.com&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;drive.usercontent.google.com&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;14&lt;/span&gt;         &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;www.googleapis.com&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;content.googleapis.com&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;15&lt;/span&gt;         &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;drive.googleapis.com&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;16&lt;/span&gt;         &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;suffix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;compare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;suffix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;suffix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;suffix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;17&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h4 id=&#34;2在-socketcorecc-拦截-getaddrinfo-与-tls-sni&#34;&gt;（2）在 &lt;code&gt;SocketCore.cc&lt;/code&gt; 拦截 &lt;code&gt;getaddrinfo&lt;/code&gt; 与 TLS SNI&lt;/h4&gt;&#xA;&lt;p&gt;&lt;code&gt;SocketCore&lt;/code&gt; 真正调用 &lt;code&gt;getaddrinfo&lt;/code&gt; 建立连接时，不仅命中 &lt;code&gt;gdirect_host(host)&lt;/code&gt; 的主机名会被替换成扫描出的 &lt;code&gt;gdirect_ip&lt;/code&gt;（并加 &lt;code&gt;AI_NUMERICHOST&lt;/code&gt; 跳过 DNS），伪造身份下的 &lt;code&gt;g.cn&lt;/code&gt; 也会被钉到同一个 IP 上——否则换了身份就又去查一次系统 DNS。同一处还接管了 &lt;code&gt;tlsSession_-&amp;gt;setSNIHostname()&lt;/code&gt;：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;vendor/aria2-1.37.0.patch&lt;/b&gt;&lt;span&gt;在 SocketCore.cc 中接管 TLS SNI 与 getaddrinfo&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/vendor/aria2-1.37.0.patch#L23-L45&#34;&gt;L23–45 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;gu&#34;&gt;@@ -936,7 +942,9 @@&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;24&lt;/span&gt;     // &amp;#34;localhost&amp;#34; will produce TLS alert with GNUTLS.&#xA;&lt;span class=&#34;ln&#34;&gt;25&lt;/span&gt;     if (tlsctx-&amp;gt;getSide() == TLS_CLIENT &amp;amp;&amp;amp; !util::isNumericHost(hostname) &amp;amp;&amp;amp;&#xA;&lt;span class=&#34;ln&#34;&gt;26&lt;/span&gt;         hostname.find(&amp;#34;.&amp;#34;) != std::string::npos) {&#xA;&lt;span class=&#34;ln&#34;&gt;27&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;-      rv = tlsSession_-&amp;gt;setSNIHostname(hostname);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;28&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      const char* sni = gdirect_host(hostname) ? gdirect_sni : nullptr;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;29&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      rv = (sni &amp;amp;&amp;amp; !*sni) ? TLS_ERR_OK :&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;30&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+          tlsSession_-&amp;gt;setSNIHostname(sni ? std::string(sni) : hostname);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;31&lt;/span&gt;       if (rv != TLS_ERR_OK) {&#xA;&lt;span class=&#34;ln&#34;&gt;32&lt;/span&gt;         throw DL_ABORT_EX(fmt(EX_SSL_INIT_FAILURE,&#xA;&lt;span class=&#34;ln&#34;&gt;33&lt;/span&gt;                               tlsSession_-&amp;gt;getLastErrorString().c_str()));&#xA;&lt;span class=&#34;ln&#34;&gt;34&lt;/span&gt;&lt;span class=&#34;gu&#34;&gt;@@ -1450,6 +1458,12 @@&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;35&lt;/span&gt;   hints.ai_flags = getDefaultAIFlags();&#xA;&lt;span class=&#34;ln&#34;&gt;36&lt;/span&gt;   hints.ai_flags |= flags;&#xA;&lt;span class=&#34;ln&#34;&gt;37&lt;/span&gt;   hints.ai_protocol = protocol;&#xA;&lt;span class=&#34;ln&#34;&gt;38&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+  if (host &amp;amp;&amp;amp; gdirect_ip &amp;amp;&amp;amp; *gdirect_ip &amp;amp;&amp;amp;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;39&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      (gdirect_host(host) ||&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;40&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+       (gdirect_sni &amp;amp;&amp;amp; *gdirect_sni &amp;amp;&amp;amp; strcmp(host, gdirect_sni) == 0))) {&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;41&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    host = gdirect_ip;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;42&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    hints.ai_flags |= AI_NUMERICHOST;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;43&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+  }&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;44&lt;/span&gt;   return getaddrinfo(host, service, &amp;amp;hints, resPtr);&#xA;&lt;span class=&#34;ln&#34;&gt;45&lt;/span&gt; }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;当 &lt;code&gt;gdirect_sni&lt;/code&gt; 为 &lt;code&gt;nullptr&lt;/code&gt;（默认）时，正常发送原域名作为 SNI；&lt;/li&gt;&#xA;&lt;li&gt;当伪造身份生效时（&lt;code&gt;gdirect_sni&lt;/code&gt; 指向 &lt;code&gt;g.cn&lt;/code&gt;），连接名与 SNI 都变成 &lt;code&gt;g.cn&lt;/code&gt;，证书也按 &lt;code&gt;g.cn&lt;/code&gt; 校验；&lt;/li&gt;&#xA;&lt;li&gt;当传入 &lt;code&gt;--no-sni&lt;/code&gt; 时（&lt;code&gt;gdirect_sni&lt;/code&gt; 为空字符串 &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;），直接返回 &lt;code&gt;TLS_ERR_OK&lt;/code&gt; 跳过设置，让 OpenSSL 发出不带 SNI 扩展的 &lt;code&gt;ClientHello&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;3在-requestcc-完成身份调换连接用-gcnhost-头用真实域名&#34;&gt;（3）在 &lt;code&gt;Request.cc&lt;/code&gt; 完成身份调换：连接用 &lt;code&gt;g.cn&lt;/code&gt;，&lt;code&gt;Host&lt;/code&gt; 头用真实域名&lt;/h4&gt;&#xA;&lt;p&gt;伪造身份的关键一跳在 &lt;code&gt;Request::parseUri&lt;/code&gt;：解析 URL 时如果发现是受控的 Google 域名且伪造身份已开启，就把 URI 的主机名换成 &lt;code&gt;g.cn&lt;/code&gt;（此后连接、SNI、证书校验都按 &lt;code&gt;g.cn&lt;/code&gt; 走），把真实主机名存进 &lt;code&gt;gdirect_hosthdr&lt;/code&gt;；&lt;code&gt;HttpRequest&lt;/code&gt; 拼 &lt;code&gt;Host:&lt;/code&gt; 头时优先使用它。这样线路上看到的是一个无害的 &lt;code&gt;g.cn&lt;/code&gt; 连接，Google 前端却仍然按真实云盘主机名路由：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;vendor/aria2-1.37.0.patch&lt;/b&gt;&lt;span&gt;Request.cc：URI 主机换成伪造身份，真实主机名留给 Host 头&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/vendor/aria2-1.37.0.patch#L58-L86&#34;&gt;L58–86 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;58&lt;/span&gt;&lt;span class=&#34;gu&#34;&gt;@@ -150,13 +152,24 @@&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;59&lt;/span&gt; {&#xA;&lt;span class=&#34;ln&#34;&gt;60&lt;/span&gt;   currentUri_ = removeFragment(srcUri);&#xA;&lt;span class=&#34;ln&#34;&gt;61&lt;/span&gt;   uri::UriStruct us;&#xA;&lt;span class=&#34;ln&#34;&gt;62&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;-  if (uri::parse(us, currentUri_)) {&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;63&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;-    us_.swap(us);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;-    return true;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;65&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+  if (uri::parse(us, currentUri_) &amp;amp;&amp;amp; us.protocol == &amp;#34;https&amp;#34;) {&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;66&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    if (gdirect_sni &amp;amp;&amp;amp; *gdirect_sni &amp;amp;&amp;amp; gdirect_host(us.host)) {&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;67&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      // Forged identity: the connection, SNI and certificate name become&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;68&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      // gdirect_sni; the real host is kept only for the HTTP Host header.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;69&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      gdirect_hosthdr = us.host;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;70&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      us.host = gdirect_sni;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;71&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      us_.swap(us);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;72&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      return true;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;73&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    }&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;74&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    // Real hosts pass through unchanged; a redirect relative to an already&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;75&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    // swapped URI lands on the forged host and keeps its Host header.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;76&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    if (gdirect_host(us.host) ||&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;77&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+        (gdirect_sni &amp;amp;&amp;amp; *gdirect_sni &amp;amp;&amp;amp; us.host == gdirect_sni)) {&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;78&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      us_.swap(us);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;79&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      return true;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;80&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    }&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;81&lt;/span&gt;   }&#xA;&lt;span class=&#34;ln&#34;&gt;82&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;-  else {&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;83&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;-    return false;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;84&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;-  }&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;85&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+  return false;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;86&lt;/span&gt; }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;vendor/aria2-1.37.0.patch&lt;/b&gt;&lt;span&gt;HttpRequest.cc：Host 头携带真实主机名&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/vendor/aria2-1.37.0.patch#L100-L107&#34;&gt;L100–107 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;gu&#34;&gt;@@ -190,7 +192,9 @@&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;101&lt;/span&gt;       builtinHds.emplace_back(&amp;#34;Accept-Encoding:&amp;#34;, acceptableEncodings);&#xA;&lt;span class=&#34;ln&#34;&gt;102&lt;/span&gt;     }&#xA;&lt;span class=&#34;ln&#34;&gt;103&lt;/span&gt;   }&#xA;&lt;span class=&#34;ln&#34;&gt;104&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;-  builtinHds.emplace_back(&amp;#34;Host:&amp;#34;, getHostText(getURIHost(), getPort()));&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;105&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+  builtinHds.emplace_back(&amp;#34;Host:&amp;#34;, gdirect_hosthdr.empty()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;106&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+                                       ? getHostText(getURIHost(), getPort())&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;107&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+                                       : gdirect_hosthdr);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h4 id=&#34;4跨域重定向自动剥离-authorization-头与重定向白名单&#34;&gt;（4）跨域重定向自动剥离 &lt;code&gt;Authorization&lt;/code&gt; 头与重定向白名单&lt;/h4&gt;&#xA;&lt;p&gt;下载私有文件时，初始请求发往 &lt;code&gt;https://www.googleapis.com/drive/v3/files/{id}?alt=media&lt;/code&gt; 并携带 &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;。一旦 302 跳转到 &lt;code&gt;*.googleusercontent.com&lt;/code&gt; 的临时签名 URL，如果继续携带 &lt;code&gt;Authorization&lt;/code&gt; 头，Google 内容节点会因为同时收到两套认证信息而报错，也有跨域名泄露令牌的风险。因此补丁在 &lt;code&gt;HttpRequest.cc&lt;/code&gt; 中规定：只有当&lt;strong&gt;真实主机名&lt;/strong&gt;（&lt;code&gt;gdirect_hosthdr&lt;/code&gt; 非空时用它，否则用 URI 主机名）是 &lt;code&gt;www.googleapis.com&lt;/code&gt; 时才输出 &lt;code&gt;Authorization:&lt;/code&gt; 头——注意这里必须按真实主机名判断而不是连接名，否则伪造身份下令牌会被误剥离；同时 &lt;code&gt;Request.cc&lt;/code&gt; 限制所有重定向目标必须是 &lt;code&gt;https&lt;/code&gt; 且满足 &lt;code&gt;gdirect_host()&lt;/code&gt; 或已是伪造主机本身。&lt;/p&gt;&#xA;&lt;h4 id=&#34;5去掉-openssl-3-legacy-provider-依赖&#34;&gt;（5）去掉 OpenSSL 3 Legacy Provider 依赖&lt;/h4&gt;&#xA;&lt;p&gt;原版 &lt;code&gt;aria2&lt;/code&gt; 在 OpenSSL 3 下启动时会无条件调用 &lt;code&gt;OSSL_PROVIDER_load(nullptr, &amp;quot;legacy&amp;quot;)&lt;/code&gt;（为了 BitTorrent 加密里的 RC4）。在没有安装 &lt;code&gt;legacy&lt;/code&gt; 模块的系统上会导致启动直接崩溃。补丁用 &lt;code&gt;#ifdef ENABLE_BITTORRENT&lt;/code&gt; 把它围了起来，使纯 HTTP 静态构建不再依赖任何外部 OpenSSL 模块。&lt;/p&gt;&#xA;&lt;h3 id=&#34;5-证书校验只信任-google-自家的-gts-根&#34;&gt;5. 证书校验只信任 Google 自家的 GTS 根&lt;/h3&gt;&#xA;&lt;p&gt;抗审查不只是“让连接建立起来”，还要保证建立起来的连接真的连到了 Google。&lt;code&gt;gdirect&lt;/code&gt; 曾经随包附带一份完整的 &lt;code&gt;cacert.pem&lt;/code&gt; CA 包（数百张受信根证书），但通用 CA 包意味着：&lt;strong&gt;只要其中任何一家 CA 被攻破或误发证书，攻击者就能对 googleapis.com 做中间人&lt;/strong&gt;，静默篡改下载内容或截获 OAuth 令牌。2026 年 10 月 eMudhra 中间 CA 私钥公开泄露就是现实例子——而 OpenSSL/webpki 这类客户端根本不做吊销检查，泄露的密钥在过期前一直有效。&lt;/p&gt;&#xA;&lt;p&gt;但 Google 把我们用到的每个端点（&lt;code&gt;googleapis.com&lt;/code&gt;、&lt;code&gt;googleusercontent.com&lt;/code&gt;、&lt;code&gt;g.cn&lt;/code&gt;）都签在它自家的 Google Trust Services 根下。所以 &lt;code&gt;gdirect&lt;/code&gt; 干脆把几张 GTS 根证书直接内嵌进二进制，校验时&lt;strong&gt;只认这几张根&lt;/strong&gt;，其他 CA 签发的任何链条一律拒绝：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/api.rs&lt;/b&gt;&lt;span&gt;禁用内置 CA 包，只加载内嵌的 Google GTS 根&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/api.rs#L19-L36&#34;&gt;L19–36 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;19&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// Google issues every endpoint we touch under its own Google Trust Services&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// roots. Anchoring verification there (instead of a full CA bundle) means a&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;21&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// compromised or mis-issuing third-party CA cannot impersonate Google for&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;22&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;/// this client, even with a publicly leaked intermediate key.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;23&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;pub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;GTS_PEM&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;include_str!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;gts-roots.pem&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;pub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;client_builder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nc&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;blocking&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;ClientBuilder&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;25&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;builder&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Client&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;builder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tls_built_in_root_certs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;26&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;BEGIN&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-----BEGIN CERTIFICATE-----&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;27&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;END&lt;/span&gt;: &lt;span class=&#34;kp&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;str&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;-----END CERTIFICATE-----&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;28&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;block&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;GTS_PEM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;BEGIN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;skip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;29&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;block&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;no&#34;&gt;END&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;bundled GTS root must be complete&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;END&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;30&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;builder&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;builder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add_root_certificate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;31&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;reqwest&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Certificate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from_pem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;format!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{BEGIN}{}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;block&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;32&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;expect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;bundled GTS root must parse&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;33&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;34&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;35&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;builder&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;36&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;&lt;code&gt;reqwest&lt;/code&gt; 侧用 &lt;code&gt;tls_built_in_root_certs(false)&lt;/code&gt; 关掉系统 CA 包、逐张加载 GTS 根；&lt;code&gt;libaria2&lt;/code&gt; 侧则把同一份 PEM 写到状态数据库旁边，作为 &lt;code&gt;ca-certificate&lt;/code&gt; 传给 aria2：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/download.rs&lt;/b&gt;&lt;span&gt;把内嵌的 GTS 根写盘供 libaria2 校验&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/download.rs#L366-L375&#34;&gt;L366–375 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;366&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;bundled_ca&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; -&amp;gt; &lt;span class=&#34;nb&#34;&gt;Option&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PathBuf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;367&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirs&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;data_local_dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;gdirect&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;368&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;fs&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;create_dir_all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;369&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;gts-roots.pem&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;370&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fs&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;371&lt;/span&gt;        &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;existing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;existing&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;GTS_PEM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;372&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fs&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;GTS_PEM&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;373&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;374&lt;/span&gt;    &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;375&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;这样即使审查者部署了由某家被攻破 CA 签发的假 Google 证书，&lt;code&gt;gdirect&lt;/code&gt; 也会直接拒绝握手。随包分发的 &lt;code&gt;cacert.pem&lt;/code&gt; 副文件从此消失；&lt;code&gt;--ca-cert&lt;/code&gt; 仍保留为显式覆盖入口。&lt;/p&gt;&#xA;&lt;h3 id=&#34;6-下载防呆大文件病毒扫描确认页与-md5-校验&#34;&gt;6. 下载防呆、大文件病毒扫描确认页与 MD5 校验&lt;/h3&gt;&#xA;&lt;p&gt;在 Rust 侧的 &lt;code&gt;src/download.rs&lt;/code&gt; 中，围绕 &lt;code&gt;libaria2&lt;/code&gt; 还有三层保障。&lt;/p&gt;&#xA;&lt;p&gt;已存在文件的安全检查：如果目标文件已存在且没有对应的 &lt;code&gt;.aria2&lt;/code&gt; 断点控制文件，先算一次本地 MD5 与云端 &lt;code&gt;md5Checksum&lt;/code&gt; 比对——匹配则直接提示 &lt;code&gt;Already downloaded&lt;/code&gt;，不匹配则拒绝覆盖，绝不把无关文件当成半截下载：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/download.rs&lt;/b&gt;&lt;span&gt;不把无关的已存在文件当成可续传下载&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/download.rs#L207-L218&#34;&gt;L207–218 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;207&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// Never treat an unrelated existing file as a resumable download.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;208&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;control&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PathBuf&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;fm&#34;&gt;format!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;.aria2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()));&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;209&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exists&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;control&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;exists&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;210&lt;/span&gt;        &lt;span class=&#34;fm&#34;&gt;ensure!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;211&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;matches_md5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;md5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;212&lt;/span&gt;            &lt;span class=&#34;s&#34;&gt;&amp;#34;output already exists; choose another -o path&amp;#34;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;213&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;214&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;progress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;215&lt;/span&gt;            &lt;span class=&#34;fm&#34;&gt;println!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Already downloaded &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;216&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;217&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(());&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;218&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;公开大文件的病毒扫描确认页与落盘校验：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/download.rs&lt;/b&gt;&lt;span&gt;HTML 确认页自动跳转与 MD5 校验&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/download.rs#L290-L323&#34;&gt;L290–323 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;290&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;prefix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;291&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;fs&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;File&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;292&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;take&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1024&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1024&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;293&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;read_to_end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;294&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// Public downloads can return the virus-scan confirmation form with HTTP 200.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;295&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// Only Drive&amp;#39;s own HTML pages are inspected; other files stay untouched.&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;296&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;html_page&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;297&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from_utf8_lossy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;298&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;download-form&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;299&lt;/span&gt;                &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;next&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drive&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;confirmation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;300&lt;/span&gt;                &lt;span class=&#34;fm&#34;&gt;ensure!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;attempt&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Drive confirmation loop&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;301&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;fs&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;remove_file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;302&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;303&lt;/span&gt;                &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;304&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;305&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;accounts.google.com/ServiceLogin&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;306&lt;/span&gt;                &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Google Drive - Quota exceeded&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;307&lt;/span&gt;                &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Google Drive - Access denied&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;308&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;309&lt;/span&gt;                &lt;span class=&#34;fm&#34;&gt;bail!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;310&lt;/span&gt;                    &lt;span class=&#34;s&#34;&gt;&amp;#34;Drive returned a login/quota error page; saved for inspection at {}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;311&lt;/span&gt;                    &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;312&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;313&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;314&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;315&lt;/span&gt;        &lt;span class=&#34;fm&#34;&gt;ensure!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;316&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;md5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;matches_md5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;md5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_deref&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;317&lt;/span&gt;            &lt;span class=&#34;s&#34;&gt;&amp;#34;MD5 does not match Drive&amp;#39;s checksum; the file is kept at {} for inspection&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;318&lt;/span&gt;            &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;319&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;320&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;progress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_none&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;321&lt;/span&gt;            &lt;span class=&#34;fm&#34;&gt;println!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Saved &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;args&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;322&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;323&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(());&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;确认页只认真正的 HTML&lt;/strong&gt;：未登录状态下通过分享链接下载大文件时，Google Drive 会返回 HTTP 200 的 HTML 确认页（包含 &lt;code&gt;&amp;lt;form id=&amp;quot;download-form&amp;quot;&amp;gt;&lt;/code&gt;）。&lt;code&gt;gdirect&lt;/code&gt; 只在文件头部确实是 &lt;code&gt;&amp;lt;!doctype html&lt;/code&gt; 或 &lt;code&gt;&amp;lt;html&lt;/code&gt; 时解析该表单，拼出带 &lt;code&gt;confirm&lt;/code&gt; / &lt;code&gt;uuid&lt;/code&gt; 的真实下载地址自动重下，绝不会误伤内容里恰好带有 &lt;code&gt;download-form&lt;/code&gt; 字样的普通压缩包或源码文件。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;落盘后 MD5 校验&lt;/strong&gt;：下载完成后再次流式计算整个文件的 MD5 并与 Drive 元数据核对，确保多连接切片拼接毫无差错。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;7-32-mib-分块可恢复上传&#34;&gt;7. 32 MiB 分块可恢复上传&lt;/h3&gt;&#xA;&lt;p&gt;上传模块（&lt;code&gt;src/upload.rs&lt;/code&gt;）基于 Google Drive v3 的 Resumable Upload 协议。考虑到跨国直连链路的 RTT 较高，分块过小会导致大量时间浪费在等待每个块的确认上，因此将单次分块大小设为 &lt;code&gt;32 MiB&lt;/code&gt;。所有请求都包在第 3 节的 &lt;code&gt;deliver&lt;/code&gt; 里，所以上传同样具备身份回退和端点切换能力。&lt;/p&gt;&#xA;&lt;p&gt;每次开始上传时，程序以 &lt;code&gt;(本地绝对路径, 目标父目录 ID, 云端文件名)&lt;/code&gt; 为键查询本地 SQLite，向云端探测已有会话的已接收偏移量：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/upload.rs&lt;/b&gt;&lt;span&gt;经 deliver 探测已有上传会话的已接收偏移量&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/upload.rs#L85-L105&#34;&gt;L85–105 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;85&lt;/span&gt;        &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;session&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;saved&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;modified&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;modified&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;86&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Some&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;session&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;87&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;session_url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;88&lt;/span&gt;            &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deliver&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;89&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;90&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;put&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;crate&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;api&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;gate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;forged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;91&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bearer_auth&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;token&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;92&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Content-Range&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;format!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;bytes */&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{size}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;93&lt;/span&gt;                    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Content-Length&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;94&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;95&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;96&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;StatusCode&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;OK&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;StatusCode&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;CREATED&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;97&lt;/span&gt;                    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;98&lt;/span&gt;                    &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;99&lt;/span&gt;                    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;100&lt;/span&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;101&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;StatusCode&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;PERMANENT_REDIRECT&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;102&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;StatusCode&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;NOT_FOUND&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;StatusCode&lt;/span&gt;::&lt;span class=&#34;no&#34;&gt;GONE&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;session&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;103&lt;/span&gt;                &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;failure&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;104&lt;/span&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;105&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;只要本地文件的大小和纳秒级修改时间没有变化，中断后重跑同一条命令就会先发送 &lt;code&gt;Content-Range: bytes */{size}&lt;/code&gt; 探测云端进度，从 &lt;code&gt;308 Permanent Redirect&lt;/code&gt; 返回的 &lt;code&gt;Range&lt;/code&gt; 头里拿到断点偏移量继续上传。&lt;/p&gt;&#xA;&lt;h3 id=&#34;8-本机-loopback--pkce-授权流&#34;&gt;8. 本机 Loopback + PKCE 授权流&lt;/h3&gt;&#xA;&lt;p&gt;在 &lt;code&gt;src/auth.rs&lt;/code&gt; 中，&lt;code&gt;gdirect login&lt;/code&gt; 实现了无需外部 SDK 的 &lt;strong&gt;OAuth 2.0 Loopback + PKCE（RFC 7636 &lt;code&gt;S256&lt;/code&gt;）&lt;/strong&gt; 流程：&lt;/p&gt;&#xA;&lt;figure class=&#34;code&#34;&gt;&lt;figcaption&gt;&lt;span class=&#34;code-path&#34;&gt;&lt;b&gt;src/auth.rs&lt;/b&gt;&lt;span&gt;本机随机端口监听与 PKCE 授权 URL 构造&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;code-tools&#34;&gt;&lt;a href=&#34;https://github.com/mizorewww/gdirect/blob/338cd9620ba09e451aac654394376762674eea6c/src/auth.rs#L196-L215&#34;&gt;L196–215 · 338cd96 ↗&lt;/a&gt;&lt;button class=&#34;copy&#34; type=&#34;button&#34;&gt;copy&lt;/button&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;ln&#34;&gt;196&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;TcpListener&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;bind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;127.0.0.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;197&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;set_nonblocking&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;198&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;redirect&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;format!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;199&lt;/span&gt;        &lt;span class=&#34;s&#34;&gt;&amp;#34;http://127.0.0.1:&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;/callback&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;200&lt;/span&gt;        &lt;span class=&#34;n&#34;&gt;listener&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;local_addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;port&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;201&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;202&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;random_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;203&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;verifier&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;random_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;204&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;Url&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;https://accounts.google.com/o/oauth2/v2/auth&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;205&lt;/span&gt;    &lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;query_pairs_mut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;extend_pairs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;206&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;client_id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;207&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;redirect_uri&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;redirect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;208&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;response_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;209&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;scope&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;SCOPE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;210&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;access_type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;offline&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;211&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;prompt&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;consent select_account&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;212&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;state&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;213&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;code_challenge&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;challenge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;verifier&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_str&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;214&lt;/span&gt;        &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;code_challenge_method&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;S256&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;&#xA;&lt;span class=&#34;ln&#34;&gt;215&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;它在 &lt;code&gt;127.0.0.1:0&lt;/code&gt; 上开启一个临时的非阻塞 TCP 监听，校验浏览器回调的 &lt;code&gt;state&lt;/code&gt; 参数防 CSRF，再用授权码和 &lt;code&gt;code_verifier&lt;/code&gt; 向 &lt;code&gt;https://oauth2.googleapis.com/token&lt;/code&gt; 换取 &lt;code&gt;refresh_token&lt;/code&gt;。落盘的 SQLite 数据库和临时导出的 &lt;code&gt;rclone&lt;/code&gt; 凭据文件在创建时即强制设为 &lt;code&gt;0600&lt;/code&gt; 权限，&lt;code&gt;unmount&lt;/code&gt; 或挂载失败时也会自动清理导出的临时凭据文件。&lt;/p&gt;&#xA;</description>
      <category>技术</category>
      <pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate>
      <guid>https://mizore.blog/posts/gdirect/</guid>
    </item>
  </channel>
</rss>