@push.rocks/smartproxy 21.1.7 → 22.6.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.
Files changed (155) hide show
  1. package/changelog.md +109 -0
  2. package/dist_rust/rustproxy +0 -0
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/core/utils/shared-security-manager.d.ts +17 -0
  5. package/dist_ts/core/utils/shared-security-manager.js +66 -1
  6. package/dist_ts/index.d.ts +1 -5
  7. package/dist_ts/index.js +3 -9
  8. package/dist_ts/protocols/common/fragment-handler.js +5 -1
  9. package/dist_ts/proxies/http-proxy/default-certificates.d.ts +54 -0
  10. package/dist_ts/proxies/http-proxy/default-certificates.js +127 -0
  11. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +1 -1
  12. package/dist_ts/proxies/http-proxy/http-proxy.js +9 -14
  13. package/dist_ts/proxies/http-proxy/index.d.ts +5 -1
  14. package/dist_ts/proxies/http-proxy/index.js +6 -2
  15. package/dist_ts/proxies/http-proxy/security-manager.d.ts +4 -12
  16. package/dist_ts/proxies/http-proxy/security-manager.js +66 -99
  17. package/dist_ts/proxies/index.d.ts +1 -5
  18. package/dist_ts/proxies/index.js +2 -6
  19. package/dist_ts/proxies/nftables-proxy/index.d.ts +1 -0
  20. package/dist_ts/proxies/nftables-proxy/index.js +2 -1
  21. package/dist_ts/proxies/nftables-proxy/nftables-proxy.d.ts +4 -26
  22. package/dist_ts/proxies/nftables-proxy/nftables-proxy.js +84 -236
  23. package/dist_ts/proxies/nftables-proxy/utils/index.d.ts +9 -0
  24. package/dist_ts/proxies/nftables-proxy/utils/index.js +12 -0
  25. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.d.ts +66 -0
  26. package/dist_ts/proxies/nftables-proxy/utils/nft-command-executor.js +131 -0
  27. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.d.ts +39 -0
  28. package/dist_ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.js +112 -0
  29. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.d.ts +59 -0
  30. package/dist_ts/proxies/nftables-proxy/utils/nft-rule-validator.js +130 -0
  31. package/dist_ts/proxies/smart-proxy/certificate-manager.js +4 -3
  32. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +13 -2
  33. package/dist_ts/proxies/smart-proxy/connection-manager.js +16 -6
  34. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +35 -10
  35. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
  36. package/dist_ts/proxies/smart-proxy/index.js +7 -13
  37. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -3
  38. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +17 -0
  39. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +72 -9
  40. package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
  41. package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
  42. package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
  43. package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
  44. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
  45. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
  46. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
  47. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
  48. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +14 -12
  49. package/dist_ts/proxies/smart-proxy/security-manager.js +80 -74
  50. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
  51. package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -622
  52. package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
  53. package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
  54. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +2 -9
  55. package/dist_ts/proxies/smart-proxy/tls-manager.js +3 -26
  56. package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -1
  57. package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
  58. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.d.ts +49 -0
  59. package/dist_ts/proxies/smart-proxy/utils/route-helpers/api-helpers.js +108 -0
  60. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.d.ts +57 -0
  61. package/dist_ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.js +89 -0
  62. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.d.ts +17 -0
  63. package/dist_ts/proxies/smart-proxy/utils/route-helpers/http-helpers.js +32 -0
  64. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.d.ts +68 -0
  65. package/dist_ts/proxies/smart-proxy/utils/route-helpers/https-helpers.js +117 -0
  66. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.d.ts +17 -0
  67. package/dist_ts/proxies/smart-proxy/utils/route-helpers/index.js +27 -0
  68. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.d.ts +63 -0
  69. package/dist_ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.js +105 -0
  70. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.d.ts +83 -0
  71. package/dist_ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.js +126 -0
  72. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.d.ts +47 -0
  73. package/dist_ts/proxies/smart-proxy/utils/route-helpers/security-helpers.js +66 -0
  74. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.d.ts +70 -0
  75. package/dist_ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.js +287 -0
  76. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.d.ts +46 -0
  77. package/dist_ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.js +67 -0
  78. package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +4 -457
  79. package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +6 -950
  80. package/dist_ts/proxies/smart-proxy/utils/route-utils.js +2 -2
  81. package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +67 -1
  82. package/dist_ts/proxies/smart-proxy/utils/route-validator.js +251 -3
  83. package/dist_ts/routing/index.d.ts +1 -1
  84. package/dist_ts/routing/index.js +3 -3
  85. package/dist_ts/routing/models/http-types.d.ts +119 -4
  86. package/dist_ts/routing/models/http-types.js +93 -5
  87. package/npmextra.json +12 -6
  88. package/package.json +34 -24
  89. package/readme.hints.md +184 -1
  90. package/readme.md +580 -266
  91. package/ts/00_commitinfo_data.ts +1 -1
  92. package/ts/core/utils/shared-security-manager.ts +98 -13
  93. package/ts/index.ts +4 -12
  94. package/ts/protocols/common/fragment-handler.ts +4 -0
  95. package/ts/proxies/index.ts +1 -9
  96. package/ts/proxies/nftables-proxy/index.ts +1 -0
  97. package/ts/proxies/nftables-proxy/nftables-proxy.ts +116 -290
  98. package/ts/proxies/nftables-proxy/utils/index.ts +38 -0
  99. package/ts/proxies/nftables-proxy/utils/nft-command-executor.ts +162 -0
  100. package/ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts +125 -0
  101. package/ts/proxies/nftables-proxy/utils/nft-rule-validator.ts +156 -0
  102. package/ts/proxies/smart-proxy/index.ts +6 -13
  103. package/ts/proxies/smart-proxy/models/interfaces.ts +6 -5
  104. package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
  105. package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
  106. package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
  107. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
  108. package/ts/proxies/smart-proxy/smart-proxy.ts +282 -800
  109. package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
  110. package/ts/proxies/smart-proxy/utils/index.ts +3 -5
  111. package/ts/proxies/smart-proxy/utils/route-helpers/api-helpers.ts +144 -0
  112. package/ts/proxies/smart-proxy/utils/route-helpers/dynamic-helpers.ts +124 -0
  113. package/ts/proxies/smart-proxy/utils/route-helpers/http-helpers.ts +40 -0
  114. package/ts/proxies/smart-proxy/utils/route-helpers/https-helpers.ts +163 -0
  115. package/ts/proxies/smart-proxy/utils/route-helpers/index.ts +62 -0
  116. package/ts/proxies/smart-proxy/utils/route-helpers/load-balancer-helpers.ts +154 -0
  117. package/ts/proxies/smart-proxy/utils/route-helpers/nftables-helpers.ts +202 -0
  118. package/ts/proxies/smart-proxy/utils/route-helpers/security-helpers.ts +96 -0
  119. package/ts/proxies/smart-proxy/utils/route-helpers/socket-handlers.ts +337 -0
  120. package/ts/proxies/smart-proxy/utils/route-helpers/websocket-helpers.ts +98 -0
  121. package/ts/proxies/smart-proxy/utils/route-helpers.ts +5 -1302
  122. package/ts/proxies/smart-proxy/utils/route-utils.ts +1 -1
  123. package/ts/proxies/smart-proxy/utils/route-validator.ts +274 -4
  124. package/ts/routing/index.ts +2 -2
  125. package/ts/routing/models/http-types.ts +147 -4
  126. package/ts/proxies/http-proxy/certificate-manager.ts +0 -244
  127. package/ts/proxies/http-proxy/connection-pool.ts +0 -228
  128. package/ts/proxies/http-proxy/context-creator.ts +0 -145
  129. package/ts/proxies/http-proxy/function-cache.ts +0 -279
  130. package/ts/proxies/http-proxy/handlers/index.ts +0 -5
  131. package/ts/proxies/http-proxy/http-proxy.ts +0 -675
  132. package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
  133. package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
  134. package/ts/proxies/http-proxy/index.ts +0 -13
  135. package/ts/proxies/http-proxy/models/http-types.ts +0 -148
  136. package/ts/proxies/http-proxy/models/index.ts +0 -5
  137. package/ts/proxies/http-proxy/models/types.ts +0 -125
  138. package/ts/proxies/http-proxy/request-handler.ts +0 -878
  139. package/ts/proxies/http-proxy/security-manager.ts +0 -433
  140. package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
  141. package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
  142. package/ts/proxies/smart-proxy/cert-store.ts +0 -92
  143. package/ts/proxies/smart-proxy/certificate-manager.ts +0 -894
  144. package/ts/proxies/smart-proxy/connection-manager.ts +0 -796
  145. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -187
  146. package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
  147. package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
  148. package/ts/proxies/smart-proxy/port-manager.ts +0 -358
  149. package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1640
  150. package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
  151. package/ts/proxies/smart-proxy/security-manager.ts +0 -257
  152. package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
  153. package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
  154. package/ts/proxies/smart-proxy/tls-manager.ts +0 -207
  155. package/ts/proxies/smart-proxy/utils/route-validators.ts +0 -283
