snell 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE +21 -0
- data/README.md +177 -0
- data/docs/ARCHITECTURE.md +87 -0
- data/docs/COMPATIBILITY.md +160 -0
- data/docs/PROVENANCE.md +59 -0
- data/docs/SECURITY.md +38 -0
- data/exe/snell +6 -0
- data/lib/snell/address.rb +26 -0
- data/lib/snell/cli.rb +140 -0
- data/lib/snell/client.rb +141 -0
- data/lib/snell/configuration.rb +193 -0
- data/lib/snell/crypto.rb +77 -0
- data/lib/snell/datagram.rb +14 -0
- data/lib/snell/datagram_socket.rb +68 -0
- data/lib/snell/direct_connector.rb +28 -0
- data/lib/snell/errors.rb +22 -0
- data/lib/snell/limits.rb +43 -0
- data/lib/snell/obfuscation.rb +241 -0
- data/lib/snell/protocol.rb +166 -0
- data/lib/snell/record_reader.rb +133 -0
- data/lib/snell/record_writer.rb +218 -0
- data/lib/snell/replay_cache.rb +40 -0
- data/lib/snell/server.rb +352 -0
- data/lib/snell/session.rb +84 -0
- data/lib/snell/session_pool.rb +100 -0
- data/lib/snell/socks_server.rb +371 -0
- data/lib/snell/stream.rb +203 -0
- data/lib/snell/transport.rb +140 -0
- data/lib/snell/v6/profile.rb +303 -0
- data/lib/snell/version.rb +5 -0
- data/lib/snell.rb +68 -0
- metadata +155 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 29f68d3067c039f247dfe38173babd869d1562c9c51fb80cf3fd9ac1bfbad78b
|
|
4
|
+
data.tar.gz: b97030df6c5b6ac78708f5714f018a463e3ab1218d8ccd4ba9e18c915bc842cf
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 8b0a529b757fffec2ae849608a4c9b19f225f6d50fdda2fbeaf07b14743b45a6393853035c36d5a4081ea2a066affeab682ff6a31717facb7ae62244685af975
|
|
7
|
+
data.tar.gz: 24f3a709614915c3206ab3f78b0b7bb36c225447aaea9e274187b3f6227cf66319f0da10de0203d31651b4c6fc8fcc39b853d4f660d0994fde0e3e5e594a7f4f
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-10-01
|
|
4
|
+
|
|
5
|
+
- Add Async Ruby Snell v4 client, v5 server, and v6 client/server.
|
|
6
|
+
- Add authenticated records, v6 shaping, explicit raw mode, and legacy HTTP/TLS obfuscation.
|
|
7
|
+
- Add TCP streams, bounded sequential reuse, UDP associations, user snapshots, replay checks, and admission limits.
|
|
8
|
+
- Add local SOCKS5 CLI, strict JSON configuration, graceful signals, and safe server counters.
|
|
9
|
+
- Add independent tests, external interop scripts, three-process capacity checks, and Ruby 3.4/4.0 CI configuration.
|
|
10
|
+
- Record external-peer compatibility limits and the independent provenance review required before distribution.
|
|
11
|
+
- Prevent concurrent stream closes from returning an already borrowed session to the idle pool.
|
|
12
|
+
- Close newly created UDP sockets when binding fails and validate SOCKS resource limits through Limits.
|
|
13
|
+
- Document that SOCKS CONNECT success confirms request submission rather than target connection establishment.
|
|
14
|
+
- Record the maintainer-authorized first-release exception; independent provenance/licensing clearance remains pending.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Snell Ruby contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Snell for Ruby
|
|
2
|
+
|
|
3
|
+
Ruby 3.4+ 的 Snell 客户端、服务端和本地 SOCKS5 桥接器。使用 Async/Fiber 处理并发,
|
|
4
|
+
OpenSSL 与 libsodium 提供加密原语。当前版本为首版 0.1.0。
|
|
5
|
+
|
|
6
|
+
支持 v4 客户端、兼容 v4 的 v5 服务端,以及 v6 双端;包含 TCP、UDP over TCP、
|
|
7
|
+
顺序连接复用、用户密钥、HTTP/TLS 混淆和 v6 三种模式。v5 QUIC 不在范围内。
|
|
8
|
+
|
|
9
|
+
实现和测试在此工作区独立编写;协议分析参考固定版本的 sing-snell。
|
|
10
|
+
MIT 许可目标、角色隔离方式及分发前待办见 [来源说明](docs/PROVENANCE.md)。
|
|
11
|
+
该记录不是法律意义上的 clean-room 认证。
|
|
12
|
+
|
|
13
|
+
## 安装和运行
|
|
14
|
+
|
|
15
|
+
先安装 Ruby 3.4 或更新版本,以及 libsodium 开发库:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
# macOS
|
|
19
|
+
brew install ruby libsodium
|
|
20
|
+
|
|
21
|
+
# Debian / Ubuntu,Ruby 需单独安装到受支持版本
|
|
22
|
+
sudo apt-get install libsodium-dev build-essential
|
|
23
|
+
|
|
24
|
+
bundle install
|
|
25
|
+
bundle exec snell check --config examples/server.json
|
|
26
|
+
bundle exec snell server --config examples/server.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
另一个终端启动客户端:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
bundle exec snell client --config examples/client.json
|
|
33
|
+
curl --socks5-hostname 127.0.0.1:1080 https://example.com/
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
示例两端监听回环地址。部署时替换示例 PSK,并将客户端的 `server` 指向实际服务端。
|
|
37
|
+
PSK 从 JSON 文件读取;`check` 只校验配置,不建立连接。配置错误退出码为 2,运行失败为 1,
|
|
38
|
+
SIGINT 为 130,SIGTERM 正常结束为 0。INT/TERM 停止接入,等待宽限期,再关闭残留连接。
|
|
39
|
+
|
|
40
|
+
## 配置
|
|
41
|
+
|
|
42
|
+
服务端示例:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"type": "server",
|
|
47
|
+
"listen": "127.0.0.1:8388",
|
|
48
|
+
"version": 6,
|
|
49
|
+
"psk": "replace-with-a-shared-secret",
|
|
50
|
+
"mode": "default",
|
|
51
|
+
"users": [{"name": "alice", "user_key": "replace-user-key"}],
|
|
52
|
+
"limits": {"max_connections": 2048, "shutdown_grace": 30}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
客户端使用相同的 `psk`、`version: 6` 和 `mode`,添加 `user_key`、`server`,
|
|
57
|
+
并设置 `type: "client"` 和 `listen: "127.0.0.1:1080"`。不配置服务端 `users` 时,
|
|
58
|
+
使用 PSK 认证而不限制 user key;配置时必须为非空数组,连接必须匹配其中一个 key。
|
|
59
|
+
IPv6 端点写作 `[::1]:8388`。
|
|
60
|
+
|
|
61
|
+
| 配置 | 客户端 | 服务端 |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `version` | `4`(默认)、`6` | `5`(默认)、`6` |
|
|
64
|
+
| `mode` | v6:`default`、`unshaped`、`unsafe-raw` | 同左 |
|
|
65
|
+
| `obfs` | v4:`none`、`http`、`tls` | v5:同左 |
|
|
66
|
+
| `obfs_host` / `obfs_uri` | HTTP/TLS 伪装主机、HTTP 路径 | 接口配置可接收这些字段 |
|
|
67
|
+
| `reuse` | 布尔值,默认 `true` | 不接受 |
|
|
68
|
+
| `user_key` | 单个用户密钥,可省略 | 不接受 |
|
|
69
|
+
| `users` | 不接受 | 非空用户数组,可省略 |
|
|
70
|
+
|
|
71
|
+
v6 PSK 长度为 12–255 字节;用户密钥不超过 255 字节。JSON 配置使用 UTF-8 字符串,
|
|
72
|
+
协议长度按字节计算。未知字段、重复 JSON 键、跨角色字段和非法数值均拒绝。
|
|
73
|
+
`unsafe-raw` 没有加密、完整性或 PSK 身份证明;不会自动降级到此模式。
|
|
74
|
+
HTTP/TLS 混淆也不提供 HTTPS/TLS 的证书认证。
|
|
75
|
+
|
|
76
|
+
## Ruby API
|
|
77
|
+
|
|
78
|
+
所有网络对象都在同一个 `Snell.run` 或 Async reactor 内创建、使用和关闭;不在线程、
|
|
79
|
+
reactor 之间共享客户端。可并发创建不同流,同一物理连接只顺序复用。
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
require "snell"
|
|
83
|
+
|
|
84
|
+
Snell.run do
|
|
85
|
+
client = Snell::Client.new(
|
|
86
|
+
server: ["127.0.0.1", 8388],
|
|
87
|
+
psk: ENV.fetch("SNELL_PSK"),
|
|
88
|
+
version: 6
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
begin
|
|
92
|
+
request = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n".b
|
|
93
|
+
client.connect("example.com", 80, early_data: request) do |stream|
|
|
94
|
+
puts stream.read
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
client.open_udp do |socket|
|
|
98
|
+
socket.send_packet("hello".b, to: ["127.0.0.1", 9000])
|
|
99
|
+
packet = socket.receive_packet # 目标需要存在 UDP 应答服务
|
|
100
|
+
puts [packet.address.host, packet.address.port, packet.payload].inspect
|
|
101
|
+
end
|
|
102
|
+
ensure
|
|
103
|
+
client.close
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`Stream` 提供 `read(length = nil, outbuf = nil)`、`readpartial(length, outbuf = nil)`、
|
|
109
|
+
`write`、`close_write`、`close`、`closed?`。`read` 不带长度会收集到 EOF,处理大文件应循环
|
|
110
|
+
`readpartial`。`write("")` 不发送 EOF;`close_write` 发送协议逻辑 EOF,读方向仍可继续。
|
|
111
|
+
收到首个回复的读取才确认远端连接结果,`connect` 返回不代表目标已经建立连接。
|
|
112
|
+
收到远端错误回复时抛出 `RemoteError` 并保留错误码;对端直接关闭物理连接时读取返回 EOF。
|
|
113
|
+
|
|
114
|
+
块形式保证关闭。可复用流的 `close` 会在字节和时间上限内排空未读数据;不满足条件时关闭
|
|
115
|
+
物理连接。连接池默认最多保留 10 个空闲连接,180 秒后主动释放。`close_idle_connections`
|
|
116
|
+
仅关闭空闲连接,`close` 关闭整个客户端。失败或取消的会话不进入池。
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
Snell.run do
|
|
120
|
+
server = Snell::Server.new(psk: ENV.fetch("SNELL_PSK"), version: 6)
|
|
121
|
+
server.run(listen: ["127.0.0.1", 8388])
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
嵌入应用可在同一 reactor 的另一任务调用 `server.stop(grace: 30)`、读取 `server.stats`,
|
|
126
|
+
或 `server.update_users([{name: "alice", user_key: "new-key"}])`。
|
|
127
|
+
用户更新先完整校验后替换快照,影响后续请求;已建立的 TCP 转发及 UDP 关联继续完成,不重新认证。
|
|
128
|
+
`ping` 返回往返秒数,仅检测协议可达性,不证明用户授权或目标可连接。
|
|
129
|
+
|
|
130
|
+
## 资源边界
|
|
131
|
+
|
|
132
|
+
| `limits` 字段 | 默认值 | 作用 |
|
|
133
|
+
|---|---:|---|
|
|
134
|
+
| `max_connections` | 2048 | 物理连接上限;满额立即拒绝 |
|
|
135
|
+
| `handshake_concurrency` | 64 | 同时握手上限 |
|
|
136
|
+
| `handshake_rate` / `handshake_burst` | 100 / 200 | 每秒准入令牌 / 突发量 |
|
|
137
|
+
| `udp_associations` | 256 | UDP 关联上限 |
|
|
138
|
+
| `buffer_bytes` | 131072 | 目标 TCP/适配器读取块上限;协议记录另受长度边界约束 |
|
|
139
|
+
| `dial_timeout` / `handshake_timeout` | 10 / 10 秒 | 拨号 / 服务端初始握手、客户端初始请求发送 |
|
|
140
|
+
| `idle_timeout` | 300 秒 | 单次读写操作的绝对期限(碎片进度不续期) |
|
|
141
|
+
| `pool_max_idle` / `pool_idle_timeout` | 10 / 180 秒 | 空闲池容量 / 回收时间 |
|
|
142
|
+
| `drain_timeout` / `drain_max_bytes` | 2 秒 / 524288 | 关闭流时的排空预算 |
|
|
143
|
+
| `shutdown_grace` | 30 秒 | 退出宽限期 |
|
|
144
|
+
|
|
145
|
+
`idle_timeout` 应用于一次传输操作/协议记录;碎片进度不会重置期限。TCP 客户端的首回复
|
|
146
|
+
惰性读取也使用这一期限,`Stream#read` 跨多条记录不具有统一总时限。调用者可通过 Async
|
|
147
|
+
的外层 timeout 限定整个应用操作。
|
|
148
|
+
|
|
149
|
+
SOCKS5 只接受回环监听和无认证方法,支持 CONNECT、UDP ASSOCIATE;不支持 BIND、认证扩展
|
|
150
|
+
和 UDP 分片。UDP 来源绑定 TCP 对端 IP,并在收到第一个有效包后绑定来源端口;关闭控制 TCP
|
|
151
|
+
同时结束关联。计数器通过 Ruby API 读取,服务端 CLI 退出时向 stderr 输出不含密钥的 JSON 快照。
|
|
152
|
+
没有内置 HTTP 监控端口或多进程管理器。
|
|
153
|
+
|
|
154
|
+
SOCKS CONNECT 成功回复只表示 Snell 隧道请求已发送,不确认目标已连接。
|
|
155
|
+
目标拒绝连接或 DNS 失败时,SOCKS 调用者可能先收到成功回复,随后连接断开。
|
|
156
|
+
Snell 对端可能等待应用数据才发送首回复,因此桥接器不在接收应用数据前等待该回复。
|
|
157
|
+
|
|
158
|
+
## 验证和设计
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
bundle exec rake test lint build
|
|
162
|
+
SING_BOX=/path/to/sing-box bundle exec rake interop
|
|
163
|
+
OFFICIAL_BINARIES=/path/to/official-binaries bundle exec rake official
|
|
164
|
+
CONNECTIONS=1000 SECONDS=1800 bundle exec rake performance
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
官方工具按 `v4.1.1/snell-server`、`v5.0.1/snell-server`、`v6.0.0rc/snell-server` 放置;
|
|
168
|
+
验收脚本校验摘要并通过 Docker 运行 Linux amd64 二进制。Go 工具固定为 sing-box
|
|
169
|
+
`v1.15.0-alpha.9`。这些工具由调用者自行取得,不随 Gem 分发。
|
|
170
|
+
|
|
171
|
+
- [设计与职责](docs/ARCHITECTURE.md)
|
|
172
|
+
- [兼容范围、实测结果与已知限制](docs/COMPATIBILITY.md)
|
|
173
|
+
- [安全与部署边界](docs/SECURITY.md)
|
|
174
|
+
- [来源及发布门禁](docs/PROVENANCE.md)
|
|
175
|
+
|
|
176
|
+
默认官方验收测量完整响应后关闭的传输;提前半关闭和空 UDP 的失败探针必须单独报告,
|
|
177
|
+
不能用默认脚本通过代替这些能力的兼容证明。详见兼容性文档。
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# 设计与职责
|
|
2
|
+
|
|
3
|
+
本实现将 Snell 分为协议值对象、记录层、会话生命周期和应用适配四部分。
|
|
4
|
+
Ruby 接口使用关键字参数、块、异常、不可变 Data 和显式资源所有权,不把 Go 的 goroutine、
|
|
5
|
+
context、接口层次逐项搬入 Ruby。网络调度交给 Async 与 Ruby Fiber scheduler。
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart TD
|
|
9
|
+
CLI[CLI / JSON 配置] --> SOCKS[本地 SOCKS5]
|
|
10
|
+
SOCKS --> Client[Client]
|
|
11
|
+
API[Ruby 调用者] --> Client
|
|
12
|
+
API --> Server[Server]
|
|
13
|
+
Client --> Pool[SessionPool]
|
|
14
|
+
Pool --> Session[Session]
|
|
15
|
+
Server --> Session
|
|
16
|
+
Server --> Connector[DirectConnector]
|
|
17
|
+
Stream[Stream / DatagramSocket] --> Session
|
|
18
|
+
Session --> Records[RecordReader / RecordWriter]
|
|
19
|
+
Records --> Crypto[Crypto / V6::Profile]
|
|
20
|
+
Records --> Obfs[Obfuscation]
|
|
21
|
+
Obfs --> Transport[Transport]
|
|
22
|
+
Transport --> Socket[Socket / Async reactor]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 边界和契约
|
|
26
|
+
|
|
27
|
+
| 组件 | 负责 | 不负责 |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `Address` / `Datagram` | 字节、地址、端口值的校验和所有权 | DNS、连接、日志 |
|
|
30
|
+
| `Protocol` | 请求、回复、UDP 报文编码及严格解析 | Socket、用户数据库 |
|
|
31
|
+
| `Crypto` / `V6::Profile` | Argon2id/AES-GCM、计数器、v6 参数派生及形态 | 重试、连接复用 |
|
|
32
|
+
| `RecordReader/Writer` | 有限记录、首包、分片、逻辑 EOF、连续方向状态 | 应用目的地址与认证策略 |
|
|
33
|
+
| `Transport` | 部分读写、单调时钟期限、真实 socket 关闭 | 协议解释 |
|
|
34
|
+
| `Session` | 一个物理连接及其两个独立加密方向 | 并发流多路复用 |
|
|
35
|
+
| `Stream` | 惰性回复、IO 风格读写、半关闭、受限排空 | 主动重试应用写入 |
|
|
36
|
+
| `SessionPool` | 预留容量、借还、空闲限制与主动过期 | 跨 reactor 共享 |
|
|
37
|
+
| `DatagramSocket` | 一条 UDP 关联、一个记录一个报文 | TCP 流池复用 |
|
|
38
|
+
| `Client` | 目的请求、专用 ping/UDP 会话、资源关闭 | SOCKS wire 格式 |
|
|
39
|
+
| `Server` | 准入、用户快照、TCP/UDP 转发、优雅停止 | 业务路由、Web 管理 |
|
|
40
|
+
| `Configuration/CLI` | 严格 JSON、角色校验、信号桥、退出码 | 协议与加密细节 |
|
|
41
|
+
| `SocksServer` | RFC 1928 边界、来源绑定、控制连接生命周期 | 公网无认证代理 |
|
|
42
|
+
|
|
43
|
+
## TCP 生命周期
|
|
44
|
+
|
|
45
|
+
1. `Client#connect` 先取可复用空闲会话;需要新连接时先预留容量,再拨号。
|
|
46
|
+
2. 请求头完整放在首记录;early data 可填充剩余空间,多余数据继续分片。
|
|
47
|
+
3. 返回 `Stream`,由第一次读取解释服务端回复。服务端先发 banner 与客户端先发请求都可工作。
|
|
48
|
+
4. 一次写入可能拆成多个记录;同一方向串行,同一流允许一个读者和一个写者并行。
|
|
49
|
+
5. `close_write` 发送零载荷记录,保留反向读能力;空字符串写入不产生 EOF。
|
|
50
|
+
6. `close` 按预算排空反向数据。双方逻辑 EOF 都完成且记录层健康时才允许会话回池。
|
|
51
|
+
7. 新请求复用同一方向的盐、密钥、nonce 和 v6 序列,不重置加密状态。
|
|
52
|
+
|
|
53
|
+
物理会话状态为 `ready → active → draining → ready`,任一失败进入 `closed`。
|
|
54
|
+
UDP 和 ping 使用专用会话。不会把两个活跃 TCP 流放入同一个会话,也不会在网络失败后
|
|
55
|
+
重放应用数据。中断记录、认证失败、部分写失败会使记录对象不可再使用。
|
|
56
|
+
|
|
57
|
+
## 服务端与调度
|
|
58
|
+
|
|
59
|
+
准入检查先于任务创建,使用连接上限、握手并发和令牌桶,拒绝超量连接而不创建无界等待队列。
|
|
60
|
+
握手期限覆盖碎片读取,不能靠每发一个字节无限续期。转发一段数据后等待下游完成写入,
|
|
61
|
+
由 socket 背压限制积压;没有每连接独立线程或无界 payload 队列。
|
|
62
|
+
|
|
63
|
+
libsodium 原生调用按每次握手的固定参数执行,不能假设它释放 Ruby GVL。
|
|
64
|
+
小成本调用直接运行,靠握手限流控制突发;记录写入也定期让出 reactor。
|
|
65
|
+
超时不能抢占正在运行的原生调用,不能将握手期限理解为 KDF 的硬执行时间上限。
|
|
66
|
+
本项目的容量目标是长期并发稳定性,吞吐量必须按实际负载另测。
|
|
67
|
+
|
|
68
|
+
服务器为每个方向保留结构化子任务。退出时先停止接入,等待 `shutdown_grace`,再取消残余
|
|
69
|
+
任务和关闭 socket。信号处理器只写 pipe,所有调度与资源清理在 reactor 中执行。
|
|
70
|
+
Ruby 3.4 的 KQueue 路径不依赖“另一 Fiber 关闭 socket 一定唤醒等待者”这一假设。
|
|
71
|
+
|
|
72
|
+
## 用户与重放
|
|
73
|
+
|
|
74
|
+
`users: nil` 表示仅 PSK;配置用户数组后执行精确 user key 匹配。
|
|
75
|
+
`update_users` 校验整个新数组再一次性替换不可变快照;非法更新保留旧值,后续请求使用新快照。
|
|
76
|
+
CLI 错误和持有密钥的运行时对象 inspect 不输出 PSK/user key,内部日志不记录目标负载。
|
|
77
|
+
`Datagram` 等业务 Data 对象的 inspect 会展示 payload,调用者应避免将其用于敏感流量日志。
|
|
78
|
+
|
|
79
|
+
认证后的首盐才加入有界重放缓存;缓存满且未过期时拒绝新盐,不为了接入删除有效证据。
|
|
80
|
+
默认缓存属于一个 `Server` 实例,显式注入可在同一 reactor 的多个服务共享。它不是跨进程、
|
|
81
|
+
跨主机或持久化重放数据库。
|
|
82
|
+
|
|
83
|
+
## 可验证性
|
|
84
|
+
|
|
85
|
+
测试分为独立已知向量、字段/记录边界、真实 Ruby socket 生命周期、分进程 Go 双向、
|
|
86
|
+
官方服务端和三进程容量负载。各层分别报告结果,不将自洽回环等同于跨实现兼容。
|
|
87
|
+
验收脚本不嵌入外部实现或上游测试,外部工具及其许可保留在工作区之外。
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# 兼容性与验收
|
|
2
|
+
|
|
3
|
+
本次参考基线固定为 sing-snell `bc5a12ac736f235b2de2926ecd2791cc925e6b8c`,
|
|
4
|
+
不是随远端 HEAD 变化的目标。测试日期为 2026-10-01。外部 Go 进程使用 sing-box
|
|
5
|
+
`v1.15.0-alpha.9`,构建元数据中的 sing-snell 模块对应上述提交。
|
|
6
|
+
|
|
7
|
+
## 实现范围
|
|
8
|
+
|
|
9
|
+
| 能力 | 实现及限制 |
|
|
10
|
+
|---|---|
|
|
11
|
+
| v4 客户端 / v5 服务端 | TCP、UDP over TCP、用户密钥、顺序连接复用 |
|
|
12
|
+
| v6 双端 | `default`、`unshaped`、`unsafe-raw`;双方模式必须相同 |
|
|
13
|
+
| legacy 混淆 | none、HTTP、TLS;TLS 是混淆,不是标准 TLS 安全会话 |
|
|
14
|
+
| 流 | early data、惰性回复、server-first、半关闭、有限排空 |
|
|
15
|
+
| 地址 | TCP 文本地址;UDP IPv4、IPv6、域名;域名由 Ruby/Async 环境执行名称解析 |
|
|
16
|
+
| SOCKS5 | 回环无认证 CONNECT、UDP ASSOCIATE;FRAG 非零丢弃;BIND 拒绝 |
|
|
17
|
+
| v5 QUIC / v1–v3 客户端 | 不实现 |
|
|
18
|
+
| Rails | 通用 Ruby gem;不附带 Rails 引擎、数据库或后台作业系统 |
|
|
19
|
+
|
|
20
|
+
### SOCKS CONNECT 的确认边界
|
|
21
|
+
|
|
22
|
+
首版保留提前回复行为:SOCKS 成功仅表示 Snell 请求已发送,目标拨号失败可能随后以 EOF 表现。
|
|
23
|
+
客户端 Ruby API 的首次读取才解析远端回复;对端可能等待应用数据后才回复,
|
|
24
|
+
若 SOCKS 桥接器先等待回复再允许应用发送,会让请求响应协议互相等待。
|
|
25
|
+
该回复不构成目标连接成功的证明。
|
|
26
|
+
|
|
27
|
+
Legacy 服务端按累计载荷每 192 KiB 增长分帧,约 2.1 MiB 达到最大帧,调度细节与参考
|
|
28
|
+
实现不同。HTTP/TLS 握手模板独立构造;已验证流互通,不声称握手指纹、随机字段或
|
|
29
|
+
流量统计分布完全一致。v6 的 12 种 generator × mixing 组合互通通过,同样不等于
|
|
30
|
+
流量指纹不可区分的证明。
|
|
31
|
+
|
|
32
|
+
## 本地验证
|
|
33
|
+
|
|
34
|
+
首版发布前修复并发关闭、旧流操作、UDP 绑定失败清理、SOCKS 限制校验及重复打包的旧文件问题。
|
|
35
|
+
2026-10-01 的当前候选在 macOS arm64 / Ruby 4.0.6 上通过 86 tests、842 assertions、
|
|
36
|
+
RuboCop、构建、隔离安装和六种模式的 CLI TCP/UDP 验证。下面矩阵和长期容量结果
|
|
37
|
+
保留原始验收环境与源码摘要,不将后续修复的验证追溯到历史记录。
|
|
38
|
+
|
|
39
|
+
| 环境 / 门禁 | 结果 |
|
|
40
|
+
|---|---|
|
|
41
|
+
| macOS arm64 / Ruby 3.4.11 | 79 tests、812 assertions,无失败、错误、跳过 |
|
|
42
|
+
| macOS arm64 / Ruby 4.0.7 | 79 tests、812 assertions,无失败、错误、跳过 |
|
|
43
|
+
| RuboCop | 42 文件通过 |
|
|
44
|
+
| Linux arm64 / Ruby 3.4、4.0 | 两个容器各 79 tests、812 assertions,无失败、错误、跳过 |
|
|
45
|
+
| Gem 构建与隔离安装 | 0.1.0 构建通过;独立 GEM_HOME 离线安装、真实 ping、CLI version 通过 |
|
|
46
|
+
| GitHub Actions | 已配置 Ruby 3.4/4.0 × Linux/macOS;未运行远端 CI |
|
|
47
|
+
|
|
48
|
+
测试包括独立 AES-GCM/BLAKE2b 向量、nonce 进位/耗尽、逐字节分片、黏包、截断、
|
|
49
|
+
认证失败无明文输出、非法 header 在读取 body 前拒绝、uint16 长度边界、真实 socket
|
|
50
|
+
半关闭、复用、排空、取消、重放缓存、准入和 CLI 退出。
|
|
51
|
+
|
|
52
|
+
真实 CLI 客户端 → CLI 服务端另在 Ruby 3.4.11 / 4.0.7 分进程通过:每个版本的六种模式
|
|
53
|
+
覆盖 IPv4、localhost 域名和真实 IPv6;36 次 TCP、54 个 UDP(含 18 个空包),包含
|
|
54
|
+
server-first 和半关闭。每轮的两个 CLI 子进程正常 SIGTERM 退出,服务端 `failed/rejected` 计数器为零,
|
|
55
|
+
连接、握手、UDP 计数均归零。复现入口为 `bundle exec ruby test/acceptance/cli.rb`。
|
|
56
|
+
|
|
57
|
+
`unsafe-raw` 的错误 PSK 和 payload 改动无法检测;测试明确确认此边界,不能归入
|
|
58
|
+
“认证失败无明文”结论。legacy 未参与混合的 padding 字节也没有 payload/header 的认证保证。
|
|
59
|
+
|
|
60
|
+
## Go 双向互通
|
|
61
|
+
|
|
62
|
+
以下每项均已通过 Ruby → Go 与 Go → Ruby:
|
|
63
|
+
|
|
64
|
+
| 变体 | TCP | UDP |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| v4 none | 通过 | 通过 |
|
|
67
|
+
| v4 HTTP | 通过 | 通过 |
|
|
68
|
+
| v4 TLS | 通过 | 通过 |
|
|
69
|
+
| v6 default | 通过 | 通过 |
|
|
70
|
+
| v6 unshaped | 通过 | 通过 |
|
|
71
|
+
| v6 unsafe-raw | 通过 | 通过 |
|
|
72
|
+
|
|
73
|
+
单次场景包含三条顺序流、1 MiB 正反向字节校验、early data(Ruby 发起)、立即逻辑半关闭、
|
|
74
|
+
用户密钥,以及 3、800、1400 字节 UDP 包。Ruby → Go 还覆盖空 UDP 包。
|
|
75
|
+
Go → Ruby 经 sing-box 的 SOCKS 前端,该前端在此测试路径丢弃空 UDP,故不声称该方向
|
|
76
|
+
空包已通过;Ruby 自身真实 socket 测试另有空包覆盖。
|
|
77
|
+
|
|
78
|
+
另有 12 组 v6 generator × mixing 参数、24 个方向场景通过;那轮的反向只覆盖 TCP,
|
|
79
|
+
不把后续加入的 UDP 验证追溯到旧报告。
|
|
80
|
+
|
|
81
|
+
精简的机器可读证据保存在源码工作区的 `docs/validation/interoperability.json`,不随 Gem 打包。
|
|
82
|
+
|
|
83
|
+
复现:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
SING_BOX=/path/to/sing-box bundle exec rake interop
|
|
87
|
+
# 可缩小变体
|
|
88
|
+
bundle exec ruby test/acceptance/interop.rb --sing-box /path/to/sing-box --filter 6/default
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 官方服务端
|
|
92
|
+
|
|
93
|
+
摘要固定的 Linux amd64 官方二进制在 Docker 中执行,客户端和原始 TCP/UDP echo 目标运行于
|
|
94
|
+
宿主 macOS。默认验收是普通请求/完整响应后关闭,不把该场景称作提前半关闭通过。
|
|
95
|
+
|
|
96
|
+
| 官方版本 | 普通 TCP、顺序会话、非空 UDP |
|
|
97
|
+
|---|---|
|
|
98
|
+
| v4.1.1 / none、HTTP | 通过 |
|
|
99
|
+
| v5.0.1 / none、HTTP | 通过 |
|
|
100
|
+
| v6.0.0rc / default、unshaped、unsafe-raw | 通过 |
|
|
101
|
+
|
|
102
|
+
已知限制与证据:
|
|
103
|
+
|
|
104
|
+
- **立即半关闭可能截断大响应。** 官方 v5.0.1 收到 1 MiB 后,目标确认完整读写,但 Ruby
|
|
105
|
+
客户端曾只收到 216064 字节后物理 EOF;外部 Go 客户端同场景也只收到 878747 字节。
|
|
106
|
+
这支持“不是 Ruby 独有”的判断,但不证明具体官方内部原因。未添加固定 sleep 规避。
|
|
107
|
+
- **空 UDP。** 官方 v4.1.1 在空包探针上记录 `Invalid request` 并无响应;普通非空包通过。
|
|
108
|
+
其他官方版本的空包结果不能从这一次失败推断。
|
|
109
|
+
- **TLS 混淆。** 官方 v4.1.1、v5.0.1 的 `obfs=tls` 配置下,Ruby 和 Go TLS 客户端均失败;
|
|
110
|
+
同一服务配置配合无混淆 Go 客户端反而通过。这两个构建在所测配置下没有表现出 TLS 混淆接入能力,
|
|
111
|
+
不能用 Go 双向 TLS 通过来宣称官方 TLS 兼容。
|
|
112
|
+
- 官方 Surge 客户端没有在本轮接入 Ruby 服务端,**未验证官方客户端 → Ruby 服务端**。
|
|
113
|
+
- v6 版本仍在变化;本次固定 `v6.0.0rc`,不替其他 beta/RC 或未来版本作保证。
|
|
114
|
+
|
|
115
|
+
失败探针保持可复现,不计入默认通过矩阵:
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
bundle exec ruby test/acceptance/official.rb --binaries /path/to/binaries \
|
|
119
|
+
--filter v5.0.1/default/none --strict-half-close
|
|
120
|
+
bundle exec ruby test/acceptance/official.rb --binaries /path/to/binaries \
|
|
121
|
+
--filter v4.1.1/default/none --empty-udp
|
|
122
|
+
bundle exec ruby test/acceptance/official.rb --binaries /path/to/binaries \
|
|
123
|
+
--filter v4.1.1/default/tls
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
官方版本说明与下载入口见 [Snell release notes](https://kb.nssurge.com/surge-knowledge-base/release-notes/snell)。
|
|
127
|
+
该页说明 v5 服务端兼容 v4 客户端,并区分 QUIC 与 UDP over TCP;本项目只覆盖后者。
|
|
128
|
+
|
|
129
|
+
## 容量基准
|
|
130
|
+
|
|
131
|
+
完整验收脚本使用三个独立进程:负载发生器、Ruby Snell worker、原始 echo 目标。
|
|
132
|
+
预热以每秒最多 50 条建立 1000 个连接,全部建立后才开始 1800 秒计时。
|
|
133
|
+
每条 TCP 隧道每秒交换 1 KiB 带连接/序号校验的数据,直到计时结束再进行逻辑 EOF 清理。
|
|
134
|
+
|
|
135
|
+
门槛:持续 1000 活跃连接、1800000 次正确交换、零意外错误/损坏、worker RSS ≤ 1 GiB、
|
|
136
|
+
10 ms 周期采样的 reactor 延迟 P99 ≤ 50 ms,清理后 active/handshake/UDP 计数归零、FD 回到基线。
|
|
137
|
+
RSS 使用 Linux `VmHWM` 高水位;不是全部三进程之和。此负载不是最大吞吐、弱网、公网或
|
|
138
|
+
真实设备基准,也不承诺 Go/官方实现的吞吐量。
|
|
139
|
+
|
|
140
|
+
最终 **1000 × 1800 秒验收通过**:完成 1,800,000 次 1 KiB 交换,持续活跃连接最低为
|
|
141
|
+
1000,客户端错误、服务端失败/拒绝与数据损坏均为零。worker 延迟
|
|
142
|
+
P99 **11.50 ms**,最大观测延迟 56.93 ms,RSS 高水位
|
|
143
|
+
**417.86 MiB**。结束后连接/握手/UDP 计数归零,FD 从 9 回到 9。
|
|
144
|
+
计时窗口 1800.002 秒;环境为 Linux arm64 / Ruby 4.0.7,
|
|
145
|
+
整个三进程容器限额为 4 CPU、2 GiB,并非独占物理机器。原始结果与源码摘要分别保存在
|
|
146
|
+
`docs/validation/capacity-linux-v6-default.json` 和 `capacity-source.json`,不随 Gem 打包。
|
|
147
|
+
20 × 3 秒 smoke 在 Ruby 3.4/4.0 也通过。
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
docker build -f Dockerfile.acceptance -t snell:acceptance .
|
|
151
|
+
docker run --rm --cpus 4 --memory 2g --ulimit nofile=16384:16384 \
|
|
152
|
+
snell:acceptance bundle exec ruby test/acceptance/capacity.rb \
|
|
153
|
+
--connections 1000 --seconds 1800 --version 6 --mode default
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 交付结论的边界
|
|
157
|
+
|
|
158
|
+
实现、测试、文档和本地打包可以交付审阅;它们不等于已经通过公开分发许可审查、
|
|
159
|
+
独立安全审计或官方客户端完整矩阵。远端 CI 与公开发行状态以 GitHub 和 RubyGems 为准;未执行生产部署。
|
|
160
|
+
来源审查与发布门禁仍以 [PROVENANCE](PROVENANCE.md) 为准。
|
data/docs/PROVENANCE.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# 来源与分发边界
|
|
2
|
+
|
|
3
|
+
本项目目标许可为 MIT。该声明不替代来源审查,也不授予任何第三方实现的权利。
|
|
4
|
+
尚未进行外部法律审查,不宣称已经取得闭源分发的法律确认。
|
|
5
|
+
|
|
6
|
+
## 0.1.0 首版发布例外
|
|
7
|
+
|
|
8
|
+
2026-10-01,维护者明确授权覆盖首版来源审查门禁,按现有 MIT 声明公开发布 0.1.0。
|
|
9
|
+
独立技术审阅检查了本地实现、测试、交接规范及依赖元数据,但未能确认全部来源资料
|
|
10
|
+
可按 MIT 分发;特别是源码分析交接中的 v6 常量、查表资料及规范,仍缺独立来源与许可确认。
|
|
11
|
+
本次发布不表示这项审查已经完成,也不构成 clean-room 认证或法律许可结论。
|
|
12
|
+
该例外仅适用于 0.1.0,不自动豁免后续版本的发布门禁。
|
|
13
|
+
|
|
14
|
+
## 角色隔离
|
|
15
|
+
|
|
16
|
+
- 协议分析者阅读过 GPL-3.0-or-later 的 sing-snell 提交
|
|
17
|
+
`bc5a12ac736f235b2de2926ecd2791cc925e6b8c`,负责字段、数值关系和黑盒互通分析。
|
|
18
|
+
- `independent_protocol`、`independent_runtime`、`independent_cli` 均以不继承研究上下文的方式启动,
|
|
19
|
+
被明确禁止阅读 GPL 协议实现、测试或临时参考检出。他们根据接口契约和协议事实编写 Ruby 生产代码。
|
|
20
|
+
- 数学和字段事实交接见 `PROTOCOL_FACTS.md`。该文档的来源包含源码分析,不是独立黑盒逆向成果。
|
|
21
|
+
分析者检查了单位、字段边界和方向性;这只是技术审阅,不是许可审查。
|
|
22
|
+
- 实现者可以阅读 Ruby、Async、RbNaCl、OpenSSL 的官方文档及 SOCKS RFC。
|
|
23
|
+
- 集成者检查新 Ruby 代码、执行测试、记录故障,由独立实现者修正生产代码。
|
|
24
|
+
|
|
25
|
+
上下文隔离记录并不自动证明法律意义上的 clean-room 独立性。发布负责人须逐项审查协议规范、
|
|
26
|
+
实现及测试来源;如发现受保护表达的衍生内容,应在分发前解决其授权或替换问题。
|
|
27
|
+
|
|
28
|
+
## 外部验收工具
|
|
29
|
+
|
|
30
|
+
Go 黑盒端使用独立下载的 sing-box `v1.15.0-alpha.9`,其模块元数据固定 sing-snell
|
|
31
|
+
`v0.0.0-20260904135315-bc5a12ac736f`。通过进程、JSON 配置和网络端口使用公开接口。
|
|
32
|
+
不把该工具、其 GPL 代码、Go 测试桥或上游测试夹具放入 Gem。
|
|
33
|
+
|
|
34
|
+
官方服务端使用单独下载、校验和验证的二进制,只供互通测试,不随 Gem 再分发:
|
|
35
|
+
|
|
36
|
+
| 版本(Linux amd64) | snell-server 文件 SHA256 |
|
|
37
|
+
|---|---|
|
|
38
|
+
| v4.1.1 | 8e02974916645aafc0521c49faf0ab9916a0a8a7922ae7261cfd74b96899d25a |
|
|
39
|
+
| v5.0.1 | 5b2e221f2c6e29b1db8e47053e1221be29d5627da807cb932b089f514a3609f0 |
|
|
40
|
+
| v6.0.0rc | 02fa15ac1e18cde6a3e072eeb5d15328c7fd759dbefb1e77e33891a71a1202ae |
|
|
41
|
+
|
|
42
|
+
版本/摘要是验收元数据,不代表官方背书。外部工具的运行和分发条件仍由其各自许可决定。
|
|
43
|
+
|
|
44
|
+
## 公开来源
|
|
45
|
+
|
|
46
|
+
- Snell 官方说明:https://kb.nssurge.com/surge-knowledge-base/release-notes/snell
|
|
47
|
+
- 参考实现:https://github.com/SagerNet/sing-snell/tree/bc5a12ac736f235b2de2926ecd2791cc925e6b8c
|
|
48
|
+
- SOCKS5:https://www.rfc-editor.org/rfc/rfc1928.html
|
|
49
|
+
- Async:https://github.com/socketry/async
|
|
50
|
+
- IO::Endpoint:https://github.com/socketry/io-endpoint
|
|
51
|
+
- RbNaCl:https://github.com/RubyCrypto/rbnacl
|
|
52
|
+
- Ruby OpenSSL:https://ruby.github.io/openssl/
|
|
53
|
+
|
|
54
|
+
## 发布门禁
|
|
55
|
+
|
|
56
|
+
1. 独立审查人确认交接规范、实现、测试数据、文档和原生依赖的来源及许可。
|
|
57
|
+
2. 检查 Gem 文件列表,无外部二进制、参考源码、研究规范、测试密钥、安装缓存。
|
|
58
|
+
3. 单独报告 Ruby 闭环、Go 双向、官方服务端、官方客户端与容量验收的实际结果。
|
|
59
|
+
4. 来源审查未完成、必要互通/容量验收未通过时,不公开发布,不声称完整生产验收。
|
data/docs/SECURITY.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 安全与部署边界
|
|
2
|
+
|
|
3
|
+
本实现尚未完成独立安全审计。已实现的约束与已验证的行为见兼容性文档;单元测试、
|
|
4
|
+
一次容量测试不构成针对公开互联网流量的完整安全证明。
|
|
5
|
+
|
|
6
|
+
## 模式与密钥
|
|
7
|
+
|
|
8
|
+
- `default` 和 `unshaped` 使用 AES-128-GCM;盐通过 Ruby `SecureRandom.random_bytes` 生成。
|
|
9
|
+
- `unsafe-raw` 是显式明文模式,没有完整性校验,也不能依赖 PSK 阻止未授权访问。
|
|
10
|
+
仅应在已有受信任安全通道的场景考虑它,不能作为加密失败时的后备。
|
|
11
|
+
- HTTP/TLS 模式是外层混淆,不提供 HTTPS/TLS 的证书、身份验证或标准 TLS 会话。
|
|
12
|
+
- legacy 记录的部分 padding 不在认证范围内;payload/header 认证与“每个 wire 字节都认证”不同。
|
|
13
|
+
- 使用独立随机高熵 PSK;配置文件由部署者限制读取权限。CLI 不接受命令行 PSK 参数,
|
|
14
|
+
日志不输出 PSK、用户密钥、配置正文或应用字节。协议的 Argon2 参数为互通固定值,
|
|
15
|
+
不应把它当作可抵御弱口令猜测的通用密码存储方案。
|
|
16
|
+
|
|
17
|
+
## 接入和资源
|
|
18
|
+
|
|
19
|
+
SOCKS 无认证桥接器只允许回环监听;不提供公网开放代理开关。Snell 服务可监听公网,
|
|
20
|
+
其目标访问权限继承服务进程的网络权限。需要目的网段限制或出口策略时,在部署网络层实施,
|
|
21
|
+
或通过 Ruby API 注入符合 `DirectConnector` 契约的连接策略。
|
|
22
|
+
|
|
23
|
+
握手并发、速率、连接数、UDP 关联数、超时、排空预算和有限记录长度共同限制资源消耗。
|
|
24
|
+
这些不取代操作系统 FD、内存、CPU 和网络限额。一个活跃 TCP 隧道至少消耗服务端入口及出口
|
|
25
|
+
两个 socket,部署 1000 个连接时应为监听、UDP 和进程其他描述符留出余量。
|
|
26
|
+
|
|
27
|
+
重放缓存默认每服务实例独立、只在内存中。重启会清空缓存;多个 worker 不自动共享。
|
|
28
|
+
不要将多实例配置解释为全局重放防护。
|
|
29
|
+
|
|
30
|
+
## 关闭与错误
|
|
31
|
+
|
|
32
|
+
读写失败、认证错误、取消和未完成的记录使物理会话关闭,不自动重连并重放命令。
|
|
33
|
+
逻辑半关闭不等于 TCP 连接关闭;官方服务端在部分提前关闭场景中的限制见兼容性文档。
|
|
34
|
+
用户更新不撤销已经建立的 TCP 转发或 UDP 关联;需要即时撤销时由宿主停止对应服务或连接。
|
|
35
|
+
|
|
36
|
+
发现问题时先保留版本、模式、Ruby/OpenSSL/libsodium 版本、字节计数和脱敏错误类型。
|
|
37
|
+
不要在公开报告中提交真实密钥、完整代理流量或第三方敏感目标信息。本项目尚未配置外部
|
|
38
|
+
漏洞接收邮箱或发布站点,因此没有虚构联系方式。
|
data/exe/snell
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ipaddr"
|
|
4
|
+
|
|
5
|
+
module Snell
|
|
6
|
+
# 持有独立、冻结的二进制主机名副本,并校验协议地址与端口。
|
|
7
|
+
# 构造时不解析 DNS;域名和不支持的 IP 格式由 ip 返回 nil。
|
|
8
|
+
Address = Data.define(:host, :port) do
|
|
9
|
+
def initialize(host:, port:)
|
|
10
|
+
unless host.is_a?(String) && host.bytesize.between?(1, 255) && !host.include?("\0")
|
|
11
|
+
raise ArgumentError, "host must contain 1 to 255 bytes without NUL"
|
|
12
|
+
end
|
|
13
|
+
raise ArgumentError, "port must be an integer in 1..65535" unless port.is_a?(Integer) && port.between?(1, 65_535)
|
|
14
|
+
|
|
15
|
+
super(host: host.b.freeze, port: port)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def ip
|
|
19
|
+
return nil if host.include?("/") || host.include?("%")
|
|
20
|
+
|
|
21
|
+
IPAddr.new(host)
|
|
22
|
+
rescue IPAddr::InvalidAddressError
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|