@push.rocks/smartproxy 20.0.1 → 21.1.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 +26 -0
- package/dist_ts/core/utils/proxy-protocol.d.ts +5 -17
- package/dist_ts/core/utils/proxy-protocol.js +13 -97
- package/dist_ts/core/utils/websocket-utils.d.ts +6 -7
- package/dist_ts/core/utils/websocket-utils.js +10 -66
- package/dist_ts/detection/detectors/http-detector-v2.d.ts +33 -0
- package/dist_ts/detection/detectors/http-detector-v2.js +87 -0
- package/dist_ts/detection/detectors/http-detector.d.ts +33 -0
- package/dist_ts/detection/detectors/http-detector.js +89 -0
- package/dist_ts/detection/detectors/quick-detector.d.ts +28 -0
- package/dist_ts/detection/detectors/quick-detector.js +131 -0
- package/dist_ts/detection/detectors/routing-extractor.d.ts +28 -0
- package/dist_ts/detection/detectors/routing-extractor.js +122 -0
- package/dist_ts/detection/detectors/tls-detector-v2.d.ts +33 -0
- package/dist_ts/detection/detectors/tls-detector-v2.js +80 -0
- package/dist_ts/detection/detectors/tls-detector.d.ts +33 -0
- package/dist_ts/detection/detectors/tls-detector.js +106 -0
- package/dist_ts/detection/index.d.ts +17 -0
- package/dist_ts/detection/index.js +22 -0
- package/dist_ts/detection/models/detection-types.d.ts +87 -0
- package/dist_ts/detection/models/detection-types.js +5 -0
- package/dist_ts/detection/models/interfaces.d.ts +97 -0
- package/dist_ts/detection/models/interfaces.js +5 -0
- package/dist_ts/detection/protocol-detector-v2.d.ts +46 -0
- package/dist_ts/detection/protocol-detector-v2.js +116 -0
- package/dist_ts/detection/protocol-detector.d.ts +74 -0
- package/dist_ts/detection/protocol-detector.js +173 -0
- package/dist_ts/detection/utils/buffer-utils.d.ts +61 -0
- package/dist_ts/detection/utils/buffer-utils.js +127 -0
- package/dist_ts/detection/utils/fragment-manager.d.ts +31 -0
- package/dist_ts/detection/utils/fragment-manager.js +53 -0
- package/dist_ts/detection/utils/parser-utils.d.ts +42 -0
- package/dist_ts/detection/utils/parser-utils.js +63 -0
- package/dist_ts/index.d.ts +2 -1
- package/dist_ts/index.js +3 -2
- package/dist_ts/protocols/common/fragment-handler.d.ts +73 -0
- package/dist_ts/protocols/common/fragment-handler.js +117 -0
- package/dist_ts/protocols/common/index.d.ts +7 -0
- package/dist_ts/protocols/common/index.js +8 -0
- package/dist_ts/protocols/common/types.d.ts +68 -0
- package/dist_ts/protocols/common/types.js +7 -0
- package/dist_ts/protocols/http/constants.d.ts +119 -0
- package/dist_ts/protocols/http/constants.js +200 -0
- package/dist_ts/protocols/http/index.d.ts +7 -0
- package/dist_ts/protocols/http/index.js +8 -0
- package/dist_ts/protocols/http/parser.d.ts +58 -0
- package/dist_ts/protocols/http/parser.js +184 -0
- package/dist_ts/protocols/http/types.d.ts +62 -0
- package/dist_ts/protocols/http/types.js +5 -0
- package/dist_ts/protocols/index.d.ts +11 -0
- package/dist_ts/protocols/index.js +12 -0
- package/dist_ts/protocols/proxy/index.d.ts +6 -0
- package/dist_ts/protocols/proxy/index.js +7 -0
- package/dist_ts/protocols/proxy/parser.d.ts +44 -0
- package/dist_ts/protocols/proxy/parser.js +153 -0
- package/dist_ts/protocols/proxy/types.d.ts +47 -0
- package/dist_ts/protocols/proxy/types.js +6 -0
- package/dist_ts/protocols/tls/alerts/index.d.ts +4 -0
- package/dist_ts/protocols/tls/alerts/index.js +5 -0
- package/dist_ts/protocols/tls/alerts/tls-alert.d.ts +150 -0
- package/dist_ts/protocols/tls/alerts/tls-alert.js +226 -0
- package/dist_ts/protocols/tls/constants.d.ts +122 -0
- package/dist_ts/protocols/tls/constants.js +135 -0
- package/dist_ts/protocols/tls/index.d.ts +12 -0
- package/dist_ts/protocols/tls/index.js +27 -0
- package/dist_ts/protocols/tls/parser.d.ts +53 -0
- package/dist_ts/protocols/tls/parser.js +294 -0
- package/dist_ts/protocols/tls/sni/client-hello-parser.d.ts +100 -0
- package/dist_ts/protocols/tls/sni/client-hello-parser.js +463 -0
- package/dist_ts/protocols/tls/sni/index.d.ts +5 -0
- package/dist_ts/protocols/tls/sni/index.js +6 -0
- package/dist_ts/protocols/tls/sni/sni-extraction.d.ts +58 -0
- package/dist_ts/protocols/tls/sni/sni-extraction.js +275 -0
- package/dist_ts/protocols/tls/types.d.ts +65 -0
- package/dist_ts/protocols/tls/types.js +5 -0
- package/dist_ts/protocols/tls/utils/index.d.ts +4 -0
- package/dist_ts/protocols/tls/utils/index.js +5 -0
- package/dist_ts/protocols/tls/utils/tls-utils.d.ts +158 -0
- package/dist_ts/protocols/tls/utils/tls-utils.js +187 -0
- package/dist_ts/protocols/websocket/constants.d.ts +55 -0
- package/dist_ts/protocols/websocket/constants.js +58 -0
- package/dist_ts/protocols/websocket/index.d.ts +7 -0
- package/dist_ts/protocols/websocket/index.js +8 -0
- package/dist_ts/protocols/websocket/types.d.ts +47 -0
- package/dist_ts/protocols/websocket/types.js +5 -0
- package/dist_ts/protocols/websocket/utils.d.ts +25 -0
- package/dist_ts/protocols/websocket/utils.js +103 -0
- package/dist_ts/proxies/http-proxy/models/http-types.d.ts +25 -27
- package/dist_ts/proxies/http-proxy/models/http-types.js +24 -44
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +81 -61
- package/dist_ts/proxies/smart-proxy/tls-manager.js +2 -1
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -2
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +112 -8
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +231 -76
- package/dist_ts/tls/index.d.ts +5 -7
- package/dist_ts/tls/index.js +8 -11
- package/dist_ts/tls/sni/client-hello-parser.js +3 -2
- package/dist_ts/tls/sni/sni-handler.js +4 -4
- package/dist_ts/tls/utils/tls-utils.d.ts +1 -110
- package/dist_ts/tls/utils/tls-utils.js +4 -116
- package/package.json +17 -8
- package/readme.md +471 -2345
- package/readme.plan.md +0 -0
- package/ts/core/utils/proxy-protocol.ts +14 -131
- package/ts/core/utils/websocket-utils.ts +12 -60
- package/ts/detection/detectors/http-detector.ts +114 -0
- package/ts/detection/detectors/quick-detector.ts +148 -0
- package/ts/detection/detectors/routing-extractor.ts +147 -0
- package/ts/detection/detectors/tls-detector.ts +120 -0
- package/ts/detection/index.ts +25 -0
- package/ts/detection/models/detection-types.ts +102 -0
- package/ts/detection/models/interfaces.ts +115 -0
- package/ts/detection/protocol-detector.ts +230 -0
- package/ts/detection/utils/buffer-utils.ts +141 -0
- package/ts/detection/utils/fragment-manager.ts +64 -0
- package/ts/detection/utils/parser-utils.ts +77 -0
- package/ts/index.ts +3 -2
- package/ts/protocols/common/fragment-handler.ts +163 -0
- package/ts/protocols/common/index.ts +8 -0
- package/ts/protocols/common/types.ts +76 -0
- package/ts/protocols/http/constants.ts +219 -0
- package/ts/protocols/http/index.ts +8 -0
- package/ts/protocols/http/parser.ts +219 -0
- package/ts/protocols/http/types.ts +70 -0
- package/ts/protocols/index.ts +12 -0
- package/ts/protocols/proxy/index.ts +7 -0
- package/ts/protocols/proxy/parser.ts +183 -0
- package/ts/protocols/proxy/types.ts +53 -0
- package/ts/{tls → protocols/tls}/alerts/tls-alert.ts +1 -1
- package/ts/protocols/tls/index.ts +37 -0
- package/ts/protocols/tls/sni/index.ts +6 -0
- package/ts/{tls → protocols/tls}/utils/tls-utils.ts +1 -1
- package/ts/protocols/websocket/constants.ts +60 -0
- package/ts/protocols/websocket/index.ts +8 -0
- package/ts/protocols/websocket/types.ts +53 -0
- package/ts/protocols/websocket/utils.ts +98 -0
- package/ts/proxies/http-proxy/models/http-types.ts +29 -46
- package/ts/proxies/smart-proxy/models/interfaces.ts +7 -1
- package/ts/proxies/smart-proxy/models/route-types.ts +0 -1
- package/ts/proxies/smart-proxy/route-connection-handler.ts +91 -68
- package/ts/proxies/smart-proxy/tls-manager.ts +1 -0
- package/ts/proxies/smart-proxy/utils/index.ts +2 -13
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +323 -86
- package/ts/tls/index.ts +8 -12
- package/ts/tls/sni/sni-handler.ts +3 -3
- package/ts/forwarding/config/forwarding-types.ts +0 -76
- package/ts/forwarding/config/index.ts +0 -26
- package/ts/forwarding/factory/forwarding-factory.ts +0 -189
- package/ts/forwarding/factory/index.ts +0 -5
- package/ts/forwarding/handlers/base-handler.ts +0 -155
- package/ts/forwarding/handlers/http-handler.ts +0 -163
- package/ts/forwarding/handlers/https-passthrough-handler.ts +0 -185
- package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +0 -312
- package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +0 -297
- package/ts/forwarding/handlers/index.ts +0 -9
- package/ts/forwarding/index.ts +0 -35
- package/ts/proxies/smart-proxy/utils/route-patterns.ts +0 -403
- /package/ts/{tls → protocols/tls}/alerts/index.ts +0 -0
- /package/ts/{tls → protocols/tls}/sni/client-hello-parser.ts +0 -0
- /package/ts/{tls → protocols/tls}/sni/sni-extraction.ts +0 -0
- /package/ts/{tls → protocols/tls}/utils/index.ts +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROXY Protocol Type Definitions
|
|
3
|
+
* Based on HAProxy PROXY protocol specification
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* PROXY protocol version
|
|
8
|
+
*/
|
|
9
|
+
export type TProxyProtocolVersion = 'v1' | 'v2';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Connection protocol type
|
|
13
|
+
*/
|
|
14
|
+
export type TProxyProtocol = 'TCP4' | 'TCP6' | 'UNKNOWN';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Interface representing parsed PROXY protocol information
|
|
18
|
+
*/
|
|
19
|
+
export interface IProxyInfo {
|
|
20
|
+
protocol: TProxyProtocol;
|
|
21
|
+
sourceIP: string;
|
|
22
|
+
sourcePort: number;
|
|
23
|
+
destinationIP: string;
|
|
24
|
+
destinationPort: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Interface for parse result including remaining data
|
|
29
|
+
*/
|
|
30
|
+
export interface IProxyParseResult {
|
|
31
|
+
proxyInfo: IProxyInfo | null;
|
|
32
|
+
remainingData: Buffer;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* PROXY protocol v2 header format
|
|
37
|
+
*/
|
|
38
|
+
export interface IProxyV2Header {
|
|
39
|
+
signature: Buffer;
|
|
40
|
+
versionCommand: number;
|
|
41
|
+
family: number;
|
|
42
|
+
length: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Connection information for PROXY protocol
|
|
47
|
+
*/
|
|
48
|
+
export interface IProxyConnectionInfo {
|
|
49
|
+
sourceIp?: string;
|
|
50
|
+
sourcePort?: number;
|
|
51
|
+
destIp?: string;
|
|
52
|
+
destPort?: number;
|
|
53
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TLS Protocol Module
|
|
3
|
+
* Contains generic TLS protocol knowledge including parsers, constants, and utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Export all sub-modules
|
|
7
|
+
export * from './alerts/index.js';
|
|
8
|
+
export * from './sni/index.js';
|
|
9
|
+
export * from './utils/index.js';
|
|
10
|
+
|
|
11
|
+
// Re-export main utilities and types for convenience
|
|
12
|
+
export {
|
|
13
|
+
TlsUtils,
|
|
14
|
+
TlsRecordType,
|
|
15
|
+
TlsHandshakeType,
|
|
16
|
+
TlsExtensionType,
|
|
17
|
+
TlsAlertLevel,
|
|
18
|
+
TlsAlertDescription,
|
|
19
|
+
TlsVersion
|
|
20
|
+
} from './utils/tls-utils.js';
|
|
21
|
+
export { TlsAlert } from './alerts/tls-alert.js';
|
|
22
|
+
export { ClientHelloParser } from './sni/client-hello-parser.js';
|
|
23
|
+
export { SniExtraction } from './sni/sni-extraction.js';
|
|
24
|
+
|
|
25
|
+
// Export tlsVersionToString helper
|
|
26
|
+
export function tlsVersionToString(major: number, minor: number): string | null {
|
|
27
|
+
if (major === 0x03) {
|
|
28
|
+
switch (minor) {
|
|
29
|
+
case 0x00: return 'SSLv3';
|
|
30
|
+
case 0x01: return 'TLSv1.0';
|
|
31
|
+
case 0x02: return 'TLSv1.1';
|
|
32
|
+
case 0x03: return 'TLSv1.2';
|
|
33
|
+
case 0x04: return 'TLSv1.3';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Protocol Constants
|
|
3
|
+
* Based on RFC 6455
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WebSocket opcode types
|
|
8
|
+
*/
|
|
9
|
+
export enum WebSocketOpcode {
|
|
10
|
+
CONTINUATION = 0x0,
|
|
11
|
+
TEXT = 0x1,
|
|
12
|
+
BINARY = 0x2,
|
|
13
|
+
CLOSE = 0x8,
|
|
14
|
+
PING = 0x9,
|
|
15
|
+
PONG = 0xa,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* WebSocket close codes
|
|
20
|
+
*/
|
|
21
|
+
export enum WebSocketCloseCode {
|
|
22
|
+
NORMAL_CLOSURE = 1000,
|
|
23
|
+
GOING_AWAY = 1001,
|
|
24
|
+
PROTOCOL_ERROR = 1002,
|
|
25
|
+
UNSUPPORTED_DATA = 1003,
|
|
26
|
+
NO_STATUS_RECEIVED = 1005,
|
|
27
|
+
ABNORMAL_CLOSURE = 1006,
|
|
28
|
+
INVALID_FRAME_PAYLOAD_DATA = 1007,
|
|
29
|
+
POLICY_VIOLATION = 1008,
|
|
30
|
+
MESSAGE_TOO_BIG = 1009,
|
|
31
|
+
MISSING_EXTENSION = 1010,
|
|
32
|
+
INTERNAL_ERROR = 1011,
|
|
33
|
+
SERVICE_RESTART = 1012,
|
|
34
|
+
TRY_AGAIN_LATER = 1013,
|
|
35
|
+
BAD_GATEWAY = 1014,
|
|
36
|
+
TLS_HANDSHAKE = 1015,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* WebSocket protocol version
|
|
41
|
+
*/
|
|
42
|
+
export const WEBSOCKET_VERSION = 13;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* WebSocket magic string for handshake
|
|
46
|
+
*/
|
|
47
|
+
export const WEBSOCKET_MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* WebSocket headers
|
|
51
|
+
*/
|
|
52
|
+
export const WEBSOCKET_HEADERS = {
|
|
53
|
+
UPGRADE: 'upgrade',
|
|
54
|
+
CONNECTION: 'connection',
|
|
55
|
+
SEC_WEBSOCKET_KEY: 'sec-websocket-key',
|
|
56
|
+
SEC_WEBSOCKET_VERSION: 'sec-websocket-version',
|
|
57
|
+
SEC_WEBSOCKET_ACCEPT: 'sec-websocket-accept',
|
|
58
|
+
SEC_WEBSOCKET_PROTOCOL: 'sec-websocket-protocol',
|
|
59
|
+
SEC_WEBSOCKET_EXTENSIONS: 'sec-websocket-extensions',
|
|
60
|
+
} as const;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Protocol Type Definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { WebSocketOpcode, WebSocketCloseCode } from './constants.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* WebSocket frame header
|
|
9
|
+
*/
|
|
10
|
+
export interface IWebSocketFrameHeader {
|
|
11
|
+
fin: boolean;
|
|
12
|
+
rsv1: boolean;
|
|
13
|
+
rsv2: boolean;
|
|
14
|
+
rsv3: boolean;
|
|
15
|
+
opcode: WebSocketOpcode;
|
|
16
|
+
masked: boolean;
|
|
17
|
+
payloadLength: number;
|
|
18
|
+
maskingKey?: Buffer;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* WebSocket frame
|
|
23
|
+
*/
|
|
24
|
+
export interface IWebSocketFrame {
|
|
25
|
+
header: IWebSocketFrameHeader;
|
|
26
|
+
payload: Buffer;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* WebSocket close frame payload
|
|
31
|
+
*/
|
|
32
|
+
export interface IWebSocketClosePayload {
|
|
33
|
+
code: WebSocketCloseCode;
|
|
34
|
+
reason?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* WebSocket handshake request headers
|
|
39
|
+
*/
|
|
40
|
+
export interface IWebSocketHandshakeHeaders {
|
|
41
|
+
upgrade: string;
|
|
42
|
+
connection: string;
|
|
43
|
+
'sec-websocket-key': string;
|
|
44
|
+
'sec-websocket-version': string;
|
|
45
|
+
'sec-websocket-protocol'?: string;
|
|
46
|
+
'sec-websocket-extensions'?: string;
|
|
47
|
+
[key: string]: string | undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Type for WebSocket raw data (matching ws library)
|
|
52
|
+
*/
|
|
53
|
+
export type RawData = Buffer | ArrayBuffer | Buffer[] | any;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Protocol Utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as crypto from 'crypto';
|
|
6
|
+
import { WEBSOCKET_MAGIC_STRING } from './constants.js';
|
|
7
|
+
import type { RawData } from './types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get the length of a WebSocket message regardless of its type
|
|
11
|
+
* (handles all possible WebSocket message data types)
|
|
12
|
+
*/
|
|
13
|
+
export function getMessageSize(data: RawData): number {
|
|
14
|
+
if (typeof data === 'string') {
|
|
15
|
+
// For string data, get the byte length
|
|
16
|
+
return Buffer.from(data, 'utf8').length;
|
|
17
|
+
} else if (data instanceof Buffer) {
|
|
18
|
+
// For Node.js Buffer
|
|
19
|
+
return data.length;
|
|
20
|
+
} else if (data instanceof ArrayBuffer) {
|
|
21
|
+
// For ArrayBuffer
|
|
22
|
+
return data.byteLength;
|
|
23
|
+
} else if (Array.isArray(data)) {
|
|
24
|
+
// For array of buffers, sum their lengths
|
|
25
|
+
return data.reduce((sum, chunk) => {
|
|
26
|
+
if (chunk instanceof Buffer) {
|
|
27
|
+
return sum + chunk.length;
|
|
28
|
+
} else if (chunk instanceof ArrayBuffer) {
|
|
29
|
+
return sum + chunk.byteLength;
|
|
30
|
+
}
|
|
31
|
+
return sum;
|
|
32
|
+
}, 0);
|
|
33
|
+
} else {
|
|
34
|
+
// For other types, try to determine the size or return 0
|
|
35
|
+
try {
|
|
36
|
+
return Buffer.from(data).length;
|
|
37
|
+
} catch (e) {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert any raw WebSocket data to Buffer for consistent handling
|
|
45
|
+
*/
|
|
46
|
+
export function toBuffer(data: RawData): Buffer {
|
|
47
|
+
if (typeof data === 'string') {
|
|
48
|
+
return Buffer.from(data, 'utf8');
|
|
49
|
+
} else if (data instanceof Buffer) {
|
|
50
|
+
return data;
|
|
51
|
+
} else if (data instanceof ArrayBuffer) {
|
|
52
|
+
return Buffer.from(data);
|
|
53
|
+
} else if (Array.isArray(data)) {
|
|
54
|
+
// For array of buffers, concatenate them
|
|
55
|
+
return Buffer.concat(data.map(chunk => {
|
|
56
|
+
if (chunk instanceof Buffer) {
|
|
57
|
+
return chunk;
|
|
58
|
+
} else if (chunk instanceof ArrayBuffer) {
|
|
59
|
+
return Buffer.from(chunk);
|
|
60
|
+
}
|
|
61
|
+
return Buffer.from(chunk);
|
|
62
|
+
}));
|
|
63
|
+
} else {
|
|
64
|
+
// For other types, try to convert to Buffer or return empty Buffer
|
|
65
|
+
try {
|
|
66
|
+
return Buffer.from(data);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
return Buffer.alloc(0);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Generate WebSocket accept key from client key
|
|
75
|
+
*/
|
|
76
|
+
export function generateAcceptKey(clientKey: string): string {
|
|
77
|
+
const hash = crypto.createHash('sha1');
|
|
78
|
+
hash.update(clientKey + WEBSOCKET_MAGIC_STRING);
|
|
79
|
+
return hash.digest('base64');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Validate WebSocket upgrade request
|
|
84
|
+
*/
|
|
85
|
+
export function isWebSocketUpgrade(headers: Record<string, string>): boolean {
|
|
86
|
+
const upgrade = headers['upgrade'];
|
|
87
|
+
const connection = headers['connection'];
|
|
88
|
+
|
|
89
|
+
return upgrade?.toLowerCase() === 'websocket' &&
|
|
90
|
+
connection?.toLowerCase().includes('upgrade');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generate random WebSocket key for client handshake
|
|
95
|
+
*/
|
|
96
|
+
export function generateWebSocketKey(): string {
|
|
97
|
+
return crypto.randomBytes(16).toString('base64');
|
|
98
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../../plugins.js';
|
|
2
|
+
// Import from protocols for consistent status codes
|
|
3
|
+
import { HttpStatus as ProtocolHttpStatus, getStatusText as getProtocolStatusText } from '../../../protocols/http/index.js';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* HTTP-specific event types
|
|
@@ -10,34 +12,33 @@ export enum HttpEvents {
|
|
|
10
12
|
REQUEST_ERROR = 'request-error',
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
15
|
+
|
|
16
|
+
// Re-export for backward compatibility with subset of commonly used codes
|
|
17
|
+
export const HttpStatus = {
|
|
18
|
+
OK: ProtocolHttpStatus.OK,
|
|
19
|
+
MOVED_PERMANENTLY: ProtocolHttpStatus.MOVED_PERMANENTLY,
|
|
20
|
+
FOUND: ProtocolHttpStatus.FOUND,
|
|
21
|
+
TEMPORARY_REDIRECT: ProtocolHttpStatus.TEMPORARY_REDIRECT,
|
|
22
|
+
PERMANENT_REDIRECT: ProtocolHttpStatus.PERMANENT_REDIRECT,
|
|
23
|
+
BAD_REQUEST: ProtocolHttpStatus.BAD_REQUEST,
|
|
24
|
+
UNAUTHORIZED: ProtocolHttpStatus.UNAUTHORIZED,
|
|
25
|
+
FORBIDDEN: ProtocolHttpStatus.FORBIDDEN,
|
|
26
|
+
NOT_FOUND: ProtocolHttpStatus.NOT_FOUND,
|
|
27
|
+
METHOD_NOT_ALLOWED: ProtocolHttpStatus.METHOD_NOT_ALLOWED,
|
|
28
|
+
REQUEST_TIMEOUT: ProtocolHttpStatus.REQUEST_TIMEOUT,
|
|
29
|
+
TOO_MANY_REQUESTS: ProtocolHttpStatus.TOO_MANY_REQUESTS,
|
|
30
|
+
INTERNAL_SERVER_ERROR: ProtocolHttpStatus.INTERNAL_SERVER_ERROR,
|
|
31
|
+
NOT_IMPLEMENTED: ProtocolHttpStatus.NOT_IMPLEMENTED,
|
|
32
|
+
BAD_GATEWAY: ProtocolHttpStatus.BAD_GATEWAY,
|
|
33
|
+
SERVICE_UNAVAILABLE: ProtocolHttpStatus.SERVICE_UNAVAILABLE,
|
|
34
|
+
GATEWAY_TIMEOUT: ProtocolHttpStatus.GATEWAY_TIMEOUT,
|
|
35
|
+
} as const;
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Base error class for HTTP-related errors
|
|
38
39
|
*/
|
|
39
40
|
export class HttpError extends Error {
|
|
40
|
-
constructor(message: string, public readonly statusCode:
|
|
41
|
+
constructor(message: string, public readonly statusCode: number = HttpStatus.INTERNAL_SERVER_ERROR) {
|
|
41
42
|
super(message);
|
|
42
43
|
this.name = 'HttpError';
|
|
43
44
|
}
|
|
@@ -61,7 +62,7 @@ export class CertificateError extends HttpError {
|
|
|
61
62
|
* Error related to server operations
|
|
62
63
|
*/
|
|
63
64
|
export class ServerError extends HttpError {
|
|
64
|
-
constructor(message: string, public readonly code?: string, statusCode:
|
|
65
|
+
constructor(message: string, public readonly code?: string, statusCode: number = HttpStatus.INTERNAL_SERVER_ERROR) {
|
|
65
66
|
super(message, statusCode);
|
|
66
67
|
this.name = 'ServerError';
|
|
67
68
|
}
|
|
@@ -93,7 +94,7 @@ export class NotFoundError extends HttpError {
|
|
|
93
94
|
export interface IRedirectConfig {
|
|
94
95
|
source: string; // Source path or pattern
|
|
95
96
|
destination: string; // Destination URL
|
|
96
|
-
type:
|
|
97
|
+
type: number; // Redirect status code
|
|
97
98
|
preserveQuery?: boolean; // Whether to preserve query parameters
|
|
98
99
|
}
|
|
99
100
|
|
|
@@ -115,30 +116,12 @@ export interface IRouterConfig {
|
|
|
115
116
|
*/
|
|
116
117
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
|
|
117
118
|
|
|
119
|
+
|
|
118
120
|
/**
|
|
119
121
|
* Helper function to get HTTP status text
|
|
120
122
|
*/
|
|
121
|
-
export function getStatusText(status:
|
|
122
|
-
|
|
123
|
-
[HttpStatus.OK]: 'OK',
|
|
124
|
-
[HttpStatus.MOVED_PERMANENTLY]: 'Moved Permanently',
|
|
125
|
-
[HttpStatus.FOUND]: 'Found',
|
|
126
|
-
[HttpStatus.TEMPORARY_REDIRECT]: 'Temporary Redirect',
|
|
127
|
-
[HttpStatus.PERMANENT_REDIRECT]: 'Permanent Redirect',
|
|
128
|
-
[HttpStatus.BAD_REQUEST]: 'Bad Request',
|
|
129
|
-
[HttpStatus.UNAUTHORIZED]: 'Unauthorized',
|
|
130
|
-
[HttpStatus.FORBIDDEN]: 'Forbidden',
|
|
131
|
-
[HttpStatus.NOT_FOUND]: 'Not Found',
|
|
132
|
-
[HttpStatus.METHOD_NOT_ALLOWED]: 'Method Not Allowed',
|
|
133
|
-
[HttpStatus.REQUEST_TIMEOUT]: 'Request Timeout',
|
|
134
|
-
[HttpStatus.TOO_MANY_REQUESTS]: 'Too Many Requests',
|
|
135
|
-
[HttpStatus.INTERNAL_SERVER_ERROR]: 'Internal Server Error',
|
|
136
|
-
[HttpStatus.NOT_IMPLEMENTED]: 'Not Implemented',
|
|
137
|
-
[HttpStatus.BAD_GATEWAY]: 'Bad Gateway',
|
|
138
|
-
[HttpStatus.SERVICE_UNAVAILABLE]: 'Service Unavailable',
|
|
139
|
-
[HttpStatus.GATEWAY_TIMEOUT]: 'Gateway Timeout',
|
|
140
|
-
};
|
|
141
|
-
return statusTexts[status] || 'Unknown';
|
|
123
|
+
export function getStatusText(status: number): string {
|
|
124
|
+
return getProtocolStatusText(status as ProtocolHttpStatus);
|
|
142
125
|
}
|
|
143
126
|
|
|
144
127
|
// Legacy interfaces for backward compatibility
|
|
@@ -16,7 +16,6 @@ export interface IAcmeOptions {
|
|
|
16
16
|
routeForwards?: any[];
|
|
17
17
|
}
|
|
18
18
|
import type { IRouteConfig } from './route-types.js';
|
|
19
|
-
import type { TForwardingType } from '../../../forwarding/config/forwarding-types.js';
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* Provision object for static or HTTP-01 certificate
|
|
@@ -196,4 +195,11 @@ export interface IConnectionRecord {
|
|
|
196
195
|
|
|
197
196
|
// NFTables tracking
|
|
198
197
|
nftablesHandled?: boolean; // Whether this connection is being handled by NFTables at kernel level
|
|
198
|
+
|
|
199
|
+
// HTTP-specific information (extracted from protocol detection)
|
|
200
|
+
httpInfo?: {
|
|
201
|
+
method?: string;
|
|
202
|
+
path?: string;
|
|
203
|
+
headers?: Record<string, string>;
|
|
204
|
+
};
|
|
199
205
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as plugins from '../../../plugins.js';
|
|
2
2
|
// Certificate types removed - use local definition
|
|
3
|
-
import type { TForwardingType } from '../../../forwarding/config/forwarding-types.js';
|
|
4
3
|
import type { PortRange } from '../../../proxies/nftables-proxy/models/interfaces.js';
|
|
5
4
|
import type { IRouteContext } from '../../../core/models/route-context.js';
|
|
6
5
|
|