@@ -1,196 +0,0 @@
1
- import type { IConnectionRecord } from './models/interfaces.js';
2
- import type { SmartProxy } from './smart-proxy.js';
3
-
4
- /**
5
- * Manages timeouts and inactivity tracking for connections
6
- */
7
- export class TimeoutManager {
8
- constructor(private smartProxy: SmartProxy) {}
9
-
10
- /**
11
- * Ensure timeout values don't exceed Node.js max safe integer
12
- */
13
- public ensureSafeTimeout(timeout: number): number {
14
- const MAX_SAFE_TIMEOUT = 2147483647; // Maximum safe value (2^31 - 1)
15
- return Math.min(Math.floor(timeout), MAX_SAFE_TIMEOUT);
16
- }
17
-
18
- /**
19
- * Generate a slightly randomized timeout to prevent thundering herd
20
- */
21
- public randomizeTimeout(baseTimeout: number, variationPercent: number = 5): number {
22
- const safeBaseTimeout = this.ensureSafeTimeout(baseTimeout);
23
- const variation = safeBaseTimeout * (variationPercent / 100);
24
- return this.ensureSafeTimeout(
25
- safeBaseTimeout + Math.floor(Math.random() * variation * 2) - variation
26
- );
27
- }
28
-
29
- /**
30
- * Update connection activity timestamp
31
- */
32
- public updateActivity(record: IConnectionRecord): void {
33
- record.lastActivity = Date.now();
34
-
35
- // Clear any inactivity warning
36
- if (record.inactivityWarningIssued) {
37
- record.inactivityWarningIssued = false;
38
- }
39
- }
40
-
41
- /**
42
- * Calculate effective inactivity timeout based on connection type
43
- */
44
- public getEffectiveInactivityTimeout(record: IConnectionRecord): number {
45
- let effectiveTimeout = this.smartProxy.settings.inactivityTimeout || 14400000; // 4 hours default
46
-
47
- // For immortal keep-alive connections, use an extremely long timeout
48
- if (record.hasKeepAlive && this.smartProxy.settings.keepAliveTreatment === 'immortal') {
49
- return Number.MAX_SAFE_INTEGER;
50
- }
51
-
52
- // For extended keep-alive connections, apply multiplier
53
- if (record.hasKeepAlive && this.smartProxy.settings.keepAliveTreatment === 'extended') {
54
- const multiplier = this.smartProxy.settings.keepAliveInactivityMultiplier || 6;
55
- effectiveTimeout = effectiveTimeout * multiplier;
56
- }
57
-
58
- return this.ensureSafeTimeout(effectiveTimeout);
59
- }
60
-
61
- /**
62
- * Calculate effective max lifetime based on connection type
63
- */
64
- public getEffectiveMaxLifetime(record: IConnectionRecord): number {
65
- // Use route-specific timeout if available from the routeConfig
66
- const baseTimeout = record.routeConfig?.action.advanced?.timeout ||
67
- this.smartProxy.settings.maxConnectionLifetime ||
68
- 86400000; // 24 hours default
69
-
70
- // For immortal keep-alive connections, use an extremely long lifetime
71
- if (record.hasKeepAlive && this.smartProxy.settings.keepAliveTreatment === 'immortal') {
72
- return Number.MAX_SAFE_INTEGER;
73
- }
74
-
75
- // For extended keep-alive connections, use the extended lifetime setting
76
- if (record.hasKeepAlive && this.smartProxy.settings.keepAliveTreatment === 'extended') {
77
- return this.ensureSafeTimeout(
78
- this.smartProxy.settings.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000 // 7 days default
79
- );
80
- }
81
-
82
- // Apply randomization if enabled
83
- if (this.smartProxy.settings.enableRandomizedTimeouts) {
84
- return this.randomizeTimeout(baseTimeout);
85
- }
86
-
87
- return this.ensureSafeTimeout(baseTimeout);
88
- }
89
-
90
- /**
91
- * Setup connection timeout
92
- * @returns The cleanup timer
93
- */
94
- public setupConnectionTimeout(
95
- record: IConnectionRecord,
96
- onTimeout: (record: IConnectionRecord, reason: string) => void
97
- ): NodeJS.Timeout | null {
98
- // Clear any existing timer
99
- if (record.cleanupTimer) {
100
- clearTimeout(record.cleanupTimer);
101
- }
102
-
103
- // Skip timeout for immortal keep-alive connections
104
- if (record.hasKeepAlive && this.smartProxy.settings.keepAliveTreatment === 'immortal') {
105
- return null;
106
- }
107
-
108
- // Calculate effective timeout
109
- const effectiveLifetime = this.getEffectiveMaxLifetime(record);
110
-
111
- // Set up the timeout
112
- const timer = setTimeout(() => {
113
- // Call the provided callback
114
- onTimeout(record, 'connection_timeout');
115
- }, effectiveLifetime);
116
-
117
- // Make sure timeout doesn't keep the process alive
118
- if (timer.unref) {
119
- timer.unref();
120
- }
121
-
122
- return timer;
123
- }
124
-
125
- /**
126
- * Check for inactivity on a connection
127
- * @returns Object with check results
128
- */
129
- public checkInactivity(record: IConnectionRecord): {
130
- isInactive: boolean;
131
- shouldWarn: boolean;
132
- inactivityTime: number;
133
- effectiveTimeout: number;
134
- } {
135
- // Skip for connections with inactivity check disabled
136
- if (this.smartProxy.settings.disableInactivityCheck) {
137
- return {
138
- isInactive: false,
139
- shouldWarn: false,
140
- inactivityTime: 0,
141
- effectiveTimeout: 0
142
- };
143
- }
144
-
145
- // Skip for immortal keep-alive connections
146
- if (record.hasKeepAlive && this.smartProxy.settings.keepAliveTreatment === 'immortal') {
147
- return {
148
- isInactive: false,
149
- shouldWarn: false,
150
- inactivityTime: 0,
151
- effectiveTimeout: 0
152
- };
153
- }
154
-
155
- const now = Date.now();
156
- const inactivityTime = now - record.lastActivity;
157
- const effectiveTimeout = this.getEffectiveInactivityTimeout(record);
158
-
159
- // Check if inactive
160
- const isInactive = inactivityTime > effectiveTimeout;
161
-
162
- // For keep-alive connections, we should warn first
163
- const shouldWarn = record.hasKeepAlive &&
164
- isInactive &&
165
- !record.inactivityWarningIssued;
166
-
167
- return {
168
- isInactive,
169
- shouldWarn,
170
- inactivityTime,
171
- effectiveTimeout
172
- };
173
- }
174
-
175
- /**
176
- * Apply socket timeout settings
177
- */
178
- public applySocketTimeouts(record: IConnectionRecord): void {
179
- // Skip for immortal keep-alive connections
180
- if (record.hasKeepAlive && this.smartProxy.settings.keepAliveTreatment === 'immortal') {
181
- // Disable timeouts completely for immortal connections
182
- record.incoming.setTimeout(0);
183
- if (record.outgoing) {
184
- record.outgoing.setTimeout(0);
185
- }
186
- return;
187
- }
188
-
189
- // Apply normal timeouts
190
- const timeout = this.ensureSafeTimeout(this.smartProxy.settings.socketTimeout || 3600000); // 1 hour default
191
- record.incoming.setTimeout(timeout);
192
- if (record.outgoing) {
193
- record.outgoing.setTimeout(timeout);
194
- }
195
- }
196
- }
@@ -1,207 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import { SniHandler } from '../../tls/sni/sni-handler.js';
3
- import { ProtocolDetector, TlsDetector } from '../../detection/index.js';
4
- import type { SmartProxy } from './smart-proxy.js';
5
-
6
- /**
7
- * Interface for connection information used for SNI extraction
8
- */
9
- interface IConnectionInfo {
10
- sourceIp: string;
11
- sourcePort: number;
12
- destIp: string;
13
- destPort: number;
14
- }
15
-
16
- /**
17
- * Manages TLS-related operations including SNI extraction and validation
18
- */
19
- export class TlsManager {
20
- constructor(private smartProxy: SmartProxy) {}
21
-
22
- /**
23
- * Check if a data chunk appears to be a TLS handshake
24
- */
25
- public isTlsHandshake(chunk: Buffer): boolean {
26
- return SniHandler.isTlsHandshake(chunk);
27
- }
28
-
29
- /**
30
- * Check if a data chunk appears to be a TLS ClientHello
31
- */
32
- public isClientHello(chunk: Buffer): boolean {
33
- return SniHandler.isClientHello(chunk);
34
- }
35
-
36
- /**
37
- * Extract Server Name Indication (SNI) from TLS handshake
38
- */
39
- public extractSNI(
40
- chunk: Buffer,
41
- connInfo: IConnectionInfo,
42
- previousDomain?: string
43
- ): string | undefined {
44
- // Use the SniHandler to process the TLS packet
45
- return SniHandler.processTlsPacket(
46
- chunk,
47
- connInfo,
48
- this.smartProxy.settings.enableTlsDebugLogging || false,
49
- previousDomain
50
- );
51
- }
52
-
53
- /**
54
- * Handle session resumption attempts
55
- */
56
- public handleSessionResumption(
57
- chunk: Buffer,
58
- connectionId: string,
59
- hasSNI: boolean
60
- ): { shouldBlock: boolean; reason?: string } {
61
- // Skip if session tickets are allowed
62
- if (this.smartProxy.settings.allowSessionTicket !== false) {
63
- return { shouldBlock: false };
64
- }
65
-
66
- // Check for session resumption attempt
67
- const resumptionInfo = SniHandler.hasSessionResumption(
68
- chunk,
69
- this.smartProxy.settings.enableTlsDebugLogging || false
70
- );
71
-
72
- // If this is a resumption attempt without SNI, block it
73
- if (resumptionInfo.isResumption && !hasSNI && !resumptionInfo.hasSNI) {
74
- if (this.smartProxy.settings.enableTlsDebugLogging) {
75
- console.log(
76
- `[${connectionId}] Session resumption detected without SNI and allowSessionTicket=false. ` +
77
- `Terminating connection to force new TLS handshake.`
78
- );
79
- }
80
- return {
81
- shouldBlock: true,
82
- reason: 'session_ticket_blocked'
83
- };
84
- }
85
-
86
- return { shouldBlock: false };
87
- }
88
-
89
- /**
90
- * Check for SNI mismatch during renegotiation
91
- */
92
- public checkRenegotiationSNI(
93
- chunk: Buffer,
94
- connInfo: IConnectionInfo,
95
- expectedDomain: string,
96
- connectionId: string
97
- ): { hasMismatch: boolean; extractedSNI?: string } {
98
- // Only process if this looks like a TLS ClientHello
99
- if (!this.isClientHello(chunk)) {
100
- return { hasMismatch: false };
101
- }
102
-
103
- try {
104
- // Extract SNI with renegotiation support
105
- const newSNI = SniHandler.extractSNIWithResumptionSupport(
106
- chunk,
107
- connInfo,
108
- this.smartProxy.settings.enableTlsDebugLogging || false
109
- );
110
-
111
- // Skip if no SNI was found
112
- if (!newSNI) return { hasMismatch: false };
113
-
114
- // Check for SNI mismatch
115
- if (newSNI !== expectedDomain) {
116
- if (this.smartProxy.settings.enableTlsDebugLogging) {
117
- console.log(
118
- `[${connectionId}] Renegotiation with different SNI: ${expectedDomain} -> ${newSNI}. ` +
119
- `Terminating connection - SNI domain switching is not allowed.`
120
- );
121
- }
122
- return { hasMismatch: true, extractedSNI: newSNI };
123
- } else if (this.smartProxy.settings.enableTlsDebugLogging) {
124
- console.log(
125
- `[${connectionId}] Renegotiation detected with same SNI: ${newSNI}. Allowing.`
126
- );
127
- }
128
- } catch (err) {
129
- console.log(
130
- `[${connectionId}] Error processing ClientHello: ${err}. Allowing connection to continue.`
131
- );
132
- }
133
-
134
- return { hasMismatch: false };
135
- }
136
-
137
- /**
138
- * Create a renegotiation handler function for a connection
139
- */
140
- public createRenegotiationHandler(
141
- connectionId: string,
142
- lockedDomain: string,
143
- connInfo: IConnectionInfo,
144
- onMismatch: (connectionId: string, reason: string) => void
145
- ): (chunk: Buffer) => void {
146
- return (chunk: Buffer) => {
147
- const result = this.checkRenegotiationSNI(chunk, connInfo, lockedDomain, connectionId);
148
- if (result.hasMismatch) {
149
- onMismatch(connectionId, 'sni_mismatch');
150
- }
151
- };
152
- }
153
-
154
- /**
155
- * Analyze TLS connection for browser fingerprinting
156
- * This helps identify browser vs non-browser connections
157
- */
158
- public analyzeClientHello(chunk: Buffer): {
159
- isBrowserConnection: boolean;
160
- isRenewal: boolean;
161
- hasSNI: boolean;
162
- } {
163
- // Default result
164
- const result = {
165
- isBrowserConnection: false,
166
- isRenewal: false,
167
- hasSNI: false
168
- };
169
-
170
- try {
171
- // Check if it's a ClientHello
172
- if (!this.isClientHello(chunk)) {
173
- return result;
174
- }
175
-
176
- // Check for session resumption
177
- const resumptionInfo = SniHandler.hasSessionResumption(
178
- chunk,
179
- this.smartProxy.settings.enableTlsDebugLogging || false
180
- );
181
-
182
- // Extract SNI
183
- const sni = SniHandler.extractSNI(
184
- chunk,
185
- this.smartProxy.settings.enableTlsDebugLogging || false
186
- );
187
-
188
- // Update result
189
- result.isRenewal = resumptionInfo.isResumption;
190
- result.hasSNI = !!sni;
191
-
192
- // Browsers typically:
193
- // 1. Send SNI extension
194
- // 2. Have a variety of extensions (ALPN, etc.)
195
- // 3. Use standard cipher suites
196
- // ...more complex heuristics could be implemented here
197
-
198
- // Simple heuristic: presence of SNI suggests browser
199
- result.isBrowserConnection = !!sni;
200
-
201
- return result;
202
- } catch (err) {
203
- console.log(`Error analyzing ClientHello: ${err}`);
204
- return result;
205
- }
206
- }
207
- }
@@ -1,283 +0,0 @@
1
- /**
2
- * Route Validators
3
- *
4
- * This file provides utility functions for validating route configurations.
5
- * These validators help ensure that route configurations are valid and correctly structured.
6
- */
7
-
8
- import type { IRouteConfig, IRouteMatch, IRouteAction, TPortRange } from '../models/route-types.js';
9
-
10
- /**
11
- * Validates a port range or port number
12
- * @param port Port number, port range, or port function
13
- * @returns True if valid, false otherwise
14
- */
15
- export function isValidPort(port: any): boolean {
16
- if (typeof port === 'number') {
17
- return port > 0 && port < 65536; // Valid port range is 1-65535
18
- } else if (Array.isArray(port)) {
19
- return port.every(p =>
20
- (typeof p === 'number' && p > 0 && p < 65536) ||
21
- (typeof p === 'object' && 'from' in p && 'to' in p &&
22
- p.from > 0 && p.from < 65536 && p.to > 0 && p.to < 65536)
23
- );
24
- } else if (typeof port === 'function') {
25
- // For function-based ports, we can't validate the result at config time
26
- // so we just check that it's a function
27
- return true;
28
- } else if (typeof port === 'object' && 'from' in port && 'to' in port) {
29
- return port.from > 0 && port.from < 65536 && port.to > 0 && port.to < 65536;
30
- }
31
- return false;
32
- }
33
-
34
- /**
35
- * Validates a domain string
36
- * @param domain Domain string to validate
37
- * @returns True if valid, false otherwise
38
- */
39
- export function isValidDomain(domain: string): boolean {
40
- // Basic domain validation regex - allows wildcards (*.example.com)
41
- const domainRegex = /^(\*\.)?([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
42
- return domainRegex.test(domain);
43
- }
44
-
45
- /**
46
- * Validates a route match configuration
47
- * @param match Route match configuration to validate
48
- * @returns { valid: boolean, errors: string[] } Validation result
49
- */
50
- export function validateRouteMatch(match: IRouteMatch): { valid: boolean; errors: string[] } {
51
- const errors: string[] = [];
52
-
53
- // Validate ports
54
- if (match.ports !== undefined) {
55
- if (!isValidPort(match.ports)) {
56
- errors.push('Invalid port number or port range in match.ports');
57
- }
58
- }
59
-
60
- // Validate domains
61
- if (match.domains !== undefined) {
62
- if (typeof match.domains === 'string') {
63
- if (!isValidDomain(match.domains)) {
64
- errors.push(`Invalid domain format: ${match.domains}`);
65
- }
66
- } else if (Array.isArray(match.domains)) {
67
- for (const domain of match.domains) {
68
- if (!isValidDomain(domain)) {
69
- errors.push(`Invalid domain format: ${domain}`);
70
- }
71
- }
72
- } else {
73
- errors.push('Domains must be a string or an array of strings');
74
- }
75
- }
76
-
77
- // Validate path
78
- if (match.path !== undefined) {
79
- if (typeof match.path !== 'string' || !match.path.startsWith('/')) {
80
- errors.push('Path must be a string starting with /');
81
- }
82
- }
83
-
84
- return {
85
- valid: errors.length === 0,
86
- errors
87
- };
88
- }
89
-
90
- /**
91
- * Validates a route action configuration
92
- * @param action Route action configuration to validate
93
- * @returns { valid: boolean, errors: string[] } Validation result
94
- */
95
- export function validateRouteAction(action: IRouteAction): { valid: boolean; errors: string[] } {
96
- const errors: string[] = [];
97
-
98
- // Validate action type
99
- if (!action.type) {
100
- errors.push('Action type is required');
101
- } else if (!['forward', 'socket-handler'].includes(action.type)) {
102
- errors.push(`Invalid action type: ${action.type}`);
103
- }
104
-
105
- // Validate targets for 'forward' action
106
- if (action.type === 'forward') {
107
- if (!action.targets || !Array.isArray(action.targets) || action.targets.length === 0) {
108
- errors.push('Targets array is required for forward action');
109
- } else {
110
- // Validate each target
111
- action.targets.forEach((target, index) => {
112
- // Validate target host
113
- if (!target.host) {
114
- errors.push(`Target[${index}] host is required`);
115
- } else if (typeof target.host !== 'string' &&
116
- !Array.isArray(target.host) &&
117
- typeof target.host !== 'function') {
118
- errors.push(`Target[${index}] host must be a string, array of strings, or function`);
119
- }
120
-
121
- // Validate target port
122
- if (target.port === undefined) {
123
- errors.push(`Target[${index}] port is required`);
124
- } else if (typeof target.port !== 'number' &&
125
- typeof target.port !== 'function' &&
126
- target.port !== 'preserve') {
127
- errors.push(`Target[${index}] port must be a number, 'preserve', or a function`);
128
- } else if (typeof target.port === 'number' && !isValidPort(target.port)) {
129
- errors.push(`Target[${index}] port must be between 1 and 65535`);
130
- }
131
-
132
- // Validate match criteria if present
133
- if (target.match) {
134
- if (target.match.ports && !Array.isArray(target.match.ports)) {
135
- errors.push(`Target[${index}] match.ports must be an array`);
136
- }
137
- if (target.match.method && !Array.isArray(target.match.method)) {
138
- errors.push(`Target[${index}] match.method must be an array`);
139
- }
140
- }
141
- });
142
- }
143
-
144
- // Validate TLS options for forward actions
145
- if (action.tls) {
146
- if (!['passthrough', 'terminate', 'terminate-and-reencrypt'].includes(action.tls.mode)) {
147
- errors.push(`Invalid TLS mode: ${action.tls.mode}`);
148
- }
149
-
150
- // For termination modes, validate certificate
151
- if (['terminate', 'terminate-and-reencrypt'].includes(action.tls.mode)) {
152
- if (action.tls.certificate !== 'auto' &&
153
- (!action.tls.certificate || !action.tls.certificate.key || !action.tls.certificate.cert)) {
154
- errors.push('Certificate must be "auto" or an object with key and cert properties');
155
- }
156
- }
157
- }
158
- }
159
-
160
- // Validate socket handler for 'socket-handler' action
161
- if (action.type === 'socket-handler') {
162
- if (!action.socketHandler) {
163
- errors.push('Socket handler function is required for socket-handler action');
164
- } else if (typeof action.socketHandler !== 'function') {
165
- errors.push('Socket handler must be a function');
166
- }
167
- }
168
-
169
- return {
170
- valid: errors.length === 0,
171
- errors
172
- };
173
- }
174
-
175
- /**
176
- * Validates a complete route configuration
177
- * @param route Route configuration to validate
178
- * @returns { valid: boolean, errors: string[] } Validation result
179
- */
180
- export function validateRouteConfig(route: IRouteConfig): { valid: boolean; errors: string[] } {
181
- const errors: string[] = [];
182
-
183
- // Check for required properties
184
- if (!route.match) {
185
- errors.push('Route match configuration is required');
186
- }
187
-
188
- if (!route.action) {
189
- errors.push('Route action configuration is required');
190
- }
191
-
192
- // Validate match configuration
193
- if (route.match) {
194
- const matchValidation = validateRouteMatch(route.match);
195
- if (!matchValidation.valid) {
196
- errors.push(...matchValidation.errors.map(err => `Match: ${err}`));
197
- }
198
- }
199
-
200
- // Validate action configuration
201
- if (route.action) {
202
- const actionValidation = validateRouteAction(route.action);
203
- if (!actionValidation.valid) {
204
- errors.push(...actionValidation.errors.map(err => `Action: ${err}`));
205
- }
206
- }
207
-
208
- // Ensure the route has a unique identifier
209
- if (!route.id && !route.name) {
210
- errors.push('Route should have either an id or a name for identification');
211
- }
212
-
213
- return {
214
- valid: errors.length === 0,
215
- errors
216
- };
217
- }
218
-
219
- /**
220
- * Validate an array of route configurations
221
- * @param routes Array of route configurations to validate
222
- * @returns { valid: boolean, errors: { index: number, errors: string[] }[] } Validation result
223
- */
224
- export function validateRoutes(routes: IRouteConfig[]): {
225
- valid: boolean;
226
- errors: { index: number; errors: string[] }[]
227
- } {
228
- const results: { index: number; errors: string[] }[] = [];
229
-
230
- routes.forEach((route, index) => {
231
- const validation = validateRouteConfig(route);
232
- if (!validation.valid) {
233
- results.push({
234
- index,
235
- errors: validation.errors
236
- });
237
- }
238
- });
239
-
240
- return {
241
- valid: results.length === 0,
242
- errors: results
243
- };
244
- }
245
-
246
- /**
247
- * Check if a route configuration has the required properties for a specific action type
248
- * @param route Route configuration to check
249
- * @param actionType Expected action type
250
- * @returns True if the route has the necessary properties, false otherwise
251
- */
252
- export function hasRequiredPropertiesForAction(route: IRouteConfig, actionType: string): boolean {
253
- if (!route.action || route.action.type !== actionType) {
254
- return false;
255
- }
256
-
257
- switch (actionType) {
258
- case 'forward':
259
- return !!route.action.targets &&
260
- Array.isArray(route.action.targets) &&
261
- route.action.targets.length > 0 &&
262
- route.action.targets.every(t => t.host && t.port !== undefined);
263
- case 'socket-handler':
264
- return !!route.action.socketHandler && typeof route.action.socketHandler === 'function';
265
- default:
266
- return false;
267
- }
268
- }
269
-
270
- /**
271
- * Throws an error if the route config is invalid, returns the config if valid
272
- * Useful for immediate validation when creating routes
273
- * @param route Route configuration to validate
274
- * @returns The validated route configuration
275
- * @throws Error if the route configuration is invalid
276
- */
277
- export function assertValidRoute(route: IRouteConfig): IRouteConfig {
278
- const validation = validateRouteConfig(route);
279
- if (!validation.valid) {
280
- throw new Error(`Invalid route configuration: ${validation.errors.join(', ')}`);
281
- }
282
- return route;
283
- }