ddan-js 2.6.17 → 2.6.19

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.
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SocksServer = void 0;
4
+ const net_1 = require("net");
5
+ const socks_1 = require("socks");
6
+ class SocksServer {
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 = debug;
17
+ }
18
+ validateProxyConfig() {
19
+ const { ipaddress, port, userId, password } = this.upstreamProxy;
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.validateProxyConfig())
36
+ return 0;
37
+ if (this.server) {
38
+ console.log(`Server is already running`);
39
+ return 0;
40
+ }
41
+ const port = await this.findAvailablePort(startPort);
42
+ if (!port)
43
+ return port;
44
+ this.server = net_1.default.createServer((socket) => this.handleSocksConnection(socket));
45
+ this.server.listen(port, () => {
46
+ this.__debug && console.log(`SOCKS5 proxy server is running on port ${port}`);
47
+ });
48
+ this.server.on('error', (err) => {
49
+ console.error('SOCKS5 proxy server error:', err);
50
+ this.close();
51
+ });
52
+ return port;
53
+ }
54
+ async findAvailablePort(startPort, maxRetries = 200 // 最大重试次数
55
+ ) {
56
+ let port = startPort;
57
+ let retries = 0;
58
+ while (retries < maxRetries) {
59
+ try {
60
+ await new Promise((resolve, reject) => {
61
+ const testServer = net_1.default.createServer();
62
+ testServer.unref();
63
+ testServer.on('error', reject);
64
+ testServer.listen(port, () => {
65
+ testServer.close(() => resolve());
66
+ });
67
+ });
68
+ return port; // 找到可用端口
69
+ }
70
+ catch {
71
+ retries++; // 记录重试次数
72
+ port++; // 尝试下一个端口
73
+ }
74
+ }
75
+ console.warn('findAvailablePort failed');
76
+ return 0;
77
+ }
78
+ async handleSocksConnection(clientSocket) {
79
+ this.clientSockets.add(clientSocket);
80
+ clientSocket.on('close', () => {
81
+ this.clientSockets.delete(clientSocket);
82
+ clientSocket?.destroy();
83
+ });
84
+ try {
85
+ await this.performHandshake(clientSocket);
86
+ const destination = await this.parseClientRequest(clientSocket);
87
+ this.__debug && console.log(`SockServer handleSocksConnection`, destination.addr);
88
+ if (this.isAllowedDomain(destination.addr)) {
89
+ // 允许的域名,走上游代理
90
+ const upstreamSocket = await this.connectToUpstreamProxy(destination);
91
+ this.setupDataForwarding(clientSocket, upstreamSocket);
92
+ }
93
+ else {
94
+ // 不允许的域名,走本地连接
95
+ const localSocket = await this.connectToLocal(destination);
96
+ this.setupDataForwarding(clientSocket, localSocket);
97
+ }
98
+ }
99
+ catch (err) {
100
+ console.error('Error handling SOCKS connection:', err);
101
+ clientSocket.end(new Uint8Array([0x05, 0x01]));
102
+ }
103
+ }
104
+ performHandshake(clientSocket) {
105
+ return new Promise((resolve, reject) => {
106
+ clientSocket.once('data', (data) => {
107
+ if (data[0] !== 0x05) {
108
+ return reject(new Error('Unsupported SOCKS version'));
109
+ }
110
+ clientSocket.write(new Uint8Array([0x05, 0x00]));
111
+ resolve();
112
+ });
113
+ });
114
+ }
115
+ parseClientRequest(clientSocket) {
116
+ return new Promise((resolve, reject) => {
117
+ clientSocket.once('data', (data) => {
118
+ let addr;
119
+ let port;
120
+ if (data[1] !== 0x01) {
121
+ return reject(new Error('Unsupported command'));
122
+ }
123
+ if (data[3] === 0x01) {
124
+ // IPv4 地址
125
+ addr = data.slice(4, 8).join('.');
126
+ port = data.readUInt16BE(8);
127
+ }
128
+ else if (data[3] === 0x03) {
129
+ // 域名
130
+ const len = data[4];
131
+ addr = data.slice(5, 5 + len).toString();
132
+ port = data.readUInt16BE(5 + len);
133
+ }
134
+ else {
135
+ return reject(new Error('Unsupported address type'));
136
+ }
137
+ resolve({ addr, port });
138
+ });
139
+ });
140
+ }
141
+ // 上游代理连接
142
+ async connectToUpstreamProxy(destination) {
143
+ const destKey = `${destination.addr}:${destination.port}`;
144
+ // 如果已有连接在池中,复用
145
+ if (this.connectionPool[destKey] && !this.connectionPool[destKey].destroyed) {
146
+ console.log(`Reusing connection for ${destKey}`);
147
+ return this.connectionPool[destKey];
148
+ }
149
+ const options = {
150
+ proxy: { ...this.upstreamProxy, type: 5 },
151
+ command: 'connect',
152
+ destination: {
153
+ host: destination.addr,
154
+ port: destination.port,
155
+ },
156
+ };
157
+ try {
158
+ const { socket: upstreamSocket } = await socks_1.SocksClient.createConnection(options);
159
+ this.connectionPool[destKey] = upstreamSocket;
160
+ upstreamSocket.on('close', () => {
161
+ delete this.connectionPool[destKey];
162
+ });
163
+ return upstreamSocket;
164
+ }
165
+ catch (err) {
166
+ throw new Error('Failed to connect to upstream proxy: ' + err);
167
+ }
168
+ }
169
+ // 本地连接
170
+ connectToLocal(destination) {
171
+ return new Promise((resolve, reject) => {
172
+ const localSocket = net_1.default.createConnection(destination.port, destination.addr, () => {
173
+ resolve(localSocket);
174
+ });
175
+ localSocket.on('error', (err) => {
176
+ reject(err);
177
+ });
178
+ });
179
+ }
180
+ setupDataForwarding(clientSocket, targetSocket) {
181
+ clientSocket.write(new Uint8Array([0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
182
+ clientSocket.pipe(targetSocket);
183
+ targetSocket.pipe(clientSocket);
184
+ clientSocket.on('close', () => {
185
+ targetSocket.end();
186
+ });
187
+ targetSocket.on('close', () => {
188
+ clientSocket.end();
189
+ });
190
+ clientSocket.on('error', (err) => {
191
+ console.error('Client socket error:', err);
192
+ targetSocket.end();
193
+ });
194
+ targetSocket.on('error', (err) => {
195
+ console.error('Target socket error:', err);
196
+ clientSocket.end();
197
+ });
198
+ }
199
+ isAllowedDomain(addr) {
200
+ // return this.allowedDomains.has(addr);
201
+ // 只匹配主域名,忽略子域名
202
+ for (const domain of this.allowedDomains) {
203
+ if (domain === '*')
204
+ return true;
205
+ if (addr.includes(domain))
206
+ return true;
207
+ }
208
+ return false;
209
+ }
210
+ close() {
211
+ if (this.server) {
212
+ console.log('Closing SOCKS5 proxy server...');
213
+ this.server.close((err) => {
214
+ if (err) {
215
+ console.error('Error closing the server:', err);
216
+ }
217
+ });
218
+ // 销毁客户端 socket
219
+ this.clientSockets.forEach((socket) => socket.destroy());
220
+ // 销毁所有连接池中的 socket
221
+ Object.values(this.connectionPool).forEach((socket) => socket.destroy());
222
+ this.server = null;
223
+ }
224
+ }
225
+ }
226
+ exports.SocksServer = SocksServer;
@@ -1,24 +1,30 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const _object_1 = require("../common/_object");
4
+ const _root_1 = require("../common/_root");
5
+ const nodeTypes_1 = require("../common/internal/nodeTypes");
4
6
  /** `Object#toString` result references. */
5
7
  var argsTag = '[object Arguments]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]', asyncTag = '[object AsyncFunction]', stringTag = '[object String]', promiseTag = '[object Promise]';
6
8
  var MAX_SAFE_INTEGER = 9007199254740991, NAN = 0 / 0;
7
9
  /** Used to detect unsigned integer values. */
8
10
  const reIsUint = /^(?:0|[1-9]\d*)$/;
11
+ const nativeIsBuffer = _root_1.default?.Buffer?.isBuffer;
12
+ const reTypedTag = /^\[object (?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)Array\]$/;
13
+ /* Node.js helper references. */
14
+ const nodeIsTypedArray = nodeTypes_1.default && nodeTypes_1.default.isTypedArray;
9
15
  function is(val, type) {
10
16
  return _object_1.default.getTag(val) === `[object ${type}]`;
11
17
  }
12
18
  function isNumber(value) {
13
- return _object_1.default.getTag(value) === "[object Number]";
19
+ return _object_1.default.getTag(value) === '[object Number]';
14
20
  }
15
21
  function isString(value) {
16
- return typeof value == 'string' ||
17
- (!isArray(value) && isObjectLike(value) && _object_1.default.getTag(value) == stringTag);
22
+ return (typeof value == 'string' ||
23
+ (!isArray(value) && isObjectLike(value) && _object_1.default.getTag(value) == stringTag));
18
24
  }
19
25
  function isObject(value) {
20
26
  const type = typeof value;
21
- return value != null && (type == "object" || type == "function");
27
+ return value != null && (type == 'object' || type == 'function');
22
28
  }
23
29
  function isObjectLike(value) {
24
30
  return value != null && typeof value == 'object';
@@ -37,7 +43,8 @@ function isPlainObject(value) {
37
43
  return Object.getPrototypeOf(value) === proto;
38
44
  }
39
45
  function isSymbol(value) {
40
- return typeof value === 'symbol' || (isObjectLike(value) && _object_1.default.getTag(value) === '[object Symbol]');
46
+ return (typeof value === 'symbol' ||
47
+ (isObjectLike(value) && _object_1.default.getTag(value) === '[object Symbol]'));
41
48
  }
42
49
  const isArray = Array.isArray;
43
50
  function isArrayLike(value) {
@@ -55,22 +62,24 @@ function isPromise(value) {
55
62
  return tag === promiseTag;
56
63
  }
57
64
  function isLength(value) {
58
- return typeof value == 'number' &&
59
- value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
65
+ return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
60
66
  }
61
67
  function isIndex(value, length) {
62
68
  length = length == null ? MAX_SAFE_INTEGER : length;
63
- return !!length &&
69
+ return (!!length &&
64
70
  (typeof value == 'number' || reIsUint.test(value)) &&
65
- (value > -1 && value % 1 == 0 && value < length);
71
+ value > -1 &&
72
+ value % 1 == 0 &&
73
+ value < length);
66
74
  }
67
75
  function isPrototype(value) {
68
76
  var Ctor = value && value.constructor, proto = (typeof Ctor == 'function' && Ctor.prototype) || Object.prototype;
69
77
  return value === proto;
70
78
  }
71
79
  function isArguments(value) {
72
- return isArrayLikeObject(value) && _object_1.default.hasOwnProperty.call(value, 'callee') &&
73
- (!_object_1.default.propertyIsEnumerable.call(value, 'callee') || _object_1.default.toString.call(value) == argsTag);
80
+ return (isArrayLikeObject(value) &&
81
+ _object_1.default.hasOwnProperty.call(value, 'callee') &&
82
+ (!_object_1.default.propertyIsEnumerable.call(value, 'callee') || _object_1.default.toString.call(value) == argsTag));
74
83
  }
75
84
  const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
76
85
  const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
@@ -83,6 +92,37 @@ const isUint8Array = (data) => {
83
92
  const isArrayBuffer = (data) => {
84
93
  return data instanceof ArrayBuffer;
85
94
  };
95
+ const isTypedArray = nodeIsTypedArray
96
+ ? (value) => nodeIsTypedArray(value)
97
+ : (value) => isObjectLike(value) && reTypedTag.test(_object_1.default.getTag(value));
98
+ const isBuffer = typeof nativeIsBuffer === 'function' ? nativeIsBuffer : () => false;
99
+ const isEmpty = (value) => {
100
+ if (value == null) {
101
+ return true;
102
+ }
103
+ if (isArrayLike(value) &&
104
+ (Array.isArray(value) ||
105
+ typeof value === 'string' ||
106
+ typeof value.splice === 'function' ||
107
+ isBuffer(value) ||
108
+ isTypedArray(value) ||
109
+ isArguments(value))) {
110
+ return !value.length;
111
+ }
112
+ const tag = _object_1.default.getTag(value);
113
+ if (tag === '[object Map]' || tag === '[object Set]') {
114
+ return !value.size;
115
+ }
116
+ if (isPrototype(value)) {
117
+ return !Object.keys(value).length;
118
+ }
119
+ for (const key in value) {
120
+ if (_object_1.default.hasOwnProperty.call(value, key)) {
121
+ return false;
122
+ }
123
+ }
124
+ return true;
125
+ };
86
126
  exports.default = {
87
127
  is,
88
128
  isNumber,
@@ -103,5 +143,6 @@ exports.default = {
103
143
  isNode,
104
144
  isBlobOrBuffer,
105
145
  isUint8Array,
106
- isArrayBuffer
146
+ isArrayBuffer,
147
+ isEmpty,
107
148
  };
@@ -1,3 +1,3 @@
1
1
  /** Detect free variable `global` from Node.js. */
2
- declare var freeGlobal: any;
2
+ declare const freeGlobal: any;
3
3
  export default freeGlobal;
@@ -1,3 +1,3 @@
1
1
  /** Used as a reference to the global object. */
2
- declare var root: any;
2
+ declare const root: any;
3
3
  export default root;
@@ -0,0 +1,3 @@
1
+ /** Used to access faster Node.js helpers. */
2
+ declare const nodeTypes: any;
3
+ export default nodeTypes;
@@ -500,7 +500,8 @@ declare const dWeb: {
500
500
  };
501
501
  declare const dNode: {
502
502
  Ecdh: typeof import("./modules/node/ecdh").default;
503
- EcdhSpki: typeof import("./modules/node/ecdhSpki").default;
503
+ EcdhSpki: typeof import("./modules/node/ecdh-spki").default;
504
+ SocksServer: typeof import("./modules/node/socks-server").SocksServer;
504
505
  brotliCompress: typeof import("./modules/node/brotli").brotliCompress;
505
506
  brotliDecompress: typeof import("./modules/node/brotli").brotliDecompress;
506
507
  };
@@ -1,8 +1,10 @@
1
1
  import Ecdh from "./ecdh";
2
- import EcdhSpki from "./ecdhSpki";
2
+ import EcdhSpki from "./ecdh-spki";
3
+ import { SocksServer } from "./socks-server";
3
4
  declare const _default: {
4
5
  Ecdh: typeof Ecdh;
5
6
  EcdhSpki: typeof EcdhSpki;
7
+ SocksServer: typeof SocksServer;
6
8
  brotliCompress: typeof import("./brotli").brotliCompress;
7
9
  brotliDecompress: typeof import("./brotli").brotliDecompress;
8
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 SocksServer {
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(): "无效的上游代理 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 {};
@@ -33,5 +33,6 @@ declare const _default: {
33
33
  isBlobOrBuffer: (data: any) => boolean;
34
34
  isUint8Array: (data: any) => boolean;
35
35
  isArrayBuffer: (data: any) => boolean;
36
+ isEmpty: (value: any) => boolean;
36
37
  };
37
38
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ddan-js",
3
- "version": "2.6.17",
3
+ "version": "2.6.19",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "ddan-js",
@@ -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
  }