@push.rocks/smartproxy 3.9.4 → 3.10.1
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.
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '3.
|
|
6
|
+
version: '3.10.1',
|
|
7
7
|
description: 'a proxy for handling high workloads of proxying'
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLGlEQUFpRDtDQUMvRCxDQUFBIn0=
|
|
@@ -20,7 +20,9 @@ export declare class PortProxy {
|
|
|
20
20
|
private incomingConnectionTimes;
|
|
21
21
|
private outgoingConnectionTimes;
|
|
22
22
|
private connectionLogger;
|
|
23
|
+
private terminationStats;
|
|
23
24
|
constructor(settings: IProxySettings);
|
|
25
|
+
private incrementTerminationStat;
|
|
24
26
|
start(): Promise<void>;
|
|
25
27
|
stop(): Promise<void>;
|
|
26
28
|
}
|
|
@@ -92,22 +92,32 @@ export class PortProxy {
|
|
|
92
92
|
// Record start times for outgoing connections
|
|
93
93
|
this.outgoingConnectionTimes = new Map();
|
|
94
94
|
this.connectionLogger = null;
|
|
95
|
+
// Overall termination statistics
|
|
96
|
+
this.terminationStats = {
|
|
97
|
+
incoming: {},
|
|
98
|
+
outgoing: {},
|
|
99
|
+
};
|
|
95
100
|
this.settings = {
|
|
96
101
|
...settings,
|
|
97
102
|
toHost: settings.toHost || 'localhost'
|
|
98
103
|
};
|
|
99
104
|
}
|
|
105
|
+
// Helper to update termination stats.
|
|
106
|
+
incrementTerminationStat(side, reason) {
|
|
107
|
+
if (!this.terminationStats[side][reason]) {
|
|
108
|
+
this.terminationStats[side][reason] = 1;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
this.terminationStats[side][reason]++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
100
114
|
async start() {
|
|
101
|
-
// Adjusted cleanUpSockets
|
|
115
|
+
// Adjusted cleanUpSockets: forcefully destroy both sockets if they haven't been destroyed.
|
|
102
116
|
const cleanUpSockets = (from, to) => {
|
|
103
|
-
from.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (to) {
|
|
108
|
-
to.end();
|
|
109
|
-
to.removeAllListeners();
|
|
110
|
-
to.unpipe();
|
|
117
|
+
if (!from.destroyed) {
|
|
118
|
+
from.destroy();
|
|
119
|
+
}
|
|
120
|
+
if (to && !to.destroyed) {
|
|
111
121
|
to.destroy();
|
|
112
122
|
}
|
|
113
123
|
};
|
|
@@ -141,6 +151,9 @@ export class PortProxy {
|
|
|
141
151
|
console.log(`New connection from ${remoteIP}. Active connections: ${this.activeConnections.size}`);
|
|
142
152
|
// Flag to detect if we've received the first data chunk.
|
|
143
153
|
let initialDataReceived = false;
|
|
154
|
+
// Local termination reason trackers for each side.
|
|
155
|
+
let incomingTermReason = null;
|
|
156
|
+
let outgoingTermReason = null;
|
|
144
157
|
// Immediately attach an error handler to catch early errors.
|
|
145
158
|
socket.on('error', (err) => {
|
|
146
159
|
if (!initialDataReceived) {
|
|
@@ -150,7 +163,7 @@ export class PortProxy {
|
|
|
150
163
|
console.log(`(Immediate) Incoming socket error from ${remoteIP}: ${err.message}`);
|
|
151
164
|
}
|
|
152
165
|
});
|
|
153
|
-
//
|
|
166
|
+
// Ensure cleanup happens only once.
|
|
154
167
|
let connectionClosed = false;
|
|
155
168
|
const cleanupOnce = () => {
|
|
156
169
|
if (!connectionClosed) {
|
|
@@ -166,20 +179,40 @@ export class PortProxy {
|
|
|
166
179
|
}
|
|
167
180
|
}
|
|
168
181
|
};
|
|
169
|
-
//
|
|
182
|
+
// Outgoing connection placeholder.
|
|
170
183
|
let to = null;
|
|
184
|
+
// Handle errors by recording termination reason and cleaning up.
|
|
171
185
|
const handleError = (side) => (err) => {
|
|
172
186
|
const code = err.code;
|
|
187
|
+
let reason = 'error';
|
|
173
188
|
if (code === 'ECONNRESET') {
|
|
189
|
+
reason = 'econnreset';
|
|
174
190
|
console.log(`ECONNRESET on ${side} side from ${remoteIP}: ${err.message}`);
|
|
175
191
|
}
|
|
176
192
|
else {
|
|
177
193
|
console.log(`Error on ${side} side from ${remoteIP}: ${err.message}`);
|
|
178
194
|
}
|
|
195
|
+
if (side === 'incoming' && incomingTermReason === null) {
|
|
196
|
+
incomingTermReason = reason;
|
|
197
|
+
this.incrementTerminationStat('incoming', reason);
|
|
198
|
+
}
|
|
199
|
+
else if (side === 'outgoing' && outgoingTermReason === null) {
|
|
200
|
+
outgoingTermReason = reason;
|
|
201
|
+
this.incrementTerminationStat('outgoing', reason);
|
|
202
|
+
}
|
|
179
203
|
cleanupOnce();
|
|
180
204
|
};
|
|
205
|
+
// Handle close events. If no termination reason was recorded, mark as "normal".
|
|
181
206
|
const handleClose = (side) => () => {
|
|
182
207
|
console.log(`Connection closed on ${side} side from ${remoteIP}`);
|
|
208
|
+
if (side === 'incoming' && incomingTermReason === null) {
|
|
209
|
+
incomingTermReason = 'normal';
|
|
210
|
+
this.incrementTerminationStat('incoming', 'normal');
|
|
211
|
+
}
|
|
212
|
+
else if (side === 'outgoing' && outgoingTermReason === null) {
|
|
213
|
+
outgoingTermReason = 'normal';
|
|
214
|
+
this.incrementTerminationStat('outgoing', 'normal');
|
|
215
|
+
}
|
|
183
216
|
cleanupOnce();
|
|
184
217
|
};
|
|
185
218
|
// Setup connection, optionally accepting the initial data chunk.
|
|
@@ -191,12 +224,20 @@ export class PortProxy {
|
|
|
191
224
|
if (!domainConfig) {
|
|
192
225
|
console.log(`Connection rejected: No matching domain config for ${serverName} from ${remoteIP}`);
|
|
193
226
|
socket.end();
|
|
227
|
+
if (incomingTermReason === null) {
|
|
228
|
+
incomingTermReason = 'rejected';
|
|
229
|
+
this.incrementTerminationStat('incoming', 'rejected');
|
|
230
|
+
}
|
|
194
231
|
cleanupOnce();
|
|
195
232
|
return;
|
|
196
233
|
}
|
|
197
234
|
if (!isAllowed(remoteIP, domainConfig.allowedIPs)) {
|
|
198
235
|
console.log(`Connection rejected: IP ${remoteIP} not allowed for domain ${serverName}`);
|
|
199
236
|
socket.end();
|
|
237
|
+
if (incomingTermReason === null) {
|
|
238
|
+
incomingTermReason = 'rejected';
|
|
239
|
+
this.incrementTerminationStat('incoming', 'rejected');
|
|
240
|
+
}
|
|
200
241
|
cleanupOnce();
|
|
201
242
|
return;
|
|
202
243
|
}
|
|
@@ -204,6 +245,10 @@ export class PortProxy {
|
|
|
204
245
|
else if (!isDefaultAllowed && !serverName) {
|
|
205
246
|
console.log(`Connection rejected: No SNI and IP ${remoteIP} not in default allowed list`);
|
|
206
247
|
socket.end();
|
|
248
|
+
if (incomingTermReason === null) {
|
|
249
|
+
incomingTermReason = 'rejected';
|
|
250
|
+
this.incrementTerminationStat('incoming', 'rejected');
|
|
251
|
+
}
|
|
207
252
|
cleanupOnce();
|
|
208
253
|
return;
|
|
209
254
|
}
|
|
@@ -223,7 +268,6 @@ export class PortProxy {
|
|
|
223
268
|
}
|
|
224
269
|
// Establish outgoing connection.
|
|
225
270
|
to = plugins.net.connect(connectionOptions);
|
|
226
|
-
// Record start time for the outgoing connection.
|
|
227
271
|
if (to) {
|
|
228
272
|
this.outgoingConnectionTimes.set(to, Date.now());
|
|
229
273
|
}
|
|
@@ -233,20 +277,27 @@ export class PortProxy {
|
|
|
233
277
|
socket.unshift(initialChunk);
|
|
234
278
|
}
|
|
235
279
|
socket.setTimeout(120000);
|
|
236
|
-
// Since 'to' is not null here, we can use the non-null assertion.
|
|
237
280
|
socket.pipe(to);
|
|
238
281
|
to.pipe(socket);
|
|
239
|
-
// Attach
|
|
282
|
+
// Attach event handlers for both sockets.
|
|
240
283
|
socket.on('error', handleError('incoming'));
|
|
241
284
|
to.on('error', handleError('outgoing'));
|
|
242
285
|
socket.on('close', handleClose('incoming'));
|
|
243
286
|
to.on('close', handleClose('outgoing'));
|
|
244
287
|
socket.on('timeout', () => {
|
|
245
288
|
console.log(`Timeout on incoming side from ${remoteIP}`);
|
|
289
|
+
if (incomingTermReason === null) {
|
|
290
|
+
incomingTermReason = 'timeout';
|
|
291
|
+
this.incrementTerminationStat('incoming', 'timeout');
|
|
292
|
+
}
|
|
246
293
|
cleanupOnce();
|
|
247
294
|
});
|
|
248
295
|
to.on('timeout', () => {
|
|
249
296
|
console.log(`Timeout on outgoing side from ${remoteIP}`);
|
|
297
|
+
if (outgoingTermReason === null) {
|
|
298
|
+
outgoingTermReason = 'timeout';
|
|
299
|
+
this.incrementTerminationStat('outgoing', 'timeout');
|
|
300
|
+
}
|
|
250
301
|
cleanupOnce();
|
|
251
302
|
});
|
|
252
303
|
socket.on('end', handleClose('incoming'));
|
|
@@ -256,7 +307,6 @@ export class PortProxy {
|
|
|
256
307
|
if (this.settings.sniEnabled) {
|
|
257
308
|
socket.once('data', (chunk) => {
|
|
258
309
|
initialDataReceived = true;
|
|
259
|
-
// Try to extract the server name from the ClientHello.
|
|
260
310
|
const serverName = extractSNI(chunk) || '';
|
|
261
311
|
console.log(`Received connection from ${remoteIP} with SNI: ${serverName}`);
|
|
262
312
|
setupConnection(serverName, chunk);
|
|
@@ -268,6 +318,10 @@ export class PortProxy {
|
|
|
268
318
|
if (!this.settings.defaultAllowedIPs || !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
|
|
269
319
|
console.log(`Connection rejected: IP ${remoteIP} not allowed for non-SNI connection`);
|
|
270
320
|
socket.end();
|
|
321
|
+
if (incomingTermReason === null) {
|
|
322
|
+
incomingTermReason = 'rejected';
|
|
323
|
+
this.incrementTerminationStat('incoming', 'rejected');
|
|
324
|
+
}
|
|
271
325
|
cleanupOnce();
|
|
272
326
|
return;
|
|
273
327
|
}
|
|
@@ -280,7 +334,8 @@ export class PortProxy {
|
|
|
280
334
|
.listen(this.settings.fromPort, () => {
|
|
281
335
|
console.log(`PortProxy -> OK: Now listening on port ${this.settings.fromPort}${this.settings.sniEnabled ? ' (SNI passthrough enabled)' : ''}`);
|
|
282
336
|
});
|
|
283
|
-
// Log active connection count
|
|
337
|
+
// Log active connection count, longest running connection durations,
|
|
338
|
+
// and termination statistics every 10 seconds.
|
|
284
339
|
this.connectionLogger = setInterval(() => {
|
|
285
340
|
const now = Date.now();
|
|
286
341
|
let maxIncoming = 0;
|
|
@@ -297,7 +352,7 @@ export class PortProxy {
|
|
|
297
352
|
maxOutgoing = duration;
|
|
298
353
|
}
|
|
299
354
|
}
|
|
300
|
-
console.log(`(Interval Log) Active connections: ${this.activeConnections.size}. Longest running incoming: ${plugins.prettyMs(maxIncoming)}, outgoing: ${plugins.prettyMs(maxOutgoing)}`);
|
|
355
|
+
console.log(`(Interval Log) Active connections: ${this.activeConnections.size}. Longest running incoming: ${plugins.prettyMs(maxIncoming)}, outgoing: ${plugins.prettyMs(maxOutgoing)}. Termination stats (incoming): ${JSON.stringify(this.terminationStats.incoming)}, (outgoing): ${JSON.stringify(this.terminationStats.outgoing)}`);
|
|
301
356
|
}, 10000);
|
|
302
357
|
}
|
|
303
358
|
async stop() {
|
|
@@ -312,4 +367,4 @@ export class PortProxy {
|
|
|
312
367
|
await done.promise;
|
|
313
368
|
}
|
|
314
369
|
}
|
|
315
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwcm94eS5wb3J0cHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHByb3h5LnBvcnRwcm94eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQXFCeEM7OztHQUdHO0FBQ0gsU0FBUyxVQUFVLENBQUMsTUFBYztJQUNoQyxJQUFJLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDZixrREFBa0Q7SUFDbEQsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3RCLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRCxvQkFBb0I7SUFDcEIsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN2QyxJQUFJLFVBQVUsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLGlCQUFpQjtRQUN4QyxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBQ0QscUJBQXFCO0lBQ3JCLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUMsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsR0FBRyxZQUFZLEVBQUUsQ0FBQztRQUNyQyxrRkFBa0Y7UUFDbEYsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVELE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDWCxzREFBc0Q7SUFDdEQsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMvQyxJQUFJLGFBQWEsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN4QixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsdURBQXVEO0lBQ3ZELE1BQU0sSUFBSSxDQUFDLENBQUM7SUFFWixzREFBc0Q7SUFDdEQsTUFBTSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFFakIsYUFBYTtJ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
|
|
370
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwcm94eS5wb3J0cHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHByb3h5LnBvcnRwcm94eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQXFCeEM7OztHQUdHO0FBQ0gsU0FBUyxVQUFVLENBQUMsTUFBYztJQUNoQyxJQUFJLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDZixrREFBa0Q7SUFDbEQsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3RCLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFRCxvQkFBb0I7SUFDcEIsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN2QyxJQUFJLFVBQVUsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLGlCQUFpQjtRQUN4QyxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBQ0QscUJBQXFCO0lBQ3JCLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUMsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsR0FBRyxZQUFZLEVBQUUsQ0FBQztRQUNyQyxrRkFBa0Y7UUFDbEYsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVELE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDWCxzREFBc0Q7SUFDdEQsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMvQyxJQUFJLGFBQWEsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN4QixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsdURBQXVEO0lBQ3ZELE1BQU0sSUFBSSxDQUFDLENBQUM7SUFFWixzREFBc0Q7SUFDdEQsTUFBTSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFFakIsYUFBYTtJ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
|
package/package.json
CHANGED
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -123,6 +123,15 @@ export class PortProxy {
|
|
|
123
123
|
private outgoingConnectionTimes: Map<plugins.net.Socket, number> = new Map();
|
|
124
124
|
private connectionLogger: NodeJS.Timeout | null = null;
|
|
125
125
|
|
|
126
|
+
// Overall termination statistics
|
|
127
|
+
private terminationStats: {
|
|
128
|
+
incoming: Record<string, number>;
|
|
129
|
+
outgoing: Record<string, number>;
|
|
130
|
+
} = {
|
|
131
|
+
incoming: {},
|
|
132
|
+
outgoing: {},
|
|
133
|
+
};
|
|
134
|
+
|
|
126
135
|
constructor(settings: IProxySettings) {
|
|
127
136
|
this.settings = {
|
|
128
137
|
...settings,
|
|
@@ -130,17 +139,22 @@ export class PortProxy {
|
|
|
130
139
|
};
|
|
131
140
|
}
|
|
132
141
|
|
|
142
|
+
// Helper to update termination stats.
|
|
143
|
+
private incrementTerminationStat(side: 'incoming' | 'outgoing', reason: string): void {
|
|
144
|
+
if (!this.terminationStats[side][reason]) {
|
|
145
|
+
this.terminationStats[side][reason] = 1;
|
|
146
|
+
} else {
|
|
147
|
+
this.terminationStats[side][reason]++;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
133
151
|
public async start() {
|
|
134
|
-
// Adjusted cleanUpSockets
|
|
152
|
+
// Adjusted cleanUpSockets: forcefully destroy both sockets if they haven't been destroyed.
|
|
135
153
|
const cleanUpSockets = (from: plugins.net.Socket, to?: plugins.net.Socket) => {
|
|
136
|
-
from.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (to) {
|
|
141
|
-
to.end();
|
|
142
|
-
to.removeAllListeners();
|
|
143
|
-
to.unpipe();
|
|
154
|
+
if (!from.destroyed) {
|
|
155
|
+
from.destroy();
|
|
156
|
+
}
|
|
157
|
+
if (to && !to.destroyed) {
|
|
144
158
|
to.destroy();
|
|
145
159
|
}
|
|
146
160
|
};
|
|
@@ -183,6 +197,10 @@ export class PortProxy {
|
|
|
183
197
|
// Flag to detect if we've received the first data chunk.
|
|
184
198
|
let initialDataReceived = false;
|
|
185
199
|
|
|
200
|
+
// Local termination reason trackers for each side.
|
|
201
|
+
let incomingTermReason: string | null = null;
|
|
202
|
+
let outgoingTermReason: string | null = null;
|
|
203
|
+
|
|
186
204
|
// Immediately attach an error handler to catch early errors.
|
|
187
205
|
socket.on('error', (err: Error) => {
|
|
188
206
|
if (!initialDataReceived) {
|
|
@@ -192,7 +210,7 @@ export class PortProxy {
|
|
|
192
210
|
}
|
|
193
211
|
});
|
|
194
212
|
|
|
195
|
-
//
|
|
213
|
+
// Ensure cleanup happens only once.
|
|
196
214
|
let connectionClosed = false;
|
|
197
215
|
const cleanupOnce = () => {
|
|
198
216
|
if (!connectionClosed) {
|
|
@@ -209,21 +227,39 @@ export class PortProxy {
|
|
|
209
227
|
}
|
|
210
228
|
};
|
|
211
229
|
|
|
212
|
-
//
|
|
230
|
+
// Outgoing connection placeholder.
|
|
213
231
|
let to: plugins.net.Socket | null = null;
|
|
214
232
|
|
|
233
|
+
// Handle errors by recording termination reason and cleaning up.
|
|
215
234
|
const handleError = (side: 'incoming' | 'outgoing') => (err: Error) => {
|
|
216
235
|
const code = (err as any).code;
|
|
236
|
+
let reason = 'error';
|
|
217
237
|
if (code === 'ECONNRESET') {
|
|
238
|
+
reason = 'econnreset';
|
|
218
239
|
console.log(`ECONNRESET on ${side} side from ${remoteIP}: ${err.message}`);
|
|
219
240
|
} else {
|
|
220
241
|
console.log(`Error on ${side} side from ${remoteIP}: ${err.message}`);
|
|
221
242
|
}
|
|
243
|
+
if (side === 'incoming' && incomingTermReason === null) {
|
|
244
|
+
incomingTermReason = reason;
|
|
245
|
+
this.incrementTerminationStat('incoming', reason);
|
|
246
|
+
} else if (side === 'outgoing' && outgoingTermReason === null) {
|
|
247
|
+
outgoingTermReason = reason;
|
|
248
|
+
this.incrementTerminationStat('outgoing', reason);
|
|
249
|
+
}
|
|
222
250
|
cleanupOnce();
|
|
223
251
|
};
|
|
224
252
|
|
|
253
|
+
// Handle close events. If no termination reason was recorded, mark as "normal".
|
|
225
254
|
const handleClose = (side: 'incoming' | 'outgoing') => () => {
|
|
226
255
|
console.log(`Connection closed on ${side} side from ${remoteIP}`);
|
|
256
|
+
if (side === 'incoming' && incomingTermReason === null) {
|
|
257
|
+
incomingTermReason = 'normal';
|
|
258
|
+
this.incrementTerminationStat('incoming', 'normal');
|
|
259
|
+
} else if (side === 'outgoing' && outgoingTermReason === null) {
|
|
260
|
+
outgoingTermReason = 'normal';
|
|
261
|
+
this.incrementTerminationStat('outgoing', 'normal');
|
|
262
|
+
}
|
|
227
263
|
cleanupOnce();
|
|
228
264
|
};
|
|
229
265
|
|
|
@@ -236,18 +272,30 @@ export class PortProxy {
|
|
|
236
272
|
if (!domainConfig) {
|
|
237
273
|
console.log(`Connection rejected: No matching domain config for ${serverName} from ${remoteIP}`);
|
|
238
274
|
socket.end();
|
|
275
|
+
if (incomingTermReason === null) {
|
|
276
|
+
incomingTermReason = 'rejected';
|
|
277
|
+
this.incrementTerminationStat('incoming', 'rejected');
|
|
278
|
+
}
|
|
239
279
|
cleanupOnce();
|
|
240
280
|
return;
|
|
241
281
|
}
|
|
242
282
|
if (!isAllowed(remoteIP, domainConfig.allowedIPs)) {
|
|
243
283
|
console.log(`Connection rejected: IP ${remoteIP} not allowed for domain ${serverName}`);
|
|
244
284
|
socket.end();
|
|
285
|
+
if (incomingTermReason === null) {
|
|
286
|
+
incomingTermReason = 'rejected';
|
|
287
|
+
this.incrementTerminationStat('incoming', 'rejected');
|
|
288
|
+
}
|
|
245
289
|
cleanupOnce();
|
|
246
290
|
return;
|
|
247
291
|
}
|
|
248
292
|
} else if (!isDefaultAllowed && !serverName) {
|
|
249
293
|
console.log(`Connection rejected: No SNI and IP ${remoteIP} not in default allowed list`);
|
|
250
294
|
socket.end();
|
|
295
|
+
if (incomingTermReason === null) {
|
|
296
|
+
incomingTermReason = 'rejected';
|
|
297
|
+
this.incrementTerminationStat('incoming', 'rejected');
|
|
298
|
+
}
|
|
251
299
|
cleanupOnce();
|
|
252
300
|
return;
|
|
253
301
|
} else {
|
|
@@ -269,7 +317,6 @@ export class PortProxy {
|
|
|
269
317
|
|
|
270
318
|
// Establish outgoing connection.
|
|
271
319
|
to = plugins.net.connect(connectionOptions);
|
|
272
|
-
// Record start time for the outgoing connection.
|
|
273
320
|
if (to) {
|
|
274
321
|
this.outgoingConnectionTimes.set(to, Date.now());
|
|
275
322
|
}
|
|
@@ -280,21 +327,28 @@ export class PortProxy {
|
|
|
280
327
|
socket.unshift(initialChunk);
|
|
281
328
|
}
|
|
282
329
|
socket.setTimeout(120000);
|
|
283
|
-
// Since 'to' is not null here, we can use the non-null assertion.
|
|
284
330
|
socket.pipe(to!);
|
|
285
331
|
to!.pipe(socket);
|
|
286
332
|
|
|
287
|
-
// Attach
|
|
333
|
+
// Attach event handlers for both sockets.
|
|
288
334
|
socket.on('error', handleError('incoming'));
|
|
289
335
|
to!.on('error', handleError('outgoing'));
|
|
290
336
|
socket.on('close', handleClose('incoming'));
|
|
291
337
|
to!.on('close', handleClose('outgoing'));
|
|
292
338
|
socket.on('timeout', () => {
|
|
293
339
|
console.log(`Timeout on incoming side from ${remoteIP}`);
|
|
340
|
+
if (incomingTermReason === null) {
|
|
341
|
+
incomingTermReason = 'timeout';
|
|
342
|
+
this.incrementTerminationStat('incoming', 'timeout');
|
|
343
|
+
}
|
|
294
344
|
cleanupOnce();
|
|
295
345
|
});
|
|
296
346
|
to!.on('timeout', () => {
|
|
297
347
|
console.log(`Timeout on outgoing side from ${remoteIP}`);
|
|
348
|
+
if (outgoingTermReason === null) {
|
|
349
|
+
outgoingTermReason = 'timeout';
|
|
350
|
+
this.incrementTerminationStat('outgoing', 'timeout');
|
|
351
|
+
}
|
|
298
352
|
cleanupOnce();
|
|
299
353
|
});
|
|
300
354
|
socket.on('end', handleClose('incoming'));
|
|
@@ -305,7 +359,6 @@ export class PortProxy {
|
|
|
305
359
|
if (this.settings.sniEnabled) {
|
|
306
360
|
socket.once('data', (chunk: Buffer) => {
|
|
307
361
|
initialDataReceived = true;
|
|
308
|
-
// Try to extract the server name from the ClientHello.
|
|
309
362
|
const serverName = extractSNI(chunk) || '';
|
|
310
363
|
console.log(`Received connection from ${remoteIP} with SNI: ${serverName}`);
|
|
311
364
|
setupConnection(serverName, chunk);
|
|
@@ -316,6 +369,10 @@ export class PortProxy {
|
|
|
316
369
|
if (!this.settings.defaultAllowedIPs || !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
|
|
317
370
|
console.log(`Connection rejected: IP ${remoteIP} not allowed for non-SNI connection`);
|
|
318
371
|
socket.end();
|
|
372
|
+
if (incomingTermReason === null) {
|
|
373
|
+
incomingTermReason = 'rejected';
|
|
374
|
+
this.incrementTerminationStat('incoming', 'rejected');
|
|
375
|
+
}
|
|
319
376
|
cleanupOnce();
|
|
320
377
|
return;
|
|
321
378
|
}
|
|
@@ -329,7 +386,8 @@ export class PortProxy {
|
|
|
329
386
|
console.log(`PortProxy -> OK: Now listening on port ${this.settings.fromPort}${this.settings.sniEnabled ? ' (SNI passthrough enabled)' : ''}`);
|
|
330
387
|
});
|
|
331
388
|
|
|
332
|
-
// Log active connection count
|
|
389
|
+
// Log active connection count, longest running connection durations,
|
|
390
|
+
// and termination statistics every 10 seconds.
|
|
333
391
|
this.connectionLogger = setInterval(() => {
|
|
334
392
|
const now = Date.now();
|
|
335
393
|
let maxIncoming = 0;
|
|
@@ -346,7 +404,7 @@ export class PortProxy {
|
|
|
346
404
|
maxOutgoing = duration;
|
|
347
405
|
}
|
|
348
406
|
}
|
|
349
|
-
console.log(`(Interval Log) Active connections: ${this.activeConnections.size}. Longest running incoming: ${plugins.prettyMs(maxIncoming)}, outgoing: ${plugins.prettyMs(maxOutgoing)}`);
|
|
407
|
+
console.log(`(Interval Log) Active connections: ${this.activeConnections.size}. Longest running incoming: ${plugins.prettyMs(maxIncoming)}, outgoing: ${plugins.prettyMs(maxOutgoing)}. Termination stats (incoming): ${JSON.stringify(this.terminationStats.incoming)}, (outgoing): ${JSON.stringify(this.terminationStats.outgoing)}`);
|
|
350
408
|
}, 10000);
|
|
351
409
|
}
|
|
352
410
|
|