block-proxy 0.1.9 → 0.1.11
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.
- package/.claude/settings.local.json +8 -0
- package/.claude/skills/commit/skill.md +32 -0
- package/.claude/skills/pcap-analyse/skill.md +100 -0
- package/CLAUDE.md +114 -0
- package/Dockerfile +1 -0
- package/README.md +3 -2
- package/bin/start.js +0 -1
- package/config.json +3 -3
- package/example/rule.js +1 -0
- package/hack-of-anyproxy/lib/requestHandler.js +33 -1
- package/package.json +4 -3
- package/proxy/fs.js +29 -2
- package/proxy/proxy.js +7 -4
- package/proxy/scan.js +49 -3
- package/server/express.js +2 -0
- package/socks5/test_tls_reuse.js +1 -0
- package/src/setupTests.js +1 -0
- package/tools/speed_test.sh +1 -0
- package/tools/tcpdump/output.pcap +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Skill: Smart Git Commit
|
|
2
|
+
|
|
3
|
+
Use this skill when the user runs `/commit` or asks to commit changes.
|
|
4
|
+
|
|
5
|
+
## Instructions
|
|
6
|
+
|
|
7
|
+
1. **Analyze Context**:
|
|
8
|
+
- Run `git status` to identify modified, added, or deleted files.
|
|
9
|
+
- Run `git diff` to inspect the actual code changes in unstaged files.
|
|
10
|
+
- Run `git diff --cached` to inspect changes in staged files.
|
|
11
|
+
|
|
12
|
+
2. **Stage Changes**:
|
|
13
|
+
- If there are unstaged changes, run `git add -A` to stage all changes (unless the user specifically asked to partial commit).
|
|
14
|
+
|
|
15
|
+
3. **Generate Commit Message**:
|
|
16
|
+
- Analyze the diffs to understand the *intent* of the changes.
|
|
17
|
+
- Draft a commit message following the **Conventional Commits** specification:
|
|
18
|
+
```
|
|
19
|
+
<type>(<scope>): <description>
|
|
20
|
+
|
|
21
|
+
[optional body]
|
|
22
|
+
```
|
|
23
|
+
- **Types**: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`.
|
|
24
|
+
- **Scope**: (Optional) The module or file affected (e.g., `proxy`, `ui`, `deps`).
|
|
25
|
+
- **Description**: Concise summary in imperative mood (e.g., "add support for...", not "added").
|
|
26
|
+
|
|
27
|
+
4. **Execute Commit**:
|
|
28
|
+
- Run `git commit -m "generated_message"`.
|
|
29
|
+
- If the message has a body, use multiple `-m` flags or a heredoc.
|
|
30
|
+
|
|
31
|
+
5. **Report**:
|
|
32
|
+
- Inform the user of the commit message used and the result.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
model: default
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Pcap Analyse Skill
|
|
6
|
+
|
|
7
|
+
## Description
|
|
8
|
+
分析 pcap/pcapng 网络抓包文件,专注于代理服务器(Socks5 over TLS/HTTP)场景下的连接诊断、性能评估和异常检测。
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
当用户请求分析 pcap 文件,或者提供一个抓包文件并询问网络连接质量、异常或性能问题时使用此 skill。
|
|
12
|
+
|
|
13
|
+
## Instructions
|
|
14
|
+
执行此 skill 时,请遵循以下步骤进行分析。如果用户没有提供 pcap 文件路径,请先询问。
|
|
15
|
+
|
|
16
|
+
**重要**:你不能直接读取二进制 pcap 文件。你必须编写并执行 Python 脚本(推荐使用 `scapy` 库,如果环境没有则尝试使用 `tshark` 命令行工具)来提取信息。
|
|
17
|
+
|
|
18
|
+
### 1. 编写分析脚本
|
|
19
|
+
编写一个 Python 脚本来解析 pcap 文件,脚本需要提取并计算以下指标:
|
|
20
|
+
|
|
21
|
+
#### A. 基础信息与抓包点推断
|
|
22
|
+
- 统计源 IP 和目的 IP 的出现频率。
|
|
23
|
+
- 分析 TCP SYN 包的 TTL (Time To Live) 和 MSS (Max Segment Size)。通常客户端发出的包 TTL 较大(如 64, 128),经过路由后会减少。如果抓到的入站包 TTL 接近默认值(未减少太多),可能是服务端抓包;如果出站包 TTL 是默认值,则是本地发出的。
|
|
24
|
+
- 结合端口号(如 8001, 8002)判断流量方向。
|
|
25
|
+
|
|
26
|
+
#### B. 连接异常分析
|
|
27
|
+
- **重传率 (Retransmission Rate)**: 重传包数 / 总包数。
|
|
28
|
+
- **复位连接 (RST)**: 统计 RST 包的数量及出现阶段(握手期、数据传输期、结束期)。
|
|
29
|
+
- **握手失败**: 有 SYN 但无 SYN/ACK 的比例。
|
|
30
|
+
- **TLS 错误**: 检查是否有 TLS Alert 消息。
|
|
31
|
+
|
|
32
|
+
#### C. 性能分析
|
|
33
|
+
- **TCP 握手延迟 (RTT)**: SYN 到 SYN/ACK 的时间差。
|
|
34
|
+
- **TLS 握手延迟**: Client Hello 到 Handshake Finished 的时间。
|
|
35
|
+
- **数据传输 RTT**: 数据包与对应 ACK 之间的时间差统计(平均值、最大值、最小值、抖动)。
|
|
36
|
+
|
|
37
|
+
#### D. 连接复用情况
|
|
38
|
+
- 统计每个 TCP 流 (Stream) 的持续时间。
|
|
39
|
+
- 统计每个流的数据传输量。
|
|
40
|
+
- 判断长连接 (Keep-Alive) 的使用情况:是否有长时间空闲但未断开的连接,或者单连接承载多次数据交互。
|
|
41
|
+
|
|
42
|
+
#### E. 阻塞与拥塞分析
|
|
43
|
+
- **Zero Window**: 统计 TCP Zero Window 通告次数(接收端缓冲区满,通知发送端暂停发送)。
|
|
44
|
+
- **Window Full**: 统计发送端未收到 ACK 而导致发送窗口耗尽的情况。
|
|
45
|
+
- **吞吐量波动**: 检查是否有明显的吞吐量骤降。
|
|
46
|
+
|
|
47
|
+
#### F. 高并发与吞吐量评估
|
|
48
|
+
- **并发连接数**: 统计每一秒内的活跃 TCP 连接数峰值。
|
|
49
|
+
- **每秒请求数 (RPS)**: 如果是 HTTP,估算请求速率。
|
|
50
|
+
- **峰值带宽**: 计算每秒传输的最大字节数。
|
|
51
|
+
|
|
52
|
+
### 2. 执行分析与报告
|
|
53
|
+
运行脚本获取输出,并生成一份详细的分析报告。报告必须包含以下章节:
|
|
54
|
+
|
|
55
|
+
1. **抓包环境推断**:
|
|
56
|
+
- 结论:是客户端抓包还是代理服务器端抓包?
|
|
57
|
+
- 依据:IP 分布、TTL 特征、端口方向。
|
|
58
|
+
|
|
59
|
+
2. **连接健康度与异常**:
|
|
60
|
+
- 异常连接比例。
|
|
61
|
+
- 具体的错误类型(重传、RST、超时等)及其对用户体验的潜在影响。
|
|
62
|
+
|
|
63
|
+
3. **性能指标**:
|
|
64
|
+
- 平均/P95 延迟数据。
|
|
65
|
+
- 握手耗时分析。
|
|
66
|
+
|
|
67
|
+
4. **连接复用分析**:
|
|
68
|
+
- 连接是否被有效复用(Socks5/HTTP Keep-Alive)。
|
|
69
|
+
- 是否存在频繁新建连接导致的开销。
|
|
70
|
+
|
|
71
|
+
5. **阻塞情况**:
|
|
72
|
+
- 是否发现 Zero Window 或流控阻塞。
|
|
73
|
+
- 是否有队头阻塞迹象。
|
|
74
|
+
|
|
75
|
+
6. **负载能力评估**:
|
|
76
|
+
- 当前并发峰值和吞吐峰值。
|
|
77
|
+
- 基于当前性能指标(如 RTT 抖动、丢包率随流量变化),预估系统在高并发下的抗压能力。
|
|
78
|
+
|
|
79
|
+
### 3. 示例 Python 代码结构 (供参考)
|
|
80
|
+
```python
|
|
81
|
+
from scapy.all import *
|
|
82
|
+
import collections
|
|
83
|
+
|
|
84
|
+
# 读取 pcap
|
|
85
|
+
packets = rdpcap("path/to/file.pcap")
|
|
86
|
+
|
|
87
|
+
# 初始化统计变量
|
|
88
|
+
# ...
|
|
89
|
+
|
|
90
|
+
for pkt in packets:
|
|
91
|
+
if TCP in pkt:
|
|
92
|
+
# 统计逻辑
|
|
93
|
+
# ...
|
|
94
|
+
|
|
95
|
+
# 输出结果
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**注意**: 如果环境不支持 Python scapy,请尝试使用 `tshark` 命令:
|
|
99
|
+
`tshark -r file.pcap -q -z io,stat,1 -z conv,tcp`
|
|
100
|
+
以及 `tshark -r file.pcap -Y "tcp.analysis.flags"` 来分析异常。
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Common Commands
|
|
6
|
+
|
|
7
|
+
### Development
|
|
8
|
+
- `npm run dev` – Start development mode with BLOCK_PROXY_DEV=1 (starts all services)
|
|
9
|
+
- `npm run craco` – Start React development server with CRACO (port 3000)
|
|
10
|
+
- `npm run start` / `npm run express` – Start backend + proxy server for production
|
|
11
|
+
- `npm run proxy` – Start proxy only (no admin interface)
|
|
12
|
+
- `npm run socks5` – Start SOCKS5 server only
|
|
13
|
+
|
|
14
|
+
### Build & Deployment
|
|
15
|
+
- `npm run build` – Build React frontend
|
|
16
|
+
- `npm run docker:build` – Build Docker image for current architecture
|
|
17
|
+
- `npm run docker:build_arm` – Build ARM64 Docker image
|
|
18
|
+
- `npm test` – Run React tests
|
|
19
|
+
- `npm run eject` – Eject from Create React App (irreversible)
|
|
20
|
+
|
|
21
|
+
### Global CLI
|
|
22
|
+
- `block-proxy` – Start the proxy system (auto-restart on failure)
|
|
23
|
+
- `block-proxy -c rule.js` – Start with external MITM rule configuration
|
|
24
|
+
|
|
25
|
+
## Architecture Overview
|
|
26
|
+
|
|
27
|
+
Block-Proxy is a MITM-based proxy filtering tool designed for parental control and ad blocking, built with Node.js, React, and a custom AnyProxy fork. It runs on OpenWRT routers or Docker containers.
|
|
28
|
+
|
|
29
|
+
### Core Components
|
|
30
|
+
|
|
31
|
+
1. **Proxy Engine** (`/proxy/`)
|
|
32
|
+
- `proxy.js` – Main AnyProxy integration with MITM logic
|
|
33
|
+
- `mitm/` – MITM rule implementations (YouTube ads, dictionary, etc.)
|
|
34
|
+
- `scan.js` – Network scanning for device discovery (every 2 hours)
|
|
35
|
+
- `fs.js` – Configuration file management
|
|
36
|
+
|
|
37
|
+
2. **SOCKS5 Proxy** (`/socks5/`)
|
|
38
|
+
- `server.js` – SOCKS5 over TLS implementation (port 8002)
|
|
39
|
+
- `start.js` – SOCKS5 server entry point
|
|
40
|
+
|
|
41
|
+
3. **Backend Server** (`/server/`)
|
|
42
|
+
- `express.js` – Express.js API server for admin interface (port 8004)
|
|
43
|
+
- `start.js` – Main server entry point (decides whether to start admin UI)
|
|
44
|
+
|
|
45
|
+
4. **React Frontend** (`/src/`)
|
|
46
|
+
- `App.js` – Admin interface for managing blocking rules
|
|
47
|
+
- Built with Create React App, configured via CRACO
|
|
48
|
+
|
|
49
|
+
5. **CLI Interface** (`/bin/`)
|
|
50
|
+
- `start.js` – Global CLI entry point with auto-restart capabilities and config cleanup
|
|
51
|
+
|
|
52
|
+
6. **Configuration** (`config.json`)
|
|
53
|
+
- Runtime configuration: ports, blocked hosts, authentication
|
|
54
|
+
- Auto-saved from admin interface
|
|
55
|
+
|
|
56
|
+
### Port Configuration
|
|
57
|
+
- `8001` – HTTP proxy port (mandatory)
|
|
58
|
+
- `8002` – SOCKS5 over TLS port (optional)
|
|
59
|
+
- `8003` – AnyProxy monitoring interface (optional)
|
|
60
|
+
- `8004` – Admin configuration interface (optional)
|
|
61
|
+
- `3000` – React development server (dev only)
|
|
62
|
+
|
|
63
|
+
### Entry Points
|
|
64
|
+
- **Primary**: `bin/start.js` (CLI) → `server/start.js` → decides between proxy-only or full stack
|
|
65
|
+
- **Proxy-only**: `proxy/start.js` → `proxy/proxy.js`
|
|
66
|
+
- **Development**: `npm run dev` → starts everything with dev flag
|
|
67
|
+
|
|
68
|
+
## Key Patterns
|
|
69
|
+
|
|
70
|
+
### MITM Rule System
|
|
71
|
+
- Host-based blocking with regex pattern matching
|
|
72
|
+
- Time-based restrictions (start/end times, weekdays)
|
|
73
|
+
- MAC address targeting for device-specific rules
|
|
74
|
+
- YouTube ad blocking with predefined regex patterns
|
|
75
|
+
- Custom rule injection via external `rule.js` configuration
|
|
76
|
+
|
|
77
|
+
### Configuration Management
|
|
78
|
+
- Configuration stored in `config.json` at runtime
|
|
79
|
+
- Supports external rule files via `-c` flag
|
|
80
|
+
- Network device scanning every 2 hours (stored in `config.json`)
|
|
81
|
+
- Auto-clears global config file on exit/restart
|
|
82
|
+
|
|
83
|
+
### Deployment Patterns
|
|
84
|
+
- Designed for OpenWRT router deployment with host networking (`--network=host`)
|
|
85
|
+
- Docker container with volume mounting for configuration
|
|
86
|
+
- Multi-architecture support (ARM/X86)
|
|
87
|
+
- Auto-restart on failure with config cleanup
|
|
88
|
+
- Production vs. development modes controlled by `BLOCK_PROXY_DEV` env var
|
|
89
|
+
|
|
90
|
+
### Development Workflow
|
|
91
|
+
1. **Development**: `npm run dev` starts proxy + admin UI + SOCKS5 (if enabled)
|
|
92
|
+
2. **Testing**: Proxy-only mode with `npm run proxy`
|
|
93
|
+
3. **Building**: `npm run build` compiles React frontend to `/build/`
|
|
94
|
+
4. **Docker**: Separate commands for x86 and ARM architectures
|
|
95
|
+
|
|
96
|
+
### Dependencies
|
|
97
|
+
- `@bachi/anyproxy` – Modified AnyProxy fork for MITM
|
|
98
|
+
- `express` – Backend API server
|
|
99
|
+
- `react`, `react-dom` – Frontend framework
|
|
100
|
+
- `commander` – CLI argument parsing
|
|
101
|
+
- `axios` – HTTP client for API calls
|
|
102
|
+
- `qrcode` – Certificate QR code generation for MITM setup
|
|
103
|
+
|
|
104
|
+
## Important Notes
|
|
105
|
+
- SOCKS5 proxy does not support MAC address targeting (only HTTP proxy does)
|
|
106
|
+
- Clients must install AnyProxy certificate for HTTPS MITM inspection
|
|
107
|
+
- Service needs network scanning permissions (best deployed on OpenWRT gateway)
|
|
108
|
+
- Admin interface allows real-time rule management with proxy restart
|
|
109
|
+
- Docker builds use Chinese npm registry (registry.npmmirror.com) by default
|
|
110
|
+
|
|
111
|
+
# Project Rules & Skills
|
|
112
|
+
|
|
113
|
+
- **Import Skill**: 实时遵循 `.claude/skills/*/skill.md` 中的指令。
|
|
114
|
+
|
package/Dockerfile
CHANGED
package/README.md
CHANGED
package/bin/start.js
CHANGED
package/config.json
CHANGED
|
@@ -135,8 +135,8 @@
|
|
|
135
135
|
"web_interface_port": 8003,
|
|
136
136
|
"your_domain": "yui.cool",
|
|
137
137
|
"vpn_proxy": "",
|
|
138
|
-
"auth_username": "
|
|
139
|
-
"auth_password": "
|
|
138
|
+
"auth_username": "",
|
|
139
|
+
"auth_password": "",
|
|
140
140
|
"enable_express": "1",
|
|
141
141
|
"enable_socks5": "1",
|
|
142
142
|
"socks5_port": 8002,
|
|
@@ -147,4 +147,4 @@
|
|
|
147
147
|
"mac": "FA:27:3C:E5:31:5F"
|
|
148
148
|
}
|
|
149
149
|
]
|
|
150
|
-
}
|
|
150
|
+
}
|
package/example/rule.js
CHANGED
|
@@ -46,6 +46,28 @@ function normalizeSocketKey(ip, port) {
|
|
|
46
46
|
return ip + ":" + port
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// 判断是否命中 responseRules
|
|
50
|
+
function matchResponseRule(responseRules, userConfig) {
|
|
51
|
+
try {
|
|
52
|
+
var hostname = userConfig.requestOptions.hostname.split(":")[0].toLowerCase();
|
|
53
|
+
var pathname = userConfig.requestOptions.path; // 带query
|
|
54
|
+
var url = `${userConfig.protocol}://${hostname}${pathname}`
|
|
55
|
+
var matched = false;
|
|
56
|
+
for (const item of responseRules) {
|
|
57
|
+
if (hostname.endsWith(item["host"]) && new RegExp(item['regexp']).test(url)) {
|
|
58
|
+
matched = true;
|
|
59
|
+
break;
|
|
60
|
+
} else {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return matched;
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.log(e);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
49
71
|
class CommonReadableStream extends Readable {
|
|
50
72
|
constructor(config) {
|
|
51
73
|
super({
|
|
@@ -467,6 +489,15 @@ function getUserReqHandler(userRule, recorder) {
|
|
|
467
489
|
|
|
468
490
|
// route user config
|
|
469
491
|
.then(co.wrap(function *(userConfig) {
|
|
492
|
+
// Modified 2026-1-15
|
|
493
|
+
// 增加了对 responseRules 的判断,如果需要对 BeforeResponse 进行拦截
|
|
494
|
+
// 则让 chunkSizeThreshold 为很大的值(默认20M),以便让 response 一次性返回,方便BeforeResponse处理
|
|
495
|
+
// 否则以 512k 为上线,只要返回的数据达到阈值,就直接返回给调用端,以提高性能
|
|
496
|
+
if (userRule.responseRules && matchResponseRule(userRule.responseRules, userConfig)) {
|
|
497
|
+
var _chunkSizeThreshold = chunkSizeThreshold;
|
|
498
|
+
} else {
|
|
499
|
+
var _chunkSizeThreshold = 0.5 * 1024 * 1024; // 512K
|
|
500
|
+
}
|
|
470
501
|
if (userConfig.response) {
|
|
471
502
|
// user-assigned local response
|
|
472
503
|
userConfig._directlyPassToRespond = true;
|
|
@@ -474,7 +505,7 @@ function getUserReqHandler(userRule, recorder) {
|
|
|
474
505
|
} else if (userConfig.requestOptions) {
|
|
475
506
|
const remoteResponse = yield fetchRemoteResponse(userConfig.protocol, userConfig.requestOptions, userConfig.requestData, {
|
|
476
507
|
dangerouslyIgnoreUnauthorized: reqHandlerCtx.dangerouslyIgnoreUnauthorized,
|
|
477
|
-
chunkSizeThreshold,
|
|
508
|
+
chunkSizeThreshold: _chunkSizeThreshold,
|
|
478
509
|
});
|
|
479
510
|
return {
|
|
480
511
|
response: {
|
|
@@ -1026,3 +1057,4 @@ class RequestHandler {
|
|
|
1026
1057
|
}
|
|
1027
1058
|
|
|
1028
1059
|
module.exports = RequestHandler;
|
|
1060
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "block-proxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Small-scale network mitm proxy filter",
|
|
5
5
|
"bin": {
|
|
6
6
|
"block-proxy": "bin/start.js"
|
|
@@ -16,14 +16,15 @@
|
|
|
16
16
|
"url": "git+https://github.com/jayli/block-proxy.git"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"cp": "echo '
|
|
19
|
+
"cp": "echo '----- program start -----'",
|
|
20
|
+
"rm_bkconfig": "rm ./config_backup.json",
|
|
20
21
|
"craco": "craco start",
|
|
21
22
|
"dev": "BLOCK_PROXY_DEV=1 npm run express",
|
|
22
23
|
"start": "npm run express",
|
|
23
24
|
"socks5": "node ./socks5/start.js",
|
|
24
25
|
"express": "npm run cp && node ./server/start.js",
|
|
25
26
|
"proxy": "npm run cp && node ./proxy/start.js",
|
|
26
|
-
"build": "react-scripts build",
|
|
27
|
+
"build": "npm run rm_bkconfig && react-scripts build",
|
|
27
28
|
"docker:build": "npm run build && docker build -t block-proxy .",
|
|
28
29
|
"docker:build_arm": "npm run build && docker buildx build --platform linux/arm64/v8 -t block-proxy .",
|
|
29
30
|
"test": "react-scripts test",
|
package/proxy/fs.js
CHANGED
|
@@ -4,6 +4,7 @@ const path = require('path');
|
|
|
4
4
|
|
|
5
5
|
const configPath = path.join(__dirname, '../config.json');
|
|
6
6
|
const CONFIG_FILE_PATH = configPath;
|
|
7
|
+
const BACKUP_FILE_PATH = path.join(__dirname, '../config_backup.json');
|
|
7
8
|
|
|
8
9
|
// 传入的是对象
|
|
9
10
|
async function writeConfig(newData) {
|
|
@@ -16,20 +17,45 @@ async function writeConfig(newData) {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
async function backupConfig(newData) {
|
|
21
|
+
try {
|
|
22
|
+
await fs.writeFile(BACKUP_FILE_PATH, JSON.stringify(newData, null, 2), 'utf8');
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error('Error writing backup config file:', error.message);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
// 示例:读取配置
|
|
20
29
|
// 返回的是对象
|
|
21
30
|
async function readConfig() {
|
|
22
31
|
try {
|
|
23
32
|
const data = await fs.readFile(CONFIG_FILE_PATH, 'utf8');
|
|
24
33
|
const config = JSON.parse(data);
|
|
34
|
+
|
|
35
|
+
// 第一次运行:如果备份不存在,则生成一个
|
|
36
|
+
try {
|
|
37
|
+
await fs.access(BACKUP_FILE_PATH);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
await backupConfig(config);
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
return config;
|
|
26
43
|
} catch (error) {
|
|
27
44
|
if (error.code === 'ENOENT') {
|
|
28
45
|
console.error('Config file does not exist.');
|
|
29
46
|
return {}; // 或返回一个默认配置对象
|
|
30
47
|
} else if (error instanceof SyntaxError) {
|
|
31
|
-
console.error('Error parsing config file JSON:', error.message);
|
|
32
|
-
|
|
48
|
+
console.error('Error parsing config file JSON, attempting to restore from backup:', error.message);
|
|
49
|
+
try {
|
|
50
|
+
const backupData = await fs.readFile(BACKUP_FILE_PATH, 'utf8');
|
|
51
|
+
const config = JSON.parse(backupData);
|
|
52
|
+
// 自动恢复主配置文件
|
|
53
|
+
await writeConfig(config);
|
|
54
|
+
return config;
|
|
55
|
+
} catch (backupError) {
|
|
56
|
+
console.error('Backup file also failed:', backupError.message);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
33
59
|
} else {
|
|
34
60
|
console.error('Error reading config file:', error.message);
|
|
35
61
|
throw error;
|
|
@@ -63,6 +89,7 @@ async function clearGlobalConfigFile() {
|
|
|
63
89
|
module.exports = {
|
|
64
90
|
writeConfig,
|
|
65
91
|
readConfig,
|
|
92
|
+
backupConfig,
|
|
66
93
|
setGlobalConfigFile,
|
|
67
94
|
getGlobalConfigFile,
|
|
68
95
|
clearGlobalConfigFile
|
package/proxy/proxy.js
CHANGED
|
@@ -29,11 +29,12 @@ var Rule = require("./mitm/rule.js");
|
|
|
29
29
|
// 启用全局 keep-alive,使 AnyProxy 内部转发也复用连接
|
|
30
30
|
http.globalAgent.keepAlive = true;
|
|
31
31
|
https.globalAgent.keepAlive = true;
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
// 连接上限 50 应该足够了,设置 100 留足 buffer
|
|
33
|
+
http.globalAgent.maxSockets = 100;
|
|
34
|
+
https.globalAgent.maxSockets = 100;
|
|
34
35
|
|
|
35
|
-
const httpAgent = new http.Agent({ keepAlive: true, maxSockets:
|
|
36
|
-
const httpsAgent = new https.Agent({ keepAlive: true, maxSockets:
|
|
36
|
+
const httpAgent = new http.Agent({ keepAlive: true, maxSockets: 100 });
|
|
37
|
+
const httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 100 });
|
|
37
38
|
|
|
38
39
|
// 全局参数
|
|
39
40
|
const configPath = path.join(__dirname, '../config.json');
|
|
@@ -90,6 +91,8 @@ function authPass(protocol, host, url) {
|
|
|
90
91
|
"weixin.qq.com",
|
|
91
92
|
// xiaohongshu.com:443,小红书App和知乎 App 里发起带端口的请求,收到 407 后第二次
|
|
92
93
|
"xiaohongshu.com:443",
|
|
94
|
+
"xiaohongshu.com",
|
|
95
|
+
"xhscdn.com",
|
|
93
96
|
"zhihu.com:443",
|
|
94
97
|
...filtered_mitm_domains
|
|
95
98
|
];
|
package/proxy/scan.js
CHANGED
|
@@ -78,6 +78,46 @@ async function getScanStatus() {
|
|
|
78
78
|
return loadedConfig.network_scanning_status;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// 并发控制器,限制同时进行的 ping 数量
|
|
82
|
+
class ConcurrentPingController {
|
|
83
|
+
constructor(maxConcurrent = 32, batchDelay = 50) {
|
|
84
|
+
this.maxConcurrent = maxConcurrent;
|
|
85
|
+
this.batchDelay = batchDelay; // 批次之间的延迟(毫秒)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 批量执行 ping,控制并发数(真正的批次并发)
|
|
89
|
+
async batchPing(ips, timeout = 1) {
|
|
90
|
+
const results = [];
|
|
91
|
+
|
|
92
|
+
// 将IP列表分成多个批次
|
|
93
|
+
for (let i = 0; i < ips.length; i += this.maxConcurrent) {
|
|
94
|
+
const batch = ips.slice(i, i + this.maxConcurrent);
|
|
95
|
+
|
|
96
|
+
// 并发执行当前批次的所有ping
|
|
97
|
+
const batchPromises = batch.map(ip =>
|
|
98
|
+
ping.promise.probe(ip, { timeout })
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const batchResults = await Promise.allSettled(batchPromises);
|
|
102
|
+
|
|
103
|
+
// 将批次结果添加到总结果中
|
|
104
|
+
batch.forEach((ip, index) => {
|
|
105
|
+
results.push({
|
|
106
|
+
ip,
|
|
107
|
+
result: batchResults[index]
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// 如果不是最后一个批次,则添加延迟
|
|
112
|
+
if (i + this.maxConcurrent < ips.length) {
|
|
113
|
+
await new Promise(resolve => setTimeout(resolve, this.batchDelay));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return results;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
81
121
|
async function doScan() {
|
|
82
122
|
const subnet = getLocalSubnet();
|
|
83
123
|
// 如果是 30 网段,直接返回空
|
|
@@ -86,11 +126,17 @@ async function doScan() {
|
|
|
86
126
|
}
|
|
87
127
|
console.log(`正在扫描网段: ${subnet}.0/24`);
|
|
88
128
|
|
|
89
|
-
//
|
|
129
|
+
// 创建并发控制器,限制同时进行32个ping,批次间延迟50ms
|
|
130
|
+
const controller = new ConcurrentPingController(32, 50);
|
|
131
|
+
|
|
132
|
+
// 生成所有IP地址
|
|
90
133
|
const ips = Array.from({ length: 254 }, (_, i) => `${subnet}.${i + 1}`);
|
|
91
|
-
await Promise.allSettled(ips.map(ip => ping.promise.probe(ip, { timeout: 1 })));
|
|
92
134
|
|
|
93
|
-
//
|
|
135
|
+
// 使用并发控制器分批ping所有IP
|
|
136
|
+
console.log(`开始并发ping扫描,最大并发数: 32,批次延迟: 50ms`);
|
|
137
|
+
await controller.batchPing(ips, 1); // 使用1秒超时,ping结果仅用于填充ARP缓存
|
|
138
|
+
|
|
139
|
+
// 读取ARP表以获取MAC地址
|
|
94
140
|
const cmd = process.platform === 'win32' ? 'arp -a' : 'arp -a';
|
|
95
141
|
const arpOutput = await new Promise((resolve, reject) => {
|
|
96
142
|
exec(cmd, async (error, stdout) => {
|
package/server/express.js
CHANGED
|
@@ -96,6 +96,7 @@ app.post('/api/config', async (req, res) => {
|
|
|
96
96
|
const configPath = path.join(__dirname, '../config.json');
|
|
97
97
|
// fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2));
|
|
98
98
|
_fs.writeConfig(newConfig);
|
|
99
|
+
_fs.backupConfig(newConfig);
|
|
99
100
|
res.status(200).json({ message: 'Config updated successfully' });
|
|
100
101
|
} catch (err) {
|
|
101
102
|
res.status(400).json({ error: 'Invalid JSON or write error: ' + err.message });
|
|
@@ -232,3 +233,4 @@ module.exports = {
|
|
|
232
233
|
});
|
|
233
234
|
}
|
|
234
235
|
};
|
|
236
|
+
|
package/socks5/test_tls_reuse.js
CHANGED
package/src/setupTests.js
CHANGED
package/tools/speed_test.sh
CHANGED
|
Binary file
|