ddan-js 2.6.16 → 2.6.18
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/bin/ddan-js.esm.js +1 -1
- package/bin/ddan-js.js +1 -1
- package/bin/lib/index.js +2 -1
- package/bin/lib/modules/browser/ecdh.js +74 -0
- package/bin/lib/modules/browser/index.js +4 -0
- package/bin/lib/modules/node/ecdhSpki.js +76 -0
- package/bin/lib/modules/node/index.js +3 -1
- package/bin/lib/modules/node/sockServer.js +224 -0
- package/bin/types/index.d.ts +3 -0
- package/bin/types/modules/browser/ecdh.d.ts +17 -0
- package/bin/types/modules/browser/index.d.ts +5 -0
- package/bin/types/modules/node/ecdhSpki.d.ts +19 -0
- package/bin/types/modules/node/index.d.ts +4 -0
- package/bin/types/modules/node/sockServer.d.ts +28 -0
- package/package.json +6 -3
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SockServer = void 0;
|
|
4
|
+
const net_1 = require("net");
|
|
5
|
+
const socks_1 = require("socks");
|
|
6
|
+
class SockServer {
|
|
7
|
+
upstreamProxy;
|
|
8
|
+
server = null;
|
|
9
|
+
clientSockets = new Set();
|
|
10
|
+
allowedDomains;
|
|
11
|
+
connectionPool = {};
|
|
12
|
+
__debug = false;
|
|
13
|
+
constructor(upstreamProxy, allowedDomains = [], debug = false) {
|
|
14
|
+
this.upstreamProxy = upstreamProxy;
|
|
15
|
+
this.allowedDomains = new Set(allowedDomains);
|
|
16
|
+
this.__debug = false;
|
|
17
|
+
}
|
|
18
|
+
validateProxyConfig(proxyConfig) {
|
|
19
|
+
const { ipaddress, port, userId, password } = proxyConfig;
|
|
20
|
+
if (!ipaddress || typeof ipaddress !== 'string') {
|
|
21
|
+
return '无效的上游代理 IP 地址';
|
|
22
|
+
}
|
|
23
|
+
if (!port || typeof port !== 'number') {
|
|
24
|
+
return '无效的上游代理端口';
|
|
25
|
+
}
|
|
26
|
+
if (!userId || typeof userId !== 'string') {
|
|
27
|
+
return '无效的上游代理用户名';
|
|
28
|
+
}
|
|
29
|
+
if (!password || typeof password !== 'string') {
|
|
30
|
+
return '无效的上游代理密码';
|
|
31
|
+
}
|
|
32
|
+
return '';
|
|
33
|
+
}
|
|
34
|
+
async start(startPort = 9968) {
|
|
35
|
+
if (this.server) {
|
|
36
|
+
console.log(`Server is already running`);
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
const port = await this.findAvailablePort(startPort);
|
|
40
|
+
if (!port)
|
|
41
|
+
return port;
|
|
42
|
+
this.server = net_1.default.createServer((socket) => this.handleSocksConnection(socket));
|
|
43
|
+
this.server.listen(port, () => {
|
|
44
|
+
this.__debug && console.log(`SOCKS5 proxy server is running on port ${port}`);
|
|
45
|
+
});
|
|
46
|
+
this.server.on('error', (err) => {
|
|
47
|
+
console.error('SOCKS5 proxy server error:', err);
|
|
48
|
+
this.close();
|
|
49
|
+
});
|
|
50
|
+
return port;
|
|
51
|
+
}
|
|
52
|
+
async findAvailablePort(startPort, maxRetries = 200 // 最大重试次数
|
|
53
|
+
) {
|
|
54
|
+
let port = startPort;
|
|
55
|
+
let retries = 0;
|
|
56
|
+
while (retries < maxRetries) {
|
|
57
|
+
try {
|
|
58
|
+
await new Promise((resolve, reject) => {
|
|
59
|
+
const testServer = net_1.default.createServer();
|
|
60
|
+
testServer.unref();
|
|
61
|
+
testServer.on('error', reject);
|
|
62
|
+
testServer.listen(port, () => {
|
|
63
|
+
testServer.close(() => resolve());
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
return port; // 找到可用端口
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
retries++; // 记录重试次数
|
|
70
|
+
port++; // 尝试下一个端口
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
console.warn('findAvailablePort failed');
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
async handleSocksConnection(clientSocket) {
|
|
77
|
+
this.clientSockets.add(clientSocket);
|
|
78
|
+
clientSocket.on('close', () => {
|
|
79
|
+
this.clientSockets.delete(clientSocket);
|
|
80
|
+
clientSocket?.destroy();
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
await this.performHandshake(clientSocket);
|
|
84
|
+
const destination = await this.parseClientRequest(clientSocket);
|
|
85
|
+
this.__debug && console.log(`SockServer handleSocksConnection`, destination.addr);
|
|
86
|
+
if (this.isAllowedDomain(destination.addr)) {
|
|
87
|
+
// 允许的域名,走上游代理
|
|
88
|
+
const upstreamSocket = await this.connectToUpstreamProxy(destination);
|
|
89
|
+
this.setupDataForwarding(clientSocket, upstreamSocket);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// 不允许的域名,走本地连接
|
|
93
|
+
const localSocket = await this.connectToLocal(destination);
|
|
94
|
+
this.setupDataForwarding(clientSocket, localSocket);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
console.error('Error handling SOCKS connection:', err);
|
|
99
|
+
clientSocket.end(new Uint8Array([0x05, 0x01]));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
performHandshake(clientSocket) {
|
|
103
|
+
return new Promise((resolve, reject) => {
|
|
104
|
+
clientSocket.once('data', (data) => {
|
|
105
|
+
if (data[0] !== 0x05) {
|
|
106
|
+
return reject(new Error('Unsupported SOCKS version'));
|
|
107
|
+
}
|
|
108
|
+
clientSocket.write(new Uint8Array([0x05, 0x00]));
|
|
109
|
+
resolve();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
parseClientRequest(clientSocket) {
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
clientSocket.once('data', (data) => {
|
|
116
|
+
let addr;
|
|
117
|
+
let port;
|
|
118
|
+
if (data[1] !== 0x01) {
|
|
119
|
+
return reject(new Error('Unsupported command'));
|
|
120
|
+
}
|
|
121
|
+
if (data[3] === 0x01) {
|
|
122
|
+
// IPv4 地址
|
|
123
|
+
addr = data.slice(4, 8).join('.');
|
|
124
|
+
port = data.readUInt16BE(8);
|
|
125
|
+
}
|
|
126
|
+
else if (data[3] === 0x03) {
|
|
127
|
+
// 域名
|
|
128
|
+
const len = data[4];
|
|
129
|
+
addr = data.slice(5, 5 + len).toString();
|
|
130
|
+
port = data.readUInt16BE(5 + len);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return reject(new Error('Unsupported address type'));
|
|
134
|
+
}
|
|
135
|
+
resolve({ addr, port });
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// 上游代理连接
|
|
140
|
+
async connectToUpstreamProxy(destination) {
|
|
141
|
+
const destKey = `${destination.addr}:${destination.port}`;
|
|
142
|
+
// 如果已有连接在池中,复用
|
|
143
|
+
if (this.connectionPool[destKey] && !this.connectionPool[destKey].destroyed) {
|
|
144
|
+
console.log(`Reusing connection for ${destKey}`);
|
|
145
|
+
return this.connectionPool[destKey];
|
|
146
|
+
}
|
|
147
|
+
const options = {
|
|
148
|
+
proxy: { ...this.upstreamProxy, type: 5 },
|
|
149
|
+
command: 'connect',
|
|
150
|
+
destination: {
|
|
151
|
+
host: destination.addr,
|
|
152
|
+
port: destination.port,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
try {
|
|
156
|
+
const { socket: upstreamSocket } = await socks_1.SocksClient.createConnection(options);
|
|
157
|
+
this.connectionPool[destKey] = upstreamSocket;
|
|
158
|
+
upstreamSocket.on('close', () => {
|
|
159
|
+
delete this.connectionPool[destKey];
|
|
160
|
+
});
|
|
161
|
+
return upstreamSocket;
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
throw new Error('Failed to connect to upstream proxy: ' + err);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// 本地连接
|
|
168
|
+
connectToLocal(destination) {
|
|
169
|
+
return new Promise((resolve, reject) => {
|
|
170
|
+
const localSocket = net_1.default.createConnection(destination.port, destination.addr, () => {
|
|
171
|
+
resolve(localSocket);
|
|
172
|
+
});
|
|
173
|
+
localSocket.on('error', (err) => {
|
|
174
|
+
reject(err);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
setupDataForwarding(clientSocket, targetSocket) {
|
|
179
|
+
clientSocket.write(new Uint8Array([0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
|
|
180
|
+
clientSocket.pipe(targetSocket);
|
|
181
|
+
targetSocket.pipe(clientSocket);
|
|
182
|
+
clientSocket.on('close', () => {
|
|
183
|
+
targetSocket.end();
|
|
184
|
+
});
|
|
185
|
+
targetSocket.on('close', () => {
|
|
186
|
+
clientSocket.end();
|
|
187
|
+
});
|
|
188
|
+
clientSocket.on('error', (err) => {
|
|
189
|
+
console.error('Client socket error:', err);
|
|
190
|
+
targetSocket.end();
|
|
191
|
+
});
|
|
192
|
+
targetSocket.on('error', (err) => {
|
|
193
|
+
console.error('Target socket error:', err);
|
|
194
|
+
clientSocket.end();
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
isAllowedDomain(addr) {
|
|
198
|
+
// return this.allowedDomains.has(addr);
|
|
199
|
+
// 只匹配主域名,忽略子域名
|
|
200
|
+
for (const domain of this.allowedDomains) {
|
|
201
|
+
if (domain === '*')
|
|
202
|
+
return true;
|
|
203
|
+
if (addr.includes(domain))
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
close() {
|
|
209
|
+
if (this.server) {
|
|
210
|
+
console.log('Closing SOCKS5 proxy server...');
|
|
211
|
+
this.server.close((err) => {
|
|
212
|
+
if (err) {
|
|
213
|
+
console.error('Error closing the server:', err);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
// 销毁客户端 socket
|
|
217
|
+
this.clientSockets.forEach((socket) => socket.destroy());
|
|
218
|
+
// 销毁所有连接池中的 socket
|
|
219
|
+
Object.values(this.connectionPool).forEach((socket) => socket.destroy());
|
|
220
|
+
this.server = null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
exports.SockServer = SockServer;
|
package/bin/types/index.d.ts
CHANGED
|
@@ -390,6 +390,7 @@ declare const dLogger: {
|
|
|
390
390
|
}) => void;
|
|
391
391
|
};
|
|
392
392
|
declare const dWeb: {
|
|
393
|
+
Ecdh: typeof import("./modules/browser/ecdh").default;
|
|
393
394
|
fetch: {
|
|
394
395
|
getDataURL: (url: string) => Promise<[any, undefined] | [null, {
|
|
395
396
|
contentType: string;
|
|
@@ -499,6 +500,8 @@ declare const dWeb: {
|
|
|
499
500
|
};
|
|
500
501
|
declare const dNode: {
|
|
501
502
|
Ecdh: typeof import("./modules/node/ecdh").default;
|
|
503
|
+
EcdhSpki: typeof import("./modules/node/ecdhSpki").default;
|
|
504
|
+
SockServer: typeof import("./modules/node/sockServer").SockServer;
|
|
502
505
|
brotliCompress: typeof import("./modules/node/brotli").brotliCompress;
|
|
503
506
|
brotliDecompress: typeof import("./modules/node/brotli").brotliDecompress;
|
|
504
507
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default class Ecdh {
|
|
2
|
+
publicKey: string;
|
|
3
|
+
iv: string;
|
|
4
|
+
__keyPair: CryptoKeyPair | undefined;
|
|
5
|
+
__sharedSecret: CryptoKey | undefined;
|
|
6
|
+
__curve: string;
|
|
7
|
+
constructor(curve?: string);
|
|
8
|
+
generateKeys(iv: string): Promise<this>;
|
|
9
|
+
exportSpkiKey(cryptoKey: CryptoKey): Promise<string>;
|
|
10
|
+
importSpkiKey(keyBase64: string): Promise<CryptoKey>;
|
|
11
|
+
deriveSharedSecret(publicKeyBase64: string): Promise<this>;
|
|
12
|
+
encode(data: string): Promise<{
|
|
13
|
+
text: string;
|
|
14
|
+
iv: string;
|
|
15
|
+
}>;
|
|
16
|
+
decode(encrypted: string, iv: string): Promise<string>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
export default class EcdhSpki {
|
|
4
|
+
publicKey: string;
|
|
5
|
+
iv: string;
|
|
6
|
+
__keyPair: CryptoKeyPair | undefined;
|
|
7
|
+
__sharedSecret: CryptoKey | undefined;
|
|
8
|
+
__curve: string;
|
|
9
|
+
constructor(curve?: string);
|
|
10
|
+
generateKeys(iv: string): Promise<this>;
|
|
11
|
+
exportSpkiKey(cryptoKey: CryptoKey): Promise<string>;
|
|
12
|
+
importSpkiKey(keyBase64: string): Promise<crypto.webcrypto.CryptoKey>;
|
|
13
|
+
deriveSharedSecret(publicKeyBase64: string): Promise<this>;
|
|
14
|
+
encode(data: string): Promise<{
|
|
15
|
+
text: string;
|
|
16
|
+
iv: string;
|
|
17
|
+
}>;
|
|
18
|
+
decode(encrypted: string, iv: string): Promise<string>;
|
|
19
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import Ecdh from "./ecdh";
|
|
2
|
+
import EcdhSpki from "./ecdhSpki";
|
|
3
|
+
import { SockServer } from "./sockServer";
|
|
2
4
|
declare const _default: {
|
|
3
5
|
Ecdh: typeof Ecdh;
|
|
6
|
+
EcdhSpki: typeof EcdhSpki;
|
|
7
|
+
SockServer: typeof SockServer;
|
|
4
8
|
brotliCompress: typeof import("./brotli").brotliCompress;
|
|
5
9
|
brotliDecompress: typeof import("./brotli").brotliDecompress;
|
|
6
10
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
interface IProxyConfig {
|
|
2
|
+
ipaddress: string;
|
|
3
|
+
port: number;
|
|
4
|
+
type?: 5;
|
|
5
|
+
userId: string;
|
|
6
|
+
password: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class SockServer {
|
|
9
|
+
private upstreamProxy;
|
|
10
|
+
private server;
|
|
11
|
+
private clientSockets;
|
|
12
|
+
private allowedDomains;
|
|
13
|
+
private connectionPool;
|
|
14
|
+
__debug: boolean;
|
|
15
|
+
constructor(upstreamProxy: IProxyConfig, allowedDomains?: string[], debug?: boolean);
|
|
16
|
+
validateProxyConfig(proxyConfig: IProxyConfig): "无效的上游代理 IP 地址" | "无效的上游代理端口" | "无效的上游代理用户名" | "无效的上游代理密码" | "";
|
|
17
|
+
start(startPort?: number): Promise<number>;
|
|
18
|
+
private findAvailablePort;
|
|
19
|
+
private handleSocksConnection;
|
|
20
|
+
private performHandshake;
|
|
21
|
+
private parseClientRequest;
|
|
22
|
+
private connectToUpstreamProxy;
|
|
23
|
+
private connectToLocal;
|
|
24
|
+
private setupDataForwarding;
|
|
25
|
+
private isAllowedDomain;
|
|
26
|
+
close(): void;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ddan-js",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.18",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ddan-js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
38
|
+
"node": ">=15.0.0"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"lint": "tslint --project tsconfig.json -t codeFrame 'src/**/*.ts' 'test/**/*.ts'",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"@types/babel__core": "7.1.19",
|
|
108
108
|
"@types/babel__traverse": "7.17.1",
|
|
109
109
|
"@types/jest": "^23.3.2",
|
|
110
|
-
"@types/node": "^
|
|
110
|
+
"@types/node": "^16.18.0",
|
|
111
111
|
"colors": "^1.4.0",
|
|
112
112
|
"commitizen": "^4.3.0",
|
|
113
113
|
"coveralls": "^3.1.1",
|
|
@@ -143,5 +143,8 @@
|
|
|
143
143
|
"tslint-config-standard": "^9.0.0",
|
|
144
144
|
"typedoc": "^0.24.0",
|
|
145
145
|
"typescript": "^5.0.4"
|
|
146
|
+
},
|
|
147
|
+
"dependencies": {
|
|
148
|
+
"socks": "^2.8.3"
|
|
146
149
|
}
|
|
147
150
|
}
|