@push.rocks/smartproxy 19.3.2 → 19.3.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.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/forwarding/factory/forwarding-factory.js +29 -1
- package/dist_ts/http/index.d.ts +1 -3
- package/dist_ts/http/index.js +4 -10
- package/dist_ts/http/models/http-types.d.ts +4 -91
- package/dist_ts/http/models/http-types.js +5 -60
- package/dist_ts/http/router/proxy-router.d.ts +1 -1
- package/dist_ts/http/router/route-router.d.ts +1 -1
- package/dist_ts/index.d.ts +9 -7
- package/dist_ts/index.js +10 -7
- package/dist_ts/proxies/{network-proxy → http-proxy}/certificate-manager.d.ts +2 -2
- package/dist_ts/proxies/{network-proxy → http-proxy}/certificate-manager.js +1 -1
- package/dist_ts/proxies/{network-proxy → http-proxy}/connection-pool.d.ts +2 -2
- package/dist_ts/proxies/http-proxy/connection-pool.js +210 -0
- package/dist_ts/proxies/http-proxy/context-creator.js +108 -0
- package/dist_ts/proxies/{network-proxy → http-proxy}/function-cache.js +1 -1
- package/dist_ts/proxies/http-proxy/handlers/index.d.ts +5 -0
- package/dist_ts/proxies/http-proxy/handlers/index.js +6 -0
- package/dist_ts/proxies/http-proxy/handlers/redirect-handler.d.ts +18 -0
- package/dist_ts/proxies/http-proxy/handlers/redirect-handler.js +78 -0
- package/dist_ts/proxies/http-proxy/handlers/static-handler.d.ts +19 -0
- package/dist_ts/proxies/http-proxy/handlers/static-handler.js +203 -0
- package/dist_ts/proxies/{network-proxy/network-proxy.d.ts → http-proxy/http-proxy.d.ts} +10 -9
- package/dist_ts/proxies/{network-proxy/network-proxy.js → http-proxy/http-proxy.js} +13 -12
- package/dist_ts/proxies/{network-proxy → http-proxy}/http-request-handler.js +1 -1
- package/dist_ts/proxies/http-proxy/http2-request-handler.js +201 -0
- package/dist_ts/proxies/{network-proxy → http-proxy}/index.d.ts +2 -2
- package/dist_ts/proxies/http-proxy/index.js +12 -0
- package/dist_ts/proxies/http-proxy/models/http-types.d.ts +119 -0
- package/dist_ts/proxies/http-proxy/models/http-types.js +112 -0
- package/dist_ts/proxies/http-proxy/models/index.d.ts +5 -0
- package/dist_ts/proxies/http-proxy/models/index.js +6 -0
- package/dist_ts/proxies/{network-proxy → http-proxy}/models/types.d.ts +2 -2
- package/dist_ts/proxies/http-proxy/models/types.js +276 -0
- package/dist_ts/proxies/{network-proxy → http-proxy}/request-handler.d.ts +3 -3
- package/dist_ts/proxies/{network-proxy → http-proxy}/request-handler.js +2 -2
- package/dist_ts/proxies/http-proxy/security-manager.js +255 -0
- package/dist_ts/proxies/{network-proxy → http-proxy}/websocket-handler.d.ts +3 -3
- package/dist_ts/proxies/{network-proxy → http-proxy}/websocket-handler.js +2 -2
- package/dist_ts/proxies/index.d.ts +5 -5
- package/dist_ts/proxies/index.js +5 -5
- package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +4 -4
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +11 -11
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +41 -0
- package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +121 -0
- package/dist_ts/proxies/smart-proxy/index.d.ts +2 -1
- package/dist_ts/proxies/smart-proxy/index.js +4 -2
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +2 -2
- package/dist_ts/proxies/smart-proxy/port-manager.js +3 -3
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +3 -3
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +24 -265
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +25 -25
- package/dist_ts/routing/index.d.ts +5 -0
- package/dist_ts/routing/index.js +8 -0
- package/dist_ts/routing/models/http-types.d.ts +6 -0
- package/dist_ts/routing/models/http-types.js +7 -0
- package/dist_ts/routing/router/index.d.ts +8 -0
- package/dist_ts/routing/router/index.js +7 -0
- package/dist_ts/{classes.router.d.ts → routing/router/proxy-router.d.ts} +14 -11
- package/dist_ts/{classes.router.js → routing/router/proxy-router.js} +2 -2
- package/dist_ts/routing/router/route-router.d.ts +108 -0
- package/dist_ts/routing/router/route-router.js +393 -0
- package/package.json +1 -1
- package/readme.md +12 -12
- package/readme.plan.md +152 -257
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/forwarding/factory/forwarding-factory.ts +28 -0
- package/ts/index.ts +13 -9
- package/ts/proxies/{network-proxy → http-proxy}/certificate-manager.ts +2 -2
- package/ts/proxies/{network-proxy → http-proxy}/connection-pool.ts +2 -2
- package/ts/proxies/http-proxy/handlers/index.ts +6 -0
- package/ts/proxies/http-proxy/handlers/redirect-handler.ts +105 -0
- package/ts/proxies/http-proxy/handlers/static-handler.ts +251 -0
- package/ts/proxies/{network-proxy/network-proxy.ts → http-proxy/http-proxy.ts} +15 -14
- package/ts/proxies/{network-proxy → http-proxy}/index.ts +3 -3
- package/ts/proxies/http-proxy/models/http-types.ts +165 -0
- package/ts/proxies/http-proxy/models/index.ts +5 -0
- package/ts/proxies/{network-proxy → http-proxy}/models/types.ts +2 -2
- package/ts/proxies/{network-proxy → http-proxy}/request-handler.ts +3 -3
- package/ts/proxies/{network-proxy → http-proxy}/websocket-handler.ts +3 -3
- package/ts/proxies/index.ts +7 -7
- package/ts/proxies/smart-proxy/certificate-manager.ts +10 -10
- package/ts/proxies/smart-proxy/{network-proxy-bridge.ts → http-proxy-bridge.ts} +44 -44
- package/ts/proxies/smart-proxy/index.ts +4 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +3 -3
- package/ts/proxies/smart-proxy/port-manager.ts +2 -2
- package/ts/proxies/smart-proxy/route-connection-handler.ts +23 -307
- package/ts/proxies/smart-proxy/smart-proxy.ts +25 -25
- package/ts/routing/index.ts +9 -0
- package/ts/routing/models/http-types.ts +6 -0
- package/ts/{http → routing}/router/proxy-router.ts +1 -1
- package/ts/{http → routing}/router/route-router.ts +1 -1
- package/dist_ts/certificate/acme/acme-factory.d.ts +0 -17
- package/dist_ts/certificate/acme/acme-factory.js +0 -40
- package/dist_ts/certificate/acme/challenge-handler.d.ts +0 -44
- package/dist_ts/certificate/acme/challenge-handler.js +0 -92
- package/dist_ts/certificate/acme/index.d.ts +0 -4
- package/dist_ts/certificate/acme/index.js +0 -5
- package/dist_ts/certificate/certificate-manager.d.ts +0 -150
- package/dist_ts/certificate/certificate-manager.js +0 -505
- package/dist_ts/certificate/events/certificate-events.d.ts +0 -33
- package/dist_ts/certificate/events/certificate-events.js +0 -38
- package/dist_ts/certificate/events/simplified-events.d.ts +0 -56
- package/dist_ts/certificate/events/simplified-events.js +0 -13
- package/dist_ts/certificate/index.d.ts +0 -30
- package/dist_ts/certificate/index.js +0 -37
- package/dist_ts/certificate/models/certificate-errors.d.ts +0 -69
- package/dist_ts/certificate/models/certificate-errors.js +0 -141
- package/dist_ts/certificate/models/certificate-strategy.d.ts +0 -60
- package/dist_ts/certificate/models/certificate-strategy.js +0 -73
- package/dist_ts/certificate/models/certificate-types.d.ts +0 -97
- package/dist_ts/certificate/models/certificate-types.js +0 -2
- package/dist_ts/certificate/providers/cert-provisioner.d.ts +0 -119
- package/dist_ts/certificate/providers/cert-provisioner.js +0 -422
- package/dist_ts/certificate/providers/index.d.ts +0 -4
- package/dist_ts/certificate/providers/index.js +0 -5
- package/dist_ts/certificate/simplified-certificate-manager.d.ts +0 -150
- package/dist_ts/certificate/simplified-certificate-manager.js +0 -501
- package/dist_ts/certificate/storage/file-storage.d.ts +0 -66
- package/dist_ts/certificate/storage/file-storage.js +0 -194
- package/dist_ts/certificate/storage/index.d.ts +0 -4
- package/dist_ts/certificate/storage/index.js +0 -5
- package/dist_ts/certificate/utils/certificate-helpers.d.ts +0 -17
- package/dist_ts/certificate/utils/certificate-helpers.js +0 -45
- package/dist_ts/classes.iptablesproxy.d.ts +0 -112
- package/dist_ts/classes.iptablesproxy.js +0 -765
- package/dist_ts/classes.networkproxy.d.ts +0 -243
- package/dist_ts/classes.networkproxy.js +0 -1424
- package/dist_ts/classes.nftablesproxy.d.ts +0 -219
- package/dist_ts/classes.nftablesproxy.js +0 -1542
- package/dist_ts/classes.port80handler.d.ts +0 -215
- package/dist_ts/classes.port80handler.js +0 -736
- package/dist_ts/classes.portproxy.d.ts +0 -171
- package/dist_ts/classes.portproxy.js +0 -1802
- package/dist_ts/classes.pp.acmemanager.d.ts +0 -34
- package/dist_ts/classes.pp.acmemanager.js +0 -123
- package/dist_ts/classes.pp.connectionhandler.d.ts +0 -39
- package/dist_ts/classes.pp.connectionhandler.js +0 -754
- package/dist_ts/classes.pp.connectionmanager.d.ts +0 -78
- package/dist_ts/classes.pp.connectionmanager.js +0 -378
- package/dist_ts/classes.pp.domainconfigmanager.d.ts +0 -55
- package/dist_ts/classes.pp.domainconfigmanager.js +0 -103
- package/dist_ts/classes.pp.interfaces.d.ts +0 -133
- package/dist_ts/classes.pp.interfaces.js +0 -2
- package/dist_ts/classes.pp.networkproxybridge.d.ts +0 -57
- package/dist_ts/classes.pp.networkproxybridge.js +0 -306
- package/dist_ts/classes.pp.portproxy.d.ts +0 -64
- package/dist_ts/classes.pp.portproxy.js +0 -567
- package/dist_ts/classes.pp.portrangemanager.d.ts +0 -56
- package/dist_ts/classes.pp.portrangemanager.js +0 -179
- package/dist_ts/classes.pp.securitymanager.d.ts +0 -47
- package/dist_ts/classes.pp.securitymanager.js +0 -126
- package/dist_ts/classes.pp.snihandler.d.ts +0 -153
- package/dist_ts/classes.pp.snihandler.js +0 -1053
- package/dist_ts/classes.pp.timeoutmanager.d.ts +0 -47
- package/dist_ts/classes.pp.timeoutmanager.js +0 -154
- package/dist_ts/classes.pp.tlsalert.d.ts +0 -149
- package/dist_ts/classes.pp.tlsalert.js +0 -225
- package/dist_ts/classes.pp.tlsmanager.d.ts +0 -57
- package/dist_ts/classes.pp.tlsmanager.js +0 -132
- package/dist_ts/classes.snihandler.d.ts +0 -198
- package/dist_ts/classes.snihandler.js +0 -1210
- package/dist_ts/classes.sslredirect.d.ts +0 -8
- package/dist_ts/classes.sslredirect.js +0 -28
- package/dist_ts/common/acmeFactory.d.ts +0 -9
- package/dist_ts/common/acmeFactory.js +0 -20
- package/dist_ts/common/port80-adapter.d.ts +0 -11
- package/dist_ts/common/port80-adapter.js +0 -87
- package/dist_ts/examples/forwarding-example.d.ts +0 -1
- package/dist_ts/examples/forwarding-example.js +0 -96
- package/dist_ts/forwarding/config/domain-config.d.ts +0 -12
- package/dist_ts/forwarding/config/domain-config.js +0 -12
- package/dist_ts/forwarding/config/domain-manager.d.ts +0 -86
- package/dist_ts/forwarding/config/domain-manager.js +0 -242
- package/dist_ts/helpers.certificates.d.ts +0 -5
- package/dist_ts/helpers.certificates.js +0 -23
- package/dist_ts/http/port80/acme-interfaces.d.ts +0 -108
- package/dist_ts/http/port80/acme-interfaces.js +0 -51
- package/dist_ts/http/port80/challenge-responder.d.ts +0 -53
- package/dist_ts/http/port80/challenge-responder.js +0 -203
- package/dist_ts/http/port80/index.d.ts +0 -6
- package/dist_ts/http/port80/index.js +0 -9
- package/dist_ts/http/port80/port80-handler.d.ts +0 -136
- package/dist_ts/http/port80/port80-handler.js +0 -592
- package/dist_ts/http/redirects/index.d.ts +0 -4
- package/dist_ts/http/redirects/index.js +0 -5
- package/dist_ts/networkproxy/classes.np.certificatemanager.d.ts +0 -77
- package/dist_ts/networkproxy/classes.np.certificatemanager.js +0 -372
- package/dist_ts/networkproxy/classes.np.connectionpool.d.ts +0 -47
- package/dist_ts/networkproxy/classes.np.connectionpool.js +0 -210
- package/dist_ts/networkproxy/classes.np.networkproxy.d.ts +0 -118
- package/dist_ts/networkproxy/classes.np.networkproxy.js +0 -387
- package/dist_ts/networkproxy/classes.np.requesthandler.d.ts +0 -56
- package/dist_ts/networkproxy/classes.np.requesthandler.js +0 -393
- package/dist_ts/networkproxy/classes.np.types.d.ts +0 -83
- package/dist_ts/networkproxy/classes.np.types.js +0 -35
- package/dist_ts/networkproxy/classes.np.websockethandler.d.ts +0 -38
- package/dist_ts/networkproxy/classes.np.websockethandler.js +0 -188
- package/dist_ts/networkproxy/index.d.ts +0 -1
- package/dist_ts/networkproxy/index.js +0 -4
- package/dist_ts/nfttablesproxy/classes.nftablesproxy.d.ts +0 -219
- package/dist_ts/nfttablesproxy/classes.nftablesproxy.js +0 -1542
- package/dist_ts/port80handler/classes.port80handler.d.ts +0 -10
- package/dist_ts/port80handler/classes.port80handler.js +0 -16
- package/dist_ts/proxies/network-proxy/connection-pool.js +0 -210
- package/dist_ts/proxies/network-proxy/context-creator.js +0 -108
- package/dist_ts/proxies/network-proxy/http2-request-handler.js +0 -201
- package/dist_ts/proxies/network-proxy/index.js +0 -12
- package/dist_ts/proxies/network-proxy/models/index.d.ts +0 -4
- package/dist_ts/proxies/network-proxy/models/index.js +0 -5
- package/dist_ts/proxies/network-proxy/models/types.js +0 -276
- package/dist_ts/proxies/network-proxy/security-manager.js +0 -255
- package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.d.ts +0 -48
- package/dist_ts/proxies/network-proxy/simplified-certificate-bridge.js +0 -76
- package/dist_ts/proxies/smart-proxy/connection-handler.d.ts +0 -39
- package/dist_ts/proxies/smart-proxy/connection-handler.js +0 -894
- package/dist_ts/proxies/smart-proxy/domain-config-manager.d.ts +0 -110
- package/dist_ts/proxies/smart-proxy/domain-config-manager.js +0 -386
- package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.d.ts +0 -168
- package/dist_ts/proxies/smart-proxy/legacy-smart-proxy.js +0 -642
- package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.d.ts +0 -65
- package/dist_ts/proxies/smart-proxy/models/simplified-smartproxy-config.js +0 -31
- package/dist_ts/proxies/smart-proxy/models/smartproxy-options.d.ts +0 -102
- package/dist_ts/proxies/smart-proxy/models/smartproxy-options.js +0 -73
- package/dist_ts/proxies/smart-proxy/network-proxy-bridge.d.ts +0 -41
- package/dist_ts/proxies/smart-proxy/network-proxy-bridge.js +0 -121
- package/dist_ts/proxies/smart-proxy/port-range-manager.d.ts +0 -56
- package/dist_ts/proxies/smart-proxy/port-range-manager.js +0 -176
- package/dist_ts/proxies/smart-proxy/route-helpers/index.d.ts +0 -9
- package/dist_ts/proxies/smart-proxy/route-helpers/index.js +0 -11
- package/dist_ts/proxies/smart-proxy/route-helpers.d.ts +0 -7
- package/dist_ts/proxies/smart-proxy/route-helpers.js +0 -9
- package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.d.ts +0 -41
- package/dist_ts/proxies/smart-proxy/simplified-smart-proxy.js +0 -132
- package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.d.ts +0 -51
- package/dist_ts/proxies/smart-proxy/utils/route-migration-utils.js +0 -124
- package/dist_ts/redirect/classes.redirect.d.ts +0 -96
- package/dist_ts/redirect/classes.redirect.js +0 -194
- package/dist_ts/smartproxy/classes.pp.certprovisioner.d.ts +0 -54
- package/dist_ts/smartproxy/classes.pp.certprovisioner.js +0 -179
- package/dist_ts/smartproxy/classes.pp.connectionhandler.d.ts +0 -39
- package/dist_ts/smartproxy/classes.pp.connectionhandler.js +0 -894
- package/dist_ts/smartproxy/classes.pp.connectionmanager.d.ts +0 -78
- package/dist_ts/smartproxy/classes.pp.connectionmanager.js +0 -378
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +0 -94
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +0 -255
- package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +0 -103
- package/dist_ts/smartproxy/classes.pp.interfaces.js +0 -2
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.d.ts +0 -62
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.js +0 -316
- package/dist_ts/smartproxy/classes.pp.portrangemanager.d.ts +0 -56
- package/dist_ts/smartproxy/classes.pp.portrangemanager.js +0 -176
- package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +0 -64
- package/dist_ts/smartproxy/classes.pp.securitymanager.js +0 -149
- package/dist_ts/smartproxy/classes.pp.snihandler.d.ts +0 -153
- package/dist_ts/smartproxy/classes.pp.snihandler.js +0 -1053
- package/dist_ts/smartproxy/classes.pp.timeoutmanager.d.ts +0 -47
- package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +0 -154
- package/dist_ts/smartproxy/classes.pp.tlsalert.d.ts +0 -149
- package/dist_ts/smartproxy/classes.pp.tlsalert.js +0 -225
- package/dist_ts/smartproxy/classes.pp.tlsmanager.d.ts +0 -57
- package/dist_ts/smartproxy/classes.pp.tlsmanager.js +0 -132
- package/dist_ts/smartproxy/classes.smartproxy.d.ts +0 -63
- package/dist_ts/smartproxy/classes.smartproxy.js +0 -521
- package/dist_ts/smartproxy/forwarding/domain-config.d.ts +0 -12
- package/dist_ts/smartproxy/forwarding/domain-config.js +0 -12
- package/dist_ts/smartproxy/forwarding/domain-manager.d.ts +0 -86
- package/dist_ts/smartproxy/forwarding/domain-manager.js +0 -241
- package/dist_ts/smartproxy/forwarding/forwarding.factory.d.ts +0 -24
- package/dist_ts/smartproxy/forwarding/forwarding.factory.js +0 -137
- package/dist_ts/smartproxy/forwarding/forwarding.handler.d.ts +0 -55
- package/dist_ts/smartproxy/forwarding/forwarding.handler.js +0 -94
- package/dist_ts/smartproxy/forwarding/http.handler.d.ts +0 -25
- package/dist_ts/smartproxy/forwarding/http.handler.js +0 -123
- package/dist_ts/smartproxy/forwarding/https-passthrough.handler.d.ts +0 -24
- package/dist_ts/smartproxy/forwarding/https-passthrough.handler.js +0 -154
- package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.d.ts +0 -36
- package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.js +0 -229
- package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.d.ts +0 -35
- package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.js +0 -254
- package/dist_ts/smartproxy/forwarding/index.d.ts +0 -16
- package/dist_ts/smartproxy/forwarding/index.js +0 -23
- package/dist_ts/smartproxy/types/forwarding.types.d.ts +0 -104
- package/dist_ts/smartproxy/types/forwarding.types.js +0 -50
- package/dist_ts/smartproxy.classes.networkproxy.d.ts +0 -31
- package/dist_ts/smartproxy.classes.networkproxy.js +0 -305
- package/dist_ts/smartproxy.classes.router.d.ts +0 -13
- package/dist_ts/smartproxy.classes.router.js +0 -33
- package/dist_ts/smartproxy.classes.sslredirect.d.ts +0 -8
- package/dist_ts/smartproxy.classes.sslredirect.js +0 -28
- package/dist_ts/smartproxy.helpers.certificates.d.ts +0 -5
- package/dist_ts/smartproxy.helpers.certificates.js +0 -23
- package/dist_ts/smartproxy.plugins.d.ts +0 -18
- package/dist_ts/smartproxy.plugins.js +0 -23
- package/dist_ts/smartproxy.portproxy.d.ts +0 -26
- package/dist_ts/smartproxy.portproxy.js +0 -295
- package/ts/http/index.ts +0 -16
- package/ts/http/models/http-types.ts +0 -108
- package/ts/http/redirects/index.ts +0 -3
- package/ts/proxies/network-proxy/models/index.ts +0 -4
- package/ts/redirect/classes.redirect.ts +0 -295
- /package/dist_ts/proxies/{network-proxy → http-proxy}/context-creator.d.ts +0 -0
- /package/dist_ts/proxies/{network-proxy → http-proxy}/function-cache.d.ts +0 -0
- /package/dist_ts/proxies/{network-proxy → http-proxy}/http-request-handler.d.ts +0 -0
- /package/dist_ts/proxies/{network-proxy → http-proxy}/http2-request-handler.d.ts +0 -0
- /package/dist_ts/proxies/{network-proxy → http-proxy}/security-manager.d.ts +0 -0
- /package/ts/proxies/{network-proxy → http-proxy}/context-creator.ts +0 -0
- /package/ts/proxies/{network-proxy → http-proxy}/function-cache.ts +0 -0
- /package/ts/proxies/{network-proxy → http-proxy}/http-request-handler.ts +0 -0
- /package/ts/proxies/{network-proxy → http-proxy}/http2-request-handler.ts +0 -0
- /package/ts/proxies/{network-proxy → http-proxy}/security-manager.ts +0 -0
- /package/ts/{http → routing}/router/index.ts +0 -0
|
@@ -1,765 +0,0 @@
|
|
|
1
|
-
import { exec, execSync } from 'child_process';
|
|
2
|
-
import { promisify } from 'util';
|
|
3
|
-
const execAsync = promisify(exec);
|
|
4
|
-
/**
|
|
5
|
-
* IPTablesProxy sets up iptables NAT rules to forward TCP traffic.
|
|
6
|
-
* Enhanced with multi-port support, IPv6, and integration with PortProxy/NetworkProxy.
|
|
7
|
-
*/
|
|
8
|
-
export class IPTablesProxy {
|
|
9
|
-
constructor(settings) {
|
|
10
|
-
this.rules = [];
|
|
11
|
-
this.customChain = null;
|
|
12
|
-
// Validate inputs to prevent command injection
|
|
13
|
-
this.validateSettings(settings);
|
|
14
|
-
// Set default settings
|
|
15
|
-
this.settings = {
|
|
16
|
-
...settings,
|
|
17
|
-
toHost: settings.toHost || 'localhost',
|
|
18
|
-
protocol: settings.protocol || 'tcp',
|
|
19
|
-
enableLogging: settings.enableLogging !== undefined ? settings.enableLogging : false,
|
|
20
|
-
ipv6Support: settings.ipv6Support !== undefined ? settings.ipv6Support : false,
|
|
21
|
-
checkExistingRules: settings.checkExistingRules !== undefined ? settings.checkExistingRules : true,
|
|
22
|
-
netProxyIntegration: settings.netProxyIntegration || { enabled: false }
|
|
23
|
-
};
|
|
24
|
-
// Generate a unique identifier for the rules added by this instance
|
|
25
|
-
this.ruleTag = `IPTablesProxy:${Date.now()}:${Math.random().toString(36).substr(2, 5)}`;
|
|
26
|
-
if (this.settings.addJumpRule) {
|
|
27
|
-
this.customChain = `IPTablesProxy_${Math.random().toString(36).substr(2, 5)}`;
|
|
28
|
-
}
|
|
29
|
-
// Register cleanup handlers if deleteOnExit is true
|
|
30
|
-
if (this.settings.deleteOnExit) {
|
|
31
|
-
const cleanup = () => {
|
|
32
|
-
try {
|
|
33
|
-
this.stopSync();
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
console.error('Error cleaning iptables rules on exit:', err);
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
process.on('exit', cleanup);
|
|
40
|
-
process.on('SIGINT', () => {
|
|
41
|
-
cleanup();
|
|
42
|
-
process.exit();
|
|
43
|
-
});
|
|
44
|
-
process.on('SIGTERM', () => {
|
|
45
|
-
cleanup();
|
|
46
|
-
process.exit();
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Validates settings to prevent command injection and ensure valid values
|
|
52
|
-
*/
|
|
53
|
-
validateSettings(settings) {
|
|
54
|
-
// Validate port numbers
|
|
55
|
-
const validatePorts = (port) => {
|
|
56
|
-
if (Array.isArray(port)) {
|
|
57
|
-
port.forEach(p => validatePorts(p));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (typeof port === 'number') {
|
|
61
|
-
if (port < 1 || port > 65535) {
|
|
62
|
-
throw new Error(`Invalid port number: ${port}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
else if (typeof port === 'object') {
|
|
66
|
-
if (port.from < 1 || port.from > 65535 || port.to < 1 || port.to > 65535 || port.from > port.to) {
|
|
67
|
-
throw new Error(`Invalid port range: ${port.from}-${port.to}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
validatePorts(settings.fromPort);
|
|
72
|
-
validatePorts(settings.toPort);
|
|
73
|
-
// Define regex patterns at the method level so they're available throughout
|
|
74
|
-
const ipRegex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$/;
|
|
75
|
-
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/;
|
|
76
|
-
// Validate IP addresses
|
|
77
|
-
const validateIPs = (ips) => {
|
|
78
|
-
if (!ips)
|
|
79
|
-
return;
|
|
80
|
-
for (const ip of ips) {
|
|
81
|
-
if (!ipRegex.test(ip) && !ipv6Regex.test(ip)) {
|
|
82
|
-
throw new Error(`Invalid IP address format: ${ip}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
validateIPs(settings.allowedSourceIPs);
|
|
87
|
-
validateIPs(settings.bannedSourceIPs);
|
|
88
|
-
// Validate toHost - only allow hostnames or IPs
|
|
89
|
-
if (settings.toHost) {
|
|
90
|
-
const hostRegex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
|
|
91
|
-
if (!hostRegex.test(settings.toHost) && !ipRegex.test(settings.toHost) && !ipv6Regex.test(settings.toHost)) {
|
|
92
|
-
throw new Error(`Invalid host format: ${settings.toHost}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Normalizes port specifications into an array of port ranges
|
|
98
|
-
*/
|
|
99
|
-
normalizePortSpec(portSpec) {
|
|
100
|
-
const result = [];
|
|
101
|
-
if (Array.isArray(portSpec)) {
|
|
102
|
-
// If it's an array, process each element
|
|
103
|
-
for (const spec of portSpec) {
|
|
104
|
-
result.push(...this.normalizePortSpec(spec));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
else if (typeof portSpec === 'number') {
|
|
108
|
-
// Single port becomes a range with the same start and end
|
|
109
|
-
result.push({ from: portSpec, to: portSpec });
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
// Already a range
|
|
113
|
-
result.push(portSpec);
|
|
114
|
-
}
|
|
115
|
-
return result;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Gets the appropriate iptables command based on settings
|
|
119
|
-
*/
|
|
120
|
-
getIptablesCommand(isIpv6 = false) {
|
|
121
|
-
return isIpv6 ? 'ip6tables' : 'iptables';
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Checks if a rule already exists in iptables
|
|
125
|
-
*/
|
|
126
|
-
async ruleExists(table, command, isIpv6 = false) {
|
|
127
|
-
try {
|
|
128
|
-
const iptablesCmd = this.getIptablesCommand(isIpv6);
|
|
129
|
-
const { stdout } = await execAsync(`${iptablesCmd}-save -t ${table}`);
|
|
130
|
-
// Convert the command to the format found in iptables-save output
|
|
131
|
-
// (This is a simplification - in reality, you'd need more parsing)
|
|
132
|
-
const rulePattern = command.replace(`${iptablesCmd} -t ${table} -A `, '-A ');
|
|
133
|
-
return stdout.split('\n').some(line => line.trim() === rulePattern);
|
|
134
|
-
}
|
|
135
|
-
catch (err) {
|
|
136
|
-
this.log('error', `Failed to check if rule exists: ${err}`);
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Sets up a custom chain for better rule management
|
|
142
|
-
*/
|
|
143
|
-
async setupCustomChain(isIpv6 = false) {
|
|
144
|
-
if (!this.customChain)
|
|
145
|
-
return true;
|
|
146
|
-
const iptablesCmd = this.getIptablesCommand(isIpv6);
|
|
147
|
-
const table = 'nat';
|
|
148
|
-
try {
|
|
149
|
-
// Create the chain
|
|
150
|
-
await execAsync(`${iptablesCmd} -t ${table} -N ${this.customChain}`);
|
|
151
|
-
this.log('info', `Created custom chain: ${this.customChain}`);
|
|
152
|
-
// Add jump rule to PREROUTING chain
|
|
153
|
-
const jumpCommand = `${iptablesCmd} -t ${table} -A PREROUTING -j ${this.customChain} -m comment --comment "${this.ruleTag}:JUMP"`;
|
|
154
|
-
await execAsync(jumpCommand);
|
|
155
|
-
this.log('info', `Added jump rule to ${this.customChain}`);
|
|
156
|
-
// Store the jump rule
|
|
157
|
-
this.rules.push({
|
|
158
|
-
table,
|
|
159
|
-
chain: 'PREROUTING',
|
|
160
|
-
command: jumpCommand,
|
|
161
|
-
tag: `${this.ruleTag}:JUMP`,
|
|
162
|
-
added: true
|
|
163
|
-
});
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
catch (err) {
|
|
167
|
-
this.log('error', `Failed to set up custom chain: ${err}`);
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Add a source IP filter rule
|
|
173
|
-
*/
|
|
174
|
-
async addSourceIPFilter(isIpv6 = false) {
|
|
175
|
-
if (!this.settings.allowedSourceIPs && !this.settings.bannedSourceIPs) {
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
const iptablesCmd = this.getIptablesCommand(isIpv6);
|
|
179
|
-
const table = 'nat';
|
|
180
|
-
const chain = this.customChain || 'PREROUTING';
|
|
181
|
-
try {
|
|
182
|
-
// Add banned IPs first (explicit deny)
|
|
183
|
-
if (this.settings.bannedSourceIPs && this.settings.bannedSourceIPs.length > 0) {
|
|
184
|
-
for (const ip of this.settings.bannedSourceIPs) {
|
|
185
|
-
const command = `${iptablesCmd} -t ${table} -A ${chain} -s ${ip} -j DROP -m comment --comment "${this.ruleTag}:BANNED"`;
|
|
186
|
-
// Check if rule already exists
|
|
187
|
-
if (this.settings.checkExistingRules && await this.ruleExists(table, command, isIpv6)) {
|
|
188
|
-
this.log('info', `Rule already exists, skipping: ${command}`);
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
await execAsync(command);
|
|
192
|
-
this.log('info', `Added banned IP rule: ${command}`);
|
|
193
|
-
this.rules.push({
|
|
194
|
-
table,
|
|
195
|
-
chain,
|
|
196
|
-
command,
|
|
197
|
-
tag: `${this.ruleTag}:BANNED`,
|
|
198
|
-
added: true
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
// Add allowed IPs (explicit allow)
|
|
203
|
-
if (this.settings.allowedSourceIPs && this.settings.allowedSourceIPs.length > 0) {
|
|
204
|
-
// First add a default deny for all
|
|
205
|
-
const denyAllCommand = `${iptablesCmd} -t ${table} -A ${chain} -p ${this.settings.protocol} -j DROP -m comment --comment "${this.ruleTag}:DENY_ALL"`;
|
|
206
|
-
// Add allow rules for specific IPs
|
|
207
|
-
for (const ip of this.settings.allowedSourceIPs) {
|
|
208
|
-
const command = `${iptablesCmd} -t ${table} -A ${chain} -s ${ip} -p ${this.settings.protocol} -j ACCEPT -m comment --comment "${this.ruleTag}:ALLOWED"`;
|
|
209
|
-
// Check if rule already exists
|
|
210
|
-
if (this.settings.checkExistingRules && await this.ruleExists(table, command, isIpv6)) {
|
|
211
|
-
this.log('info', `Rule already exists, skipping: ${command}`);
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
await execAsync(command);
|
|
215
|
-
this.log('info', `Added allowed IP rule: ${command}`);
|
|
216
|
-
this.rules.push({
|
|
217
|
-
table,
|
|
218
|
-
chain,
|
|
219
|
-
command,
|
|
220
|
-
tag: `${this.ruleTag}:ALLOWED`,
|
|
221
|
-
added: true
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
// Now add the default deny after all allows
|
|
225
|
-
if (this.settings.checkExistingRules && await this.ruleExists(table, denyAllCommand, isIpv6)) {
|
|
226
|
-
this.log('info', `Rule already exists, skipping: ${denyAllCommand}`);
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
await execAsync(denyAllCommand);
|
|
230
|
-
this.log('info', `Added default deny rule: ${denyAllCommand}`);
|
|
231
|
-
this.rules.push({
|
|
232
|
-
table,
|
|
233
|
-
chain,
|
|
234
|
-
command: denyAllCommand,
|
|
235
|
-
tag: `${this.ruleTag}:DENY_ALL`,
|
|
236
|
-
added: true
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return true;
|
|
241
|
-
}
|
|
242
|
-
catch (err) {
|
|
243
|
-
this.log('error', `Failed to add source IP filter rules: ${err}`);
|
|
244
|
-
return false;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Adds a port forwarding rule
|
|
249
|
-
*/
|
|
250
|
-
async addPortForwardingRule(fromPortRange, toPortRange, isIpv6 = false) {
|
|
251
|
-
const iptablesCmd = this.getIptablesCommand(isIpv6);
|
|
252
|
-
const table = 'nat';
|
|
253
|
-
const chain = this.customChain || 'PREROUTING';
|
|
254
|
-
try {
|
|
255
|
-
// Handle single port case
|
|
256
|
-
if (fromPortRange.from === fromPortRange.to && toPortRange.from === toPortRange.to) {
|
|
257
|
-
// Single port forward
|
|
258
|
-
const command = `${iptablesCmd} -t ${table} -A ${chain} -p ${this.settings.protocol} --dport ${fromPortRange.from} ` +
|
|
259
|
-
`-j DNAT --to-destination ${this.settings.toHost}:${toPortRange.from} ` +
|
|
260
|
-
`-m comment --comment "${this.ruleTag}:DNAT"`;
|
|
261
|
-
// Check if rule already exists
|
|
262
|
-
if (this.settings.checkExistingRules && await this.ruleExists(table, command, isIpv6)) {
|
|
263
|
-
this.log('info', `Rule already exists, skipping: ${command}`);
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
await execAsync(command);
|
|
267
|
-
this.log('info', `Added port forwarding rule: ${command}`);
|
|
268
|
-
this.rules.push({
|
|
269
|
-
table,
|
|
270
|
-
chain,
|
|
271
|
-
command,
|
|
272
|
-
tag: `${this.ruleTag}:DNAT`,
|
|
273
|
-
added: true
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
else if (fromPortRange.to - fromPortRange.from === toPortRange.to - toPortRange.from) {
|
|
278
|
-
// Port range forward with equal ranges
|
|
279
|
-
const command = `${iptablesCmd} -t ${table} -A ${chain} -p ${this.settings.protocol} --dport ${fromPortRange.from}:${fromPortRange.to} ` +
|
|
280
|
-
`-j DNAT --to-destination ${this.settings.toHost}:${toPortRange.from}-${toPortRange.to} ` +
|
|
281
|
-
`-m comment --comment "${this.ruleTag}:DNAT_RANGE"`;
|
|
282
|
-
// Check if rule already exists
|
|
283
|
-
if (this.settings.checkExistingRules && await this.ruleExists(table, command, isIpv6)) {
|
|
284
|
-
this.log('info', `Rule already exists, skipping: ${command}`);
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
await execAsync(command);
|
|
288
|
-
this.log('info', `Added port range forwarding rule: ${command}`);
|
|
289
|
-
this.rules.push({
|
|
290
|
-
table,
|
|
291
|
-
chain,
|
|
292
|
-
command,
|
|
293
|
-
tag: `${this.ruleTag}:DNAT_RANGE`,
|
|
294
|
-
added: true
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
// Unequal port ranges need individual rules
|
|
300
|
-
for (let i = 0; i <= fromPortRange.to - fromPortRange.from; i++) {
|
|
301
|
-
const fromPort = fromPortRange.from + i;
|
|
302
|
-
const toPort = toPortRange.from + i % (toPortRange.to - toPortRange.from + 1);
|
|
303
|
-
const command = `${iptablesCmd} -t ${table} -A ${chain} -p ${this.settings.protocol} --dport ${fromPort} ` +
|
|
304
|
-
`-j DNAT --to-destination ${this.settings.toHost}:${toPort} ` +
|
|
305
|
-
`-m comment --comment "${this.ruleTag}:DNAT_INDIVIDUAL"`;
|
|
306
|
-
// Check if rule already exists
|
|
307
|
-
if (this.settings.checkExistingRules && await this.ruleExists(table, command, isIpv6)) {
|
|
308
|
-
this.log('info', `Rule already exists, skipping: ${command}`);
|
|
309
|
-
continue;
|
|
310
|
-
}
|
|
311
|
-
await execAsync(command);
|
|
312
|
-
this.log('info', `Added individual port forwarding rule: ${command}`);
|
|
313
|
-
this.rules.push({
|
|
314
|
-
table,
|
|
315
|
-
chain,
|
|
316
|
-
command,
|
|
317
|
-
tag: `${this.ruleTag}:DNAT_INDIVIDUAL`,
|
|
318
|
-
added: true
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
// If preserveSourceIP is false, add a MASQUERADE rule
|
|
323
|
-
if (!this.settings.preserveSourceIP) {
|
|
324
|
-
// For port range
|
|
325
|
-
const masqCommand = `${iptablesCmd} -t nat -A POSTROUTING -p ${this.settings.protocol} -d ${this.settings.toHost} ` +
|
|
326
|
-
`--dport ${toPortRange.from}:${toPortRange.to} -j MASQUERADE ` +
|
|
327
|
-
`-m comment --comment "${this.ruleTag}:MASQ"`;
|
|
328
|
-
// Check if rule already exists
|
|
329
|
-
if (this.settings.checkExistingRules && await this.ruleExists('nat', masqCommand, isIpv6)) {
|
|
330
|
-
this.log('info', `Rule already exists, skipping: ${masqCommand}`);
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
await execAsync(masqCommand);
|
|
334
|
-
this.log('info', `Added MASQUERADE rule: ${masqCommand}`);
|
|
335
|
-
this.rules.push({
|
|
336
|
-
table: 'nat',
|
|
337
|
-
chain: 'POSTROUTING',
|
|
338
|
-
command: masqCommand,
|
|
339
|
-
tag: `${this.ruleTag}:MASQ`,
|
|
340
|
-
added: true
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return true;
|
|
345
|
-
}
|
|
346
|
-
catch (err) {
|
|
347
|
-
this.log('error', `Failed to add port forwarding rule: ${err}`);
|
|
348
|
-
// Try to roll back any rules that were already added
|
|
349
|
-
await this.rollbackRules();
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Special handling for NetworkProxy integration
|
|
355
|
-
*/
|
|
356
|
-
async setupNetworkProxyIntegration(isIpv6 = false) {
|
|
357
|
-
if (!this.settings.netProxyIntegration?.enabled) {
|
|
358
|
-
return true;
|
|
359
|
-
}
|
|
360
|
-
const netProxyConfig = this.settings.netProxyIntegration;
|
|
361
|
-
const iptablesCmd = this.getIptablesCommand(isIpv6);
|
|
362
|
-
const table = 'nat';
|
|
363
|
-
const chain = this.customChain || 'PREROUTING';
|
|
364
|
-
try {
|
|
365
|
-
// If redirectLocalhost is true, set up special rule to redirect localhost traffic to NetworkProxy
|
|
366
|
-
if (netProxyConfig.redirectLocalhost && netProxyConfig.sslTerminationPort) {
|
|
367
|
-
const redirectCommand = `${iptablesCmd} -t ${table} -A OUTPUT -p tcp -d 127.0.0.1 -j REDIRECT ` +
|
|
368
|
-
`--to-port ${netProxyConfig.sslTerminationPort} ` +
|
|
369
|
-
`-m comment --comment "${this.ruleTag}:NETPROXY_REDIRECT"`;
|
|
370
|
-
// Check if rule already exists
|
|
371
|
-
if (this.settings.checkExistingRules && await this.ruleExists(table, redirectCommand, isIpv6)) {
|
|
372
|
-
this.log('info', `Rule already exists, skipping: ${redirectCommand}`);
|
|
373
|
-
}
|
|
374
|
-
else {
|
|
375
|
-
await execAsync(redirectCommand);
|
|
376
|
-
this.log('info', `Added NetworkProxy redirection rule: ${redirectCommand}`);
|
|
377
|
-
this.rules.push({
|
|
378
|
-
table,
|
|
379
|
-
chain: 'OUTPUT',
|
|
380
|
-
command: redirectCommand,
|
|
381
|
-
tag: `${this.ruleTag}:NETPROXY_REDIRECT`,
|
|
382
|
-
added: true
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
return true;
|
|
387
|
-
}
|
|
388
|
-
catch (err) {
|
|
389
|
-
this.log('error', `Failed to set up NetworkProxy integration: ${err}`);
|
|
390
|
-
return false;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Rolls back rules that were added in case of error
|
|
395
|
-
*/
|
|
396
|
-
async rollbackRules() {
|
|
397
|
-
// Process rules in reverse order (LIFO)
|
|
398
|
-
for (let i = this.rules.length - 1; i >= 0; i--) {
|
|
399
|
-
const rule = this.rules[i];
|
|
400
|
-
if (rule.added) {
|
|
401
|
-
try {
|
|
402
|
-
// Convert -A (add) to -D (delete)
|
|
403
|
-
const deleteCommand = rule.command.replace('-A', '-D');
|
|
404
|
-
await execAsync(deleteCommand);
|
|
405
|
-
this.log('info', `Rolled back rule: ${deleteCommand}`);
|
|
406
|
-
rule.added = false;
|
|
407
|
-
}
|
|
408
|
-
catch (err) {
|
|
409
|
-
this.log('error', `Failed to roll back rule: ${err}`);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Sets up iptables rules for port forwarding with enhanced features
|
|
416
|
-
*/
|
|
417
|
-
async start() {
|
|
418
|
-
// Optionally clean the slate first
|
|
419
|
-
if (this.settings.forceCleanSlate) {
|
|
420
|
-
await IPTablesProxy.cleanSlate();
|
|
421
|
-
}
|
|
422
|
-
// First set up any custom chains
|
|
423
|
-
if (this.settings.addJumpRule) {
|
|
424
|
-
const chainSetupSuccess = await this.setupCustomChain();
|
|
425
|
-
if (!chainSetupSuccess) {
|
|
426
|
-
throw new Error('Failed to set up custom chain');
|
|
427
|
-
}
|
|
428
|
-
// For IPv6 if enabled
|
|
429
|
-
if (this.settings.ipv6Support) {
|
|
430
|
-
const chainSetupSuccessIpv6 = await this.setupCustomChain(true);
|
|
431
|
-
if (!chainSetupSuccessIpv6) {
|
|
432
|
-
this.log('warn', 'Failed to set up IPv6 custom chain, continuing with IPv4 only');
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
// Add source IP filters
|
|
437
|
-
await this.addSourceIPFilter();
|
|
438
|
-
if (this.settings.ipv6Support) {
|
|
439
|
-
await this.addSourceIPFilter(true);
|
|
440
|
-
}
|
|
441
|
-
// Set up NetworkProxy integration if enabled
|
|
442
|
-
if (this.settings.netProxyIntegration?.enabled) {
|
|
443
|
-
const netProxySetupSuccess = await this.setupNetworkProxyIntegration();
|
|
444
|
-
if (!netProxySetupSuccess) {
|
|
445
|
-
this.log('warn', 'Failed to set up NetworkProxy integration');
|
|
446
|
-
}
|
|
447
|
-
if (this.settings.ipv6Support) {
|
|
448
|
-
await this.setupNetworkProxyIntegration(true);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
// Normalize port specifications
|
|
452
|
-
const fromPortRanges = this.normalizePortSpec(this.settings.fromPort);
|
|
453
|
-
const toPortRanges = this.normalizePortSpec(this.settings.toPort);
|
|
454
|
-
// Handle the case where fromPort and toPort counts don't match
|
|
455
|
-
if (fromPortRanges.length !== toPortRanges.length) {
|
|
456
|
-
if (toPortRanges.length === 1) {
|
|
457
|
-
// If there's only one toPort, use it for all fromPorts
|
|
458
|
-
for (const fromRange of fromPortRanges) {
|
|
459
|
-
await this.addPortForwardingRule(fromRange, toPortRanges[0]);
|
|
460
|
-
if (this.settings.ipv6Support) {
|
|
461
|
-
await this.addPortForwardingRule(fromRange, toPortRanges[0], true);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
else {
|
|
466
|
-
throw new Error('Mismatched port counts: fromPort and toPort arrays must have equal length or toPort must be a single value');
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
// Add port forwarding rules for each port specification
|
|
471
|
-
for (let i = 0; i < fromPortRanges.length; i++) {
|
|
472
|
-
await this.addPortForwardingRule(fromPortRanges[i], toPortRanges[i]);
|
|
473
|
-
if (this.settings.ipv6Support) {
|
|
474
|
-
await this.addPortForwardingRule(fromPortRanges[i], toPortRanges[i], true);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
// Final check - ensure we have at least one rule added
|
|
479
|
-
if (this.rules.filter(r => r.added).length === 0) {
|
|
480
|
-
throw new Error('No rules were added');
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Removes all added iptables rules
|
|
485
|
-
*/
|
|
486
|
-
async stop() {
|
|
487
|
-
// Process rules in reverse order (LIFO)
|
|
488
|
-
for (let i = this.rules.length - 1; i >= 0; i--) {
|
|
489
|
-
const rule = this.rules[i];
|
|
490
|
-
if (rule.added) {
|
|
491
|
-
try {
|
|
492
|
-
// Convert -A (add) to -D (delete)
|
|
493
|
-
const deleteCommand = rule.command.replace('-A', '-D');
|
|
494
|
-
await execAsync(deleteCommand);
|
|
495
|
-
this.log('info', `Removed rule: ${deleteCommand}`);
|
|
496
|
-
rule.added = false;
|
|
497
|
-
}
|
|
498
|
-
catch (err) {
|
|
499
|
-
this.log('error', `Failed to remove rule: ${err}`);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
// If we created a custom chain, we need to clean it up
|
|
504
|
-
if (this.customChain) {
|
|
505
|
-
try {
|
|
506
|
-
// First flush the chain
|
|
507
|
-
await execAsync(`iptables -t nat -F ${this.customChain}`);
|
|
508
|
-
this.log('info', `Flushed custom chain: ${this.customChain}`);
|
|
509
|
-
// Then delete it
|
|
510
|
-
await execAsync(`iptables -t nat -X ${this.customChain}`);
|
|
511
|
-
this.log('info', `Deleted custom chain: ${this.customChain}`);
|
|
512
|
-
// Same for IPv6 if enabled
|
|
513
|
-
if (this.settings.ipv6Support) {
|
|
514
|
-
try {
|
|
515
|
-
await execAsync(`ip6tables -t nat -F ${this.customChain}`);
|
|
516
|
-
await execAsync(`ip6tables -t nat -X ${this.customChain}`);
|
|
517
|
-
this.log('info', `Deleted IPv6 custom chain: ${this.customChain}`);
|
|
518
|
-
}
|
|
519
|
-
catch (err) {
|
|
520
|
-
this.log('error', `Failed to delete IPv6 custom chain: ${err}`);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
catch (err) {
|
|
525
|
-
this.log('error', `Failed to delete custom chain: ${err}`);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
// Clear rules array
|
|
529
|
-
this.rules = [];
|
|
530
|
-
}
|
|
531
|
-
/**
|
|
532
|
-
* Synchronous version of stop, for use in exit handlers
|
|
533
|
-
*/
|
|
534
|
-
stopSync() {
|
|
535
|
-
// Process rules in reverse order (LIFO)
|
|
536
|
-
for (let i = this.rules.length - 1; i >= 0; i--) {
|
|
537
|
-
const rule = this.rules[i];
|
|
538
|
-
if (rule.added) {
|
|
539
|
-
try {
|
|
540
|
-
// Convert -A (add) to -D (delete)
|
|
541
|
-
const deleteCommand = rule.command.replace('-A', '-D');
|
|
542
|
-
execSync(deleteCommand);
|
|
543
|
-
this.log('info', `Removed rule: ${deleteCommand}`);
|
|
544
|
-
rule.added = false;
|
|
545
|
-
}
|
|
546
|
-
catch (err) {
|
|
547
|
-
this.log('error', `Failed to remove rule: ${err}`);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
// If we created a custom chain, we need to clean it up
|
|
552
|
-
if (this.customChain) {
|
|
553
|
-
try {
|
|
554
|
-
// First flush the chain
|
|
555
|
-
execSync(`iptables -t nat -F ${this.customChain}`);
|
|
556
|
-
// Then delete it
|
|
557
|
-
execSync(`iptables -t nat -X ${this.customChain}`);
|
|
558
|
-
this.log('info', `Deleted custom chain: ${this.customChain}`);
|
|
559
|
-
// Same for IPv6 if enabled
|
|
560
|
-
if (this.settings.ipv6Support) {
|
|
561
|
-
try {
|
|
562
|
-
execSync(`ip6tables -t nat -F ${this.customChain}`);
|
|
563
|
-
execSync(`ip6tables -t nat -X ${this.customChain}`);
|
|
564
|
-
}
|
|
565
|
-
catch (err) {
|
|
566
|
-
// IPv6 failures are non-critical
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
catch (err) {
|
|
571
|
-
this.log('error', `Failed to delete custom chain: ${err}`);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
// Clear rules array
|
|
575
|
-
this.rules = [];
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* Asynchronously cleans up any iptables rules in the nat table that were added by this module.
|
|
579
|
-
* It looks for rules with comments containing "IPTablesProxy:".
|
|
580
|
-
*/
|
|
581
|
-
static async cleanSlate() {
|
|
582
|
-
await IPTablesProxy.cleanSlateInternal();
|
|
583
|
-
// Also clean IPv6 rules
|
|
584
|
-
await IPTablesProxy.cleanSlateInternal(true);
|
|
585
|
-
}
|
|
586
|
-
/**
|
|
587
|
-
* Internal implementation of cleanSlate with IPv6 support
|
|
588
|
-
*/
|
|
589
|
-
static async cleanSlateInternal(isIpv6 = false) {
|
|
590
|
-
const iptablesCmd = isIpv6 ? 'ip6tables' : 'iptables';
|
|
591
|
-
try {
|
|
592
|
-
const { stdout } = await execAsync(`${iptablesCmd}-save -t nat`);
|
|
593
|
-
const lines = stdout.split('\n');
|
|
594
|
-
const proxyLines = lines.filter(line => line.includes('IPTablesProxy:'));
|
|
595
|
-
// First, find and remove any custom chains
|
|
596
|
-
const customChains = new Set();
|
|
597
|
-
const jumpRules = [];
|
|
598
|
-
for (const line of proxyLines) {
|
|
599
|
-
if (line.includes('IPTablesProxy:JUMP')) {
|
|
600
|
-
// Extract chain name from jump rule
|
|
601
|
-
const match = line.match(/\s+-j\s+(\S+)\s+/);
|
|
602
|
-
if (match && match[1].startsWith('IPTablesProxy_')) {
|
|
603
|
-
customChains.add(match[1]);
|
|
604
|
-
jumpRules.push(line);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
// Remove jump rules first
|
|
609
|
-
for (const line of jumpRules) {
|
|
610
|
-
const trimmedLine = line.trim();
|
|
611
|
-
if (trimmedLine.startsWith('-A')) {
|
|
612
|
-
// Replace the "-A" with "-D" to form a deletion command
|
|
613
|
-
const deleteRule = trimmedLine.replace('-A', '-D');
|
|
614
|
-
const cmd = `${iptablesCmd} -t nat ${deleteRule}`;
|
|
615
|
-
try {
|
|
616
|
-
await execAsync(cmd);
|
|
617
|
-
console.log(`Cleaned up iptables jump rule: ${cmd}`);
|
|
618
|
-
}
|
|
619
|
-
catch (err) {
|
|
620
|
-
console.error(`Failed to remove iptables jump rule: ${cmd}`, err);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
// Then remove all other rules
|
|
625
|
-
for (const line of proxyLines) {
|
|
626
|
-
if (!line.includes('IPTablesProxy:JUMP')) { // Skip jump rules we already handled
|
|
627
|
-
const trimmedLine = line.trim();
|
|
628
|
-
if (trimmedLine.startsWith('-A')) {
|
|
629
|
-
// Replace the "-A" with "-D" to form a deletion command
|
|
630
|
-
const deleteRule = trimmedLine.replace('-A', '-D');
|
|
631
|
-
const cmd = `${iptablesCmd} -t nat ${deleteRule}`;
|
|
632
|
-
try {
|
|
633
|
-
await execAsync(cmd);
|
|
634
|
-
console.log(`Cleaned up iptables rule: ${cmd}`);
|
|
635
|
-
}
|
|
636
|
-
catch (err) {
|
|
637
|
-
console.error(`Failed to remove iptables rule: ${cmd}`, err);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
// Finally clean up custom chains
|
|
643
|
-
for (const chain of customChains) {
|
|
644
|
-
try {
|
|
645
|
-
// Flush the chain
|
|
646
|
-
await execAsync(`${iptablesCmd} -t nat -F ${chain}`);
|
|
647
|
-
console.log(`Flushed custom chain: ${chain}`);
|
|
648
|
-
// Delete the chain
|
|
649
|
-
await execAsync(`${iptablesCmd} -t nat -X ${chain}`);
|
|
650
|
-
console.log(`Deleted custom chain: ${chain}`);
|
|
651
|
-
}
|
|
652
|
-
catch (err) {
|
|
653
|
-
console.error(`Failed to delete custom chain ${chain}:`, err);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
catch (err) {
|
|
658
|
-
console.error(`Failed to run ${iptablesCmd}-save: ${err}`);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
/**
|
|
662
|
-
* Synchronously cleans up any iptables rules in the nat table that were added by this module.
|
|
663
|
-
* It looks for rules with comments containing "IPTablesProxy:".
|
|
664
|
-
* This method is intended for use in process exit handlers.
|
|
665
|
-
*/
|
|
666
|
-
static cleanSlateSync() {
|
|
667
|
-
IPTablesProxy.cleanSlateSyncInternal();
|
|
668
|
-
// Also clean IPv6 rules
|
|
669
|
-
IPTablesProxy.cleanSlateSyncInternal(true);
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Internal implementation of cleanSlateSync with IPv6 support
|
|
673
|
-
*/
|
|
674
|
-
static cleanSlateSyncInternal(isIpv6 = false) {
|
|
675
|
-
const iptablesCmd = isIpv6 ? 'ip6tables' : 'iptables';
|
|
676
|
-
try {
|
|
677
|
-
const stdout = execSync(`${iptablesCmd}-save -t nat`).toString();
|
|
678
|
-
const lines = stdout.split('\n');
|
|
679
|
-
const proxyLines = lines.filter(line => line.includes('IPTablesProxy:'));
|
|
680
|
-
// First, find and remove any custom chains
|
|
681
|
-
const customChains = new Set();
|
|
682
|
-
const jumpRules = [];
|
|
683
|
-
for (const line of proxyLines) {
|
|
684
|
-
if (line.includes('IPTablesProxy:JUMP')) {
|
|
685
|
-
// Extract chain name from jump rule
|
|
686
|
-
const match = line.match(/\s+-j\s+(\S+)\s+/);
|
|
687
|
-
if (match && match[1].startsWith('IPTablesProxy_')) {
|
|
688
|
-
customChains.add(match[1]);
|
|
689
|
-
jumpRules.push(line);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
// Remove jump rules first
|
|
694
|
-
for (const line of jumpRules) {
|
|
695
|
-
const trimmedLine = line.trim();
|
|
696
|
-
if (trimmedLine.startsWith('-A')) {
|
|
697
|
-
// Replace the "-A" with "-D" to form a deletion command
|
|
698
|
-
const deleteRule = trimmedLine.replace('-A', '-D');
|
|
699
|
-
const cmd = `${iptablesCmd} -t nat ${deleteRule}`;
|
|
700
|
-
try {
|
|
701
|
-
execSync(cmd);
|
|
702
|
-
console.log(`Cleaned up iptables jump rule: ${cmd}`);
|
|
703
|
-
}
|
|
704
|
-
catch (err) {
|
|
705
|
-
console.error(`Failed to remove iptables jump rule: ${cmd}`, err);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
// Then remove all other rules
|
|
710
|
-
for (const line of proxyLines) {
|
|
711
|
-
if (!line.includes('IPTablesProxy:JUMP')) { // Skip jump rules we already handled
|
|
712
|
-
const trimmedLine = line.trim();
|
|
713
|
-
if (trimmedLine.startsWith('-A')) {
|
|
714
|
-
const deleteRule = trimmedLine.replace('-A', '-D');
|
|
715
|
-
const cmd = `${iptablesCmd} -t nat ${deleteRule}`;
|
|
716
|
-
try {
|
|
717
|
-
execSync(cmd);
|
|
718
|
-
console.log(`Cleaned up iptables rule: ${cmd}`);
|
|
719
|
-
}
|
|
720
|
-
catch (err) {
|
|
721
|
-
console.error(`Failed to remove iptables rule: ${cmd}`, err);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
// Finally clean up custom chains
|
|
727
|
-
for (const chain of customChains) {
|
|
728
|
-
try {
|
|
729
|
-
// Flush the chain
|
|
730
|
-
execSync(`${iptablesCmd} -t nat -F ${chain}`);
|
|
731
|
-
// Delete the chain
|
|
732
|
-
execSync(`${iptablesCmd} -t nat -X ${chain}`);
|
|
733
|
-
console.log(`Deleted custom chain: ${chain}`);
|
|
734
|
-
}
|
|
735
|
-
catch (err) {
|
|
736
|
-
console.error(`Failed to delete custom chain ${chain}:`, err);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
catch (err) {
|
|
741
|
-
console.error(`Failed to run ${iptablesCmd}-save: ${err}`);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* Logging utility that respects the enableLogging setting
|
|
746
|
-
*/
|
|
747
|
-
log(level, message) {
|
|
748
|
-
if (!this.settings.enableLogging && level === 'info') {
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
const timestamp = new Date().toISOString();
|
|
752
|
-
switch (level) {
|
|
753
|
-
case 'info':
|
|
754
|
-
console.log(`[${timestamp}] [INFO] ${message}`);
|
|
755
|
-
break;
|
|
756
|
-
case 'warn':
|
|
757
|
-
console.warn(`[${timestamp}] [WARN] ${message}`);
|
|
758
|
-
break;
|
|
759
|
-
case 'error':
|
|
760
|
-
console.error(`[${timestamp}] [ERROR] ${message}`);
|
|
761
|
-
break;
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5pcHRhYmxlc3Byb3h5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5pcHRhYmxlc3Byb3h5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQy9DLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFakMsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBc0RsQzs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sYUFBYTtJQU14QixZQUFZLFFBQStCO1FBSm5DLFVBQUssR0FBbUIsRUFBRSxDQUFDO1FBRTNCLGdCQUFXLEdBQWtCLElBQUksQ0FBQztRQUd4QywrQ0FBK0M7UUFDL0MsSUFBSSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBRWhDLHVCQUF1QjtRQUN2QixJQUFJLENBQUMsUUFBUSxHQUFHO1lBQ2QsR0FBRyxRQUFRO1lBQ1gsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNLElBQUksV0FBVztZQUN0QyxRQUFRLEVBQUUsUUFBUSxDQUFDLFFBQVEsSUFBSSxLQUFLO1lBQ3BDLGFBQWEsRUFBRSxRQUFRLENBQUMsYUFBYSxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsS0FBSztZQUNwRixXQUFXLEVBQUUsUUFBUSxDQUFDLFdBQVcsS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLEtBQUs7WUFDOUUsa0JBQWtCLEVBQUUsUUFBUSxDQUFDLGtCQUFrQixLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxJQUFJO1lBQ2xHLG1CQUFtQixFQUFFLFFBQVEsQ0FBQyxtQkFBbUIsSUFBSSxFQUFFLE9BQU8sRUFBRSxL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
|