@push.rocks/smartproxy 25.11.24 → 25.12.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.
- package/changelog.md +7 -0
- package/dist_rust/rustproxy_linux_amd64 +0 -0
- package/dist_rust/rustproxy_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/core/utils/index.d.ts +0 -1
- package/dist_ts/core/utils/index.js +1 -2
- package/dist_ts/protocols/proxy/index.d.ts +1 -2
- package/dist_ts/protocols/proxy/index.js +2 -3
- package/dist_ts/protocols/proxy/types.d.ts +1 -1
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/core/utils/index.ts +0 -1
- package/ts/protocols/proxy/index.ts +2 -3
- package/ts/protocols/proxy/types.ts +1 -1
- package/dist_ts/core/utils/proxy-protocol.d.ts +0 -33
- package/dist_ts/core/utils/proxy-protocol.js +0 -117
- package/dist_ts/protocols/proxy/parser.d.ts +0 -44
- package/dist_ts/protocols/proxy/parser.js +0 -153
- package/ts/core/utils/proxy-protocol.ts +0 -129
- package/ts/protocols/proxy/parser.ts +0 -183
package/changelog.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-03-19 - 25.12.0 - feat(proxy-protocol)
|
|
4
|
+
add PROXY protocol v2 support to the Rust passthrough listener and streamline TypeScript proxy protocol exports
|
|
5
|
+
|
|
6
|
+
- detect and parse PROXY protocol v2 headers in the Rust TCP listener, including TCP and UDP address families
|
|
7
|
+
- add Rust v2 header generation, incomplete-header handling, and broader parser test coverage
|
|
8
|
+
- remove deprecated TypeScript proxy protocol parser exports and tests, leaving shared type definitions only
|
|
9
|
+
|
|
3
10
|
## 2026-03-17 - 25.11.24 - fix(rustproxy-http)
|
|
4
11
|
improve async static file serving, websocket handshake buffering, and shared metric metadata handling
|
|
5
12
|
|
|
Binary file
|
|
Binary file
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '25.
|
|
6
|
+
version: '25.12.0',
|
|
7
7
|
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLHFQQUFxUDtDQUNuUSxDQUFBIn0=
|
|
@@ -14,5 +14,4 @@ export * from './lifecycle-component.js';
|
|
|
14
14
|
export * from './binary-heap.js';
|
|
15
15
|
export * from './enhanced-connection-pool.js';
|
|
16
16
|
export * from './socket-utils.js';
|
|
17
|
-
|
|
18
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3V0aWxzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBRUgsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGVBQWUsQ0FBQztBQUM5QixjQUFjLHFCQUFxQixDQUFDO0FBQ3BDLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyw4QkFBOEIsQ0FBQztBQUM3QyxjQUFjLHNCQUFzQixDQUFDO0FBQ3JDLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsK0JBQStCLENBQUM7QUFDOUMsY0FBYyxtQkFBbUIsQ0FBQztBQUNsQyxjQUFjLHFCQUFxQixDQUFDIn0=
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3V0aWxzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBRUgsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGVBQWUsQ0FBQztBQUM5QixjQUFjLHFCQUFxQixDQUFDO0FBQ3BDLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyw4QkFBOEIsQ0FBQztBQUM3QyxjQUFjLHNCQUFzQixDQUFDO0FBQ3JDLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsK0JBQStCLENBQUM7QUFDOUMsY0FBYyxtQkFBbUIsQ0FBQyJ9
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PROXY Protocol Module
|
|
3
|
-
* HAProxy PROXY protocol
|
|
3
|
+
* Type definitions for HAProxy PROXY protocol v1/v2
|
|
4
4
|
*/
|
|
5
5
|
export * from './types.js';
|
|
6
|
-
|
|
7
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm90b2NvbHMvcHJveHkvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsY0FBYyxZQUFZLENBQUM7QUFDM0IsY0FBYyxhQUFhLENBQUMifQ==
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm90b2NvbHMvcHJveHkvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsY0FBYyxZQUFZLENBQUMifQ==
|
|
@@ -9,7 +9,7 @@ export type TProxyProtocolVersion = 'v1' | 'v2';
|
|
|
9
9
|
/**
|
|
10
10
|
* Connection protocol type
|
|
11
11
|
*/
|
|
12
|
-
export type TProxyProtocol = 'TCP4' | 'TCP6' | 'UNKNOWN';
|
|
12
|
+
export type TProxyProtocol = 'TCP4' | 'TCP6' | 'UDP4' | 'UDP6' | 'UNKNOWN';
|
|
13
13
|
/**
|
|
14
14
|
* Interface representing parsed PROXY protocol information
|
|
15
15
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartproxy",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.12.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '25.
|
|
6
|
+
version: '25.12.0',
|
|
7
7
|
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
|
8
8
|
}
|
package/ts/core/utils/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type TProxyProtocolVersion = 'v1' | 'v2';
|
|
|
11
11
|
/**
|
|
12
12
|
* Connection protocol type
|
|
13
13
|
*/
|
|
14
|
-
export type TProxyProtocol = 'TCP4' | 'TCP6' | 'UNKNOWN';
|
|
14
|
+
export type TProxyProtocol = 'TCP4' | 'TCP6' | 'UDP4' | 'UDP6' | 'UNKNOWN';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Interface representing parsed PROXY protocol information
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import { type IProxyInfo, type IProxyParseResult } from '../../protocols/proxy/index.js';
|
|
3
|
-
export type { IProxyInfo, IProxyParseResult } from '../../protocols/proxy/index.js';
|
|
4
|
-
/**
|
|
5
|
-
* Parser for PROXY protocol v1 (text format)
|
|
6
|
-
* Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
|
7
|
-
*
|
|
8
|
-
* This class now delegates to the protocol parser but adds
|
|
9
|
-
* smartproxy-specific features like socket reading and logging
|
|
10
|
-
*/
|
|
11
|
-
export declare class ProxyProtocolParser {
|
|
12
|
-
static readonly PROXY_V1_SIGNATURE = "PROXY ";
|
|
13
|
-
static readonly MAX_HEADER_LENGTH = 107;
|
|
14
|
-
static readonly HEADER_TERMINATOR = "\r\n";
|
|
15
|
-
/**
|
|
16
|
-
* Parse PROXY protocol v1 header from buffer
|
|
17
|
-
* Returns proxy info and remaining data after header
|
|
18
|
-
*/
|
|
19
|
-
static parse(data: Buffer): IProxyParseResult;
|
|
20
|
-
/**
|
|
21
|
-
* Generate PROXY protocol v1 header
|
|
22
|
-
*/
|
|
23
|
-
static generate(info: IProxyInfo): Buffer;
|
|
24
|
-
/**
|
|
25
|
-
* Validate IP address format
|
|
26
|
-
*/
|
|
27
|
-
private static isValidIP;
|
|
28
|
-
/**
|
|
29
|
-
* Attempt to read a complete PROXY protocol header from a socket
|
|
30
|
-
* Returns null if no PROXY protocol detected or incomplete
|
|
31
|
-
*/
|
|
32
|
-
static readFromSocket(socket: plugins.net.Socket, timeout?: number): Promise<IProxyParseResult | null>;
|
|
33
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import { logger } from './logger.js';
|
|
3
|
-
import { ProxyProtocolParser as ProtocolParser } from '../../protocols/proxy/index.js';
|
|
4
|
-
/**
|
|
5
|
-
* Parser for PROXY protocol v1 (text format)
|
|
6
|
-
* Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
|
7
|
-
*
|
|
8
|
-
* This class now delegates to the protocol parser but adds
|
|
9
|
-
* smartproxy-specific features like socket reading and logging
|
|
10
|
-
*/
|
|
11
|
-
export class ProxyProtocolParser {
|
|
12
|
-
static { this.PROXY_V1_SIGNATURE = ProtocolParser.PROXY_V1_SIGNATURE; }
|
|
13
|
-
static { this.MAX_HEADER_LENGTH = ProtocolParser.MAX_HEADER_LENGTH; }
|
|
14
|
-
static { this.HEADER_TERMINATOR = ProtocolParser.HEADER_TERMINATOR; }
|
|
15
|
-
/**
|
|
16
|
-
* Parse PROXY protocol v1 header from buffer
|
|
17
|
-
* Returns proxy info and remaining data after header
|
|
18
|
-
*/
|
|
19
|
-
static parse(data) {
|
|
20
|
-
// Delegate to protocol parser
|
|
21
|
-
return ProtocolParser.parse(data);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Generate PROXY protocol v1 header
|
|
25
|
-
*/
|
|
26
|
-
static generate(info) {
|
|
27
|
-
// Delegate to protocol parser
|
|
28
|
-
return ProtocolParser.generate(info);
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Validate IP address format
|
|
32
|
-
*/
|
|
33
|
-
static isValidIP(ip, protocol) {
|
|
34
|
-
return ProtocolParser.isValidIP(ip, protocol);
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Attempt to read a complete PROXY protocol header from a socket
|
|
38
|
-
* Returns null if no PROXY protocol detected or incomplete
|
|
39
|
-
*/
|
|
40
|
-
static async readFromSocket(socket, timeout = 5000) {
|
|
41
|
-
return new Promise((resolve) => {
|
|
42
|
-
let buffer = Buffer.alloc(0);
|
|
43
|
-
let resolved = false;
|
|
44
|
-
const cleanup = () => {
|
|
45
|
-
socket.removeListener('data', onData);
|
|
46
|
-
socket.removeListener('error', onError);
|
|
47
|
-
clearTimeout(timer);
|
|
48
|
-
};
|
|
49
|
-
const timer = setTimeout(() => {
|
|
50
|
-
if (!resolved) {
|
|
51
|
-
resolved = true;
|
|
52
|
-
cleanup();
|
|
53
|
-
resolve({
|
|
54
|
-
proxyInfo: null,
|
|
55
|
-
remainingData: buffer
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}, timeout);
|
|
59
|
-
const onData = (chunk) => {
|
|
60
|
-
buffer = Buffer.concat([buffer, chunk]);
|
|
61
|
-
// Check if we have enough data
|
|
62
|
-
if (!buffer.toString('ascii', 0, Math.min(6, buffer.length)).startsWith(this.PROXY_V1_SIGNATURE)) {
|
|
63
|
-
// Not PROXY protocol
|
|
64
|
-
resolved = true;
|
|
65
|
-
cleanup();
|
|
66
|
-
resolve({
|
|
67
|
-
proxyInfo: null,
|
|
68
|
-
remainingData: buffer
|
|
69
|
-
});
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
// Try to parse
|
|
73
|
-
try {
|
|
74
|
-
const result = this.parse(buffer);
|
|
75
|
-
if (result.proxyInfo) {
|
|
76
|
-
// Successfully parsed
|
|
77
|
-
resolved = true;
|
|
78
|
-
cleanup();
|
|
79
|
-
resolve(result);
|
|
80
|
-
}
|
|
81
|
-
else if (buffer.length > this.MAX_HEADER_LENGTH) {
|
|
82
|
-
// Header too long
|
|
83
|
-
resolved = true;
|
|
84
|
-
cleanup();
|
|
85
|
-
resolve({
|
|
86
|
-
proxyInfo: null,
|
|
87
|
-
remainingData: buffer
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
// Otherwise continue reading
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
// Parse error
|
|
94
|
-
logger.log('error', `PROXY protocol parse error: ${error.message}`);
|
|
95
|
-
resolved = true;
|
|
96
|
-
cleanup();
|
|
97
|
-
resolve({
|
|
98
|
-
proxyInfo: null,
|
|
99
|
-
remainingData: buffer
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
const onError = (error) => {
|
|
104
|
-
logger.log('error', `Socket error while reading PROXY protocol: ${error.message}`);
|
|
105
|
-
resolved = true;
|
|
106
|
-
cleanup();
|
|
107
|
-
resolve({
|
|
108
|
-
proxyInfo: null,
|
|
109
|
-
remainingData: buffer
|
|
110
|
-
});
|
|
111
|
-
};
|
|
112
|
-
socket.on('data', onData);
|
|
113
|
-
socket.on('error', onError);
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJveHktcHJvdG9jb2wuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3V0aWxzL3Byb3h5LXByb3RvY29sLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLEVBQUUsbUJBQW1CLElBQUksY0FBYyxFQUEyQyxNQUFNLGdDQUFnQyxDQUFDO0FBS2hJOzs7Ozs7R0FNRztBQUNILE1BQU0sT0FBTyxtQkFBbUI7YUFDZCx1QkFBa0IsR0FBRyxjQUFjLENBQUMsa0JBQWtCLENBQUM7YUFDdkQsc0JBQWlCLEdBQUcsY0FBYyxDQUFDLGlCQUFpQixDQUFDO2FBQ3JELHNCQUFpQixHQUFHLGNBQWMsQ0FBQyxpQkFBaUIsQ0FBQztJQUVyRTs7O09BR0c7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLElBQVk7UUFDdkIsOEJBQThCO1FBQzlCLE9BQU8sY0FBYyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsUUFBUSxDQUFDLElBQWdCO1FBQzlCLDhCQUE4QjtRQUM5QixPQUFPLGNBQWMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUVEOztPQUVHO0lBQ0ssTUFBTSxDQUFDLFNBQVMsQ0FBQyxFQUFVLEVBQUUsUUFBcUM7UUFDeEUsT0FBTyxjQUFjLENBQUMsU0FBUyxDQUFDLEVBQUUsRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNoRCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsTUFBMEIsRUFBRSxVQUFrQixJQUFJO1FBQzVFLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUM3QixJQUFJLE1BQU0s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
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PROXY Protocol Parser
|
|
3
|
-
* Implementation of HAProxy PROXY protocol v1 (text format)
|
|
4
|
-
* Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
|
5
|
-
*/
|
|
6
|
-
import type { IProxyInfo, IProxyParseResult, TProxyProtocol } from './types.js';
|
|
7
|
-
/**
|
|
8
|
-
* PROXY protocol parser
|
|
9
|
-
*/
|
|
10
|
-
export declare class ProxyProtocolParser {
|
|
11
|
-
static readonly PROXY_V1_SIGNATURE = "PROXY ";
|
|
12
|
-
static readonly MAX_HEADER_LENGTH = 107;
|
|
13
|
-
static readonly HEADER_TERMINATOR = "\r\n";
|
|
14
|
-
/**
|
|
15
|
-
* Parse PROXY protocol v1 header from buffer
|
|
16
|
-
* Returns proxy info and remaining data after header
|
|
17
|
-
*/
|
|
18
|
-
static parse(data: Buffer): IProxyParseResult;
|
|
19
|
-
/**
|
|
20
|
-
* Generate PROXY protocol v1 header
|
|
21
|
-
*/
|
|
22
|
-
static generate(info: IProxyInfo): Buffer;
|
|
23
|
-
/**
|
|
24
|
-
* Validate IP address format
|
|
25
|
-
*/
|
|
26
|
-
static isValidIP(ip: string, protocol: TProxyProtocol): boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Check if string is valid IPv4
|
|
29
|
-
*/
|
|
30
|
-
static isIPv4(ip: string): boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Check if string is valid IPv6
|
|
33
|
-
*/
|
|
34
|
-
static isIPv6(ip: string): boolean;
|
|
35
|
-
/**
|
|
36
|
-
* Create a connection ID string for tracking
|
|
37
|
-
*/
|
|
38
|
-
static createConnectionId(connectionInfo: {
|
|
39
|
-
sourceIp?: string;
|
|
40
|
-
sourcePort?: number;
|
|
41
|
-
destIp?: string;
|
|
42
|
-
destPort?: number;
|
|
43
|
-
}): string;
|
|
44
|
-
}
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PROXY Protocol Parser
|
|
3
|
-
* Implementation of HAProxy PROXY protocol v1 (text format)
|
|
4
|
-
* Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* PROXY protocol parser
|
|
8
|
-
*/
|
|
9
|
-
export class ProxyProtocolParser {
|
|
10
|
-
static { this.PROXY_V1_SIGNATURE = 'PROXY '; }
|
|
11
|
-
static { this.MAX_HEADER_LENGTH = 107; } // Max length for v1 header
|
|
12
|
-
static { this.HEADER_TERMINATOR = '\r\n'; }
|
|
13
|
-
/**
|
|
14
|
-
* Parse PROXY protocol v1 header from buffer
|
|
15
|
-
* Returns proxy info and remaining data after header
|
|
16
|
-
*/
|
|
17
|
-
static parse(data) {
|
|
18
|
-
// Check if buffer starts with PROXY signature
|
|
19
|
-
if (!data.toString('ascii', 0, 6).startsWith(this.PROXY_V1_SIGNATURE)) {
|
|
20
|
-
return {
|
|
21
|
-
proxyInfo: null,
|
|
22
|
-
remainingData: data
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
// Find header terminator
|
|
26
|
-
const headerEndIndex = data.indexOf(this.HEADER_TERMINATOR);
|
|
27
|
-
if (headerEndIndex === -1) {
|
|
28
|
-
// Header incomplete, need more data
|
|
29
|
-
if (data.length > this.MAX_HEADER_LENGTH) {
|
|
30
|
-
// Header too long, invalid
|
|
31
|
-
throw new Error('PROXY protocol header exceeds maximum length');
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
proxyInfo: null,
|
|
35
|
-
remainingData: data
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
// Extract header line
|
|
39
|
-
const headerLine = data.toString('ascii', 0, headerEndIndex);
|
|
40
|
-
const remainingData = data.slice(headerEndIndex + 2); // Skip \r\n
|
|
41
|
-
// Parse header
|
|
42
|
-
const parts = headerLine.split(' ');
|
|
43
|
-
if (parts.length < 2) {
|
|
44
|
-
throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
|
|
45
|
-
}
|
|
46
|
-
const [signature, protocol] = parts;
|
|
47
|
-
// Validate protocol
|
|
48
|
-
if (!['TCP4', 'TCP6', 'UNKNOWN'].includes(protocol)) {
|
|
49
|
-
throw new Error(`Invalid PROXY protocol: ${protocol}`);
|
|
50
|
-
}
|
|
51
|
-
// For UNKNOWN protocol, ignore addresses
|
|
52
|
-
if (protocol === 'UNKNOWN') {
|
|
53
|
-
return {
|
|
54
|
-
proxyInfo: {
|
|
55
|
-
protocol: 'UNKNOWN',
|
|
56
|
-
sourceIP: '',
|
|
57
|
-
sourcePort: 0,
|
|
58
|
-
destinationIP: '',
|
|
59
|
-
destinationPort: 0
|
|
60
|
-
},
|
|
61
|
-
remainingData
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
// For TCP4/TCP6, we need all 6 parts
|
|
65
|
-
if (parts.length !== 6) {
|
|
66
|
-
throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
|
|
67
|
-
}
|
|
68
|
-
const [, , srcIP, dstIP, srcPort, dstPort] = parts;
|
|
69
|
-
// Validate and parse ports
|
|
70
|
-
const sourcePort = parseInt(srcPort, 10);
|
|
71
|
-
const destinationPort = parseInt(dstPort, 10);
|
|
72
|
-
if (isNaN(sourcePort) || sourcePort < 0 || sourcePort > 65535) {
|
|
73
|
-
throw new Error(`Invalid source port: ${srcPort}`);
|
|
74
|
-
}
|
|
75
|
-
if (isNaN(destinationPort) || destinationPort < 0 || destinationPort > 65535) {
|
|
76
|
-
throw new Error(`Invalid destination port: ${dstPort}`);
|
|
77
|
-
}
|
|
78
|
-
// Validate IP addresses
|
|
79
|
-
const protocolType = protocol;
|
|
80
|
-
if (!this.isValidIP(srcIP, protocolType)) {
|
|
81
|
-
throw new Error(`Invalid source IP for ${protocol}: ${srcIP}`);
|
|
82
|
-
}
|
|
83
|
-
if (!this.isValidIP(dstIP, protocolType)) {
|
|
84
|
-
throw new Error(`Invalid destination IP for ${protocol}: ${dstIP}`);
|
|
85
|
-
}
|
|
86
|
-
return {
|
|
87
|
-
proxyInfo: {
|
|
88
|
-
protocol: protocolType,
|
|
89
|
-
sourceIP: srcIP,
|
|
90
|
-
sourcePort,
|
|
91
|
-
destinationIP: dstIP,
|
|
92
|
-
destinationPort
|
|
93
|
-
},
|
|
94
|
-
remainingData
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Generate PROXY protocol v1 header
|
|
99
|
-
*/
|
|
100
|
-
static generate(info) {
|
|
101
|
-
if (info.protocol === 'UNKNOWN') {
|
|
102
|
-
return Buffer.from(`PROXY UNKNOWN\r\n`, 'ascii');
|
|
103
|
-
}
|
|
104
|
-
const header = `PROXY ${info.protocol} ${info.sourceIP} ${info.destinationIP} ${info.sourcePort} ${info.destinationPort}\r\n`;
|
|
105
|
-
if (header.length > this.MAX_HEADER_LENGTH) {
|
|
106
|
-
throw new Error('Generated PROXY protocol header exceeds maximum length');
|
|
107
|
-
}
|
|
108
|
-
return Buffer.from(header, 'ascii');
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Validate IP address format
|
|
112
|
-
*/
|
|
113
|
-
static isValidIP(ip, protocol) {
|
|
114
|
-
if (protocol === 'TCP4') {
|
|
115
|
-
return this.isIPv4(ip);
|
|
116
|
-
}
|
|
117
|
-
else if (protocol === 'TCP6') {
|
|
118
|
-
return this.isIPv6(ip);
|
|
119
|
-
}
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Check if string is valid IPv4
|
|
124
|
-
*/
|
|
125
|
-
static isIPv4(ip) {
|
|
126
|
-
const parts = ip.split('.');
|
|
127
|
-
if (parts.length !== 4)
|
|
128
|
-
return false;
|
|
129
|
-
for (const part of parts) {
|
|
130
|
-
const num = parseInt(part, 10);
|
|
131
|
-
if (isNaN(num) || num < 0 || num > 255 || part !== num.toString()) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Check if string is valid IPv6
|
|
139
|
-
*/
|
|
140
|
-
static isIPv6(ip) {
|
|
141
|
-
// Basic IPv6 validation
|
|
142
|
-
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
|
|
143
|
-
return ipv6Regex.test(ip);
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Create a connection ID string for tracking
|
|
147
|
-
*/
|
|
148
|
-
static createConnectionId(connectionInfo) {
|
|
149
|
-
const { sourceIp, sourcePort, destIp, destPort } = connectionInfo;
|
|
150
|
-
return `${sourceIp}:${sourcePort}-${destIp}:${destPort}`;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJvdG9jb2xzL3Byb3h5L3BhcnNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBSUg7O0dBRUc7QUFDSCxNQUFNLE9BQU8sbUJBQW1CO2FBQ2QsdUJBQWtCLEdBQUcsUUFBUSxDQUFDO2FBQzlCLHNCQUFpQixHQUFHLEdBQUcsQ0FBQyxHQUFDLDJCQUEyQjthQUNwRCxzQkFBaUIsR0FBRyxNQUFNLENBQUM7SUFFM0M7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFZO1FBQ3ZCLDhDQUE4QztRQUM5QyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDO1lBQ3RFLE9BQU87Z0JBQ0wsU0FBUyxFQUFFLElBQUk7Z0JBQ2YsYUFBYSxFQUFFLElBQUk7YUFDcEIsQ0FBQztRQUNKLENBQUM7UUFFRCx5QkFBeUI7UUFDekIsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUM1RCxJQUFJLGNBQWMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQzFCLG9DQUFvQztZQUNwQyxJQUFJLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7Z0JBQ3pDLDJCQUEyQjtnQkFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFDO1lBQ2xFLENBQUM7WUFDRCxPQUFPO2dCQUNMLFNBQVMsRUFBRSxJQUFJO2dCQUNmLGFBQWEsRUFBRSxJQUFJO2FBQ3BCLENBQUM7UUFDSixDQUFDO1FBRUQsc0JBQXNCO1FBQ3RCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxF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
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import { logger } from './logger.js';
|
|
3
|
-
import { ProxyProtocolParser as ProtocolParser, type IProxyInfo, type IProxyParseResult } from '../../protocols/proxy/index.js';
|
|
4
|
-
|
|
5
|
-
// Re-export types from protocols for backward compatibility
|
|
6
|
-
export type { IProxyInfo, IProxyParseResult } from '../../protocols/proxy/index.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Parser for PROXY protocol v1 (text format)
|
|
10
|
-
* Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
|
11
|
-
*
|
|
12
|
-
* This class now delegates to the protocol parser but adds
|
|
13
|
-
* smartproxy-specific features like socket reading and logging
|
|
14
|
-
*/
|
|
15
|
-
export class ProxyProtocolParser {
|
|
16
|
-
static readonly PROXY_V1_SIGNATURE = ProtocolParser.PROXY_V1_SIGNATURE;
|
|
17
|
-
static readonly MAX_HEADER_LENGTH = ProtocolParser.MAX_HEADER_LENGTH;
|
|
18
|
-
static readonly HEADER_TERMINATOR = ProtocolParser.HEADER_TERMINATOR;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Parse PROXY protocol v1 header from buffer
|
|
22
|
-
* Returns proxy info and remaining data after header
|
|
23
|
-
*/
|
|
24
|
-
static parse(data: Buffer): IProxyParseResult {
|
|
25
|
-
// Delegate to protocol parser
|
|
26
|
-
return ProtocolParser.parse(data);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Generate PROXY protocol v1 header
|
|
31
|
-
*/
|
|
32
|
-
static generate(info: IProxyInfo): Buffer {
|
|
33
|
-
// Delegate to protocol parser
|
|
34
|
-
return ProtocolParser.generate(info);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Validate IP address format
|
|
39
|
-
*/
|
|
40
|
-
private static isValidIP(ip: string, protocol: 'TCP4' | 'TCP6' | 'UNKNOWN'): boolean {
|
|
41
|
-
return ProtocolParser.isValidIP(ip, protocol);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Attempt to read a complete PROXY protocol header from a socket
|
|
46
|
-
* Returns null if no PROXY protocol detected or incomplete
|
|
47
|
-
*/
|
|
48
|
-
static async readFromSocket(socket: plugins.net.Socket, timeout: number = 5000): Promise<IProxyParseResult | null> {
|
|
49
|
-
return new Promise((resolve) => {
|
|
50
|
-
let buffer = Buffer.alloc(0);
|
|
51
|
-
let resolved = false;
|
|
52
|
-
|
|
53
|
-
const cleanup = () => {
|
|
54
|
-
socket.removeListener('data', onData);
|
|
55
|
-
socket.removeListener('error', onError);
|
|
56
|
-
clearTimeout(timer);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const timer = setTimeout(() => {
|
|
60
|
-
if (!resolved) {
|
|
61
|
-
resolved = true;
|
|
62
|
-
cleanup();
|
|
63
|
-
resolve({
|
|
64
|
-
proxyInfo: null,
|
|
65
|
-
remainingData: buffer
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}, timeout);
|
|
69
|
-
|
|
70
|
-
const onData = (chunk: Buffer) => {
|
|
71
|
-
buffer = Buffer.concat([buffer, chunk]);
|
|
72
|
-
|
|
73
|
-
// Check if we have enough data
|
|
74
|
-
if (!buffer.toString('ascii', 0, Math.min(6, buffer.length)).startsWith(this.PROXY_V1_SIGNATURE)) {
|
|
75
|
-
// Not PROXY protocol
|
|
76
|
-
resolved = true;
|
|
77
|
-
cleanup();
|
|
78
|
-
resolve({
|
|
79
|
-
proxyInfo: null,
|
|
80
|
-
remainingData: buffer
|
|
81
|
-
});
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Try to parse
|
|
86
|
-
try {
|
|
87
|
-
const result = this.parse(buffer);
|
|
88
|
-
if (result.proxyInfo) {
|
|
89
|
-
// Successfully parsed
|
|
90
|
-
resolved = true;
|
|
91
|
-
cleanup();
|
|
92
|
-
resolve(result);
|
|
93
|
-
} else if (buffer.length > this.MAX_HEADER_LENGTH) {
|
|
94
|
-
// Header too long
|
|
95
|
-
resolved = true;
|
|
96
|
-
cleanup();
|
|
97
|
-
resolve({
|
|
98
|
-
proxyInfo: null,
|
|
99
|
-
remainingData: buffer
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
// Otherwise continue reading
|
|
103
|
-
} catch (error) {
|
|
104
|
-
// Parse error
|
|
105
|
-
logger.log('error', `PROXY protocol parse error: ${error.message}`);
|
|
106
|
-
resolved = true;
|
|
107
|
-
cleanup();
|
|
108
|
-
resolve({
|
|
109
|
-
proxyInfo: null,
|
|
110
|
-
remainingData: buffer
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const onError = (error: Error) => {
|
|
116
|
-
logger.log('error', `Socket error while reading PROXY protocol: ${error.message}`);
|
|
117
|
-
resolved = true;
|
|
118
|
-
cleanup();
|
|
119
|
-
resolve({
|
|
120
|
-
proxyInfo: null,
|
|
121
|
-
remainingData: buffer
|
|
122
|
-
});
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
socket.on('data', onData);
|
|
126
|
-
socket.on('error', onError);
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PROXY Protocol Parser
|
|
3
|
-
* Implementation of HAProxy PROXY protocol v1 (text format)
|
|
4
|
-
* Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { IProxyInfo, IProxyParseResult, TProxyProtocol } from './types.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* PROXY protocol parser
|
|
11
|
-
*/
|
|
12
|
-
export class ProxyProtocolParser {
|
|
13
|
-
static readonly PROXY_V1_SIGNATURE = 'PROXY ';
|
|
14
|
-
static readonly MAX_HEADER_LENGTH = 107; // Max length for v1 header
|
|
15
|
-
static readonly HEADER_TERMINATOR = '\r\n';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Parse PROXY protocol v1 header from buffer
|
|
19
|
-
* Returns proxy info and remaining data after header
|
|
20
|
-
*/
|
|
21
|
-
static parse(data: Buffer): IProxyParseResult {
|
|
22
|
-
// Check if buffer starts with PROXY signature
|
|
23
|
-
if (!data.toString('ascii', 0, 6).startsWith(this.PROXY_V1_SIGNATURE)) {
|
|
24
|
-
return {
|
|
25
|
-
proxyInfo: null,
|
|
26
|
-
remainingData: data
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Find header terminator
|
|
31
|
-
const headerEndIndex = data.indexOf(this.HEADER_TERMINATOR);
|
|
32
|
-
if (headerEndIndex === -1) {
|
|
33
|
-
// Header incomplete, need more data
|
|
34
|
-
if (data.length > this.MAX_HEADER_LENGTH) {
|
|
35
|
-
// Header too long, invalid
|
|
36
|
-
throw new Error('PROXY protocol header exceeds maximum length');
|
|
37
|
-
}
|
|
38
|
-
return {
|
|
39
|
-
proxyInfo: null,
|
|
40
|
-
remainingData: data
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Extract header line
|
|
45
|
-
const headerLine = data.toString('ascii', 0, headerEndIndex);
|
|
46
|
-
const remainingData = data.slice(headerEndIndex + 2); // Skip \r\n
|
|
47
|
-
|
|
48
|
-
// Parse header
|
|
49
|
-
const parts = headerLine.split(' ');
|
|
50
|
-
|
|
51
|
-
if (parts.length < 2) {
|
|
52
|
-
throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const [signature, protocol] = parts;
|
|
56
|
-
|
|
57
|
-
// Validate protocol
|
|
58
|
-
if (!['TCP4', 'TCP6', 'UNKNOWN'].includes(protocol)) {
|
|
59
|
-
throw new Error(`Invalid PROXY protocol: ${protocol}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// For UNKNOWN protocol, ignore addresses
|
|
63
|
-
if (protocol === 'UNKNOWN') {
|
|
64
|
-
return {
|
|
65
|
-
proxyInfo: {
|
|
66
|
-
protocol: 'UNKNOWN',
|
|
67
|
-
sourceIP: '',
|
|
68
|
-
sourcePort: 0,
|
|
69
|
-
destinationIP: '',
|
|
70
|
-
destinationPort: 0
|
|
71
|
-
},
|
|
72
|
-
remainingData
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// For TCP4/TCP6, we need all 6 parts
|
|
77
|
-
if (parts.length !== 6) {
|
|
78
|
-
throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const [, , srcIP, dstIP, srcPort, dstPort] = parts;
|
|
82
|
-
|
|
83
|
-
// Validate and parse ports
|
|
84
|
-
const sourcePort = parseInt(srcPort, 10);
|
|
85
|
-
const destinationPort = parseInt(dstPort, 10);
|
|
86
|
-
|
|
87
|
-
if (isNaN(sourcePort) || sourcePort < 0 || sourcePort > 65535) {
|
|
88
|
-
throw new Error(`Invalid source port: ${srcPort}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (isNaN(destinationPort) || destinationPort < 0 || destinationPort > 65535) {
|
|
92
|
-
throw new Error(`Invalid destination port: ${dstPort}`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Validate IP addresses
|
|
96
|
-
const protocolType = protocol as TProxyProtocol;
|
|
97
|
-
if (!this.isValidIP(srcIP, protocolType)) {
|
|
98
|
-
throw new Error(`Invalid source IP for ${protocol}: ${srcIP}`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!this.isValidIP(dstIP, protocolType)) {
|
|
102
|
-
throw new Error(`Invalid destination IP for ${protocol}: ${dstIP}`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
proxyInfo: {
|
|
107
|
-
protocol: protocolType,
|
|
108
|
-
sourceIP: srcIP,
|
|
109
|
-
sourcePort,
|
|
110
|
-
destinationIP: dstIP,
|
|
111
|
-
destinationPort
|
|
112
|
-
},
|
|
113
|
-
remainingData
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Generate PROXY protocol v1 header
|
|
119
|
-
*/
|
|
120
|
-
static generate(info: IProxyInfo): Buffer {
|
|
121
|
-
if (info.protocol === 'UNKNOWN') {
|
|
122
|
-
return Buffer.from(`PROXY UNKNOWN\r\n`, 'ascii');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const header = `PROXY ${info.protocol} ${info.sourceIP} ${info.destinationIP} ${info.sourcePort} ${info.destinationPort}\r\n`;
|
|
126
|
-
|
|
127
|
-
if (header.length > this.MAX_HEADER_LENGTH) {
|
|
128
|
-
throw new Error('Generated PROXY protocol header exceeds maximum length');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return Buffer.from(header, 'ascii');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Validate IP address format
|
|
136
|
-
*/
|
|
137
|
-
static isValidIP(ip: string, protocol: TProxyProtocol): boolean {
|
|
138
|
-
if (protocol === 'TCP4') {
|
|
139
|
-
return this.isIPv4(ip);
|
|
140
|
-
} else if (protocol === 'TCP6') {
|
|
141
|
-
return this.isIPv6(ip);
|
|
142
|
-
}
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Check if string is valid IPv4
|
|
148
|
-
*/
|
|
149
|
-
static isIPv4(ip: string): boolean {
|
|
150
|
-
const parts = ip.split('.');
|
|
151
|
-
if (parts.length !== 4) return false;
|
|
152
|
-
|
|
153
|
-
for (const part of parts) {
|
|
154
|
-
const num = parseInt(part, 10);
|
|
155
|
-
if (isNaN(num) || num < 0 || num > 255 || part !== num.toString()) {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return true;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Check if string is valid IPv6
|
|
164
|
-
*/
|
|
165
|
-
static isIPv6(ip: string): boolean {
|
|
166
|
-
// Basic IPv6 validation
|
|
167
|
-
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
|
|
168
|
-
return ipv6Regex.test(ip);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Create a connection ID string for tracking
|
|
173
|
-
*/
|
|
174
|
-
static createConnectionId(connectionInfo: {
|
|
175
|
-
sourceIp?: string;
|
|
176
|
-
sourcePort?: number;
|
|
177
|
-
destIp?: string;
|
|
178
|
-
destPort?: number;
|
|
179
|
-
}): string {
|
|
180
|
-
const { sourceIp, sourcePort, destIp, destPort } = connectionInfo;
|
|
181
|
-
return `${sourceIp}:${sourcePort}-${destIp}:${destPort}`;
|
|
182
|
-
}
|
|
183
|
-
}
|