@push.rocks/smartproxy 23.0.0 → 23.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 +10 -0
- package/dist_rust/{rustproxy → rustproxy_linux_amd64} +0 -0
- package/dist_rust/rustproxy_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +9 -21
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +83 -212
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +2 -3
- package/npmextra.json +3 -0
- package/package.json +13 -11
- package/readme.md +41 -11
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/plugins.ts +2 -0
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +102 -233
- package/ts/proxies/smart-proxy/smart-proxy.ts +1 -2
- package/dist_ts/common/eventUtils.d.ts +0 -14
- package/dist_ts/common/eventUtils.js +0 -20
- package/dist_ts/common/types.d.ts +0 -82
- package/dist_ts/common/types.js +0 -15
- package/dist_ts/core/utils/event-system.d.ts +0 -200
- package/dist_ts/core/utils/event-system.js +0 -224
- package/dist_ts/core/utils/event-utils.d.ts +0 -15
- package/dist_ts/core/utils/event-utils.js +0 -11
- package/dist_ts/core/utils/route-manager.d.ts +0 -88
- package/dist_ts/core/utils/route-manager.js +0 -342
- package/dist_ts/core/utils/route-utils.d.ts +0 -28
- package/dist_ts/core/utils/route-utils.js +0 -67
- package/dist_ts/detection/detectors/http-detector-v2.d.ts +0 -33
- package/dist_ts/detection/detectors/http-detector-v2.js +0 -87
- package/dist_ts/detection/detectors/tls-detector-v2.d.ts +0 -33
- package/dist_ts/detection/detectors/tls-detector-v2.js +0 -80
- package/dist_ts/detection/protocol-detector-v2.d.ts +0 -46
- package/dist_ts/detection/protocol-detector-v2.js +0 -116
- package/dist_ts/forwarding/config/forwarding-types.d.ts +0 -42
- package/dist_ts/forwarding/config/forwarding-types.js +0 -18
- package/dist_ts/forwarding/config/index.d.ts +0 -9
- package/dist_ts/forwarding/config/index.js +0 -10
- package/dist_ts/forwarding/factory/forwarding-factory.d.ts +0 -25
- package/dist_ts/forwarding/factory/forwarding-factory.js +0 -172
- package/dist_ts/forwarding/factory/index.d.ts +0 -4
- package/dist_ts/forwarding/factory/index.js +0 -5
- package/dist_ts/forwarding/handlers/base-handler.d.ts +0 -62
- package/dist_ts/forwarding/handlers/base-handler.js +0 -121
- package/dist_ts/forwarding/handlers/http-handler.d.ts +0 -30
- package/dist_ts/forwarding/handlers/http-handler.js +0 -143
- package/dist_ts/forwarding/handlers/https-passthrough-handler.d.ts +0 -29
- package/dist_ts/forwarding/handlers/https-passthrough-handler.js +0 -156
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.d.ts +0 -36
- package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +0 -276
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.d.ts +0 -35
- package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +0 -261
- package/dist_ts/forwarding/handlers/index.d.ts +0 -8
- package/dist_ts/forwarding/handlers/index.js +0 -9
- package/dist_ts/forwarding/index.d.ts +0 -13
- package/dist_ts/forwarding/index.js +0 -16
- package/dist_ts/http/index.d.ts +0 -5
- package/dist_ts/http/index.js +0 -8
- package/dist_ts/http/models/http-types.d.ts +0 -6
- package/dist_ts/http/models/http-types.js +0 -7
- package/dist_ts/http/router/index.d.ts +0 -8
- package/dist_ts/http/router/index.js +0 -7
- package/dist_ts/http/router/proxy-router.d.ts +0 -115
- package/dist_ts/http/router/proxy-router.js +0 -325
- package/dist_ts/http/router/route-router.d.ts +0 -108
- package/dist_ts/http/router/route-router.js +0 -393
- package/dist_ts/protocols/tls/constants.d.ts +0 -122
- package/dist_ts/protocols/tls/constants.js +0 -135
- package/dist_ts/protocols/tls/parser.d.ts +0 -53
- package/dist_ts/protocols/tls/parser.js +0 -294
- package/dist_ts/protocols/tls/types.d.ts +0 -65
- package/dist_ts/protocols/tls/types.js +0 -5
- package/dist_ts/proxies/http-proxy/certificate-manager.d.ts +0 -95
- package/dist_ts/proxies/http-proxy/certificate-manager.js +0 -214
- package/dist_ts/proxies/http-proxy/connection-pool.d.ts +0 -47
- package/dist_ts/proxies/http-proxy/connection-pool.js +0 -195
- package/dist_ts/proxies/http-proxy/context-creator.d.ts +0 -34
- package/dist_ts/proxies/http-proxy/context-creator.js +0 -108
- package/dist_ts/proxies/http-proxy/default-certificates.d.ts +0 -54
- package/dist_ts/proxies/http-proxy/default-certificates.js +0 -127
- package/dist_ts/proxies/http-proxy/function-cache.d.ts +0 -95
- package/dist_ts/proxies/http-proxy/function-cache.js +0 -215
- package/dist_ts/proxies/http-proxy/handlers/index.d.ts +0 -4
- package/dist_ts/proxies/http-proxy/handlers/index.js +0 -6
- package/dist_ts/proxies/http-proxy/handlers/redirect-handler.d.ts +0 -18
- package/dist_ts/proxies/http-proxy/handlers/redirect-handler.js +0 -78
- package/dist_ts/proxies/http-proxy/handlers/static-handler.d.ts +0 -19
- package/dist_ts/proxies/http-proxy/handlers/static-handler.js +0 -211
- package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -117
- package/dist_ts/proxies/http-proxy/http-proxy.js +0 -521
- package/dist_ts/proxies/http-proxy/http-request-handler.d.ts +0 -40
- package/dist_ts/proxies/http-proxy/http-request-handler.js +0 -257
- package/dist_ts/proxies/http-proxy/http2-request-handler.d.ts +0 -24
- package/dist_ts/proxies/http-proxy/http2-request-handler.js +0 -201
- package/dist_ts/proxies/http-proxy/index.d.ts +0 -14
- package/dist_ts/proxies/http-proxy/index.js +0 -16
- package/dist_ts/proxies/http-proxy/models/http-types.d.ts +0 -117
- package/dist_ts/proxies/http-proxy/models/http-types.js +0 -92
- package/dist_ts/proxies/http-proxy/models/index.d.ts +0 -5
- package/dist_ts/proxies/http-proxy/models/index.js +0 -6
- package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -75
- package/dist_ts/proxies/http-proxy/models/types.js +0 -35
- package/dist_ts/proxies/http-proxy/request-handler.d.ts +0 -97
- package/dist_ts/proxies/http-proxy/request-handler.js +0 -737
- package/dist_ts/proxies/http-proxy/security-manager.d.ts +0 -98
- package/dist_ts/proxies/http-proxy/security-manager.js +0 -341
- package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +0 -50
- package/dist_ts/proxies/http-proxy/websocket-handler.js +0 -505
- package/dist_ts/proxies/smart-proxy/acme-state-manager.d.ts +0 -42
- package/dist_ts/proxies/smart-proxy/acme-state-manager.js +0 -101
- package/dist_ts/proxies/smart-proxy/cert-store.d.ts +0 -10
- package/dist_ts/proxies/smart-proxy/cert-store.js +0 -72
- package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +0 -164
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +0 -745
- package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +0 -128
- package/dist_ts/proxies/smart-proxy/connection-manager.js +0 -689
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +0 -43
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +0 -180
- package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +0 -98
- package/dist_ts/proxies/smart-proxy/metrics-collector.js +0 -355
- package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +0 -82
- package/dist_ts/proxies/smart-proxy/nftables-manager.js +0 -237
- package/dist_ts/proxies/smart-proxy/port-manager.d.ts +0 -117
- package/dist_ts/proxies/smart-proxy/port-manager.js +0 -318
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +0 -60
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +0 -1407
- package/dist_ts/proxies/smart-proxy/route-manager.d.ts +0 -112
- package/dist_ts/proxies/smart-proxy/route-manager.js +0 -453
- package/dist_ts/proxies/smart-proxy/route-orchestrator.d.ts +0 -56
- package/dist_ts/proxies/smart-proxy/route-orchestrator.js +0 -204
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +0 -23
- package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +0 -104
- package/dist_ts/proxies/smart-proxy/security-manager.d.ts +0 -74
- package/dist_ts/proxies/smart-proxy/security-manager.js +0 -227
- package/dist_ts/proxies/smart-proxy/throughput-tracker.d.ts +0 -36
- package/dist_ts/proxies/smart-proxy/throughput-tracker.js +0 -115
- package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +0 -48
- package/dist_ts/proxies/smart-proxy/timeout-manager.js +0 -158
- package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +0 -50
- package/dist_ts/proxies/smart-proxy/tls-manager.js +0 -110
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +0 -161
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +0 -282
- package/dist_ts/proxies/smart-proxy/utils/route-validators.d.ts +0 -73
- package/dist_ts/proxies/smart-proxy/utils/route-validators.js +0 -259
- package/dist_ts/routing/router/proxy-router.d.ts +0 -115
- package/dist_ts/routing/router/proxy-router.js +0 -325
- package/dist_ts/routing/router/route-router.d.ts +0 -108
- package/dist_ts/routing/router/route-router.js +0 -393
- package/dist_ts/tls/alerts/index.d.ts +0 -4
- package/dist_ts/tls/alerts/index.js +0 -5
- package/dist_ts/tls/alerts/tls-alert.d.ts +0 -150
- package/dist_ts/tls/alerts/tls-alert.js +0 -226
- package/dist_ts/tls/sni/client-hello-parser.d.ts +0 -100
- package/dist_ts/tls/sni/client-hello-parser.js +0 -464
- package/dist_ts/tls/sni/sni-extraction.d.ts +0 -58
- package/dist_ts/tls/sni/sni-extraction.js +0 -275
- package/dist_ts/tls/utils/index.d.ts +0 -4
- package/dist_ts/tls/utils/index.js +0 -5
- package/dist_ts/tls/utils/tls-utils.d.ts +0 -49
- package/dist_ts/tls/utils/tls-utils.js +0 -75
- package/ts/proxies/smart-proxy/rust-binary-locator.ts +0 -112
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
import { logger } from '../../core/utils/logger.js';
|
|
2
|
-
import { RouteValidator } from './utils/route-validator.js';
|
|
3
|
-
import { Mutex } from './utils/mutex.js';
|
|
4
|
-
/**
|
|
5
|
-
* Orchestrates route updates and coordination between components
|
|
6
|
-
* Extracted from SmartProxy to reduce class complexity
|
|
7
|
-
*/
|
|
8
|
-
export class RouteOrchestrator {
|
|
9
|
-
constructor(portManager, routeManager, httpProxyBridge, nftablesManager, certManager, logger) {
|
|
10
|
-
this.certManager = null;
|
|
11
|
-
this.portManager = portManager;
|
|
12
|
-
this.routeManager = routeManager;
|
|
13
|
-
this.httpProxyBridge = httpProxyBridge;
|
|
14
|
-
this.nftablesManager = nftablesManager;
|
|
15
|
-
this.certManager = certManager;
|
|
16
|
-
this.logger = logger;
|
|
17
|
-
this.routeUpdateLock = new Mutex();
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Set or update certificate manager reference
|
|
21
|
-
*/
|
|
22
|
-
setCertManager(certManager) {
|
|
23
|
-
this.certManager = certManager;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Get certificate manager reference
|
|
27
|
-
*/
|
|
28
|
-
getCertManager() {
|
|
29
|
-
return this.certManager;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Update routes with validation and coordination
|
|
33
|
-
*/
|
|
34
|
-
async updateRoutes(oldRoutes, newRoutes, options = {}) {
|
|
35
|
-
return this.routeUpdateLock.runExclusive(async () => {
|
|
36
|
-
// Validate route configurations
|
|
37
|
-
const validation = RouteValidator.validateRoutes(newRoutes);
|
|
38
|
-
if (!validation.valid) {
|
|
39
|
-
RouteValidator.logValidationErrors(validation.errors);
|
|
40
|
-
throw new Error(`Route validation failed: ${validation.errors.size} route(s) have errors`);
|
|
41
|
-
}
|
|
42
|
-
// Track port usage before and after updates
|
|
43
|
-
const oldPortUsage = this.updatePortUsageMap(oldRoutes);
|
|
44
|
-
const newPortUsage = this.updatePortUsageMap(newRoutes);
|
|
45
|
-
// Get the lists of currently listening ports and new ports needed
|
|
46
|
-
const currentPorts = new Set(this.portManager.getListeningPorts());
|
|
47
|
-
const newPortsSet = new Set(newPortUsage.keys());
|
|
48
|
-
// Log the port usage for debugging
|
|
49
|
-
this.logger.debug(`Current listening ports: ${Array.from(currentPorts).join(', ')}`);
|
|
50
|
-
this.logger.debug(`Ports needed for new routes: ${Array.from(newPortsSet).join(', ')}`);
|
|
51
|
-
// Find orphaned ports - ports that no longer have any routes
|
|
52
|
-
const orphanedPorts = this.findOrphanedPorts(oldPortUsage, newPortUsage);
|
|
53
|
-
// Find new ports that need binding (only ports that we aren't already listening on)
|
|
54
|
-
const newBindingPorts = Array.from(newPortsSet).filter(p => !currentPorts.has(p));
|
|
55
|
-
// Check for ACME challenge port to give it special handling
|
|
56
|
-
const acmePort = options.acmePort || 80;
|
|
57
|
-
const acmePortNeeded = newPortsSet.has(acmePort);
|
|
58
|
-
const acmePortListed = newBindingPorts.includes(acmePort);
|
|
59
|
-
if (acmePortNeeded && acmePortListed) {
|
|
60
|
-
this.logger.info(`Adding ACME challenge port ${acmePort} to routes`);
|
|
61
|
-
}
|
|
62
|
-
// Update NFTables routes
|
|
63
|
-
await this.updateNfTablesRoutes(oldRoutes, newRoutes);
|
|
64
|
-
// Update routes in RouteManager
|
|
65
|
-
this.routeManager.updateRoutes(newRoutes);
|
|
66
|
-
// Release orphaned ports first to free resources
|
|
67
|
-
if (orphanedPorts.length > 0) {
|
|
68
|
-
this.logger.info(`Releasing ${orphanedPorts.length} orphaned ports: ${orphanedPorts.join(', ')}`);
|
|
69
|
-
await this.portManager.removePorts(orphanedPorts);
|
|
70
|
-
}
|
|
71
|
-
// Add new ports if needed
|
|
72
|
-
if (newBindingPorts.length > 0) {
|
|
73
|
-
this.logger.info(`Binding to ${newBindingPorts.length} new ports: ${newBindingPorts.join(', ')}`);
|
|
74
|
-
// Handle port binding with improved error recovery
|
|
75
|
-
try {
|
|
76
|
-
await this.portManager.addPorts(newBindingPorts);
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
// Special handling for port binding errors
|
|
80
|
-
if (error.code === 'EADDRINUSE') {
|
|
81
|
-
const port = error.port || newBindingPorts[0];
|
|
82
|
-
const isAcmePort = port === acmePort;
|
|
83
|
-
if (isAcmePort) {
|
|
84
|
-
this.logger.warn(`Could not bind to ACME challenge port ${port}. It may be in use by another application.`);
|
|
85
|
-
// Re-throw with more helpful message
|
|
86
|
-
throw new Error(`ACME challenge port ${port} is already in use by another application. ` +
|
|
87
|
-
`Configure a different port in settings.acme.port (e.g., 8080) or free up port ${port}.`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
// Re-throw the original error for other cases
|
|
91
|
-
throw error;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// If HttpProxy is initialized, resync the configurations
|
|
95
|
-
if (this.httpProxyBridge.getHttpProxy()) {
|
|
96
|
-
await this.httpProxyBridge.syncRoutesToHttpProxy(newRoutes);
|
|
97
|
-
}
|
|
98
|
-
// Update certificate manager if needed
|
|
99
|
-
let newCertManager;
|
|
100
|
-
let newChallengeRouteActive = options.globalChallengeRouteActive || false;
|
|
101
|
-
if (this.certManager && options.createCertificateManager) {
|
|
102
|
-
const existingAcmeOptions = this.certManager.getAcmeOptions();
|
|
103
|
-
const existingState = this.certManager.getState();
|
|
104
|
-
// Store global state before stopping
|
|
105
|
-
newChallengeRouteActive = existingState.challengeRouteActive;
|
|
106
|
-
// Keep certificate manager routes in sync before stopping
|
|
107
|
-
this.certManager.setRoutes(newRoutes);
|
|
108
|
-
await this.certManager.stop();
|
|
109
|
-
// Verify the challenge route has been properly removed
|
|
110
|
-
if (options.verifyChallengeRouteRemoved) {
|
|
111
|
-
await options.verifyChallengeRouteRemoved();
|
|
112
|
-
}
|
|
113
|
-
// Create new certificate manager with preserved state
|
|
114
|
-
newCertManager = await options.createCertificateManager(newRoutes, './certs', existingAcmeOptions, { challengeRouteActive: newChallengeRouteActive });
|
|
115
|
-
this.certManager = newCertManager;
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
portUsageMap: newPortUsage,
|
|
119
|
-
newChallengeRouteActive,
|
|
120
|
-
newCertManager
|
|
121
|
-
};
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Update port usage map based on the provided routes
|
|
126
|
-
*/
|
|
127
|
-
updatePortUsageMap(routes) {
|
|
128
|
-
const portUsage = new Map();
|
|
129
|
-
for (const route of routes) {
|
|
130
|
-
// Get the ports for this route
|
|
131
|
-
const portsConfig = Array.isArray(route.match.ports)
|
|
132
|
-
? route.match.ports
|
|
133
|
-
: [route.match.ports];
|
|
134
|
-
// Expand port range objects to individual port numbers
|
|
135
|
-
const expandedPorts = [];
|
|
136
|
-
for (const portConfig of portsConfig) {
|
|
137
|
-
if (typeof portConfig === 'number') {
|
|
138
|
-
expandedPorts.push(portConfig);
|
|
139
|
-
}
|
|
140
|
-
else if (typeof portConfig === 'object' && 'from' in portConfig && 'to' in portConfig) {
|
|
141
|
-
// Expand the port range
|
|
142
|
-
for (let p = portConfig.from; p <= portConfig.to; p++) {
|
|
143
|
-
expandedPorts.push(p);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
// Use route name if available, otherwise generate a unique ID
|
|
148
|
-
const routeName = route.name || `unnamed_${Math.random().toString(36).substring(2, 9)}`;
|
|
149
|
-
// Add each port to the usage map
|
|
150
|
-
for (const port of expandedPorts) {
|
|
151
|
-
if (!portUsage.has(port)) {
|
|
152
|
-
portUsage.set(port, new Set());
|
|
153
|
-
}
|
|
154
|
-
portUsage.get(port).add(routeName);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
// Log port usage for debugging
|
|
158
|
-
for (const [port, routes] of portUsage.entries()) {
|
|
159
|
-
this.logger.debug(`Port ${port} is used by ${routes.size} routes: ${Array.from(routes).join(', ')}`);
|
|
160
|
-
}
|
|
161
|
-
return portUsage;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Find ports that have no routes in the new configuration
|
|
165
|
-
*/
|
|
166
|
-
findOrphanedPorts(oldUsage, newUsage) {
|
|
167
|
-
const orphanedPorts = [];
|
|
168
|
-
for (const [port, routes] of oldUsage.entries()) {
|
|
169
|
-
if (!newUsage.has(port) || newUsage.get(port).size === 0) {
|
|
170
|
-
orphanedPorts.push(port);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return orphanedPorts;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Update NFTables routes
|
|
177
|
-
*/
|
|
178
|
-
async updateNfTablesRoutes(oldRoutes, newRoutes) {
|
|
179
|
-
// Get existing routes that use NFTables and update them
|
|
180
|
-
const oldNfTablesRoutes = oldRoutes.filter(r => r.action.forwardingEngine === 'nftables');
|
|
181
|
-
const newNfTablesRoutes = newRoutes.filter(r => r.action.forwardingEngine === 'nftables');
|
|
182
|
-
// Update existing NFTables routes
|
|
183
|
-
for (const oldRoute of oldNfTablesRoutes) {
|
|
184
|
-
const newRoute = newNfTablesRoutes.find(r => r.name === oldRoute.name);
|
|
185
|
-
if (!newRoute) {
|
|
186
|
-
// Route was removed
|
|
187
|
-
await this.nftablesManager.deprovisionRoute(oldRoute);
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
// Route was updated
|
|
191
|
-
await this.nftablesManager.updateRoute(oldRoute, newRoute);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
// Add new NFTables routes
|
|
195
|
-
for (const newRoute of newNfTablesRoutes) {
|
|
196
|
-
const oldRoute = oldNfTablesRoutes.find(r => r.name === newRoute.name);
|
|
197
|
-
if (!oldRoute) {
|
|
198
|
-
// New route
|
|
199
|
-
await this.nftablesManager.provisionRoute(newRoute);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtb3JjaGVzdHJhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJveGllcy9zbWFydC1wcm94eS9yb3V0ZS1vcmNoZXN0cmF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBR3BELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFPekM7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLGlCQUFpQjtJQVM1QixZQUNFLFdBQXdCLEVBQ3hCLFlBQTBCLEVBQzFCLGVBQWdDLEVBQ2hDLGVBQWdDLEVBQ2hDLFdBQW9DLEVBQ3BDLE1BQWU7UUFUVCxnQkFBVyxHQUE0QixJQUFJLENBQUM7UUFXbEQsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7UUFDL0IsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7UUFDakMsSUFBSSxDQUFDLGVBQWUsR0FBRyxlQUFlLENBQUM7UUFDdkMsSUFBSSxDQUFDLGVBQWUsR0FBRyxlQUFlLENBQUM7UUFDdkMsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7UUFDL0IsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFJLEtBQUssRUFBRSxDQUFDO0lBQ3JDLENBQUM7SUFFRDs7T0FFRztJQUNJLGNBQWMsQ0FBQyxXQUFvQztRQUN4RCxJQUFJLENBQUMsV0FBVyxHQUFHLFdBQVcsQ0FBQztJQUNqQyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxjQUFjO1FBQ25CLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQztJQUMxQixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsWUFBWSxDQUN2QixTQUF5QixFQUN6QixTQUF5QixFQUN6QixVQVlJLEVB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
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Locates the RustProxy binary using a priority-ordered search strategy:
|
|
3
|
-
* 1. SMARTPROXY_RUST_BINARY environment variable
|
|
4
|
-
* 2. Platform-specific optional npm package
|
|
5
|
-
* 3. Local development build at ./rust/target/release/rustproxy
|
|
6
|
-
* 4. System PATH
|
|
7
|
-
*/
|
|
8
|
-
export declare class RustBinaryLocator {
|
|
9
|
-
private cachedPath;
|
|
10
|
-
/**
|
|
11
|
-
* Find the RustProxy binary path.
|
|
12
|
-
* Returns null if no binary is available.
|
|
13
|
-
*/
|
|
14
|
-
findBinary(): Promise<string | null>;
|
|
15
|
-
/**
|
|
16
|
-
* Clear the cached binary path (e.g., after a failed launch).
|
|
17
|
-
*/
|
|
18
|
-
clearCache(): void;
|
|
19
|
-
private searchBinary;
|
|
20
|
-
private findPlatformPackageBinary;
|
|
21
|
-
private isExecutable;
|
|
22
|
-
private findInPath;
|
|
23
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import { logger } from '../../core/utils/logger.js';
|
|
3
|
-
/**
|
|
4
|
-
* Locates the RustProxy binary using a priority-ordered search strategy:
|
|
5
|
-
* 1. SMARTPROXY_RUST_BINARY environment variable
|
|
6
|
-
* 2. Platform-specific optional npm package
|
|
7
|
-
* 3. Local development build at ./rust/target/release/rustproxy
|
|
8
|
-
* 4. System PATH
|
|
9
|
-
*/
|
|
10
|
-
export class RustBinaryLocator {
|
|
11
|
-
constructor() {
|
|
12
|
-
this.cachedPath = null;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Find the RustProxy binary path.
|
|
16
|
-
* Returns null if no binary is available.
|
|
17
|
-
*/
|
|
18
|
-
async findBinary() {
|
|
19
|
-
if (this.cachedPath !== null) {
|
|
20
|
-
return this.cachedPath;
|
|
21
|
-
}
|
|
22
|
-
const path = await this.searchBinary();
|
|
23
|
-
this.cachedPath = path;
|
|
24
|
-
return path;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Clear the cached binary path (e.g., after a failed launch).
|
|
28
|
-
*/
|
|
29
|
-
clearCache() {
|
|
30
|
-
this.cachedPath = null;
|
|
31
|
-
}
|
|
32
|
-
async searchBinary() {
|
|
33
|
-
// 1. Environment variable override
|
|
34
|
-
const envPath = process.env.SMARTPROXY_RUST_BINARY;
|
|
35
|
-
if (envPath) {
|
|
36
|
-
if (await this.isExecutable(envPath)) {
|
|
37
|
-
logger.log('info', `RustProxy binary found via SMARTPROXY_RUST_BINARY: ${envPath}`, { component: 'rust-locator' });
|
|
38
|
-
return envPath;
|
|
39
|
-
}
|
|
40
|
-
logger.log('warn', `SMARTPROXY_RUST_BINARY set but not executable: ${envPath}`, { component: 'rust-locator' });
|
|
41
|
-
}
|
|
42
|
-
// 2. Platform-specific optional npm package
|
|
43
|
-
const platformBinary = await this.findPlatformPackageBinary();
|
|
44
|
-
if (platformBinary) {
|
|
45
|
-
logger.log('info', `RustProxy binary found in platform package: ${platformBinary}`, { component: 'rust-locator' });
|
|
46
|
-
return platformBinary;
|
|
47
|
-
}
|
|
48
|
-
// 3. Local development build
|
|
49
|
-
const localPaths = [
|
|
50
|
-
plugins.path.resolve(process.cwd(), 'rust/target/release/rustproxy'),
|
|
51
|
-
plugins.path.resolve(process.cwd(), 'rust/target/debug/rustproxy'),
|
|
52
|
-
];
|
|
53
|
-
for (const localPath of localPaths) {
|
|
54
|
-
if (await this.isExecutable(localPath)) {
|
|
55
|
-
logger.log('info', `RustProxy binary found at local path: ${localPath}`, { component: 'rust-locator' });
|
|
56
|
-
return localPath;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
// 4. System PATH
|
|
60
|
-
const systemPath = await this.findInPath('rustproxy');
|
|
61
|
-
if (systemPath) {
|
|
62
|
-
logger.log('info', `RustProxy binary found in system PATH: ${systemPath}`, { component: 'rust-locator' });
|
|
63
|
-
return systemPath;
|
|
64
|
-
}
|
|
65
|
-
logger.log('error', 'No RustProxy binary found. Set SMARTPROXY_RUST_BINARY, install the platform package, or build with: cd rust && cargo build --release', { component: 'rust-locator' });
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
async findPlatformPackageBinary() {
|
|
69
|
-
const platform = process.platform;
|
|
70
|
-
const arch = process.arch;
|
|
71
|
-
const packageName = `@push.rocks/smartproxy-${platform}-${arch}`;
|
|
72
|
-
try {
|
|
73
|
-
// Try to resolve the platform-specific package
|
|
74
|
-
const packagePath = require.resolve(`${packageName}/rustproxy`);
|
|
75
|
-
if (await this.isExecutable(packagePath)) {
|
|
76
|
-
return packagePath;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// Package not installed - expected for development
|
|
81
|
-
}
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
async isExecutable(filePath) {
|
|
85
|
-
try {
|
|
86
|
-
await plugins.fs.promises.access(filePath, plugins.fs.constants.X_OK);
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
async findInPath(binaryName) {
|
|
94
|
-
const pathDirs = (process.env.PATH || '').split(plugins.path.delimiter);
|
|
95
|
-
for (const dir of pathDirs) {
|
|
96
|
-
const fullPath = plugins.path.join(dir, binaryName);
|
|
97
|
-
if (await this.isExecutable(fullPath)) {
|
|
98
|
-
return fullPath;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVzdC1iaW5hcnktbG9jYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvcnVzdC1iaW5hcnktbG9jYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVwRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLE9BQU8saUJBQWlCO0lBQTlCO1FBQ1UsZUFBVSxHQUFrQixJQUFJLENBQUM7SUFvRzNDLENBQUM7SUFsR0M7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLFVBQVU7UUFDckIsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLElBQUksRUFBRSxDQUFDO1lBQzdCLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQztRQUN6QixDQUFDO1FBRUQsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDdkMsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxVQUFVO1FBQ2YsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7SUFDekIsQ0FBQztJQUVPLEtBQUssQ0FBQyxZQUFZO1FBQ3hCLG1DQUFtQztRQUNuQyxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLHNCQUFzQixDQUFDO1FBQ25ELElBQUksT0FBTyxFQUFFLENBQUM7WUFDWixJQUFJLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNyQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxzREFBc0QsT0FBTyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsY0FBYyxFQUFFLENBQUMsQ0FBQztnQkFDbkgsT0FBTyxPQUFPLENB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
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import type { SmartProxy } from './smart-proxy.js';
|
|
2
|
-
/**
|
|
3
|
-
* Handles security aspects like IP tracking, rate limiting, and authorization
|
|
4
|
-
* for SmartProxy. This is a lightweight wrapper that uses shared utilities.
|
|
5
|
-
*/
|
|
6
|
-
export declare class SecurityManager {
|
|
7
|
-
private smartProxy;
|
|
8
|
-
private connectionsByIP;
|
|
9
|
-
private connectionRateByIP;
|
|
10
|
-
private cleanupInterval;
|
|
11
|
-
constructor(smartProxy: SmartProxy);
|
|
12
|
-
/**
|
|
13
|
-
* Get connections count by IP (checks normalized variants)
|
|
14
|
-
*/
|
|
15
|
-
getConnectionCountByIP(ip: string): number;
|
|
16
|
-
/**
|
|
17
|
-
* Check and update connection rate for an IP
|
|
18
|
-
* @returns true if within rate limit, false if exceeding limit
|
|
19
|
-
*/
|
|
20
|
-
checkConnectionRate(ip: string): boolean;
|
|
21
|
-
/**
|
|
22
|
-
* Track connection by IP
|
|
23
|
-
*/
|
|
24
|
-
trackConnectionByIP(ip: string, connectionId: string): void;
|
|
25
|
-
/**
|
|
26
|
-
* Remove connection tracking for an IP
|
|
27
|
-
*/
|
|
28
|
-
removeConnectionByIP(ip: string, connectionId: string): void;
|
|
29
|
-
/**
|
|
30
|
-
* Check if an IP is authorized using security rules
|
|
31
|
-
*
|
|
32
|
-
* This method is used to determine if an IP is allowed to connect, based on security
|
|
33
|
-
* rules configured in the route configuration. The allowed and blocked IPs are
|
|
34
|
-
* typically derived from route.security.ipAllowList and ipBlockList.
|
|
35
|
-
*
|
|
36
|
-
* @param ip - The IP address to check
|
|
37
|
-
* @param allowedIPs - Array of allowed IP patterns from security.ipAllowList
|
|
38
|
-
* @param blockedIPs - Array of blocked IP patterns from security.ipBlockList
|
|
39
|
-
* @returns true if IP is authorized, false if blocked
|
|
40
|
-
*/
|
|
41
|
-
isIPAuthorized(ip: string, allowedIPs: string[], blockedIPs?: string[]): boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Check if IP should be allowed considering connection rate and max connections
|
|
44
|
-
* @returns Object with result and reason
|
|
45
|
-
*/
|
|
46
|
-
validateIP(ip: string): {
|
|
47
|
-
allowed: boolean;
|
|
48
|
-
reason?: string;
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* Atomically validate an IP and track the connection if allowed.
|
|
52
|
-
* This prevents race conditions where concurrent connections could bypass per-IP limits.
|
|
53
|
-
*
|
|
54
|
-
* @param ip - The IP address to validate
|
|
55
|
-
* @param connectionId - The connection ID to track if validation passes
|
|
56
|
-
* @returns Object with validation result and reason
|
|
57
|
-
*/
|
|
58
|
-
validateAndTrackIP(ip: string, connectionId: string): {
|
|
59
|
-
allowed: boolean;
|
|
60
|
-
reason?: string;
|
|
61
|
-
};
|
|
62
|
-
/**
|
|
63
|
-
* Clears all IP tracking data (for shutdown)
|
|
64
|
-
*/
|
|
65
|
-
clearIPTracking(): void;
|
|
66
|
-
/**
|
|
67
|
-
* Start periodic cleanup of expired data
|
|
68
|
-
*/
|
|
69
|
-
private startPeriodicCleanup;
|
|
70
|
-
/**
|
|
71
|
-
* Perform cleanup of expired rate limits and empty IP entries
|
|
72
|
-
*/
|
|
73
|
-
private performCleanup;
|
|
74
|
-
}
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../../plugins.js';
|
|
2
|
-
import { connectionLogDeduplicator } from '../../core/utils/log-deduplicator.js';
|
|
3
|
-
import { isIPAuthorized, normalizeIP } from '../../core/utils/security-utils.js';
|
|
4
|
-
/**
|
|
5
|
-
* Handles security aspects like IP tracking, rate limiting, and authorization
|
|
6
|
-
* for SmartProxy. This is a lightweight wrapper that uses shared utilities.
|
|
7
|
-
*/
|
|
8
|
-
export class SecurityManager {
|
|
9
|
-
constructor(smartProxy) {
|
|
10
|
-
this.smartProxy = smartProxy;
|
|
11
|
-
this.connectionsByIP = new Map();
|
|
12
|
-
this.connectionRateByIP = new Map();
|
|
13
|
-
this.cleanupInterval = null;
|
|
14
|
-
// Start periodic cleanup every 60 seconds
|
|
15
|
-
this.startPeriodicCleanup();
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Get connections count by IP (checks normalized variants)
|
|
19
|
-
*/
|
|
20
|
-
getConnectionCountByIP(ip) {
|
|
21
|
-
// Check all normalized variants of the IP
|
|
22
|
-
const variants = normalizeIP(ip);
|
|
23
|
-
for (const variant of variants) {
|
|
24
|
-
const connections = this.connectionsByIP.get(variant);
|
|
25
|
-
if (connections) {
|
|
26
|
-
return connections.size;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return 0;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Check and update connection rate for an IP
|
|
33
|
-
* @returns true if within rate limit, false if exceeding limit
|
|
34
|
-
*/
|
|
35
|
-
checkConnectionRate(ip) {
|
|
36
|
-
const now = Date.now();
|
|
37
|
-
const minute = 60 * 1000;
|
|
38
|
-
// Find existing rate tracking (check normalized variants)
|
|
39
|
-
const variants = normalizeIP(ip);
|
|
40
|
-
let existingKey = null;
|
|
41
|
-
for (const variant of variants) {
|
|
42
|
-
if (this.connectionRateByIP.has(variant)) {
|
|
43
|
-
existingKey = variant;
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const key = existingKey || ip;
|
|
48
|
-
if (!this.connectionRateByIP.has(key)) {
|
|
49
|
-
this.connectionRateByIP.set(key, [now]);
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
// Get timestamps and filter out entries older than 1 minute
|
|
53
|
-
const timestamps = this.connectionRateByIP.get(key).filter((time) => now - time < minute);
|
|
54
|
-
timestamps.push(now);
|
|
55
|
-
this.connectionRateByIP.set(key, timestamps);
|
|
56
|
-
// Check if rate exceeds limit
|
|
57
|
-
return timestamps.length <= this.smartProxy.settings.connectionRateLimitPerMinute;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Track connection by IP
|
|
61
|
-
*/
|
|
62
|
-
trackConnectionByIP(ip, connectionId) {
|
|
63
|
-
// Check if any variant already exists
|
|
64
|
-
const variants = normalizeIP(ip);
|
|
65
|
-
let existingKey = null;
|
|
66
|
-
for (const variant of variants) {
|
|
67
|
-
if (this.connectionsByIP.has(variant)) {
|
|
68
|
-
existingKey = variant;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const key = existingKey || ip;
|
|
73
|
-
if (!this.connectionsByIP.has(key)) {
|
|
74
|
-
this.connectionsByIP.set(key, new Set());
|
|
75
|
-
}
|
|
76
|
-
this.connectionsByIP.get(key).add(connectionId);
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Remove connection tracking for an IP
|
|
80
|
-
*/
|
|
81
|
-
removeConnectionByIP(ip, connectionId) {
|
|
82
|
-
// Check all variants to find where the connection is tracked
|
|
83
|
-
const variants = normalizeIP(ip);
|
|
84
|
-
for (const variant of variants) {
|
|
85
|
-
if (this.connectionsByIP.has(variant)) {
|
|
86
|
-
const connections = this.connectionsByIP.get(variant);
|
|
87
|
-
connections.delete(connectionId);
|
|
88
|
-
if (connections.size === 0) {
|
|
89
|
-
this.connectionsByIP.delete(variant);
|
|
90
|
-
}
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Check if an IP is authorized using security rules
|
|
97
|
-
*
|
|
98
|
-
* This method is used to determine if an IP is allowed to connect, based on security
|
|
99
|
-
* rules configured in the route configuration. The allowed and blocked IPs are
|
|
100
|
-
* typically derived from route.security.ipAllowList and ipBlockList.
|
|
101
|
-
*
|
|
102
|
-
* @param ip - The IP address to check
|
|
103
|
-
* @param allowedIPs - Array of allowed IP patterns from security.ipAllowList
|
|
104
|
-
* @param blockedIPs - Array of blocked IP patterns from security.ipBlockList
|
|
105
|
-
* @returns true if IP is authorized, false if blocked
|
|
106
|
-
*/
|
|
107
|
-
isIPAuthorized(ip, allowedIPs, blockedIPs = []) {
|
|
108
|
-
return isIPAuthorized(ip, allowedIPs, blockedIPs);
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Check if IP should be allowed considering connection rate and max connections
|
|
112
|
-
* @returns Object with result and reason
|
|
113
|
-
*/
|
|
114
|
-
validateIP(ip) {
|
|
115
|
-
// Check connection count limit
|
|
116
|
-
if (this.smartProxy.settings.maxConnectionsPerIP &&
|
|
117
|
-
this.getConnectionCountByIP(ip) >= this.smartProxy.settings.maxConnectionsPerIP) {
|
|
118
|
-
return {
|
|
119
|
-
allowed: false,
|
|
120
|
-
reason: `Maximum connections per IP (${this.smartProxy.settings.maxConnectionsPerIP}) exceeded`
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
// Check connection rate limit
|
|
124
|
-
if (this.smartProxy.settings.connectionRateLimitPerMinute &&
|
|
125
|
-
!this.checkConnectionRate(ip)) {
|
|
126
|
-
return {
|
|
127
|
-
allowed: false,
|
|
128
|
-
reason: `Connection rate limit (${this.smartProxy.settings.connectionRateLimitPerMinute}/min) exceeded`
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
return { allowed: true };
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Atomically validate an IP and track the connection if allowed.
|
|
135
|
-
* This prevents race conditions where concurrent connections could bypass per-IP limits.
|
|
136
|
-
*
|
|
137
|
-
* @param ip - The IP address to validate
|
|
138
|
-
* @param connectionId - The connection ID to track if validation passes
|
|
139
|
-
* @returns Object with validation result and reason
|
|
140
|
-
*/
|
|
141
|
-
validateAndTrackIP(ip, connectionId) {
|
|
142
|
-
// Check connection count limit BEFORE tracking
|
|
143
|
-
if (this.smartProxy.settings.maxConnectionsPerIP &&
|
|
144
|
-
this.getConnectionCountByIP(ip) >= this.smartProxy.settings.maxConnectionsPerIP) {
|
|
145
|
-
return {
|
|
146
|
-
allowed: false,
|
|
147
|
-
reason: `Maximum connections per IP (${this.smartProxy.settings.maxConnectionsPerIP}) exceeded`
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
// Check connection rate limit
|
|
151
|
-
if (this.smartProxy.settings.connectionRateLimitPerMinute &&
|
|
152
|
-
!this.checkConnectionRate(ip)) {
|
|
153
|
-
return {
|
|
154
|
-
allowed: false,
|
|
155
|
-
reason: `Connection rate limit (${this.smartProxy.settings.connectionRateLimitPerMinute}/min) exceeded`
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
// Validation passed - immediately track to prevent race conditions
|
|
159
|
-
this.trackConnectionByIP(ip, connectionId);
|
|
160
|
-
return { allowed: true };
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Clears all IP tracking data (for shutdown)
|
|
164
|
-
*/
|
|
165
|
-
clearIPTracking() {
|
|
166
|
-
if (this.cleanupInterval) {
|
|
167
|
-
clearInterval(this.cleanupInterval);
|
|
168
|
-
this.cleanupInterval = null;
|
|
169
|
-
}
|
|
170
|
-
this.connectionsByIP.clear();
|
|
171
|
-
this.connectionRateByIP.clear();
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Start periodic cleanup of expired data
|
|
175
|
-
*/
|
|
176
|
-
startPeriodicCleanup() {
|
|
177
|
-
this.cleanupInterval = setInterval(() => {
|
|
178
|
-
this.performCleanup();
|
|
179
|
-
}, 60000); // Run every minute
|
|
180
|
-
// Unref the timer so it doesn't keep the process alive
|
|
181
|
-
if (this.cleanupInterval.unref) {
|
|
182
|
-
this.cleanupInterval.unref();
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Perform cleanup of expired rate limits and empty IP entries
|
|
187
|
-
*/
|
|
188
|
-
performCleanup() {
|
|
189
|
-
const now = Date.now();
|
|
190
|
-
const minute = 60 * 1000;
|
|
191
|
-
let cleanedRateLimits = 0;
|
|
192
|
-
let cleanedIPs = 0;
|
|
193
|
-
// Clean up expired rate limit timestamps
|
|
194
|
-
for (const [ip, timestamps] of this.connectionRateByIP.entries()) {
|
|
195
|
-
const validTimestamps = timestamps.filter(time => now - time < minute);
|
|
196
|
-
if (validTimestamps.length === 0) {
|
|
197
|
-
// No valid timestamps, remove the IP entry
|
|
198
|
-
this.connectionRateByIP.delete(ip);
|
|
199
|
-
cleanedRateLimits++;
|
|
200
|
-
}
|
|
201
|
-
else if (validTimestamps.length < timestamps.length) {
|
|
202
|
-
// Some timestamps expired, update with valid ones
|
|
203
|
-
this.connectionRateByIP.set(ip, validTimestamps);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
// Clean up IPs with no active connections
|
|
207
|
-
for (const [ip, connections] of this.connectionsByIP.entries()) {
|
|
208
|
-
if (connections.size === 0) {
|
|
209
|
-
this.connectionsByIP.delete(ip);
|
|
210
|
-
cleanedIPs++;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
// Log cleanup stats if anything was cleaned
|
|
214
|
-
if (cleanedRateLimits > 0 || cleanedIPs > 0) {
|
|
215
|
-
if (this.smartProxy.settings.enableDetailedLogging) {
|
|
216
|
-
connectionLogDeduplicator.log('ip-cleanup', 'debug', 'IP tracking cleanup completed', {
|
|
217
|
-
cleanedRateLimits,
|
|
218
|
-
cleanedIPs,
|
|
219
|
-
remainingIPs: this.connectionsByIP.size,
|
|
220
|
-
remainingRateLimits: this.connectionRateByIP.size,
|
|
221
|
-
component: 'security-manager'
|
|
222
|
-
}, 'periodic-cleanup');
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjdXJpdHktbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvc2VjdXJpdHktbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBRTVDLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQ2pGLE9BQU8sRUFBRSxjQUFjLEVBQUUsV0FBVyxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFFakY7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLGVBQWU7SUFLMUIsWUFBb0IsVUFBc0I7UUFBdEIsZUFBVSxHQUFWLFVBQVUsQ0FBWTtRQUpsQyxvQkFBZSxHQUE2QixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ3RELHVCQUFrQixHQUEwQixJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ3RELG9CQUFlLEdBQTBCLElBQUksQ0FBQztRQUdwRCwwQ0FBMEM7UUFDMUMsSUFBSSxDQUFDLG9CQUFvQixFQUFFLENBQUM7SUFDOUIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksc0JBQXNCLENBQUMsRUFBVTtRQUN0QywwQ0FBMEM7UUFDMUMsTUFBTSxRQUFRLEdBQUcsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ2pDLEtBQUssTUFBTSxPQUFPLElBQUksUUFBUSxFQUFFLENBQUM7WUFDL0IsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDdEQsSUFBSSxXQUFXLEVBQUUsQ0FBQztnQkFDaEIsT0FBTyxXQUFXLENBQUMsSUFBSSxDQUFDO1lBQzFCLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksbUJBQW1CLENBQUMsRUFBVTtRQUNuQyxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxF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
|