@push.rocks/smartproxy 19.3.2 → 19.3.4
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 +18 -35
- package/readme.plan.md +173 -271
- 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,894 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
import { ConnectionManager } from './classes.pp.connectionmanager.js';
|
|
3
|
-
import { SecurityManager } from './classes.pp.securitymanager.js';
|
|
4
|
-
import { DomainConfigManager } from './classes.pp.domainconfigmanager.js';
|
|
5
|
-
import { TlsManager } from './classes.pp.tlsmanager.js';
|
|
6
|
-
import { NetworkProxyBridge } from './classes.pp.networkproxybridge.js';
|
|
7
|
-
import { TimeoutManager } from './classes.pp.timeoutmanager.js';
|
|
8
|
-
import { PortRangeManager } from './classes.pp.portrangemanager.js';
|
|
9
|
-
/**
|
|
10
|
-
* Handles new connection processing and setup logic
|
|
11
|
-
*/
|
|
12
|
-
export class ConnectionHandler {
|
|
13
|
-
constructor(settings, connectionManager, securityManager, domainConfigManager, tlsManager, networkProxyBridge, timeoutManager, portRangeManager) {
|
|
14
|
-
this.settings = settings;
|
|
15
|
-
this.connectionManager = connectionManager;
|
|
16
|
-
this.securityManager = securityManager;
|
|
17
|
-
this.domainConfigManager = domainConfigManager;
|
|
18
|
-
this.tlsManager = tlsManager;
|
|
19
|
-
this.networkProxyBridge = networkProxyBridge;
|
|
20
|
-
this.timeoutManager = timeoutManager;
|
|
21
|
-
this.portRangeManager = portRangeManager;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Handle a new incoming connection
|
|
25
|
-
*/
|
|
26
|
-
handleConnection(socket) {
|
|
27
|
-
const remoteIP = socket.remoteAddress || '';
|
|
28
|
-
const localPort = socket.localPort || 0;
|
|
29
|
-
// Validate IP against rate limits and connection limits
|
|
30
|
-
const ipValidation = this.securityManager.validateIP(remoteIP);
|
|
31
|
-
if (!ipValidation.allowed) {
|
|
32
|
-
console.log(`Connection rejected from ${remoteIP}: ${ipValidation.reason}`);
|
|
33
|
-
socket.end();
|
|
34
|
-
socket.destroy();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
// Create a new connection record
|
|
38
|
-
const record = this.connectionManager.createConnection(socket);
|
|
39
|
-
const connectionId = record.id;
|
|
40
|
-
// Apply socket optimizations
|
|
41
|
-
socket.setNoDelay(this.settings.noDelay);
|
|
42
|
-
// Apply keep-alive settings if enabled
|
|
43
|
-
if (this.settings.keepAlive) {
|
|
44
|
-
socket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
|
|
45
|
-
record.hasKeepAlive = true;
|
|
46
|
-
// Apply enhanced TCP keep-alive options if enabled
|
|
47
|
-
if (this.settings.enableKeepAliveProbes) {
|
|
48
|
-
try {
|
|
49
|
-
// These are platform-specific and may not be available
|
|
50
|
-
if ('setKeepAliveProbes' in socket) {
|
|
51
|
-
socket.setKeepAliveProbes(10);
|
|
52
|
-
}
|
|
53
|
-
if ('setKeepAliveInterval' in socket) {
|
|
54
|
-
socket.setKeepAliveInterval(1000);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
catch (err) {
|
|
58
|
-
// Ignore errors - these are optional enhancements
|
|
59
|
-
if (this.settings.enableDetailedLogging) {
|
|
60
|
-
console.log(`[${connectionId}] Enhanced TCP keep-alive settings not supported: ${err}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (this.settings.enableDetailedLogging) {
|
|
66
|
-
console.log(`[${connectionId}] New connection from ${remoteIP} on port ${localPort}. ` +
|
|
67
|
-
`Keep-Alive: ${record.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` +
|
|
68
|
-
`Active connections: ${this.connectionManager.getConnectionCount()}`);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
console.log(`New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}`);
|
|
72
|
-
}
|
|
73
|
-
// Check if this connection should be forwarded directly to NetworkProxy
|
|
74
|
-
if (this.portRangeManager.shouldUseNetworkProxy(localPort)) {
|
|
75
|
-
this.handleNetworkProxyConnection(socket, record);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
// For non-NetworkProxy ports, proceed with normal processing
|
|
79
|
-
this.handleStandardConnection(socket, record);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Handle a connection that should be forwarded to NetworkProxy
|
|
84
|
-
*/
|
|
85
|
-
handleNetworkProxyConnection(socket, record) {
|
|
86
|
-
const connectionId = record.id;
|
|
87
|
-
let initialDataReceived = false;
|
|
88
|
-
// Set an initial timeout for handshake data
|
|
89
|
-
let initialTimeout = setTimeout(() => {
|
|
90
|
-
if (!initialDataReceived) {
|
|
91
|
-
console.log(`[${connectionId}] Initial data warning (${this.settings.initialDataTimeout}ms) for connection from ${record.remoteIP}`);
|
|
92
|
-
// Add a grace period instead of immediate termination
|
|
93
|
-
setTimeout(() => {
|
|
94
|
-
if (!initialDataReceived) {
|
|
95
|
-
console.log(`[${connectionId}] Final initial data timeout after grace period`);
|
|
96
|
-
if (record.incomingTerminationReason === null) {
|
|
97
|
-
record.incomingTerminationReason = 'initial_timeout';
|
|
98
|
-
this.connectionManager.incrementTerminationStat('incoming', 'initial_timeout');
|
|
99
|
-
}
|
|
100
|
-
socket.end();
|
|
101
|
-
this.connectionManager.cleanupConnection(record, 'initial_timeout');
|
|
102
|
-
}
|
|
103
|
-
}, 30000); // 30 second grace period
|
|
104
|
-
}
|
|
105
|
-
}, this.settings.initialDataTimeout);
|
|
106
|
-
// Make sure timeout doesn't keep the process alive
|
|
107
|
-
if (initialTimeout.unref) {
|
|
108
|
-
initialTimeout.unref();
|
|
109
|
-
}
|
|
110
|
-
// Set up error handler
|
|
111
|
-
socket.on('error', this.connectionManager.handleError('incoming', record));
|
|
112
|
-
// First data handler to capture initial TLS handshake for NetworkProxy
|
|
113
|
-
socket.once('data', (chunk) => {
|
|
114
|
-
// Clear the initial timeout since we've received data
|
|
115
|
-
if (initialTimeout) {
|
|
116
|
-
clearTimeout(initialTimeout);
|
|
117
|
-
initialTimeout = null;
|
|
118
|
-
}
|
|
119
|
-
initialDataReceived = true;
|
|
120
|
-
record.hasReceivedInitialData = true;
|
|
121
|
-
// Block non-TLS connections on port 443
|
|
122
|
-
const localPort = record.localPort;
|
|
123
|
-
if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
|
|
124
|
-
console.log(`[${connectionId}] Non-TLS connection detected on port 443. ` +
|
|
125
|
-
`Terminating connection - only TLS traffic is allowed on standard HTTPS port.`);
|
|
126
|
-
if (record.incomingTerminationReason === null) {
|
|
127
|
-
record.incomingTerminationReason = 'non_tls_blocked';
|
|
128
|
-
this.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked');
|
|
129
|
-
}
|
|
130
|
-
socket.end();
|
|
131
|
-
this.connectionManager.cleanupConnection(record, 'non_tls_blocked');
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
// Check if this looks like a TLS handshake
|
|
135
|
-
if (this.tlsManager.isTlsHandshake(chunk)) {
|
|
136
|
-
record.isTLS = true;
|
|
137
|
-
// Check for ClientHello to extract SNI - but don't enforce it for NetworkProxy
|
|
138
|
-
if (this.tlsManager.isClientHello(chunk)) {
|
|
139
|
-
// Create connection info for SNI extraction
|
|
140
|
-
const connInfo = {
|
|
141
|
-
sourceIp: record.remoteIP,
|
|
142
|
-
sourcePort: socket.remotePort || 0,
|
|
143
|
-
destIp: socket.localAddress || '',
|
|
144
|
-
destPort: socket.localPort || 0,
|
|
145
|
-
};
|
|
146
|
-
// Extract SNI for domain-specific forwarding if available
|
|
147
|
-
const serverName = this.tlsManager.extractSNI(chunk, connInfo);
|
|
148
|
-
// For NetworkProxy connections, we'll allow session tickets even without SNI
|
|
149
|
-
// We'll only use the serverName if available to determine the specific forwarding
|
|
150
|
-
if (serverName) {
|
|
151
|
-
// Save domain config and SNI in connection record
|
|
152
|
-
const domainConfig = this.domainConfigManager.findDomainConfig(serverName);
|
|
153
|
-
record.domainConfig = domainConfig;
|
|
154
|
-
record.lockedDomain = serverName;
|
|
155
|
-
// If we have a domain config and it has a forwarding config
|
|
156
|
-
if (domainConfig) {
|
|
157
|
-
try {
|
|
158
|
-
// Get the forwarding type for this domain
|
|
159
|
-
const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
|
|
160
|
-
// For TLS termination types, use NetworkProxy
|
|
161
|
-
if (forwardingType === 'https-terminate-to-http' ||
|
|
162
|
-
forwardingType === 'https-terminate-to-https') {
|
|
163
|
-
const networkProxyPort = this.domainConfigManager.getNetworkProxyPort(domainConfig);
|
|
164
|
-
if (this.settings.enableDetailedLogging) {
|
|
165
|
-
console.log(`[${connectionId}] Using TLS termination (${forwardingType}) for ${serverName} on port ${networkProxyPort}`);
|
|
166
|
-
}
|
|
167
|
-
// Forward to NetworkProxy with domain-specific port
|
|
168
|
-
this.networkProxyBridge.forwardToNetworkProxy(connectionId, socket, record, chunk, networkProxyPort, (reason) => this.connectionManager.initiateCleanupOnce(record, reason));
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
// For HTTPS passthrough, use the forwarding handler directly
|
|
172
|
-
if (forwardingType === 'https-passthrough') {
|
|
173
|
-
const handler = this.domainConfigManager.getForwardingHandler(domainConfig);
|
|
174
|
-
if (this.settings.enableDetailedLogging) {
|
|
175
|
-
console.log(`[${connectionId}] Using forwarding handler for SNI passthrough to ${serverName}`);
|
|
176
|
-
}
|
|
177
|
-
// Handle the connection using the handler
|
|
178
|
-
handler.handleConnection(socket);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
// For HTTP-only, we shouldn't get TLS connections
|
|
182
|
-
if (forwardingType === 'http-only') {
|
|
183
|
-
console.log(`[${connectionId}] Received TLS connection for HTTP-only domain ${serverName}`);
|
|
184
|
-
socket.end();
|
|
185
|
-
this.connectionManager.cleanupConnection(record, 'wrong_protocol');
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
catch (err) {
|
|
190
|
-
console.log(`[${connectionId}] Error using forwarding handler: ${err}`);
|
|
191
|
-
// Fall through to default NetworkProxy handling
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
else if (this.settings.allowSessionTicket === false &&
|
|
196
|
-
this.settings.enableDetailedLogging) {
|
|
197
|
-
// Log that we're allowing a session resumption without SNI for NetworkProxy
|
|
198
|
-
console.log(`[${connectionId}] Allowing session resumption without SNI for NetworkProxy forwarding`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
// Forward directly to NetworkProxy without domain-specific settings
|
|
202
|
-
this.networkProxyBridge.forwardToNetworkProxy(connectionId, socket, record, chunk, undefined, (reason) => this.connectionManager.initiateCleanupOnce(record, reason));
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
// If not TLS, handle as plain HTTP
|
|
206
|
-
console.log(`[${connectionId}] Non-TLS connection on NetworkProxy port ${record.localPort}`);
|
|
207
|
-
// Check if we have a domain config based on port
|
|
208
|
-
const portBasedDomainConfig = this.domainConfigManager.findDomainConfigForPort(record.localPort);
|
|
209
|
-
if (portBasedDomainConfig) {
|
|
210
|
-
try {
|
|
211
|
-
// If this domain supports HTTP via a forwarding handler, use it
|
|
212
|
-
if (this.domainConfigManager.supportsHttp(portBasedDomainConfig)) {
|
|
213
|
-
const handler = this.domainConfigManager.getForwardingHandler(portBasedDomainConfig);
|
|
214
|
-
if (this.settings.enableDetailedLogging) {
|
|
215
|
-
console.log(`[${connectionId}] Using forwarding handler for non-TLS connection to port ${record.localPort}`);
|
|
216
|
-
}
|
|
217
|
-
// Handle the connection using the handler
|
|
218
|
-
handler.handleConnection(socket);
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
console.log(`[${connectionId}] Error using forwarding handler for HTTP: ${err}`);
|
|
224
|
-
// Fall through to direct connection
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// Use legacy direct connection as fallback
|
|
228
|
-
this.setupDirectConnection(socket, record, undefined, undefined, chunk);
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Handle a standard (non-NetworkProxy) connection
|
|
234
|
-
*/
|
|
235
|
-
handleStandardConnection(socket, record) {
|
|
236
|
-
const connectionId = record.id;
|
|
237
|
-
const localPort = record.localPort;
|
|
238
|
-
// Define helpers for rejecting connections
|
|
239
|
-
const rejectIncomingConnection = (reason, logMessage) => {
|
|
240
|
-
console.log(`[${connectionId}] ${logMessage}`);
|
|
241
|
-
socket.end();
|
|
242
|
-
if (record.incomingTerminationReason === null) {
|
|
243
|
-
record.incomingTerminationReason = reason;
|
|
244
|
-
this.connectionManager.incrementTerminationStat('incoming', reason);
|
|
245
|
-
}
|
|
246
|
-
this.connectionManager.cleanupConnection(record, reason);
|
|
247
|
-
};
|
|
248
|
-
let initialDataReceived = false;
|
|
249
|
-
// Set an initial timeout for SNI data if needed
|
|
250
|
-
let initialTimeout = null;
|
|
251
|
-
if (this.settings.sniEnabled) {
|
|
252
|
-
initialTimeout = setTimeout(() => {
|
|
253
|
-
if (!initialDataReceived) {
|
|
254
|
-
console.log(`[${connectionId}] Initial data warning (${this.settings.initialDataTimeout}ms) for connection from ${record.remoteIP}`);
|
|
255
|
-
// Add a grace period instead of immediate termination
|
|
256
|
-
setTimeout(() => {
|
|
257
|
-
if (!initialDataReceived) {
|
|
258
|
-
console.log(`[${connectionId}] Final initial data timeout after grace period`);
|
|
259
|
-
if (record.incomingTerminationReason === null) {
|
|
260
|
-
record.incomingTerminationReason = 'initial_timeout';
|
|
261
|
-
this.connectionManager.incrementTerminationStat('incoming', 'initial_timeout');
|
|
262
|
-
}
|
|
263
|
-
socket.end();
|
|
264
|
-
this.connectionManager.cleanupConnection(record, 'initial_timeout');
|
|
265
|
-
}
|
|
266
|
-
}, 30000); // 30 second grace period
|
|
267
|
-
}
|
|
268
|
-
}, this.settings.initialDataTimeout);
|
|
269
|
-
// Make sure timeout doesn't keep the process alive
|
|
270
|
-
if (initialTimeout.unref) {
|
|
271
|
-
initialTimeout.unref();
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
initialDataReceived = true;
|
|
276
|
-
record.hasReceivedInitialData = true;
|
|
277
|
-
}
|
|
278
|
-
socket.on('error', this.connectionManager.handleError('incoming', record));
|
|
279
|
-
// Track data for bytes counting
|
|
280
|
-
socket.on('data', (chunk) => {
|
|
281
|
-
record.bytesReceived += chunk.length;
|
|
282
|
-
this.timeoutManager.updateActivity(record);
|
|
283
|
-
// Check for TLS handshake if this is the first chunk
|
|
284
|
-
if (!record.isTLS && this.tlsManager.isTlsHandshake(chunk)) {
|
|
285
|
-
record.isTLS = true;
|
|
286
|
-
if (this.settings.enableTlsDebugLogging) {
|
|
287
|
-
console.log(`[${connectionId}] TLS handshake detected from ${record.remoteIP}, ${chunk.length} bytes`);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
/**
|
|
292
|
-
* Sets up the connection to the target host.
|
|
293
|
-
*/
|
|
294
|
-
const setupConnection = (serverName, initialChunk, forcedDomain, overridePort) => {
|
|
295
|
-
// Clear the initial timeout since we've received data
|
|
296
|
-
if (initialTimeout) {
|
|
297
|
-
clearTimeout(initialTimeout);
|
|
298
|
-
initialTimeout = null;
|
|
299
|
-
}
|
|
300
|
-
// Mark that we've received initial data
|
|
301
|
-
initialDataReceived = true;
|
|
302
|
-
record.hasReceivedInitialData = true;
|
|
303
|
-
// Check if this looks like a TLS handshake
|
|
304
|
-
if (initialChunk && this.tlsManager.isTlsHandshake(initialChunk)) {
|
|
305
|
-
record.isTLS = true;
|
|
306
|
-
if (this.settings.enableTlsDebugLogging) {
|
|
307
|
-
console.log(`[${connectionId}] TLS handshake detected in setup, ${initialChunk.length} bytes`);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
// If a forcedDomain is provided (port-based routing), use it; otherwise, use SNI-based lookup.
|
|
311
|
-
const domainConfig = forcedDomain
|
|
312
|
-
? forcedDomain
|
|
313
|
-
: serverName
|
|
314
|
-
? this.domainConfigManager.findDomainConfig(serverName)
|
|
315
|
-
: undefined;
|
|
316
|
-
// Save domain config in connection record
|
|
317
|
-
record.domainConfig = domainConfig;
|
|
318
|
-
// Check if this domain should use NetworkProxy (domain-specific setting)
|
|
319
|
-
if (domainConfig &&
|
|
320
|
-
this.domainConfigManager.shouldUseNetworkProxy(domainConfig) &&
|
|
321
|
-
this.networkProxyBridge.getNetworkProxy()) {
|
|
322
|
-
if (this.settings.enableDetailedLogging) {
|
|
323
|
-
console.log(`[${connectionId}] Domain ${serverName} is configured to use NetworkProxy`);
|
|
324
|
-
}
|
|
325
|
-
const networkProxyPort = this.domainConfigManager.getNetworkProxyPort(domainConfig);
|
|
326
|
-
if (initialChunk && record.isTLS) {
|
|
327
|
-
// For TLS connections with initial chunk, forward to NetworkProxy
|
|
328
|
-
this.networkProxyBridge.forwardToNetworkProxy(connectionId, socket, record, initialChunk, networkProxyPort, (reason) => this.connectionManager.initiateCleanupOnce(record, reason));
|
|
329
|
-
return; // Skip normal connection setup
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
// IP validation
|
|
333
|
-
if (domainConfig) {
|
|
334
|
-
const ipRules = this.domainConfigManager.getEffectiveIPRules(domainConfig);
|
|
335
|
-
// Perform IP validation using security rules
|
|
336
|
-
if (!this.securityManager.isIPAuthorized(record.remoteIP, ipRules.allowedIPs, ipRules.blockedIPs)) {
|
|
337
|
-
return rejectIncomingConnection('rejected', `Connection rejected: IP ${record.remoteIP} not allowed for domain ${domainConfig.domains.join(', ')}`);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
else if (this.settings.defaultAllowedIPs && this.settings.defaultAllowedIPs.length > 0) {
|
|
341
|
-
if (!this.securityManager.isIPAuthorized(record.remoteIP, this.settings.defaultAllowedIPs, this.settings.defaultBlockedIPs || [])) {
|
|
342
|
-
return rejectIncomingConnection('rejected', `Connection rejected: IP ${record.remoteIP} not allowed by default allowed list`);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
// Save the initial SNI
|
|
346
|
-
if (serverName) {
|
|
347
|
-
record.lockedDomain = serverName;
|
|
348
|
-
}
|
|
349
|
-
// Set up the direct connection
|
|
350
|
-
this.setupDirectConnection(socket, record, domainConfig, serverName, initialChunk, overridePort);
|
|
351
|
-
};
|
|
352
|
-
// --- PORT RANGE-BASED HANDLING ---
|
|
353
|
-
// Only apply port-based rules if the incoming port is within one of the global port ranges.
|
|
354
|
-
if (this.portRangeManager.isPortInGlobalRanges(localPort)) {
|
|
355
|
-
if (this.portRangeManager.shouldUseGlobalForwarding(localPort)) {
|
|
356
|
-
// Create a virtual domain config for global forwarding with security settings
|
|
357
|
-
const globalDomainConfig = {
|
|
358
|
-
domains: ['global'],
|
|
359
|
-
forwarding: {
|
|
360
|
-
type: 'http-only',
|
|
361
|
-
target: {
|
|
362
|
-
host: this.settings.targetIP,
|
|
363
|
-
port: this.settings.toPort
|
|
364
|
-
},
|
|
365
|
-
security: {
|
|
366
|
-
allowedIps: this.settings.defaultAllowedIPs || [],
|
|
367
|
-
blockedIps: this.settings.defaultBlockedIPs || []
|
|
368
|
-
}
|
|
369
|
-
},
|
|
370
|
-
};
|
|
371
|
-
// Use the same IP filtering mechanism as domain-specific configs
|
|
372
|
-
const ipRules = this.domainConfigManager.getEffectiveIPRules(globalDomainConfig);
|
|
373
|
-
if (!this.securityManager.isIPAuthorized(record.remoteIP, ipRules.allowedIPs, ipRules.blockedIPs)) {
|
|
374
|
-
console.log(`[${connectionId}] Connection from ${record.remoteIP} rejected: IP ${record.remoteIP} not allowed in global default allowed list.`);
|
|
375
|
-
socket.end();
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
if (this.settings.enableDetailedLogging) {
|
|
379
|
-
console.log(`[${connectionId}] Port-based connection from ${record.remoteIP} on port ${localPort} forwarded to global target IP ${this.settings.targetIP}.`);
|
|
380
|
-
}
|
|
381
|
-
setupConnection('', undefined, globalDomainConfig, localPort);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
385
|
-
// Attempt to find a matching forced domain config based on the local port.
|
|
386
|
-
const forcedDomain = this.domainConfigManager.findDomainConfigForPort(localPort);
|
|
387
|
-
if (forcedDomain) {
|
|
388
|
-
// Get effective IP rules from the domain config's forwarding security settings
|
|
389
|
-
const ipRules = this.domainConfigManager.getEffectiveIPRules(forcedDomain);
|
|
390
|
-
if (!this.securityManager.isIPAuthorized(record.remoteIP, ipRules.allowedIPs, ipRules.blockedIPs)) {
|
|
391
|
-
console.log(`[${connectionId}] Connection from ${record.remoteIP} rejected: IP not allowed for domain ${forcedDomain.domains.join(', ')} on port ${localPort}.`);
|
|
392
|
-
socket.end();
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
if (this.settings.enableDetailedLogging) {
|
|
396
|
-
console.log(`[${connectionId}] Port-based connection from ${record.remoteIP} on port ${localPort} matched domain ${forcedDomain.domains.join(', ')}.`);
|
|
397
|
-
}
|
|
398
|
-
setupConnection('', undefined, forcedDomain, localPort);
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
// Fall through to SNI/default handling if no forced domain config is found.
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
// --- FALLBACK: SNI-BASED HANDLING (or default when SNI is disabled) ---
|
|
405
|
-
if (this.settings.sniEnabled) {
|
|
406
|
-
initialDataReceived = false;
|
|
407
|
-
socket.once('data', (chunk) => {
|
|
408
|
-
// Clear timeout immediately
|
|
409
|
-
if (initialTimeout) {
|
|
410
|
-
clearTimeout(initialTimeout);
|
|
411
|
-
initialTimeout = null;
|
|
412
|
-
}
|
|
413
|
-
initialDataReceived = true;
|
|
414
|
-
// Block non-TLS connections on port 443
|
|
415
|
-
if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) {
|
|
416
|
-
console.log(`[${connectionId}] Non-TLS connection detected on port 443 in SNI handler. ` +
|
|
417
|
-
`Terminating connection - only TLS traffic is allowed on standard HTTPS port.`);
|
|
418
|
-
if (record.incomingTerminationReason === null) {
|
|
419
|
-
record.incomingTerminationReason = 'non_tls_blocked';
|
|
420
|
-
this.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked');
|
|
421
|
-
}
|
|
422
|
-
socket.end();
|
|
423
|
-
this.connectionManager.cleanupConnection(record, 'non_tls_blocked');
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
// Try to extract SNI
|
|
427
|
-
let serverName = '';
|
|
428
|
-
if (this.tlsManager.isTlsHandshake(chunk)) {
|
|
429
|
-
record.isTLS = true;
|
|
430
|
-
if (this.settings.enableTlsDebugLogging) {
|
|
431
|
-
console.log(`[${connectionId}] Extracting SNI from TLS handshake, ${chunk.length} bytes`);
|
|
432
|
-
}
|
|
433
|
-
// Create connection info object for SNI extraction
|
|
434
|
-
const connInfo = {
|
|
435
|
-
sourceIp: record.remoteIP,
|
|
436
|
-
sourcePort: socket.remotePort || 0,
|
|
437
|
-
destIp: socket.localAddress || '',
|
|
438
|
-
destPort: socket.localPort || 0,
|
|
439
|
-
};
|
|
440
|
-
// Extract SNI
|
|
441
|
-
serverName = this.tlsManager.extractSNI(chunk, connInfo) || '';
|
|
442
|
-
// If allowSessionTicket is false and this is a ClientHello with no SNI, terminate the connection
|
|
443
|
-
if (this.settings.allowSessionTicket === false &&
|
|
444
|
-
this.tlsManager.isClientHello(chunk) &&
|
|
445
|
-
!serverName) {
|
|
446
|
-
// Missing SNI: forward to NetworkProxy if available
|
|
447
|
-
const proxyInstance = this.networkProxyBridge.getNetworkProxy();
|
|
448
|
-
if (proxyInstance) {
|
|
449
|
-
if (this.settings.enableDetailedLogging) {
|
|
450
|
-
console.log(`[${connectionId}] No SNI in ClientHello; forwarding to NetworkProxy.`);
|
|
451
|
-
}
|
|
452
|
-
this.networkProxyBridge.forwardToNetworkProxy(connectionId, socket, record, chunk, undefined, (_reason) => {
|
|
453
|
-
// On proxy failure, send TLS unrecognized_name alert and cleanup
|
|
454
|
-
if (record.incomingTerminationReason === null) {
|
|
455
|
-
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
|
|
456
|
-
this.connectionManager.incrementTerminationStat('incoming', 'session_ticket_blocked_no_sni');
|
|
457
|
-
}
|
|
458
|
-
const alert = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x70]);
|
|
459
|
-
try {
|
|
460
|
-
socket.cork();
|
|
461
|
-
socket.write(alert);
|
|
462
|
-
socket.uncork();
|
|
463
|
-
socket.end();
|
|
464
|
-
}
|
|
465
|
-
catch {
|
|
466
|
-
socket.end();
|
|
467
|
-
}
|
|
468
|
-
this.connectionManager.initiateCleanupOnce(record, 'session_ticket_blocked_no_sni');
|
|
469
|
-
});
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
// Fallback: send TLS unrecognized_name alert and terminate
|
|
473
|
-
console.log(`[${connectionId}] No SNI detected and proxy unavailable; sending TLS alert.`);
|
|
474
|
-
if (record.incomingTerminationReason === null) {
|
|
475
|
-
record.incomingTerminationReason = 'session_ticket_blocked_no_sni';
|
|
476
|
-
this.connectionManager.incrementTerminationStat('incoming', 'session_ticket_blocked_no_sni');
|
|
477
|
-
}
|
|
478
|
-
const alert = Buffer.from([0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x70]);
|
|
479
|
-
try {
|
|
480
|
-
socket.cork();
|
|
481
|
-
socket.write(alert);
|
|
482
|
-
socket.uncork();
|
|
483
|
-
socket.end();
|
|
484
|
-
}
|
|
485
|
-
catch {
|
|
486
|
-
socket.end();
|
|
487
|
-
}
|
|
488
|
-
this.connectionManager.cleanupConnection(record, 'session_ticket_blocked_no_sni');
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
// Lock the connection to the negotiated SNI.
|
|
493
|
-
record.lockedDomain = serverName;
|
|
494
|
-
if (this.settings.enableDetailedLogging) {
|
|
495
|
-
console.log(`[${connectionId}] Received connection from ${record.remoteIP} with SNI: ${serverName || '(empty)'}`);
|
|
496
|
-
}
|
|
497
|
-
setupConnection(serverName, chunk);
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
initialDataReceived = true;
|
|
502
|
-
record.hasReceivedInitialData = true;
|
|
503
|
-
// Create default security settings for non-SNI connections
|
|
504
|
-
const defaultSecurity = {
|
|
505
|
-
allowedIPs: this.settings.defaultAllowedIPs || [],
|
|
506
|
-
blockedIPs: this.settings.defaultBlockedIPs || []
|
|
507
|
-
};
|
|
508
|
-
if (defaultSecurity.allowedIPs.length > 0 &&
|
|
509
|
-
!this.securityManager.isIPAuthorized(record.remoteIP, defaultSecurity.allowedIPs, defaultSecurity.blockedIPs)) {
|
|
510
|
-
return rejectIncomingConnection('rejected', `Connection rejected: IP ${record.remoteIP} not allowed for non-SNI connection`);
|
|
511
|
-
}
|
|
512
|
-
setupConnection('');
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Sets up a direct connection to the target
|
|
517
|
-
*/
|
|
518
|
-
setupDirectConnection(socket, record, domainConfig, serverName, initialChunk, overridePort) {
|
|
519
|
-
const connectionId = record.id;
|
|
520
|
-
// If we have a domain config, try to use a forwarding handler
|
|
521
|
-
if (domainConfig) {
|
|
522
|
-
try {
|
|
523
|
-
// Get the forwarding handler for this domain
|
|
524
|
-
const forwardingHandler = this.domainConfigManager.getForwardingHandler(domainConfig);
|
|
525
|
-
// Check the forwarding type to determine how to handle the connection
|
|
526
|
-
const forwardingType = this.domainConfigManager.getForwardingType(domainConfig);
|
|
527
|
-
// For TLS connections, handle differently based on forwarding type
|
|
528
|
-
if (record.isTLS) {
|
|
529
|
-
// For HTTP-only, we shouldn't get TLS connections
|
|
530
|
-
if (forwardingType === 'http-only') {
|
|
531
|
-
console.log(`[${connectionId}] Received TLS connection for HTTP-only domain ${serverName || 'unknown'}`);
|
|
532
|
-
socket.end();
|
|
533
|
-
this.connectionManager.initiateCleanupOnce(record, 'wrong_protocol');
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
// For HTTPS passthrough, use the handler's connection handling
|
|
537
|
-
if (forwardingType === 'https-passthrough') {
|
|
538
|
-
// If there's initial data, process it first
|
|
539
|
-
if (initialChunk) {
|
|
540
|
-
record.bytesReceived += initialChunk.length;
|
|
541
|
-
}
|
|
542
|
-
// Let the handler take over
|
|
543
|
-
if (this.settings.enableDetailedLogging) {
|
|
544
|
-
console.log(`[${connectionId}] Using forwarding handler for ${forwardingType} connection to ${serverName || 'unknown'}`);
|
|
545
|
-
}
|
|
546
|
-
// Pass the connection to the handler
|
|
547
|
-
forwardingHandler.handleConnection(socket);
|
|
548
|
-
// Set metadata fields
|
|
549
|
-
record.usingNetworkProxy = false;
|
|
550
|
-
// Add connection information to record
|
|
551
|
-
if (serverName) {
|
|
552
|
-
record.lockedDomain = serverName;
|
|
553
|
-
}
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
// For TLS termination types, we'll fall through to the legacy connection setup
|
|
557
|
-
// because NetworkProxy is used for termination
|
|
558
|
-
}
|
|
559
|
-
// For non-TLS connections, check if we support HTTP
|
|
560
|
-
else if (!record.isTLS && this.domainConfigManager.supportsHttp(domainConfig)) {
|
|
561
|
-
// For HTTP handling that the handler supports natively
|
|
562
|
-
if (forwardingType === 'http-only' ||
|
|
563
|
-
(forwardingType === 'https-terminate-to-http' || forwardingType === 'https-terminate-to-https')) {
|
|
564
|
-
// If there's redirect to HTTPS configured and this is a normal HTTP connection
|
|
565
|
-
if (this.domainConfigManager.shouldRedirectToHttps(domainConfig)) {
|
|
566
|
-
// We'll let the handler deal with the HTTP request and potential redirect
|
|
567
|
-
// Once an HTTP request arrives, it can redirect as needed
|
|
568
|
-
}
|
|
569
|
-
// Let the handler take over for HTTP handling
|
|
570
|
-
if (this.settings.enableDetailedLogging) {
|
|
571
|
-
console.log(`[${connectionId}] Using forwarding handler for HTTP connection to ${serverName || 'unknown'}`);
|
|
572
|
-
}
|
|
573
|
-
// Pass the connection to the handler
|
|
574
|
-
forwardingHandler.handleConnection(socket);
|
|
575
|
-
// Add connection information to record
|
|
576
|
-
if (serverName) {
|
|
577
|
-
record.lockedDomain = serverName;
|
|
578
|
-
}
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
catch (err) {
|
|
584
|
-
console.log(`[${connectionId}] Error using forwarding handler: ${err}`);
|
|
585
|
-
// Fall through to legacy connection handling
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
// If we get here, we'll use legacy connection handling
|
|
589
|
-
// Determine target host
|
|
590
|
-
const targetHost = domainConfig
|
|
591
|
-
? this.domainConfigManager.getTargetIP(domainConfig)
|
|
592
|
-
: this.settings.targetIP;
|
|
593
|
-
// Determine target port - first try forwarding config, then fallback
|
|
594
|
-
const targetPort = domainConfig
|
|
595
|
-
? this.domainConfigManager.getTargetPort(domainConfig, overridePort !== undefined ? overridePort : this.settings.toPort)
|
|
596
|
-
: (overridePort !== undefined ? overridePort : this.settings.toPort);
|
|
597
|
-
// Setup connection options
|
|
598
|
-
const connectionOptions = {
|
|
599
|
-
host: targetHost,
|
|
600
|
-
port: targetPort,
|
|
601
|
-
};
|
|
602
|
-
// Preserve source IP if configured
|
|
603
|
-
if (this.settings.preserveSourceIP) {
|
|
604
|
-
connectionOptions.localAddress = record.remoteIP.replace('::ffff:', '');
|
|
605
|
-
}
|
|
606
|
-
// Create a safe queue for incoming data
|
|
607
|
-
const dataQueue = [];
|
|
608
|
-
let queueSize = 0;
|
|
609
|
-
let processingQueue = false;
|
|
610
|
-
let drainPending = false;
|
|
611
|
-
let pipingEstablished = false;
|
|
612
|
-
// Pause the incoming socket to prevent buffer overflows
|
|
613
|
-
socket.pause();
|
|
614
|
-
// Function to safely process the data queue without losing events
|
|
615
|
-
const processDataQueue = () => {
|
|
616
|
-
if (processingQueue || dataQueue.length === 0 || pipingEstablished)
|
|
617
|
-
return;
|
|
618
|
-
processingQueue = true;
|
|
619
|
-
try {
|
|
620
|
-
// Process all queued chunks with the current active handler
|
|
621
|
-
while (dataQueue.length > 0) {
|
|
622
|
-
const chunk = dataQueue.shift();
|
|
623
|
-
queueSize -= chunk.length;
|
|
624
|
-
// Once piping is established, we shouldn't get here,
|
|
625
|
-
// but just in case, pass to the outgoing socket directly
|
|
626
|
-
if (pipingEstablished && record.outgoing) {
|
|
627
|
-
record.outgoing.write(chunk);
|
|
628
|
-
continue;
|
|
629
|
-
}
|
|
630
|
-
// Track bytes received
|
|
631
|
-
record.bytesReceived += chunk.length;
|
|
632
|
-
// Check for TLS handshake
|
|
633
|
-
if (!record.isTLS && this.tlsManager.isTlsHandshake(chunk)) {
|
|
634
|
-
record.isTLS = true;
|
|
635
|
-
if (this.settings.enableTlsDebugLogging) {
|
|
636
|
-
console.log(`[${connectionId}] TLS handshake detected in tempDataHandler, ${chunk.length} bytes`);
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
// Check if adding this chunk would exceed the buffer limit
|
|
640
|
-
const newSize = record.pendingDataSize + chunk.length;
|
|
641
|
-
if (this.settings.maxPendingDataSize && newSize > this.settings.maxPendingDataSize) {
|
|
642
|
-
console.log(`[${connectionId}] Buffer limit exceeded for connection from ${record.remoteIP}: ${newSize} bytes > ${this.settings.maxPendingDataSize} bytes`);
|
|
643
|
-
socket.end(); // Gracefully close the socket
|
|
644
|
-
this.connectionManager.initiateCleanupOnce(record, 'buffer_limit_exceeded');
|
|
645
|
-
return;
|
|
646
|
-
}
|
|
647
|
-
// Buffer the chunk and update the size counter
|
|
648
|
-
record.pendingData.push(Buffer.from(chunk));
|
|
649
|
-
record.pendingDataSize = newSize;
|
|
650
|
-
this.timeoutManager.updateActivity(record);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
finally {
|
|
654
|
-
processingQueue = false;
|
|
655
|
-
// If there's a pending drain and we've processed everything,
|
|
656
|
-
// signal we're ready for more data if we haven't established piping yet
|
|
657
|
-
if (drainPending && dataQueue.length === 0 && !pipingEstablished) {
|
|
658
|
-
drainPending = false;
|
|
659
|
-
socket.resume();
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
};
|
|
663
|
-
// Unified data handler that safely queues incoming data
|
|
664
|
-
const safeDataHandler = (chunk) => {
|
|
665
|
-
// If piping is already established, just let the pipe handle it
|
|
666
|
-
if (pipingEstablished)
|
|
667
|
-
return;
|
|
668
|
-
// Add to our queue for orderly processing
|
|
669
|
-
dataQueue.push(Buffer.from(chunk)); // Make a copy to be safe
|
|
670
|
-
queueSize += chunk.length;
|
|
671
|
-
// If queue is getting large, pause socket until we catch up
|
|
672
|
-
if (this.settings.maxPendingDataSize && queueSize > this.settings.maxPendingDataSize * 0.8) {
|
|
673
|
-
socket.pause();
|
|
674
|
-
drainPending = true;
|
|
675
|
-
}
|
|
676
|
-
// Process the queue
|
|
677
|
-
processDataQueue();
|
|
678
|
-
};
|
|
679
|
-
// Add our safe data handler
|
|
680
|
-
socket.on('data', safeDataHandler);
|
|
681
|
-
// Add initial chunk to pending data if present
|
|
682
|
-
if (initialChunk) {
|
|
683
|
-
record.bytesReceived += initialChunk.length;
|
|
684
|
-
record.pendingData.push(Buffer.from(initialChunk));
|
|
685
|
-
record.pendingDataSize = initialChunk.length;
|
|
686
|
-
}
|
|
687
|
-
// Create the target socket but don't set up piping immediately
|
|
688
|
-
const targetSocket = plugins.net.connect(connectionOptions);
|
|
689
|
-
record.outgoing = targetSocket;
|
|
690
|
-
record.outgoingStartTime = Date.now();
|
|
691
|
-
// Apply socket optimizations
|
|
692
|
-
targetSocket.setNoDelay(this.settings.noDelay);
|
|
693
|
-
// Apply keep-alive settings to the outgoing connection as well
|
|
694
|
-
if (this.settings.keepAlive) {
|
|
695
|
-
targetSocket.setKeepAlive(true, this.settings.keepAliveInitialDelay);
|
|
696
|
-
// Apply enhanced TCP keep-alive options if enabled
|
|
697
|
-
if (this.settings.enableKeepAliveProbes) {
|
|
698
|
-
try {
|
|
699
|
-
if ('setKeepAliveProbes' in targetSocket) {
|
|
700
|
-
targetSocket.setKeepAliveProbes(10);
|
|
701
|
-
}
|
|
702
|
-
if ('setKeepAliveInterval' in targetSocket) {
|
|
703
|
-
targetSocket.setKeepAliveInterval(1000);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
catch (err) {
|
|
707
|
-
// Ignore errors - these are optional enhancements
|
|
708
|
-
if (this.settings.enableDetailedLogging) {
|
|
709
|
-
console.log(`[${connectionId}] Enhanced TCP keep-alive not supported for outgoing socket: ${err}`);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
// Setup specific error handler for connection phase
|
|
715
|
-
targetSocket.once('error', (err) => {
|
|
716
|
-
// This handler runs only once during the initial connection phase
|
|
717
|
-
const code = err.code;
|
|
718
|
-
console.log(`[${connectionId}] Connection setup error to ${targetHost}:${connectionOptions.port}: ${err.message} (${code})`);
|
|
719
|
-
// Resume the incoming socket to prevent it from hanging
|
|
720
|
-
socket.resume();
|
|
721
|
-
if (code === 'ECONNREFUSED') {
|
|
722
|
-
console.log(`[${connectionId}] Target ${targetHost}:${connectionOptions.port} refused connection`);
|
|
723
|
-
}
|
|
724
|
-
else if (code === 'ETIMEDOUT') {
|
|
725
|
-
console.log(`[${connectionId}] Connection to ${targetHost}:${connectionOptions.port} timed out`);
|
|
726
|
-
}
|
|
727
|
-
else if (code === 'ECONNRESET') {
|
|
728
|
-
console.log(`[${connectionId}] Connection to ${targetHost}:${connectionOptions.port} was reset`);
|
|
729
|
-
}
|
|
730
|
-
else if (code === 'EHOSTUNREACH') {
|
|
731
|
-
console.log(`[${connectionId}] Host ${targetHost} is unreachable`);
|
|
732
|
-
}
|
|
733
|
-
// Clear any existing error handler after connection phase
|
|
734
|
-
targetSocket.removeAllListeners('error');
|
|
735
|
-
// Re-add the normal error handler for established connections
|
|
736
|
-
targetSocket.on('error', this.connectionManager.handleError('outgoing', record));
|
|
737
|
-
if (record.outgoingTerminationReason === null) {
|
|
738
|
-
record.outgoingTerminationReason = 'connection_failed';
|
|
739
|
-
this.connectionManager.incrementTerminationStat('outgoing', 'connection_failed');
|
|
740
|
-
}
|
|
741
|
-
// If we have a forwarding handler for this domain, let it handle the error
|
|
742
|
-
if (domainConfig) {
|
|
743
|
-
try {
|
|
744
|
-
const forwardingHandler = this.domainConfigManager.getForwardingHandler(domainConfig);
|
|
745
|
-
forwardingHandler.emit('connection_error', {
|
|
746
|
-
socket,
|
|
747
|
-
error: err,
|
|
748
|
-
connectionId
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
catch (handlerErr) {
|
|
752
|
-
// If getting the handler fails, just log and continue with normal cleanup
|
|
753
|
-
console.log(`Error getting forwarding handler for error handling: ${handlerErr}`);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
// Clean up the connection
|
|
757
|
-
this.connectionManager.initiateCleanupOnce(record, `connection_failed_${code}`);
|
|
758
|
-
});
|
|
759
|
-
// Setup close handler
|
|
760
|
-
targetSocket.on('close', this.connectionManager.handleClose('outgoing', record));
|
|
761
|
-
socket.on('close', this.connectionManager.handleClose('incoming', record));
|
|
762
|
-
// Handle timeouts with keep-alive awareness
|
|
763
|
-
socket.on('timeout', () => {
|
|
764
|
-
// For keep-alive connections, just log a warning instead of closing
|
|
765
|
-
if (record.hasKeepAlive) {
|
|
766
|
-
console.log(`[${connectionId}] Timeout event on incoming keep-alive connection from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`);
|
|
767
|
-
return;
|
|
768
|
-
}
|
|
769
|
-
// For non-keep-alive connections, proceed with normal cleanup
|
|
770
|
-
console.log(`[${connectionId}] Timeout on incoming side from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`);
|
|
771
|
-
if (record.incomingTerminationReason === null) {
|
|
772
|
-
record.incomingTerminationReason = 'timeout';
|
|
773
|
-
this.connectionManager.incrementTerminationStat('incoming', 'timeout');
|
|
774
|
-
}
|
|
775
|
-
this.connectionManager.initiateCleanupOnce(record, 'timeout_incoming');
|
|
776
|
-
});
|
|
777
|
-
targetSocket.on('timeout', () => {
|
|
778
|
-
// For keep-alive connections, just log a warning instead of closing
|
|
779
|
-
if (record.hasKeepAlive) {
|
|
780
|
-
console.log(`[${connectionId}] Timeout event on outgoing keep-alive connection from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`);
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
// For non-keep-alive connections, proceed with normal cleanup
|
|
784
|
-
console.log(`[${connectionId}] Timeout on outgoing side from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`);
|
|
785
|
-
if (record.outgoingTerminationReason === null) {
|
|
786
|
-
record.outgoingTerminationReason = 'timeout';
|
|
787
|
-
this.connectionManager.incrementTerminationStat('outgoing', 'timeout');
|
|
788
|
-
}
|
|
789
|
-
this.connectionManager.initiateCleanupOnce(record, 'timeout_outgoing');
|
|
790
|
-
});
|
|
791
|
-
// Apply socket timeouts
|
|
792
|
-
this.timeoutManager.applySocketTimeouts(record);
|
|
793
|
-
// Track outgoing data for bytes counting
|
|
794
|
-
targetSocket.on('data', (chunk) => {
|
|
795
|
-
record.bytesSent += chunk.length;
|
|
796
|
-
this.timeoutManager.updateActivity(record);
|
|
797
|
-
});
|
|
798
|
-
// Wait for the outgoing connection to be ready before setting up piping
|
|
799
|
-
targetSocket.once('connect', () => {
|
|
800
|
-
// Clear the initial connection error handler
|
|
801
|
-
targetSocket.removeAllListeners('error');
|
|
802
|
-
// Add the normal error handler for established connections
|
|
803
|
-
targetSocket.on('error', this.connectionManager.handleError('outgoing', record));
|
|
804
|
-
// Process any remaining data in the queue before switching to piping
|
|
805
|
-
processDataQueue();
|
|
806
|
-
// Set up piping immediately
|
|
807
|
-
pipingEstablished = true;
|
|
808
|
-
// Flush all pending data to target
|
|
809
|
-
if (record.pendingData.length > 0) {
|
|
810
|
-
const combinedData = Buffer.concat(record.pendingData);
|
|
811
|
-
if (this.settings.enableDetailedLogging) {
|
|
812
|
-
console.log(`[${connectionId}] Forwarding ${combinedData.length} bytes of initial data to target`);
|
|
813
|
-
}
|
|
814
|
-
// Write pending data immediately
|
|
815
|
-
targetSocket.write(combinedData, (err) => {
|
|
816
|
-
if (err) {
|
|
817
|
-
console.log(`[${connectionId}] Error writing pending data to target: ${err.message}`);
|
|
818
|
-
return this.connectionManager.initiateCleanupOnce(record, 'write_error');
|
|
819
|
-
}
|
|
820
|
-
});
|
|
821
|
-
// Clear the buffer now that we've processed it
|
|
822
|
-
record.pendingData = [];
|
|
823
|
-
record.pendingDataSize = 0;
|
|
824
|
-
}
|
|
825
|
-
// Setup piping in both directions without any delays
|
|
826
|
-
socket.pipe(targetSocket);
|
|
827
|
-
targetSocket.pipe(socket);
|
|
828
|
-
// Resume the socket to ensure data flows
|
|
829
|
-
socket.resume();
|
|
830
|
-
// Process any data that might be queued in the interim
|
|
831
|
-
if (dataQueue.length > 0) {
|
|
832
|
-
// Write any remaining queued data directly to the target socket
|
|
833
|
-
for (const chunk of dataQueue) {
|
|
834
|
-
targetSocket.write(chunk);
|
|
835
|
-
}
|
|
836
|
-
// Clear the queue
|
|
837
|
-
dataQueue.length = 0;
|
|
838
|
-
queueSize = 0;
|
|
839
|
-
}
|
|
840
|
-
if (this.settings.enableDetailedLogging) {
|
|
841
|
-
console.log(`[${connectionId}] Connection established: ${record.remoteIP} -> ${targetHost}:${connectionOptions.port}` +
|
|
842
|
-
`${serverName
|
|
843
|
-
? ` (SNI: ${serverName})`
|
|
844
|
-
: domainConfig
|
|
845
|
-
? ` (Port-based for domain: ${domainConfig.domains.join(', ')})`
|
|
846
|
-
: ''}` +
|
|
847
|
-
` TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}`);
|
|
848
|
-
}
|
|
849
|
-
else {
|
|
850
|
-
console.log(`Connection established: ${record.remoteIP} -> ${targetHost}:${connectionOptions.port}` +
|
|
851
|
-
`${serverName
|
|
852
|
-
? ` (SNI: ${serverName})`
|
|
853
|
-
: domainConfig
|
|
854
|
-
? ` (Port-based for domain: ${domainConfig.domains.join(', ')})`
|
|
855
|
-
: ''}`);
|
|
856
|
-
}
|
|
857
|
-
// Add the renegotiation handler for SNI validation
|
|
858
|
-
if (serverName) {
|
|
859
|
-
// Create connection info object for the existing connection
|
|
860
|
-
const connInfo = {
|
|
861
|
-
sourceIp: record.remoteIP,
|
|
862
|
-
sourcePort: record.incoming.remotePort || 0,
|
|
863
|
-
destIp: record.incoming.localAddress || '',
|
|
864
|
-
destPort: record.incoming.localPort || 0,
|
|
865
|
-
};
|
|
866
|
-
// Create a renegotiation handler function
|
|
867
|
-
const renegotiationHandler = this.tlsManager.createRenegotiationHandler(connectionId, serverName, connInfo, (connectionId, reason) => this.connectionManager.initiateCleanupOnce(record, reason));
|
|
868
|
-
// Store the handler in the connection record so we can remove it during cleanup
|
|
869
|
-
record.renegotiationHandler = renegotiationHandler;
|
|
870
|
-
// Add the handler to the socket
|
|
871
|
-
socket.on('data', renegotiationHandler);
|
|
872
|
-
if (this.settings.enableDetailedLogging) {
|
|
873
|
-
console.log(`[${connectionId}] TLS renegotiation handler installed for SNI domain: ${serverName}`);
|
|
874
|
-
if (this.settings.allowSessionTicket === false) {
|
|
875
|
-
console.log(`[${connectionId}] Session ticket usage is disabled. Connection will be reset on reconnection attempts.`);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
// Set connection timeout
|
|
880
|
-
record.cleanupTimer = this.timeoutManager.setupConnectionTimeout(record, (record, reason) => {
|
|
881
|
-
console.log(`[${connectionId}] Connection from ${record.remoteIP} exceeded max lifetime, forcing cleanup.`);
|
|
882
|
-
this.connectionManager.initiateCleanupOnce(record, reason);
|
|
883
|
-
});
|
|
884
|
-
// Mark TLS handshake as complete for TLS connections
|
|
885
|
-
if (record.isTLS) {
|
|
886
|
-
record.tlsHandshakeComplete = true;
|
|
887
|
-
if (this.settings.enableTlsDebugLogging) {
|
|
888
|
-
console.log(`[${connectionId}] TLS handshake complete for connection from ${record.remoteIP}`);
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wcC5jb25uZWN0aW9uaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3NtYXJ0cHJveHkvY2xhc3Nlcy5wcC5jb25uZWN0aW9uaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQU16QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUN0RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDbEUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDMUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3hELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3hFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNoRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUlwRTs7R0FFRztBQUNILE1BQU0sT0FBTyxpQkFBaUI7SUFDNUIsWUFDVSxRQUE0QixFQUM1QixpQkFBb0MsRUFDcEMsZUFBZ0MsRUFDaEMsbUJBQXdDLEVBQ3hDLFVBQXNCLEVBQ3RCLGtCQUFzQyxFQUN0QyxjQUE4QixFQUM5QixnQkFBa0M7UUFQbEMsYUFBUSxHQUFSLFFBQVEsQ0FBb0I7UUFDNUIsc0JBQWlCLEdBQWpCLGlCQUFpQixDQUFtQjtRQUNwQyxvQkFBZSxHQUFmLGVBQWUsQ0FBaUI7UUFDaEMsd0JBQW1CLEdBQW5CLG1CQUFtQixDQUFxQjtRQUN4QyxlQUFVLEdBQVYsVUFBVSxDQUFZO1FBQ3RCLHVCQUFrQixHQUFsQixrQkFBa0IsQ0FBb0I7UUFDdEMsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBQzlCLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBa0I7SUFDekMsQ0FBQztJQUVKOztPQUVH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
|