block-proxy 0.1.1 → 0.1.4
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/README.md +34 -15
- package/bin/start.js +109 -29
- package/config.json +9 -1
- package/example/rule.js +39 -0
- package/package.json +6 -6
- package/proxy/fs.js +27 -5
- package/proxy/proxy.js +64 -9
- package/proxy/scan.js +2 -1
- package/server/express.js +1 -1
- package/socks5/server.js +15 -4
- package/AD_BLOCK.md +0 -38
package/README.md
CHANGED
|
@@ -18,27 +18,42 @@
|
|
|
18
18
|
|
|
19
19
|
### 1)使用方法
|
|
20
20
|
|
|
21
|
-
#### ①
|
|
21
|
+
#### ① 方式一:快速部署
|
|
22
22
|
|
|
23
23
|
安装:
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
```
|
|
26
|
+
npm install -g block-proxy
|
|
27
|
+
```
|
|
26
28
|
|
|
27
29
|
启动:
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
```
|
|
32
|
+
block-proxy
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
或者带配置文件启动
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
block-proxy -c rule.js
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
配置文件参照 [rule.js](example/rule.js),可留空
|
|
30
42
|
|
|
31
|
-
#### ②
|
|
43
|
+
#### ② 方式二,Docker 部署(推荐)
|
|
32
44
|
|
|
33
45
|
1. 下载 Docker 文件
|
|
34
46
|
- Arm 架构 → <a href="http://yui.cool:7001/public/downloads/block-proxy/arm/block-proxy.tar" target=_blank>block-proxy-arm.tar</a>
|
|
35
47
|
- X86 架构 → <a href="http://yui.cool:7001/public/downloads/block-proxy/x86/block-proxy-x86.tar" target=_blank>block-proxy-x86.tar</a>
|
|
36
48
|
2. 导入:`docker load < block-proxy.tar`
|
|
37
49
|
3. 启动:参照下文 Docker 部署
|
|
38
|
-
4. 服务端配置:配置面板 <http://server-ip:8004>,关闭、启用配置面板:<http://server-ip:8001>
|
|
39
|
-
5. 客户端配置:http 代理直接在 iphone wifi 详情里手动配置,socks5 代理只支持 socks5 over TLS,用小火箭配置。配置信息参照[配置面板](http://localhost:8004)
|
|
40
50
|
|
|
41
|
-
### 2
|
|
51
|
+
### 2)端口配置
|
|
52
|
+
|
|
53
|
+
1. 服务端配置:配置面板 <http://server-ip:8004>,关闭、启用配置面板:<http://server-ip:8001>
|
|
54
|
+
2. 客户端配置:http 代理直接在 iphone wifi 详情里手动配置,socks5 代理只支持 socks5 over TLS,用小火箭配置。配置信息参照[配置面板](http://localhost:8004)
|
|
55
|
+
|
|
56
|
+
### 3)开发和调试
|
|
42
57
|
|
|
43
58
|
代码 clone 下来后执行`pnpm i`,执行 `npm run dev` 运行本地服务。默认开启 5 个端口:
|
|
44
59
|
|
|
@@ -51,7 +66,7 @@
|
|
|
51
66
|
|8004 |后台配置页端口 | 可禁用 |
|
|
52
67
|
|
|
53
68
|
|
|
54
|
-
###
|
|
69
|
+
### 4)Docker 构建说明
|
|
55
70
|
|
|
56
71
|
准备工作,构建 docker 包,先启动本地 Docker:
|
|
57
72
|
|
|
@@ -77,9 +92,12 @@ docker run --init -d --restart=unless-stopped \
|
|
|
77
92
|
--log-opt max-file=3 \
|
|
78
93
|
--cpus="5" \
|
|
79
94
|
--memory 400m \
|
|
95
|
+
-v "$(pwd)/":/app/config \
|
|
80
96
|
--name block-proxy block-proxy
|
|
81
97
|
```
|
|
82
98
|
|
|
99
|
+
其中挂载目录 `$(pws)/` 下的 `rule.js` 是需要额外挂载的配置文件,可留空。
|
|
100
|
+
|
|
83
101
|
> block-proxy 可以配置只启动 proxy 不启动后台面板,首次启动后访问 http://代理IP:8001 根据提示操作。
|
|
84
102
|
|
|
85
103
|
网关里为了方便获取子网机器 ip 和 mac 地址,docker 容器需要和宿主机共享同一个网络,同时指定时区。
|
|
@@ -88,14 +106,15 @@ docker run --init -d --restart=unless-stopped \
|
|
|
88
106
|
|
|
89
107
|
```
|
|
90
108
|
docker run --init -d --restart=unless-stopped --user=root \
|
|
109
|
+
-v "$(pwd)/":/app/config \
|
|
91
110
|
-e TZ=Asia/Shanghai -p 8001:8001 -p 8002:8002 -p 8003:8003 \
|
|
92
111
|
--name block-proxy block-proxy
|
|
93
112
|
```
|
|
94
113
|
|
|
95
114
|
|
|
96
|
-
###
|
|
115
|
+
### 5)配置说明
|
|
97
116
|
|
|
98
|
-
#### 代理端口
|
|
117
|
+
#### ① 代理端口
|
|
99
118
|
|
|
100
119
|
默认开启两个代理端口:HTTP 8001 和 Socks5(over TLS) 8002。
|
|
101
120
|
|
|
@@ -103,7 +122,7 @@ docker run --init -d --restart=unless-stopped --user=root \
|
|
|
103
122
|
|
|
104
123
|
⚠️ 使用小火箭的 Socks5 over TLS 代理,TLS 选项里勾选“允许不安全”
|
|
105
124
|
|
|
106
|
-
#### 后台配置
|
|
125
|
+
#### ② 后台配置
|
|
107
126
|
|
|
108
127
|
访问路径:`http://proxy-ip:8004`
|
|
109
128
|
|
|
@@ -112,16 +131,16 @@ docker run --init -d --restart=unless-stopped --user=root \
|
|
|
112
131
|
<img src="https://github.com/user-attachments/assets/16f47d3f-1ef9-47a2-8640-c7e04ec64e1a" width=300 />
|
|
113
132
|
|
|
114
133
|
|
|
115
|
-
#### 设备配置
|
|
134
|
+
#### ③ 设备配置
|
|
116
135
|
|
|
117
136
|
1. 代理设置:iPhone/iPad 为例:设置 → 无线局域网 → 点击当前网络 → HTTP代理/配置代理,设置服务器和端口。
|
|
118
|
-
2. 证书设置:打开anproxy监控地址(8003端口),扫码安装证书,在手机设置中安装该证书,同时配置完全信任:设置→通用→关于本机→证书信任设置→打开对AnyProxy的完全信任
|
|
137
|
+
2. 证书设置:打开 anproxy 监控地址(8003端口),扫码安装证书,在手机设置中安装该证书,同时配置完全信任:设置→通用→关于本机→证书信任设置→打开对AnyProxy的完全信任
|
|
119
138
|
|
|
120
139
|
小朋友的设备里把 Mac 固定下来:
|
|
121
140
|
|
|
122
141
|
<img width="350" alt="image" src="https://github.com/user-attachments/assets/f9bfab89-7194-4a72-b1ae-5cca27911bc9" />
|
|
123
142
|
|
|
124
|
-
#### 禁掉设备直连
|
|
143
|
+
#### ④ 禁掉设备直连
|
|
125
144
|
|
|
126
145
|
防止小朋友修改网Wifi连接,只允许设备通过代理访问,把直连上网权限关掉。网关里配置防火墙规则:
|
|
127
146
|
|
|
@@ -133,7 +152,7 @@ ip6tables -I forwarding_rule -m mac --mac-source D2:9E:8D:1B:F1:4E -j REJECT
|
|
|
133
152
|
然后重启防火墙
|
|
134
153
|
|
|
135
154
|
|
|
136
|
-
###
|
|
155
|
+
### 6)更多信息
|
|
137
156
|
|
|
138
157
|
#### 应用条件:
|
|
139
158
|
|
package/bin/start.js
CHANGED
|
@@ -2,44 +2,124 @@
|
|
|
2
2
|
|
|
3
3
|
const { spawn } = require('child_process');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const { Command } = require('commander');
|
|
6
|
+
const program = new Command();
|
|
7
|
+
const _fs = require('../proxy/fs.js');
|
|
5
8
|
|
|
6
|
-
// 获取包根目录和 start.js 路径
|
|
7
9
|
const pkgDir = path.join(__dirname, '..');
|
|
8
10
|
const startScript = path.resolve(pkgDir, 'server/start.js');
|
|
11
|
+
const MAX_RESTARTS = 10000;
|
|
12
|
+
let restartCount = 0;
|
|
13
|
+
let restartTimer = null;
|
|
14
|
+
let currentChild = null; // 👈 全局引用当前子进程
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
const command = `npm run cp && node "${startScript}"`;
|
|
16
|
+
function startApp() {
|
|
17
|
+
const command = `npm run cp && node "${startScript}"`;
|
|
18
|
+
console.error(`[💟] Block-Proxy 启动 (第 ${restartCount + 1} 次): ${command}`);
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
currentChild = spawn(command, {
|
|
21
|
+
cwd: pkgDir,
|
|
22
|
+
shell: true,
|
|
23
|
+
stdio: 'pipe'
|
|
24
|
+
});
|
|
14
25
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
shell: true, // ⭐ 必须启用 shell 才能解析 &&、|、> 等
|
|
19
|
-
stdio: 'pipe' // 我们要手动处理流
|
|
20
|
-
});
|
|
26
|
+
currentChild.stdout.on('data', (data) => {
|
|
27
|
+
process.stdout.write(data);
|
|
28
|
+
});
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
});
|
|
30
|
+
currentChild.stderr.on('data', (data) => {
|
|
31
|
+
process.stderr.write(data);
|
|
32
|
+
});
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
currentChild.on('close', async (code, signal) => {
|
|
35
|
+
currentChild = null; // 清空引用
|
|
36
|
+
if (restartTimer) {
|
|
37
|
+
clearTimeout(restartTimer);
|
|
38
|
+
restartTimer = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await _fs.clearGlobalConfigFile();
|
|
42
|
+
|
|
43
|
+
if (code === 0) {
|
|
44
|
+
console.error('[block proxy] 正常退出,不重启。');
|
|
45
|
+
process.exit(0);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (signal === 'SIGINT' || signal === 'SIGTERM') {
|
|
50
|
+
console.error('[block-proxy] 被信号终止,不重启。');
|
|
51
|
+
process.exit(128 + (signal === 'SIGINT' ? 2 : 15));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (restartCount < MAX_RESTARTS) {
|
|
56
|
+
restartCount++;
|
|
57
|
+
console.error(`[block proxy] 将在 3 秒后自动重启...(已重启 ${restartCount}/${MAX_RESTARTS} 次)`);
|
|
58
|
+
restartTimer = setTimeout(() => {
|
|
59
|
+
restartTimer = null;
|
|
60
|
+
startApp();
|
|
61
|
+
}, 3000);
|
|
62
|
+
} else {
|
|
63
|
+
console.error(`[block proxy] 已达到最大重启次数 (${MAX_RESTARTS}),停止尝试。`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
31
68
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
console.error(
|
|
35
|
-
|
|
69
|
+
// ✅ 只注册一次 SIGINT 监听器(在 startApp 外部!)
|
|
70
|
+
process.on('SIGINT', async () => {
|
|
71
|
+
console.error('\n[block proxy] 收到 SIGINT,正在关闭子进程...');
|
|
72
|
+
|
|
73
|
+
await _fs.clearGlobalConfigFile();
|
|
74
|
+
|
|
75
|
+
if (restartTimer) {
|
|
76
|
+
clearTimeout(restartTimer);
|
|
77
|
+
restartTimer = null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (currentChild) {
|
|
81
|
+
currentChild.kill('SIGINT');
|
|
82
|
+
// 注意:不要在这里 exit,等 close 事件处理
|
|
83
|
+
} else {
|
|
84
|
+
// 如果没有子进程,直接退出
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
36
87
|
});
|
|
37
88
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
89
|
+
process.on('SIGTERM', () => {
|
|
90
|
+
console.error('\n[block proxy] 收到 SIGTERM,正在关闭子进程...');
|
|
91
|
+
if (restartTimer) {
|
|
92
|
+
clearTimeout(restartTimer);
|
|
93
|
+
restartTimer = null;
|
|
94
|
+
}
|
|
95
|
+
if (currentChild) {
|
|
96
|
+
currentChild.kill('SIGTERM');
|
|
97
|
+
} else {
|
|
98
|
+
process.exit(0);
|
|
99
|
+
}
|
|
45
100
|
});
|
|
101
|
+
|
|
102
|
+
(async function() {
|
|
103
|
+
program
|
|
104
|
+
.name('block-proxy')
|
|
105
|
+
.description('极简的 MITM 代理工具:https://github.com/jayli/block-proxy')
|
|
106
|
+
.version('0.1.3')
|
|
107
|
+
.option('-c, --config <config>', 'MITM 配置文件');
|
|
108
|
+
|
|
109
|
+
program.parse(process.argv);
|
|
110
|
+
const options = program.opts();
|
|
111
|
+
|
|
112
|
+
if (options.config && options.config != "") {
|
|
113
|
+
if (path.isAbsolute(options.config)) {
|
|
114
|
+
await _fs.setGlobalConfigFile(options.config);
|
|
115
|
+
} else {
|
|
116
|
+
var pwd = process.cwd();
|
|
117
|
+
var configFile = path.resolve(pwd, options.config);
|
|
118
|
+
await _fs.setGlobalConfigFile(configFile);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 启动
|
|
123
|
+
startApp();
|
|
124
|
+
})();
|
|
125
|
+
|
package/config.json
CHANGED
package/example/rule.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
AAA: [
|
|
3
|
+
{
|
|
4
|
+
'type': 'beforeSendResponse',
|
|
5
|
+
'host': '163.com',
|
|
6
|
+
'regexp': "/123/v1/(browse|next)",
|
|
7
|
+
'callback': async function(url, request, response) {
|
|
8
|
+
return {
|
|
9
|
+
response
|
|
10
|
+
}
|
|
11
|
+
} // -- callback
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
'type': 'beforeSendResponse',
|
|
15
|
+
'host': '163.com',
|
|
16
|
+
'regexp': "/456",
|
|
17
|
+
'callback': async function(url, request, response) {
|
|
18
|
+
return {
|
|
19
|
+
response
|
|
20
|
+
}
|
|
21
|
+
} // -- callback
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
BBB: [
|
|
25
|
+
{
|
|
26
|
+
type: "beforeSendRequest",
|
|
27
|
+
host: "163.com",
|
|
28
|
+
regexp: "/hello",
|
|
29
|
+
callback: async function(url, request, response) {
|
|
30
|
+
return {
|
|
31
|
+
response : {
|
|
32
|
+
statusCode: 200,
|
|
33
|
+
body: 'hello world'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "block-proxy",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Small-scale network proxy filter",
|
|
5
|
-
"bin":{
|
|
6
|
-
"block-proxy": "
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "Small-scale network mitm proxy filter",
|
|
5
|
+
"bin": {
|
|
6
|
+
"block-proxy": "bin/start.js"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"react": "^19.2.0",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"@craco/craco": "^7.1.0",
|
|
34
34
|
"anyproxy": "^4.1.3",
|
|
35
35
|
"axios": "^1.13.2",
|
|
36
|
+
"commander": "^14.0.2",
|
|
36
37
|
"express": "^5.1.0",
|
|
37
38
|
"http-proxy-agent": "^7.0.2",
|
|
38
39
|
"https-proxy-agent": "^7.0.6",
|
|
39
|
-
"ping": "^1.0.0"
|
|
40
|
-
"write-file-atomic": "^7.0.0"
|
|
40
|
+
"ping": "^1.0.0"
|
|
41
41
|
},
|
|
42
42
|
"browserslist": {
|
|
43
43
|
"production": [
|
package/proxy/fs.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// proxy/fs.js
|
|
2
2
|
const fs = require('fs').promises;
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const writeFileAtomic = require('write-file-atomic'); // 引入 write-file-atomic
|
|
5
4
|
|
|
6
5
|
const configPath = path.join(__dirname, '../config.json');
|
|
7
6
|
const CONFIG_FILE_PATH = configPath;
|
|
@@ -9,9 +8,6 @@ const CONFIG_FILE_PATH = configPath;
|
|
|
9
8
|
// 传入的是对象
|
|
10
9
|
async function writeConfig(newData) {
|
|
11
10
|
try {
|
|
12
|
-
// 使用 write-file-atomic 进行原子写入
|
|
13
|
-
// 它会在内部创建一个临时文件,写入成功后再重命名为目标文件
|
|
14
|
-
// await writeFileAtomic(CONFIG_FILE_PATH, JSON.stringify(newData, null, 2), 'utf8');
|
|
15
11
|
await fs.writeFile(CONFIG_FILE_PATH, JSON.stringify(newData, null, 2), 'utf8');
|
|
16
12
|
// console.log('Config file written successfully');
|
|
17
13
|
} catch (error) {
|
|
@@ -41,7 +37,33 @@ async function readConfig() {
|
|
|
41
37
|
}
|
|
42
38
|
}
|
|
43
39
|
|
|
40
|
+
async function setGlobalConfigFile(configFile) {
|
|
41
|
+
var data = await readConfig();
|
|
42
|
+
data.config_file = configFile;
|
|
43
|
+
await writeConfig(data);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function getGlobalConfigFile() {
|
|
47
|
+
var data = await readConfig();
|
|
48
|
+
if (data.hasOwnProperty("config_file")) {
|
|
49
|
+
return data.config_file;
|
|
50
|
+
} else {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function clearGlobalConfigFile() {
|
|
56
|
+
var data = await readConfig();
|
|
57
|
+
if (data.hasOwnProperty("config_file")) {
|
|
58
|
+
delete data.config_file
|
|
59
|
+
}
|
|
60
|
+
await writeConfig(data);
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
module.exports = {
|
|
45
64
|
writeConfig,
|
|
46
|
-
readConfig
|
|
65
|
+
readConfig,
|
|
66
|
+
setGlobalConfigFile,
|
|
67
|
+
getGlobalConfigFile,
|
|
68
|
+
clearGlobalConfigFile
|
|
47
69
|
};
|
package/proxy/proxy.js
CHANGED
|
@@ -7,6 +7,7 @@ const path = require('path');
|
|
|
7
7
|
const { start } = require('repl');
|
|
8
8
|
const net = require('net');
|
|
9
9
|
const scanNetwork = require("./scan").scanNetwork;
|
|
10
|
+
const setScanStatus = require("./scan").setScanStatus;
|
|
10
11
|
const util = require('util');
|
|
11
12
|
const zlib = require('zlib');
|
|
12
13
|
const _util = require('../server/util.js');
|
|
@@ -18,12 +19,12 @@ const axios = require('axios');
|
|
|
18
19
|
const { HttpProxyAgent } = require('http-proxy-agent');
|
|
19
20
|
const { HttpsProxyAgent } = require('https-proxy-agent');
|
|
20
21
|
const _request = require("./http.js").request;
|
|
21
|
-
const Rule = require("./mitm/rule.js");
|
|
22
22
|
const uaFilter = require("./mitm/uaFilter.js");
|
|
23
23
|
const attacker = require('./attacker.js');
|
|
24
24
|
const monitor = require('./monitor.js');
|
|
25
25
|
const domain = require('./domain.js');
|
|
26
26
|
const wanip = require('./wanip.js');
|
|
27
|
+
var Rule = require("./mitm/rule.js");
|
|
27
28
|
|
|
28
29
|
// 启用全局 keep-alive,使 AnyProxy 内部转发也复用连接
|
|
29
30
|
http.globalAgent.keepAlive = true;
|
|
@@ -80,6 +81,45 @@ function preCompileRuleRegexp() {
|
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
async function fileExists(filePath) {
|
|
85
|
+
try {
|
|
86
|
+
await fs.promises.access(filePath);
|
|
87
|
+
return true;
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 引入从命令行传进来的 rule.js,并加载
|
|
94
|
+
async function loadGlobalConfigFile() {
|
|
95
|
+
var configFile = await _fs.getGlobalConfigFile();
|
|
96
|
+
await _fs.clearGlobalConfigFile();
|
|
97
|
+
if (configFile == null) {
|
|
98
|
+
return;
|
|
99
|
+
} else {
|
|
100
|
+
var extraRule = require(configFile);
|
|
101
|
+
Rule = {
|
|
102
|
+
...Rule,
|
|
103
|
+
...extraRule
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 引入 Docker 挂载目录下的 rule.js,并加载
|
|
109
|
+
async function loadDockerMountedConfigFile() {
|
|
110
|
+
var rulePath = path.join(__dirname, '../config/rule.js');
|
|
111
|
+
var fileOK = await fileExists(rulePath);
|
|
112
|
+
if (fileOK) {
|
|
113
|
+
var extraRule = require(rulePath);
|
|
114
|
+
Rule = {
|
|
115
|
+
...Rule,
|
|
116
|
+
...extraRule
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
83
123
|
function isEmpty(obj) {
|
|
84
124
|
if (obj === null || obj === undefined) {
|
|
85
125
|
return true;
|
|
@@ -207,7 +247,12 @@ async function loadConfig() {
|
|
|
207
247
|
|
|
208
248
|
async function updateWanIp() {
|
|
209
249
|
// var ips = await domain.getDomainIP(your_domain);
|
|
210
|
-
var ip =
|
|
250
|
+
var ip = "0.0.0.0";
|
|
251
|
+
try {
|
|
252
|
+
ip = await wanip.getPublicIp();
|
|
253
|
+
} catch(e) {
|
|
254
|
+
ip = "0.0.0.0";
|
|
255
|
+
}
|
|
211
256
|
if (ip === null) {
|
|
212
257
|
ip = "0.0.0.0";
|
|
213
258
|
}
|
|
@@ -398,12 +443,10 @@ function startProxyServer() {
|
|
|
398
443
|
proxyServerInstance = new AnyProxy.ProxyServer(options);
|
|
399
444
|
|
|
400
445
|
proxyServerInstance.on('ready', () => {
|
|
401
|
-
console.log(`✅
|
|
446
|
+
console.log(`✅ \x1b[32mHTTP 代理服务启动,端口 ${proxyPort}\x1b[0m`);
|
|
402
447
|
if (enable_webinterface == "1") {
|
|
403
|
-
console.log(`✅
|
|
448
|
+
console.log(`✅ \x1b[32mAnyProxy 监控面板启动,端口 ${webInterfacePort}\x1b[0m`);
|
|
404
449
|
}
|
|
405
|
-
console.log('Intercepting requests to hosts:', blockHosts.join(', '));
|
|
406
|
-
console.log('All other requests will be passed through without HTTPS interception');
|
|
407
450
|
});
|
|
408
451
|
|
|
409
452
|
proxyServerInstance.on('error', (e) => {
|
|
@@ -1359,6 +1402,7 @@ var LocalProxy = {
|
|
|
1359
1402
|
newRouterMap = await scanNetwork();
|
|
1360
1403
|
} catch (e) {
|
|
1361
1404
|
newRouterMap = [];
|
|
1405
|
+
setScanStatus("0");
|
|
1362
1406
|
}
|
|
1363
1407
|
|
|
1364
1408
|
var mergedRouterMap = [];
|
|
@@ -1445,7 +1489,17 @@ var LocalProxy = {
|
|
|
1445
1489
|
return;
|
|
1446
1490
|
}
|
|
1447
1491
|
|
|
1448
|
-
|
|
1492
|
+
// 加载命令行里携带的配置文件
|
|
1493
|
+
await loadGlobalConfigFile();
|
|
1494
|
+
// 加载 Docker 挂载目录中的配置文件
|
|
1495
|
+
await loadDockerMountedConfigFile();
|
|
1496
|
+
// 预编译 MITM Rule 的正则
|
|
1497
|
+
preCompileRuleRegexp();
|
|
1498
|
+
|
|
1499
|
+
// 启动时重置 Scan 本地扫描
|
|
1500
|
+
setScanStatus("0");
|
|
1501
|
+
|
|
1502
|
+
console.log('启动代理服务');
|
|
1449
1503
|
console.log('Dev server started, starting LocalProxy...');
|
|
1450
1504
|
is_running_in_docker = _util.isRunningInDocker();
|
|
1451
1505
|
if (is_running_in_docker) {
|
|
@@ -1481,8 +1535,9 @@ var LocalProxy = {
|
|
|
1481
1535
|
};
|
|
1482
1536
|
|
|
1483
1537
|
// 预编译 MITM Rule 的正则
|
|
1484
|
-
(function() {
|
|
1485
|
-
|
|
1538
|
+
(async function() {
|
|
1539
|
+
// await loadGlobalConfigFile();
|
|
1540
|
+
// preCompileRuleRegexp();
|
|
1486
1541
|
})();
|
|
1487
1542
|
|
|
1488
1543
|
module.exports = LocalProxy;
|
package/proxy/scan.js
CHANGED
|
@@ -106,7 +106,7 @@ var tempDevices = [];
|
|
|
106
106
|
async function scanNetwork() {
|
|
107
107
|
var status = await getScanStatus();
|
|
108
108
|
if (status == "1") {
|
|
109
|
-
await setScanStatus("0");
|
|
109
|
+
// await setScanStatus("0");
|
|
110
110
|
return tempDevices;
|
|
111
111
|
} else {
|
|
112
112
|
await setScanStatus("1");
|
|
@@ -117,4 +117,5 @@ async function scanNetwork() {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
module.exports.setScanStatus = setScanStatus;
|
|
120
121
|
module.exports.scanNetwork = scanNetwork;
|
package/server/express.js
CHANGED
|
@@ -206,7 +206,6 @@ module.exports = {
|
|
|
206
206
|
init: function() {
|
|
207
207
|
// 启动服务器
|
|
208
208
|
app.listen(PORT, async () => {
|
|
209
|
-
console.log(`✅ 静态服务器运行在 http://localhost:${PORT}`);
|
|
210
209
|
// 如果是开发环境,则启动SSR服务,开启端口3000
|
|
211
210
|
if (DEV === '1') {
|
|
212
211
|
const child = exec('npm run craco', { cwd: path.join(__dirname,'../') });
|
|
@@ -227,6 +226,7 @@ module.exports = {
|
|
|
227
226
|
}
|
|
228
227
|
// 启动本地代理
|
|
229
228
|
await LocalProxy.init();
|
|
229
|
+
console.log(`✅ \x1b[32m后台配置面板启动 → http://localhost:${PORT}\x1b[0m`);
|
|
230
230
|
});
|
|
231
231
|
}
|
|
232
232
|
};
|
package/socks5/server.js
CHANGED
|
@@ -196,6 +196,12 @@ async function init() {
|
|
|
196
196
|
|
|
197
197
|
// 创建 TLS 封装的 SOCKS5 服务器
|
|
198
198
|
const server = tls.createServer(tlsOptions, async (socket) => {
|
|
199
|
+
// 👇 关键:捕获 socket 级别的错误(包括 ECONNRESET)
|
|
200
|
+
socket.on('error', (err) => {
|
|
201
|
+
console.warn('Client socket error (ignored):', err.message);
|
|
202
|
+
// 不需要手动 destroy(),Node.js 会自动关闭
|
|
203
|
+
});
|
|
204
|
+
|
|
199
205
|
try {
|
|
200
206
|
// Step 1: 协商认证方法
|
|
201
207
|
const authMethodsBuf = await new Promise((resolve) => {
|
|
@@ -295,6 +301,11 @@ async function init() {
|
|
|
295
301
|
}
|
|
296
302
|
});
|
|
297
303
|
|
|
304
|
+
server.on('clientError', (err, socket) => {
|
|
305
|
+
console.warn('TLS client error during handshake:', err.message);
|
|
306
|
+
socket?.end(); // 安全关闭
|
|
307
|
+
});
|
|
308
|
+
|
|
298
309
|
// 错误处理
|
|
299
310
|
server.on('tlsClientError', (err, tlsSocket) => {
|
|
300
311
|
console.warn('TLS handshake failed:', err.message);
|
|
@@ -307,10 +318,10 @@ async function init() {
|
|
|
307
318
|
|
|
308
319
|
// 启动监听
|
|
309
320
|
server.listen(LISTEN_PORT, () => {
|
|
310
|
-
console.log(`✅
|
|
311
|
-
console.log(`🔒
|
|
312
|
-
console.log(`➡️ TCP →
|
|
313
|
-
console.log(`➡️ UDP →
|
|
321
|
+
console.log(`✅ \x1b[32mSOCKS5 (over TLS) 服务启动,端口 ${LISTEN_PORT}\x1b[0m`);
|
|
322
|
+
console.log(`🔒 传输加密和认证基于 TLS`);
|
|
323
|
+
console.log(`➡️ TCP → 流量转发至 HTTP 代理 → ${DOWNSTREAM_HTTP_PROXY_HOST}:${DOWNSTREAM_HTTP_PROXY_PORT}`);
|
|
324
|
+
console.log(`➡️ UDP → 直接发起请求`);
|
|
314
325
|
});
|
|
315
326
|
} catch (err) {
|
|
316
327
|
console.error('Failed to initialize SOCKS5-TLS proxy:', err);
|
package/AD_BLOCK.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
[Rule]
|
|
2
|
-
AND,((DOMAIN-SUFFIX,googlevideo.com), (PROTOCOL,UDP)),REJECT
|
|
3
|
-
AND,((DOMAIN,youtubei.googleapis.com), (PROTOCOL,UDP)),REJECT
|
|
4
|
-
|
|
5
|
-
[URL Rewrite]
|
|
6
|
-
(^https?:\/\/[\w-]+\.googlevideo\.com\/(?!dclk_video_ads).+?)&ctier=L(&.+?),ctier,(.+) $1$2$3 302
|
|
7
|
-
^https?:\/\/[\w-]+\.googlevideo\.com\/(?!(dclk_video_ads|videoplayback\?)).+&oad _ reject-200
|
|
8
|
-
^https?:\/\/(www|s)\.youtube\.com\/api\/stats\/ads _ reject-200
|
|
9
|
-
^https?:\/\/(www|s)\.youtube\.com\/(pagead|ptracking) _ reject-200
|
|
10
|
-
^https?:\/\/s\.youtube\.com\/api\/stats\/qoe\?adcontext _ reject-200
|
|
11
|
-
|
|
12
|
-
[Script]
|
|
13
|
-
youtube.response = type=http-response,pattern=^https:\/\/youtubei\.googleapis\.com\/youtubei\/v1\/(browse|next|player|search|reel\/reel_watch_sequence|guide|account\/get_setting|get_watch),requires-body=1,max-size=-1,binary-body-mode=1,engine={{{脚本执行引擎}}},script-path=https://raw.githubusercontent.com/Maasea/sgmodule/master/Script/Youtube/youtube.response.js,argument="{"lyricLang":"{{{歌词翻译语言}}}","captionLang":"{{{字幕翻译语言}}}","blockUpload":{{{屏蔽上传按钮}}},"blockImmersive":{{{屏蔽选段按钮}}},"debug":{{{启用调试模式}}}}"
|
|
14
|
-
|
|
15
|
-
[MITM]
|
|
16
|
-
hostname = %APPEND% -redirector*.googlevideo.com,*.googlevideo.com,www.youtube.com,s.youtube.com,youtubei.googleapis.com
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
--------------------
|
|
21
|
-
|
|
22
|
-
youtube.com
|
|
23
|
-
`^https?:\/\/(www|s)\.youtube\.com\/api\/stats\/ads`
|
|
24
|
-
|
|
25
|
-
youtube.com
|
|
26
|
-
`^https?:\/\/(www|s)\.youtube\.com\/(pagead|ptracking)`
|
|
27
|
-
|
|
28
|
-
youtube.com
|
|
29
|
-
`^https?:\/\/s\.youtube\.com\/api\/stats\/qoe\?adcontext`
|
|
30
|
-
|
|
31
|
-
googlevideo.com
|
|
32
|
-
`^https?:\/\/[\w-]+\.googlevideo\.com\/(?!(dclk_video_ads|videoplayback\?)).+(&oad|ctier)`
|
|
33
|
-
|
|
34
|
-
s.youtube.com
|
|
35
|
-
`^https?:\/\/s\.youtube\.com\/api\/stats\/atr.+&is_ad=1`
|
|
36
|
-
|
|
37
|
-
youtube.com
|
|
38
|
-
`^https?:\/\/(www|s)\.youtube\.com\/pcs\/activeview.+&ad_cpn=`
|