🧑🏻‍💻 博世

「zz.ac」公益主机代理地址浅探

每日一言:

## 运伟大之思者,必行伟大之迷途。—— 海德格尔

涛叔在ZZ.NIC 公益 Linux 主机空间这篇文章中说: > 注意它已经设置了 fe80::2/64 本地链接地址。登录之后无法直接访问外部网络环境。但你用 curl 访问 github.com 就会发现是通的。这是因为系统会自动设置 HTTP 代理环境变量: $ export|grep http declare -x http_proxy="http://[fe80::1%eth0]:8888" declare -x https_proxy="http://[fe80::1%eth0]:8888"

这个代理究竟是什么意思呢?今天通过Gemini探究一下:

declare -x http_proxy="http://[fe80::1%eth0]:8888"
        declare -x https_proxy="http://[fe80::1%eth0]:8888"
  1. 语法拆解:declare -x
  • 在 Bash 中,declare 是一个用于声明变量及其属性的内置命令: declare: 告知 Shell 我要定义一个变量。

  • -x: 代表 export(导出)。它的作用是将该变量变成“环境变量”。

区别:如果只写 http_proxy=“…”,这个变量只在当前终端窗口有效;使用 -x 后,从这个终端启动的所有程序(如 curl、wget、git、apt 等)都能读取到这个变量。

  1. 地址拆解:IPv6 链路本地地址:
  • 这是这条命令中最复杂的部分:http://[fe80::1%eth0]:8888

  • http://: 即使是代理 IPv6 流量,代理服务器本身运行的协议通常仍然是 HTTP。

  • fe80::1: 这是一个 IPv6 Link-Local Address(链路本地地址)。这类地址仅在当前的物理网段内有效(可以理解为局域网?)。

  • fe80::1 通常是网关(如路由器或宿主机)在 IPv6 环境下的简写形式。 > 完整(非压缩)形式为:fe80:0000:0000:0000:0000:0000:0000:0001

  • %eth0: 这是 Zone Index(区域索引/范围标识)。 > 关键点:由于所有的网卡(eth0, eth1, wlan0)都可能拥有以 fe80 开头的地址,系统无法仅凭地址知道该往哪个网卡发包。

  • %eth0 明确告诉系统:通过名为 eth0 的网卡去寻找这个代理服务器。 > 在 IPv6 的语法中,这个 % 符号被称为 Zone Index(区域索引) 或 Scope ID(作用域标识符) 的分隔符。 > 在网络中,fe80::/10 开头的地址是链路本地地址(Link-Local Address)。这种地址在每个网络接口(网卡)上都会自动生成一个,且只在当前物理网段内有效。 由于一个操作系统可能插了多张网卡(如 eth0 是有线,wlan0 是无线),每张网卡对应的物理网段里可能都有一个 fe80::1:

    如果不加 %,电脑不知道应该从哪张网卡把数据包发出去。 加上 %eth0,就是明确告诉电脑:“请通过 eth0 这张网卡,去找那个叫 fe80::1 的目标”。

  • [ ]: 在 URL 中,如果使用 IPv6 地址,必须用方括号包围,以区分地址中的冒号和端口号的冒号。

  • :8888: 代理服务器监听的端口号。

  1. 变量名拆解:
  • http_proxy 与 https_proxy这两个是 Linux 系统中约定俗成的环境变量名:
  • http_proxy拦截所有不加密的 HTTP 请求,并转发给代理服务器。
  • https_proxy拦截所有加密的 HTTPS 请求(通常通过 HTTP CONNECT 隧道)。

我的公益主机上的代理:

export |grep http
        declare -x http_proxy="http://[fe80::1%25eth0]:8888"
        declare -x https_proxy="http://[fe80::1%25eth0]:8888"

而涛叔的文章中是:

declare -x http_proxy="http://[fe80::1%eth0]:8888"
        declare -x https_proxy="http://[fe80::1%eth0]:8888"

显而易见:

区别在于我的代理的%25eth0和默认的%eth0的区别:我网卡名前多了25

这个区别是我为了运行node js项目的时候,Gemini让我修改的。 有个困惑:为啥需要改成这样呢?区别在那里呢? > ### 关于这里的25,这涉及到了 URL 编码(URL Encoding) 的底层机制: > 简单的说:%25 其实就是百分号 % 本身经过“翻译”后的样子

  1. 为什么会有 %25?
    • 在计算机网络标准中,% 符号是一个特殊保留字符。它在 URL(统一资源定位符)中被用作“转义字符”的前缀。例如:空格会被编码为 %20,斜杠 / 会被编码为 %2F
    • 当直接写 %eth0 时,某些底层解析器(比如 npm 使用的 Node.js 网络库或某些版本的 curl)会尝试去寻找 % 后面跟随的两个十六进制数字。因为它发现后面跟着的是 et而不是数字,解析器就会报错,或者无法正确识别这个地址,导致代理失效。
    • 为了在 URL 中表示一个“纯粹的百分号”,你必须对百分号本身进行编码。在 ASCII 码表中,百分号% 的十六进制值是 25
  2. 两组代理的区别特性: |特性|第一组 (%eth0)|第二组 (%25eth0)| |——-|——–|——–| |形式|原始/未编码形式|URL 编码形式| |解析行为 | Shell 脚本或简单的系统工具能识别,但高级编程语言(Node.js, Go)往往会报错。 |符合标准规范。所有的现代网络库都能正确识别。 | |稳定性 |不稳定,取决于具体软件对 URL 的解析严谨程度。 |非常稳定,是跨平台、跨工具的标准写法。| |npm 表现 |失败。Node.js 的 URL 解析器会认为这是一个非法的 URL 格式。 |成功。Node.js 将 %25 还原为 %,从而正确找到网卡。| > 实测:在这个系统curl 进行下载正常,使用wget 下载却报错:
bosh@h1:~$ wget www.google.com
        Error parsing proxy URL http://[fe80::1%25eth0]:8888: Invalid IPv6 numeric address.
        bosh@h1:~$ crule www.google.com
        -bash: crule: command not found
        bosh@h1:~$ curl www.google.com
        <!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en"><head><meta content="Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for." name="description"><meta content="noodp, " name="robots"><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"><title>Google</title><script nonce="qOaPXbwdBh-h
  1. 为什么 npm 需要这个? npm 是基于 Node.js 运行的。Node.js 在处理网络请求时,对环境变量中的 URL 格式要求非常严格。
  • 当 Node.js 读取到 http://[fe80::1%eth0]:8888 时,它会尝试解析这个字符串。

  • 由于 % 后面不是有效的十六进制数,解析器会抛出一个内部错误,或者直接忽略这个代理配置,导致 npm install 尝试直接连接网络,进而因为连不上墙外的服务器而超时。

而使用 %25eth0 时,Node.js 会执行以下逻辑:

  • 检测到 %25。

  • 将其解码(Decode)为 %。

  • 得到最终地址 fe80::1%eth0。

  • 交给操作系统,操作系统通过 eth0 网卡连接到 fe80::1。

总结:

> 这个坑其实是 “标准之争”。虽然在终端手动 ping 的时候可以直接用 %eth0,但在程序代码、配置文件和 URL 中,永远建议使用 %25 来代替 %,以确保最大的兼容性。

:smile: :smile: :smile: :smile: :smile: