@push.rocks/smartproxy 3.4.0 → 3.4.3
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.4.
|
|
6
|
+
version: '3.4.3',
|
|
7
7
|
description: 'a proxy for handling high workloads of proxying'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLGlEQUFpRDtDQUMvRCxDQUFBIn0=
|
|
@@ -17,24 +17,58 @@ export class PortProxy {
|
|
|
17
17
|
from.destroy();
|
|
18
18
|
to.destroy();
|
|
19
19
|
};
|
|
20
|
+
const normalizeIP = (ip) => {
|
|
21
|
+
// Handle IPv4-mapped IPv6 addresses
|
|
22
|
+
if (ip.startsWith('::ffff:')) {
|
|
23
|
+
const ipv4 = ip.slice(7); // Remove '::ffff:' prefix
|
|
24
|
+
return [ip, ipv4];
|
|
25
|
+
}
|
|
26
|
+
// Handle IPv4 addresses by adding IPv4-mapped IPv6 variant
|
|
27
|
+
if (ip.match(/^\d{1,3}(\.\d{1,3}){3}$/)) {
|
|
28
|
+
return [ip, `::ffff:${ip}`];
|
|
29
|
+
}
|
|
30
|
+
return [ip];
|
|
31
|
+
};
|
|
20
32
|
const isAllowed = (value, patterns) => {
|
|
21
|
-
|
|
33
|
+
// Expand patterns to include both IPv4 and IPv6 variants
|
|
34
|
+
const expandedPatterns = patterns.flatMap(normalizeIP);
|
|
35
|
+
// Check if any variant of the IP matches any expanded pattern
|
|
36
|
+
return normalizeIP(value).some(ip => expandedPatterns.some(pattern => plugins.minimatch(ip, pattern)));
|
|
22
37
|
};
|
|
23
38
|
const findMatchingDomain = (serverName) => {
|
|
24
39
|
return this.settings.domains.find(config => plugins.minimatch(serverName, config.domain));
|
|
25
40
|
};
|
|
26
41
|
const server = this.settings.sniEnabled
|
|
27
|
-
? plugins.tls.createServer(
|
|
42
|
+
? plugins.tls.createServer({
|
|
43
|
+
...this.settings,
|
|
44
|
+
SNICallback: (serverName, cb) => {
|
|
45
|
+
console.log(`SNI request for domain: ${serverName}`);
|
|
46
|
+
const domainConfig = findMatchingDomain(serverName);
|
|
47
|
+
if (!domainConfig) {
|
|
48
|
+
console.log(`SNI rejected: No matching domain config for ${serverName}`);
|
|
49
|
+
cb(new Error(`No configuration for domain: ${serverName}`));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Create context with the provided TLS settings
|
|
53
|
+
const ctx = plugins.tls.createSecureContext(this.settings);
|
|
54
|
+
cb(null, ctx);
|
|
55
|
+
}
|
|
56
|
+
})
|
|
28
57
|
: plugins.net.createServer();
|
|
29
|
-
|
|
58
|
+
const handleConnection = (from) => {
|
|
30
59
|
const remoteIP = from.remoteAddress || '';
|
|
60
|
+
let serverName = '';
|
|
31
61
|
if (this.settings.sniEnabled && from instanceof plugins.tls.TLSSocket) {
|
|
32
|
-
|
|
33
|
-
|
|
62
|
+
serverName = from.servername || '';
|
|
63
|
+
console.log(`TLS Connection from ${remoteIP} for domain: ${serverName}`);
|
|
64
|
+
}
|
|
65
|
+
// For TLS connections, we've already validated the domain in SNICallback
|
|
66
|
+
if (!this.settings.sniEnabled || from instanceof plugins.tls.TLSSocket) {
|
|
67
|
+
const domainConfig = serverName ? findMatchingDomain(serverName) : undefined;
|
|
34
68
|
if (!domainConfig) {
|
|
35
69
|
// If no matching domain config found, check default IPs if available
|
|
36
70
|
if (!this.settings.defaultAllowedIPs || !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
|
|
37
|
-
console.log(`Connection rejected: No matching domain config for ${serverName} from IP ${remoteIP}`);
|
|
71
|
+
console.log(`Connection rejected: No matching domain config for ${serverName || 'non-SNI'} from IP ${remoteIP}`);
|
|
38
72
|
from.end();
|
|
39
73
|
return;
|
|
40
74
|
}
|
|
@@ -57,6 +91,7 @@ export class PortProxy {
|
|
|
57
91
|
host: this.settings.toHost,
|
|
58
92
|
port: this.settings.toPort,
|
|
59
93
|
});
|
|
94
|
+
console.log(`Connection established: ${remoteIP} -> ${this.settings.toHost}:${this.settings.toPort}${serverName ? ` (SNI: ${serverName})` : ''}`);
|
|
60
95
|
from.setTimeout(120000);
|
|
61
96
|
from.pipe(to);
|
|
62
97
|
to.pipe(from);
|
|
@@ -84,9 +119,18 @@ export class PortProxy {
|
|
|
84
119
|
to.on('end', () => {
|
|
85
120
|
cleanUpSockets(from, to);
|
|
86
121
|
});
|
|
122
|
+
};
|
|
123
|
+
this.netServer = server
|
|
124
|
+
.on('connection', handleConnection)
|
|
125
|
+
.on('secureConnection', handleConnection)
|
|
126
|
+
.on('tlsClientError', (err, tlsSocket) => {
|
|
127
|
+
console.log(`TLS Client Error: ${err.message}`);
|
|
128
|
+
})
|
|
129
|
+
.on('error', (err) => {
|
|
130
|
+
console.log(`Server Error: ${err.message}`);
|
|
87
131
|
})
|
|
88
132
|
.listen(this.settings.fromPort);
|
|
89
|
-
console.log(`PortProxy -> OK: Now listening on port ${this.settings.fromPort}`);
|
|
133
|
+
console.log(`PortProxy -> OK: Now listening on port ${this.settings.fromPort}${this.settings.sniEnabled ? ' (SNI enabled)' : ''}`);
|
|
90
134
|
}
|
|
91
135
|
async stop() {
|
|
92
136
|
const done = plugins.smartpromise.defer();
|
|
@@ -96,4 +140,4 @@ export class PortProxy {
|
|
|
96
140
|
await done.promise;
|
|
97
141
|
}
|
|
98
142
|
}
|
|
99
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
143
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwcm94eS5wb3J0cHJveHkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHByb3h5LnBvcnRwcm94eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHlCQUF5QixDQUFDO0FBb0JuRCxNQUFNLE9BQU8sU0FBUztJQUlwQixZQUFZLFFBQXVCO1FBQ2pDLElBQUksQ0FBQyxRQUFRLEdBQUc7WUFDZCxHQUFHLFFBQVE7WUFDWCxNQUFNLEVBQUUsUUFBUSxDQUFDLE1BQU0sSUFBSSxXQUFXO1NBQ3ZDLENBQUM7SUFDSixDQUFDO0lBRU0sS0FBSyxDQUFDLEtBQUs7UUFDaEIsTUFBTSxjQUFjLEdBQUcsQ0FBQyxJQUF3QixFQUFFLEVBQXNCLEVBQUUsRUFBRTtZQUMxRSxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDWCxFQUFFLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDVCxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztZQUMxQixFQUFFLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztZQUN4QixJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDZCxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDWixJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDZixFQUFFLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDZixDQUFDLENBQUM7UUFDRixNQUFNLFdBQVcsR0FBRyxDQUFDLEVBQVUsRUFBWSxFQUFFO1lBQzNDLG9DQUFvQztZQUNwQyxJQUFJLEVBQUUsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztnQkFDN0IsTUFBTSxJQUFJLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLDBCQUEwQjtnQkFDcEQsT0FBTyxDQUFDLEVBQUUsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUNwQixDQUFDO1lBQ0QsMkRBQTJEO1lBQzNELElBQUksRUFBRSxDQUFD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
|
package/package.json
CHANGED
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -40,8 +40,26 @@ export class PortProxy {
|
|
|
40
40
|
from.destroy();
|
|
41
41
|
to.destroy();
|
|
42
42
|
};
|
|
43
|
+
const normalizeIP = (ip: string): string[] => {
|
|
44
|
+
// Handle IPv4-mapped IPv6 addresses
|
|
45
|
+
if (ip.startsWith('::ffff:')) {
|
|
46
|
+
const ipv4 = ip.slice(7); // Remove '::ffff:' prefix
|
|
47
|
+
return [ip, ipv4];
|
|
48
|
+
}
|
|
49
|
+
// Handle IPv4 addresses by adding IPv4-mapped IPv6 variant
|
|
50
|
+
if (ip.match(/^\d{1,3}(\.\d{1,3}){3}$/)) {
|
|
51
|
+
return [ip, `::ffff:${ip}`];
|
|
52
|
+
}
|
|
53
|
+
return [ip];
|
|
54
|
+
};
|
|
55
|
+
|
|
43
56
|
const isAllowed = (value: string, patterns: string[]): boolean => {
|
|
44
|
-
|
|
57
|
+
// Expand patterns to include both IPv4 and IPv6 variants
|
|
58
|
+
const expandedPatterns = patterns.flatMap(normalizeIP);
|
|
59
|
+
// Check if any variant of the IP matches any expanded pattern
|
|
60
|
+
return normalizeIP(value).some(ip =>
|
|
61
|
+
expandedPatterns.some(pattern => plugins.minimatch(ip, pattern))
|
|
62
|
+
);
|
|
45
63
|
};
|
|
46
64
|
|
|
47
65
|
const findMatchingDomain = (serverName: string): DomainConfig | undefined => {
|
|
@@ -49,70 +67,102 @@ export class PortProxy {
|
|
|
49
67
|
};
|
|
50
68
|
|
|
51
69
|
const server = this.settings.sniEnabled
|
|
52
|
-
? plugins.tls.createServer(
|
|
70
|
+
? plugins.tls.createServer({
|
|
71
|
+
...this.settings,
|
|
72
|
+
SNICallback: (serverName: string, cb: (err: Error | null, ctx?: plugins.tls.SecureContext) => void) => {
|
|
73
|
+
console.log(`SNI request for domain: ${serverName}`);
|
|
74
|
+
const domainConfig = findMatchingDomain(serverName);
|
|
75
|
+
if (!domainConfig) {
|
|
76
|
+
console.log(`SNI rejected: No matching domain config for ${serverName}`);
|
|
77
|
+
cb(new Error(`No configuration for domain: ${serverName}`));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Create context with the provided TLS settings
|
|
81
|
+
const ctx = plugins.tls.createSecureContext(this.settings);
|
|
82
|
+
cb(null, ctx);
|
|
83
|
+
}
|
|
84
|
+
})
|
|
53
85
|
: plugins.net.createServer();
|
|
54
86
|
|
|
55
|
-
|
|
87
|
+
const handleConnection = (from: plugins.net.Socket | plugins.tls.TLSSocket) => {
|
|
56
88
|
const remoteIP = from.remoteAddress || '';
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
let serverName = '';
|
|
90
|
+
|
|
91
|
+
if (this.settings.sniEnabled && from instanceof plugins.tls.TLSSocket) {
|
|
92
|
+
serverName = (from as any).servername || '';
|
|
93
|
+
console.log(`TLS Connection from ${remoteIP} for domain: ${serverName}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// For TLS connections, we've already validated the domain in SNICallback
|
|
97
|
+
if (!this.settings.sniEnabled || from instanceof plugins.tls.TLSSocket) {
|
|
98
|
+
const domainConfig = serverName ? findMatchingDomain(serverName) : undefined;
|
|
99
|
+
|
|
100
|
+
if (!domainConfig) {
|
|
101
|
+
// If no matching domain config found, check default IPs if available
|
|
102
|
+
if (!this.settings.defaultAllowedIPs || !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
|
|
103
|
+
console.log(`Connection rejected: No matching domain config for ${serverName || 'non-SNI'} from IP ${remoteIP}`);
|
|
104
|
+
from.end();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
// Check if IP is allowed for this domain
|
|
109
|
+
if (!isAllowed(remoteIP, domainConfig.allowedIPs)) {
|
|
110
|
+
console.log(`Connection rejected: IP ${remoteIP} not allowed for domain ${serverName}`);
|
|
111
|
+
from.end();
|
|
112
|
+
return;
|
|
75
113
|
}
|
|
76
|
-
} else if (!this.settings.defaultAllowedIPs || !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
|
|
77
|
-
console.log(`Connection rejected: IP ${remoteIP} not allowed for non-SNI connection`);
|
|
78
|
-
from.end();
|
|
79
|
-
return;
|
|
80
114
|
}
|
|
115
|
+
} else if (!this.settings.defaultAllowedIPs || !isAllowed(remoteIP, this.settings.defaultAllowedIPs)) {
|
|
116
|
+
console.log(`Connection rejected: IP ${remoteIP} not allowed for non-SNI connection`);
|
|
117
|
+
from.end();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const to = plugins.net.createConnection({
|
|
122
|
+
host: this.settings.toHost!,
|
|
123
|
+
port: this.settings.toPort,
|
|
124
|
+
});
|
|
125
|
+
console.log(`Connection established: ${remoteIP} -> ${this.settings.toHost}:${this.settings.toPort}${serverName ? ` (SNI: ${serverName})` : ''}`);
|
|
126
|
+
from.setTimeout(120000);
|
|
127
|
+
from.pipe(to);
|
|
128
|
+
to.pipe(from);
|
|
129
|
+
from.on('error', () => {
|
|
130
|
+
cleanUpSockets(from, to);
|
|
131
|
+
});
|
|
132
|
+
to.on('error', () => {
|
|
133
|
+
cleanUpSockets(from, to);
|
|
134
|
+
});
|
|
135
|
+
from.on('close', () => {
|
|
136
|
+
cleanUpSockets(from, to);
|
|
137
|
+
});
|
|
138
|
+
to.on('close', () => {
|
|
139
|
+
cleanUpSockets(from, to);
|
|
140
|
+
});
|
|
141
|
+
from.on('timeout', () => {
|
|
142
|
+
cleanUpSockets(from, to);
|
|
143
|
+
});
|
|
144
|
+
to.on('timeout', () => {
|
|
145
|
+
cleanUpSockets(from, to);
|
|
146
|
+
});
|
|
147
|
+
from.on('end', () => {
|
|
148
|
+
cleanUpSockets(from, to);
|
|
149
|
+
});
|
|
150
|
+
to.on('end', () => {
|
|
151
|
+
cleanUpSockets(from, to);
|
|
152
|
+
});
|
|
153
|
+
};
|
|
81
154
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
cleanUpSockets(from, to);
|
|
91
|
-
});
|
|
92
|
-
to.on('error', () => {
|
|
93
|
-
cleanUpSockets(from, to);
|
|
94
|
-
});
|
|
95
|
-
from.on('close', () => {
|
|
96
|
-
cleanUpSockets(from, to);
|
|
97
|
-
});
|
|
98
|
-
to.on('close', () => {
|
|
99
|
-
cleanUpSockets(from, to);
|
|
100
|
-
});
|
|
101
|
-
from.on('timeout', () => {
|
|
102
|
-
cleanUpSockets(from, to);
|
|
103
|
-
});
|
|
104
|
-
to.on('timeout', () => {
|
|
105
|
-
cleanUpSockets(from, to);
|
|
106
|
-
});
|
|
107
|
-
from.on('end', () => {
|
|
108
|
-
cleanUpSockets(from, to);
|
|
109
|
-
});
|
|
110
|
-
to.on('end', () => {
|
|
111
|
-
cleanUpSockets(from, to);
|
|
112
|
-
});
|
|
155
|
+
this.netServer = server
|
|
156
|
+
.on('connection', handleConnection)
|
|
157
|
+
.on('secureConnection', handleConnection)
|
|
158
|
+
.on('tlsClientError', (err, tlsSocket) => {
|
|
159
|
+
console.log(`TLS Client Error: ${err.message}`);
|
|
160
|
+
})
|
|
161
|
+
.on('error', (err) => {
|
|
162
|
+
console.log(`Server Error: ${err.message}`);
|
|
113
163
|
})
|
|
114
164
|
.listen(this.settings.fromPort);
|
|
115
|
-
console.log(`PortProxy -> OK: Now listening on port ${this.settings.fromPort}`);
|
|
165
|
+
console.log(`PortProxy -> OK: Now listening on port ${this.settings.fromPort}${this.settings.sniEnabled ? ' (SNI enabled)' : ''}`);
|
|
116
166
|
}
|
|
117
167
|
|
|
118
168
|
public async stop() {
|