@push.rocks/smartproxy 3.41.8 → 4.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/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/classes.pp.acmemanager.d.ts +34 -0
- package/dist_ts/classes.pp.acmemanager.js +123 -0
- package/dist_ts/classes.pp.connectionhandler.d.ts +39 -0
- package/dist_ts/classes.pp.connectionhandler.js +693 -0
- package/dist_ts/classes.pp.connectionmanager.d.ts +78 -0
- package/dist_ts/classes.pp.connectionmanager.js +378 -0
- package/dist_ts/classes.pp.domainconfigmanager.d.ts +55 -0
- package/dist_ts/classes.pp.domainconfigmanager.js +103 -0
- package/dist_ts/classes.pp.interfaces.d.ts +109 -0
- package/dist_ts/classes.pp.interfaces.js +2 -0
- package/dist_ts/classes.pp.networkproxybridge.d.ts +43 -0
- package/dist_ts/classes.pp.networkproxybridge.js +211 -0
- package/dist_ts/classes.pp.portproxy.d.ts +48 -0
- package/dist_ts/classes.pp.portproxy.js +268 -0
- package/dist_ts/classes.pp.portrangemanager.d.ts +56 -0
- package/dist_ts/classes.pp.portrangemanager.js +179 -0
- package/dist_ts/classes.pp.securitymanager.d.ts +47 -0
- package/dist_ts/classes.pp.securitymanager.js +126 -0
- package/dist_ts/classes.pp.snihandler.d.ts +198 -0
- package/dist_ts/classes.pp.snihandler.js +1210 -0
- package/dist_ts/classes.pp.timeoutmanager.d.ts +47 -0
- package/dist_ts/classes.pp.timeoutmanager.js +154 -0
- package/dist_ts/classes.pp.tlsmanager.d.ts +57 -0
- package/dist_ts/classes.pp.tlsmanager.js +132 -0
- package/dist_ts/index.d.ts +2 -2
- package/dist_ts/index.js +3 -3
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.pp.acmemanager.ts +149 -0
- package/ts/classes.pp.connectionhandler.ts +982 -0
- package/ts/classes.pp.connectionmanager.ts +446 -0
- package/ts/classes.pp.domainconfigmanager.ts +123 -0
- package/ts/classes.pp.interfaces.ts +136 -0
- package/ts/classes.pp.networkproxybridge.ts +258 -0
- package/ts/classes.pp.portproxy.ts +344 -0
- package/ts/classes.pp.portrangemanager.ts +214 -0
- package/ts/classes.pp.securitymanager.ts +147 -0
- package/ts/{classes.snihandler.ts → classes.pp.snihandler.ts} +1 -1
- package/ts/classes.pp.timeoutmanager.ts +190 -0
- package/ts/classes.pp.tlsmanager.ts +206 -0
- package/ts/index.ts +2 -2
- package/ts/classes.portproxy.ts +0 -2503
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { NetworkProxy } from './classes.networkproxy.js';
|
|
3
|
+
import type { IConnectionRecord, IPortProxySettings, IDomainConfig } from './classes.pp.interfaces.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Manages NetworkProxy integration for TLS termination
|
|
7
|
+
*/
|
|
8
|
+
export class NetworkProxyBridge {
|
|
9
|
+
private networkProxy: NetworkProxy | null = null;
|
|
10
|
+
|
|
11
|
+
constructor(private settings: IPortProxySettings) {}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initialize NetworkProxy instance
|
|
15
|
+
*/
|
|
16
|
+
public async initialize(): Promise<void> {
|
|
17
|
+
if (!this.networkProxy && this.settings.useNetworkProxy && this.settings.useNetworkProxy.length > 0) {
|
|
18
|
+
// Configure NetworkProxy options based on PortProxy settings
|
|
19
|
+
const networkProxyOptions: any = {
|
|
20
|
+
port: this.settings.networkProxyPort!,
|
|
21
|
+
portProxyIntegration: true,
|
|
22
|
+
logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Add ACME settings if configured
|
|
26
|
+
if (this.settings.acme) {
|
|
27
|
+
networkProxyOptions.acme = { ...this.settings.acme };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.networkProxy = new NetworkProxy(networkProxyOptions);
|
|
31
|
+
|
|
32
|
+
console.log(`Initialized NetworkProxy on port ${this.settings.networkProxyPort}`);
|
|
33
|
+
|
|
34
|
+
// Convert and apply domain configurations to NetworkProxy
|
|
35
|
+
await this.syncDomainConfigsToNetworkProxy();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get the NetworkProxy instance
|
|
41
|
+
*/
|
|
42
|
+
public getNetworkProxy(): NetworkProxy | null {
|
|
43
|
+
return this.networkProxy;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get the NetworkProxy port
|
|
48
|
+
*/
|
|
49
|
+
public getNetworkProxyPort(): number {
|
|
50
|
+
return this.networkProxy ? this.networkProxy.getListeningPort() : this.settings.networkProxyPort || 8443;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Start NetworkProxy
|
|
55
|
+
*/
|
|
56
|
+
public async start(): Promise<void> {
|
|
57
|
+
if (this.networkProxy) {
|
|
58
|
+
await this.networkProxy.start();
|
|
59
|
+
console.log(`NetworkProxy started on port ${this.settings.networkProxyPort}`);
|
|
60
|
+
|
|
61
|
+
// Log ACME status
|
|
62
|
+
if (this.settings.acme?.enabled) {
|
|
63
|
+
console.log(
|
|
64
|
+
`ACME certificate management is enabled (${
|
|
65
|
+
this.settings.acme.useProduction ? 'Production' : 'Staging'
|
|
66
|
+
} mode)`
|
|
67
|
+
);
|
|
68
|
+
console.log(`ACME HTTP challenge server on port ${this.settings.acme.port}`);
|
|
69
|
+
|
|
70
|
+
// Register domains for ACME certificates if enabled
|
|
71
|
+
if (this.networkProxy.options.acme?.enabled) {
|
|
72
|
+
console.log('Registering domains with ACME certificate manager...');
|
|
73
|
+
// The NetworkProxy will handle this internally via registerDomainsWithAcmeManager()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Stop NetworkProxy
|
|
81
|
+
*/
|
|
82
|
+
public async stop(): Promise<void> {
|
|
83
|
+
if (this.networkProxy) {
|
|
84
|
+
try {
|
|
85
|
+
console.log('Stopping NetworkProxy...');
|
|
86
|
+
await this.networkProxy.stop();
|
|
87
|
+
console.log('NetworkProxy stopped successfully');
|
|
88
|
+
|
|
89
|
+
// Log ACME shutdown if it was enabled
|
|
90
|
+
if (this.settings.acme?.enabled) {
|
|
91
|
+
console.log('ACME certificate manager stopped');
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.log(`Error stopping NetworkProxy: ${err}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Forwards a TLS connection to a NetworkProxy for handling
|
|
101
|
+
*/
|
|
102
|
+
public forwardToNetworkProxy(
|
|
103
|
+
connectionId: string,
|
|
104
|
+
socket: plugins.net.Socket,
|
|
105
|
+
record: IConnectionRecord,
|
|
106
|
+
initialData: Buffer,
|
|
107
|
+
customProxyPort?: number,
|
|
108
|
+
onError?: (reason: string) => void
|
|
109
|
+
): void {
|
|
110
|
+
// Ensure NetworkProxy is initialized
|
|
111
|
+
if (!this.networkProxy) {
|
|
112
|
+
console.log(
|
|
113
|
+
`[${connectionId}] NetworkProxy not initialized. Cannot forward connection.`
|
|
114
|
+
);
|
|
115
|
+
if (onError) {
|
|
116
|
+
onError('network_proxy_not_initialized');
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Use the custom port if provided, otherwise use the default NetworkProxy port
|
|
122
|
+
const proxyPort = customProxyPort || this.networkProxy.getListeningPort();
|
|
123
|
+
const proxyHost = 'localhost'; // Assuming NetworkProxy runs locally
|
|
124
|
+
|
|
125
|
+
if (this.settings.enableDetailedLogging) {
|
|
126
|
+
console.log(
|
|
127
|
+
`[${connectionId}] Forwarding TLS connection to NetworkProxy at ${proxyHost}:${proxyPort}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Create a connection to the NetworkProxy
|
|
132
|
+
const proxySocket = plugins.net.connect({
|
|
133
|
+
host: proxyHost,
|
|
134
|
+
port: proxyPort,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Store the outgoing socket in the record
|
|
138
|
+
record.outgoing = proxySocket;
|
|
139
|
+
record.outgoingStartTime = Date.now();
|
|
140
|
+
record.usingNetworkProxy = true;
|
|
141
|
+
|
|
142
|
+
// Set up error handlers
|
|
143
|
+
proxySocket.on('error', (err) => {
|
|
144
|
+
console.log(`[${connectionId}] Error connecting to NetworkProxy: ${err.message}`);
|
|
145
|
+
if (onError) {
|
|
146
|
+
onError('network_proxy_connect_error');
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Handle connection to NetworkProxy
|
|
151
|
+
proxySocket.on('connect', () => {
|
|
152
|
+
if (this.settings.enableDetailedLogging) {
|
|
153
|
+
console.log(`[${connectionId}] Connected to NetworkProxy at ${proxyHost}:${proxyPort}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// First send the initial data that contains the TLS ClientHello
|
|
157
|
+
proxySocket.write(initialData);
|
|
158
|
+
|
|
159
|
+
// Now set up bidirectional piping between client and NetworkProxy
|
|
160
|
+
socket.pipe(proxySocket);
|
|
161
|
+
proxySocket.pipe(socket);
|
|
162
|
+
|
|
163
|
+
// Update activity on data transfer (caller should handle this)
|
|
164
|
+
if (this.settings.enableDetailedLogging) {
|
|
165
|
+
console.log(`[${connectionId}] TLS connection successfully forwarded to NetworkProxy`);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Synchronizes domain configurations to NetworkProxy
|
|
172
|
+
*/
|
|
173
|
+
public async syncDomainConfigsToNetworkProxy(): Promise<void> {
|
|
174
|
+
if (!this.networkProxy) {
|
|
175
|
+
console.log('Cannot sync configurations - NetworkProxy not initialized');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
// Get SSL certificates from assets
|
|
181
|
+
// Import fs directly since it's not in plugins
|
|
182
|
+
const fs = await import('fs');
|
|
183
|
+
|
|
184
|
+
let certPair;
|
|
185
|
+
try {
|
|
186
|
+
certPair = {
|
|
187
|
+
key: fs.readFileSync('assets/certs/key.pem', 'utf8'),
|
|
188
|
+
cert: fs.readFileSync('assets/certs/cert.pem', 'utf8'),
|
|
189
|
+
};
|
|
190
|
+
} catch (certError) {
|
|
191
|
+
console.log(`Warning: Could not read default certificates: ${certError}`);
|
|
192
|
+
console.log(
|
|
193
|
+
'Using empty certificate placeholders - ACME will generate proper certificates if enabled'
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Use empty placeholders - NetworkProxy will use its internal defaults
|
|
197
|
+
// or ACME will generate proper ones if enabled
|
|
198
|
+
certPair = {
|
|
199
|
+
key: '',
|
|
200
|
+
cert: '',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Convert domain configs to NetworkProxy configs
|
|
205
|
+
const proxyConfigs = this.networkProxy.convertPortProxyConfigs(
|
|
206
|
+
this.settings.domainConfigs,
|
|
207
|
+
certPair
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// Log ACME-eligible domains if ACME is enabled
|
|
211
|
+
if (this.settings.acme?.enabled) {
|
|
212
|
+
const acmeEligibleDomains = proxyConfigs
|
|
213
|
+
.filter((config) => !config.hostName.includes('*')) // Exclude wildcards
|
|
214
|
+
.map((config) => config.hostName);
|
|
215
|
+
|
|
216
|
+
if (acmeEligibleDomains.length > 0) {
|
|
217
|
+
console.log(`Domains eligible for ACME certificates: ${acmeEligibleDomains.join(', ')}`);
|
|
218
|
+
} else {
|
|
219
|
+
console.log('No domains eligible for ACME certificates found in configuration');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Update NetworkProxy with the converted configs
|
|
224
|
+
await this.networkProxy.updateProxyConfigs(proxyConfigs);
|
|
225
|
+
console.log(`Successfully synchronized ${proxyConfigs.length} domain configurations to NetworkProxy`);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
console.log(`Failed to sync configurations: ${err}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Request a certificate for a specific domain
|
|
233
|
+
*/
|
|
234
|
+
public async requestCertificate(domain: string): Promise<boolean> {
|
|
235
|
+
if (!this.networkProxy) {
|
|
236
|
+
console.log('Cannot request certificate - NetworkProxy not initialized');
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!this.settings.acme?.enabled) {
|
|
241
|
+
console.log('Cannot request certificate - ACME is not enabled');
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const result = await this.networkProxy.requestCertificate(domain);
|
|
247
|
+
if (result) {
|
|
248
|
+
console.log(`Certificate request for ${domain} submitted successfully`);
|
|
249
|
+
} else {
|
|
250
|
+
console.log(`Certificate request for ${domain} failed`);
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
} catch (err) {
|
|
254
|
+
console.log(`Error requesting certificate: ${err}`);
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IPortProxySettings, IDomainConfig } from './classes.pp.interfaces.js';
|
|
3
|
+
import { ConnectionManager } from './classes.pp.connectionmanager.js';
|
|
4
|
+
import { SecurityManager } from './classes.pp.securitymanager.js';
|
|
5
|
+
import { DomainConfigManager } from './classes.pp.domainconfigmanager.js';
|
|
6
|
+
import { TlsManager } from './classes.pp.tlsmanager.js';
|
|
7
|
+
import { NetworkProxyBridge } from './classes.pp.networkproxybridge.js';
|
|
8
|
+
import { TimeoutManager } from './classes.pp.timeoutmanager.js';
|
|
9
|
+
import { AcmeManager } from './classes.pp.acmemanager.js';
|
|
10
|
+
import { PortRangeManager } from './classes.pp.portrangemanager.js';
|
|
11
|
+
import { ConnectionHandler } from './classes.pp.connectionhandler.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* PortProxy - Main class that coordinates all components
|
|
15
|
+
*/
|
|
16
|
+
export class PortProxy {
|
|
17
|
+
private netServers: plugins.net.Server[] = [];
|
|
18
|
+
private connectionLogger: NodeJS.Timeout | null = null;
|
|
19
|
+
private isShuttingDown: boolean = false;
|
|
20
|
+
|
|
21
|
+
// Component managers
|
|
22
|
+
private connectionManager: ConnectionManager;
|
|
23
|
+
private securityManager: SecurityManager;
|
|
24
|
+
public domainConfigManager: DomainConfigManager;
|
|
25
|
+
private tlsManager: TlsManager;
|
|
26
|
+
private networkProxyBridge: NetworkProxyBridge;
|
|
27
|
+
private timeoutManager: TimeoutManager;
|
|
28
|
+
private acmeManager: AcmeManager;
|
|
29
|
+
private portRangeManager: PortRangeManager;
|
|
30
|
+
private connectionHandler: ConnectionHandler;
|
|
31
|
+
|
|
32
|
+
constructor(settingsArg: IPortProxySettings) {
|
|
33
|
+
// Set reasonable defaults for all settings
|
|
34
|
+
this.settings = {
|
|
35
|
+
...settingsArg,
|
|
36
|
+
targetIP: settingsArg.targetIP || 'localhost',
|
|
37
|
+
initialDataTimeout: settingsArg.initialDataTimeout || 120000,
|
|
38
|
+
socketTimeout: settingsArg.socketTimeout || 3600000,
|
|
39
|
+
inactivityCheckInterval: settingsArg.inactivityCheckInterval || 60000,
|
|
40
|
+
maxConnectionLifetime: settingsArg.maxConnectionLifetime || 86400000,
|
|
41
|
+
inactivityTimeout: settingsArg.inactivityTimeout || 14400000,
|
|
42
|
+
gracefulShutdownTimeout: settingsArg.gracefulShutdownTimeout || 30000,
|
|
43
|
+
noDelay: settingsArg.noDelay !== undefined ? settingsArg.noDelay : true,
|
|
44
|
+
keepAlive: settingsArg.keepAlive !== undefined ? settingsArg.keepAlive : true,
|
|
45
|
+
keepAliveInitialDelay: settingsArg.keepAliveInitialDelay || 10000,
|
|
46
|
+
maxPendingDataSize: settingsArg.maxPendingDataSize || 10 * 1024 * 1024,
|
|
47
|
+
disableInactivityCheck: settingsArg.disableInactivityCheck || false,
|
|
48
|
+
enableKeepAliveProbes:
|
|
49
|
+
settingsArg.enableKeepAliveProbes !== undefined ? settingsArg.enableKeepAliveProbes : true,
|
|
50
|
+
enableDetailedLogging: settingsArg.enableDetailedLogging || false,
|
|
51
|
+
enableTlsDebugLogging: settingsArg.enableTlsDebugLogging || false,
|
|
52
|
+
enableRandomizedTimeouts: settingsArg.enableRandomizedTimeouts || false,
|
|
53
|
+
allowSessionTicket:
|
|
54
|
+
settingsArg.allowSessionTicket !== undefined ? settingsArg.allowSessionTicket : true,
|
|
55
|
+
maxConnectionsPerIP: settingsArg.maxConnectionsPerIP || 100,
|
|
56
|
+
connectionRateLimitPerMinute: settingsArg.connectionRateLimitPerMinute || 300,
|
|
57
|
+
keepAliveTreatment: settingsArg.keepAliveTreatment || 'extended',
|
|
58
|
+
keepAliveInactivityMultiplier: settingsArg.keepAliveInactivityMultiplier || 6,
|
|
59
|
+
extendedKeepAliveLifetime: settingsArg.extendedKeepAliveLifetime || 7 * 24 * 60 * 60 * 1000,
|
|
60
|
+
networkProxyPort: settingsArg.networkProxyPort || 8443,
|
|
61
|
+
acme: settingsArg.acme || {
|
|
62
|
+
enabled: false,
|
|
63
|
+
port: 80,
|
|
64
|
+
contactEmail: 'admin@example.com',
|
|
65
|
+
useProduction: false,
|
|
66
|
+
renewThresholdDays: 30,
|
|
67
|
+
autoRenew: true,
|
|
68
|
+
certificateStore: './certs',
|
|
69
|
+
skipConfiguredCerts: false,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Initialize component managers
|
|
74
|
+
this.timeoutManager = new TimeoutManager(this.settings);
|
|
75
|
+
this.securityManager = new SecurityManager(this.settings);
|
|
76
|
+
this.connectionManager = new ConnectionManager(
|
|
77
|
+
this.settings,
|
|
78
|
+
this.securityManager,
|
|
79
|
+
this.timeoutManager
|
|
80
|
+
);
|
|
81
|
+
this.domainConfigManager = new DomainConfigManager(this.settings);
|
|
82
|
+
this.tlsManager = new TlsManager(this.settings);
|
|
83
|
+
this.networkProxyBridge = new NetworkProxyBridge(this.settings);
|
|
84
|
+
this.portRangeManager = new PortRangeManager(this.settings);
|
|
85
|
+
this.acmeManager = new AcmeManager(this.settings, this.networkProxyBridge);
|
|
86
|
+
|
|
87
|
+
// Initialize connection handler
|
|
88
|
+
this.connectionHandler = new ConnectionHandler(
|
|
89
|
+
this.settings,
|
|
90
|
+
this.connectionManager,
|
|
91
|
+
this.securityManager,
|
|
92
|
+
this.domainConfigManager,
|
|
93
|
+
this.tlsManager,
|
|
94
|
+
this.networkProxyBridge,
|
|
95
|
+
this.timeoutManager,
|
|
96
|
+
this.portRangeManager
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The settings for the port proxy
|
|
102
|
+
*/
|
|
103
|
+
public settings: IPortProxySettings;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Start the proxy server
|
|
107
|
+
*/
|
|
108
|
+
public async start() {
|
|
109
|
+
// Don't start if already shutting down
|
|
110
|
+
if (this.isShuttingDown) {
|
|
111
|
+
console.log("Cannot start PortProxy while it's shutting down");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Initialize and start NetworkProxy if needed
|
|
116
|
+
if (
|
|
117
|
+
this.settings.useNetworkProxy &&
|
|
118
|
+
this.settings.useNetworkProxy.length > 0
|
|
119
|
+
) {
|
|
120
|
+
await this.networkProxyBridge.initialize();
|
|
121
|
+
await this.networkProxyBridge.start();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Validate port configuration
|
|
125
|
+
const configWarnings = this.portRangeManager.validateConfiguration();
|
|
126
|
+
if (configWarnings.length > 0) {
|
|
127
|
+
console.log("Port configuration warnings:");
|
|
128
|
+
for (const warning of configWarnings) {
|
|
129
|
+
console.log(` - ${warning}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Get listening ports from PortRangeManager
|
|
134
|
+
const listeningPorts = this.portRangeManager.getListeningPorts();
|
|
135
|
+
|
|
136
|
+
// Create servers for each port
|
|
137
|
+
for (const port of listeningPorts) {
|
|
138
|
+
const server = plugins.net.createServer((socket) => {
|
|
139
|
+
// Check if shutting down
|
|
140
|
+
if (this.isShuttingDown) {
|
|
141
|
+
socket.end();
|
|
142
|
+
socket.destroy();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Delegate to connection handler
|
|
147
|
+
this.connectionHandler.handleConnection(socket);
|
|
148
|
+
}).on('error', (err: Error) => {
|
|
149
|
+
console.log(`Server Error on port ${port}: ${err.message}`);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
server.listen(port, () => {
|
|
153
|
+
const isNetworkProxyPort = this.settings.useNetworkProxy?.includes(port);
|
|
154
|
+
console.log(
|
|
155
|
+
`PortProxy -> OK: Now listening on port ${port}${
|
|
156
|
+
this.settings.sniEnabled && !isNetworkProxyPort ? ' (SNI passthrough enabled)' : ''
|
|
157
|
+
}${isNetworkProxyPort ? ' (NetworkProxy forwarding enabled)' : ''}`
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
this.netServers.push(server);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Set up periodic connection logging and inactivity checks
|
|
165
|
+
this.connectionLogger = setInterval(() => {
|
|
166
|
+
// Immediately return if shutting down
|
|
167
|
+
if (this.isShuttingDown) return;
|
|
168
|
+
|
|
169
|
+
// Perform inactivity check
|
|
170
|
+
this.connectionManager.performInactivityCheck();
|
|
171
|
+
|
|
172
|
+
// Log connection statistics
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
let maxIncoming = 0;
|
|
175
|
+
let maxOutgoing = 0;
|
|
176
|
+
let tlsConnections = 0;
|
|
177
|
+
let nonTlsConnections = 0;
|
|
178
|
+
let completedTlsHandshakes = 0;
|
|
179
|
+
let pendingTlsHandshakes = 0;
|
|
180
|
+
let keepAliveConnections = 0;
|
|
181
|
+
let networkProxyConnections = 0;
|
|
182
|
+
|
|
183
|
+
// Get connection records for analysis
|
|
184
|
+
const connectionRecords = this.connectionManager.getConnections();
|
|
185
|
+
|
|
186
|
+
// Analyze active connections
|
|
187
|
+
for (const record of connectionRecords.values()) {
|
|
188
|
+
// Track connection stats
|
|
189
|
+
if (record.isTLS) {
|
|
190
|
+
tlsConnections++;
|
|
191
|
+
if (record.tlsHandshakeComplete) {
|
|
192
|
+
completedTlsHandshakes++;
|
|
193
|
+
} else {
|
|
194
|
+
pendingTlsHandshakes++;
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
nonTlsConnections++;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (record.hasKeepAlive) {
|
|
201
|
+
keepAliveConnections++;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (record.usingNetworkProxy) {
|
|
205
|
+
networkProxyConnections++;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
maxIncoming = Math.max(maxIncoming, now - record.incomingStartTime);
|
|
209
|
+
if (record.outgoingStartTime) {
|
|
210
|
+
maxOutgoing = Math.max(maxOutgoing, now - record.outgoingStartTime);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Get termination stats
|
|
215
|
+
const terminationStats = this.connectionManager.getTerminationStats();
|
|
216
|
+
|
|
217
|
+
// Log detailed stats
|
|
218
|
+
console.log(
|
|
219
|
+
`Active connections: ${connectionRecords.size}. ` +
|
|
220
|
+
`Types: TLS=${tlsConnections} (Completed=${completedTlsHandshakes}, Pending=${pendingTlsHandshakes}), ` +
|
|
221
|
+
`Non-TLS=${nonTlsConnections}, KeepAlive=${keepAliveConnections}, NetworkProxy=${networkProxyConnections}. ` +
|
|
222
|
+
`Longest running: IN=${plugins.prettyMs(maxIncoming)}, OUT=${plugins.prettyMs(maxOutgoing)}. ` +
|
|
223
|
+
`Termination stats: ${JSON.stringify({
|
|
224
|
+
IN: terminationStats.incoming,
|
|
225
|
+
OUT: terminationStats.outgoing,
|
|
226
|
+
})}`
|
|
227
|
+
);
|
|
228
|
+
}, this.settings.inactivityCheckInterval || 60000);
|
|
229
|
+
|
|
230
|
+
// Make sure the interval doesn't keep the process alive
|
|
231
|
+
if (this.connectionLogger.unref) {
|
|
232
|
+
this.connectionLogger.unref();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Stop the proxy server
|
|
238
|
+
*/
|
|
239
|
+
public async stop() {
|
|
240
|
+
console.log('PortProxy shutting down...');
|
|
241
|
+
this.isShuttingDown = true;
|
|
242
|
+
|
|
243
|
+
// Stop accepting new connections
|
|
244
|
+
const closeServerPromises: Promise<void>[] = this.netServers.map(
|
|
245
|
+
(server) =>
|
|
246
|
+
new Promise<void>((resolve) => {
|
|
247
|
+
if (!server.listening) {
|
|
248
|
+
resolve();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
server.close((err) => {
|
|
252
|
+
if (err) {
|
|
253
|
+
console.log(`Error closing server: ${err.message}`);
|
|
254
|
+
}
|
|
255
|
+
resolve();
|
|
256
|
+
});
|
|
257
|
+
})
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// Stop the connection logger
|
|
261
|
+
if (this.connectionLogger) {
|
|
262
|
+
clearInterval(this.connectionLogger);
|
|
263
|
+
this.connectionLogger = null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Wait for servers to close
|
|
267
|
+
await Promise.all(closeServerPromises);
|
|
268
|
+
console.log('All servers closed. Cleaning up active connections...');
|
|
269
|
+
|
|
270
|
+
// Clean up all active connections
|
|
271
|
+
this.connectionManager.clearConnections();
|
|
272
|
+
|
|
273
|
+
// Stop NetworkProxy
|
|
274
|
+
await this.networkProxyBridge.stop();
|
|
275
|
+
|
|
276
|
+
// Clear all servers
|
|
277
|
+
this.netServers = [];
|
|
278
|
+
|
|
279
|
+
console.log('PortProxy shutdown complete.');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Updates the domain configurations for the proxy
|
|
284
|
+
*/
|
|
285
|
+
public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
|
|
286
|
+
console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
|
|
287
|
+
|
|
288
|
+
// Update domain configs in DomainConfigManager
|
|
289
|
+
this.domainConfigManager.updateDomainConfigs(newDomainConfigs);
|
|
290
|
+
|
|
291
|
+
// If NetworkProxy is initialized, resync the configurations
|
|
292
|
+
if (this.networkProxyBridge.getNetworkProxy()) {
|
|
293
|
+
await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Updates the ACME certificate settings
|
|
299
|
+
*/
|
|
300
|
+
public async updateAcmeSettings(acmeSettings: IPortProxySettings['acme']): Promise<void> {
|
|
301
|
+
console.log('Updating ACME certificate settings');
|
|
302
|
+
|
|
303
|
+
// Delegate to AcmeManager
|
|
304
|
+
await this.acmeManager.updateAcmeSettings(acmeSettings);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Requests a certificate for a specific domain
|
|
309
|
+
*/
|
|
310
|
+
public async requestCertificate(domain: string): Promise<boolean> {
|
|
311
|
+
// Delegate to AcmeManager
|
|
312
|
+
return this.acmeManager.requestCertificate(domain);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get statistics about current connections
|
|
317
|
+
*/
|
|
318
|
+
public getStatistics(): any {
|
|
319
|
+
const connectionRecords = this.connectionManager.getConnections();
|
|
320
|
+
const terminationStats = this.connectionManager.getTerminationStats();
|
|
321
|
+
|
|
322
|
+
let tlsConnections = 0;
|
|
323
|
+
let nonTlsConnections = 0;
|
|
324
|
+
let keepAliveConnections = 0;
|
|
325
|
+
let networkProxyConnections = 0;
|
|
326
|
+
|
|
327
|
+
// Analyze active connections
|
|
328
|
+
for (const record of connectionRecords.values()) {
|
|
329
|
+
if (record.isTLS) tlsConnections++;
|
|
330
|
+
else nonTlsConnections++;
|
|
331
|
+
if (record.hasKeepAlive) keepAliveConnections++;
|
|
332
|
+
if (record.usingNetworkProxy) networkProxyConnections++;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
activeConnections: connectionRecords.size,
|
|
337
|
+
tlsConnections,
|
|
338
|
+
nonTlsConnections,
|
|
339
|
+
keepAliveConnections,
|
|
340
|
+
networkProxyConnections,
|
|
341
|
+
terminationStats
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|