@@ -17,9 +17,10 @@ import {
17
17
} from 'read-tls-client-hello' ;
18
18
import { URLPattern } from "urlpattern-polyfill" ;
19
19
20
- import { TlsHandshakeFailure } from '../types' ;
20
+ import { Destination , TlsHandshakeFailure } from '../types' ;
21
21
import { getCA } from '../util/tls' ;
22
22
import { shouldPassThrough } from '../util/server-utils' ;
23
+ import { getDestination } from '../util/url' ;
23
24
import {
24
25
getParentSocket ,
25
26
buildSocketTimingInfo ,
@@ -32,9 +33,8 @@ import {
32
33
LastHopEncrypted ,
33
34
TlsMetadata ,
34
35
TlsSetupCompleted ,
35
- getAddressAndPort ,
36
- resetOrDestroy ,
37
- SocketMetadata
36
+ SocketMetadata ,
37
+ resetOrDestroy
38
38
} from '../util/socket-util' ;
39
39
import { MockttpHttpsOptions } from '../mockttp' ;
40
40
import { buildSocksServer , SocksServerOptions , SocksTcpAddress } from './socks-server' ;
@@ -151,8 +151,8 @@ export interface ComboServerOptions {
151
151
152
152
requestListener : ( req : http . IncomingMessage , res : http . ServerResponse ) => void ;
153
153
tlsClientErrorListener : ( socket : tls . TLSSocket , req : TlsHandshakeFailure ) => void ;
154
- tlsPassthroughListener : ( socket : net . Socket , address : string , port ?: number ) => void ;
155
- rawPassthroughListener : ( socket : net . Socket , address : string , port ?: number ) => void ;
154
+ tlsPassthroughListener : ( socket : net . Socket , hostname : string , port ?: number ) => void ;
155
+ rawPassthroughListener : ( socket : net . Socket , hostname : string , port ?: number ) => void ;
156
156
} ;
157
157
158
158
// The low-level server that handles all the sockets & TLS. The server will correctly call the
@@ -249,19 +249,26 @@ export async function createComboServer(options: ComboServerOptions): Promise<De
249
249
250
250
if ( options . passthroughUnknownProtocols ) {
251
251
unknownProtocolServer = net . createServer ( ( socket ) => {
252
- const destination = socket [ LastTunnelAddress ] ;
253
- if ( ! destination ) {
254
- server . emit ( 'clientError' , new Error ( 'Unknown protocol without destination' ) , socket ) ;
255
- return ;
256
- }
252
+ const tunnelAddress = socket [ LastTunnelAddress ] ;
257
253
258
- const [ host , port ] = getAddressAndPort ( destination ) ;
259
- if ( ! port ) { // Both CONNECT & SOCKS require a port, so this shouldn't happen
260
- server . emit ( 'clientError' , new Error ( 'Unknown protocol without destination port ' ) , socket ) ;
261
- return ;
262
- }
254
+ try {
255
+ if ( ! tunnelAddress ) {
256
+ server . emit ( 'clientError' , new Error ( 'Unknown protocol without destination' ) , socket ) ;
257
+ return ;
258
+ }
263
259
264
- options . rawPassthroughListener ( socket , host , port ) ;
260
+ if ( ! tunnelAddress . includes ( ':' ) ) {
261
+ // Both CONNECT & SOCKS require a port, so this shouldn't happen
262
+ server . emit ( 'clientError' , new Error ( 'Unknown protocol without destination port' ) , socket ) ;
263
+ return ;
264
+ }
265
+
266
+ const { hostname, port } = getDestination ( 'unknown' , tunnelAddress ) ; // Has port, so no protocol required
267
+ options . rawPassthroughListener ( socket , hostname , port ) ;
268
+ } catch ( e ) {
269
+ console . error ( 'Unknown protocol server error' , e ) ;
270
+ resetOrDestroy ( socket ) ;
271
+ }
265
272
} ) ;
266
273
}
267
274
@@ -432,7 +439,7 @@ function analyzeAndMaybePassThroughTls(
432
439
server : tls . Server ,
433
440
passthroughList : Required < MockttpHttpsOptions > [ 'tlsPassthrough' ] | undefined ,
434
441
interceptOnlyList : Required < MockttpHttpsOptions > [ 'tlsInterceptOnly' ] | undefined ,
435
- passthroughListener : ( socket : net . Socket , address : string , port ?: number ) => void
442
+ passthroughListener : ( socket : net . Socket , hostname : string , port ?: number ) => void
436
443
) {
437
444
if ( passthroughList && interceptOnlyList ) {
438
445
throw new Error ( 'Cannot use both tlsPassthrough and tlsInterceptOnly options at the same time.' ) ;
@@ -450,23 +457,22 @@ function analyzeAndMaybePassThroughTls(
450
457
451
458
// SNI is a good clue for where the request is headed, but an explicit proxy address (via
452
459
// CONNECT or SOCKS) is even better. Note that this may be a hostname or IPv4/6 address:
453
- let upstreamHostname : string | undefined ;
454
- let upstreamPort : number | undefined ;
460
+ let upstreamDestination : Destination | undefined ;
455
461
if ( socket [ LastTunnelAddress ] ) {
456
- ( [ upstreamHostname , upstreamPort ] = getAddressAndPort ( socket [ LastTunnelAddress ] ) ) ;
462
+ upstreamDestination = getDestination ( 'https' , socket [ LastTunnelAddress ] ) ;
457
463
}
458
464
459
465
socket [ TlsMetadata ] = {
460
466
sniHostname,
461
- connectHostname : upstreamHostname ,
462
- connectPort : upstreamPort ? .toString ( ) ,
467
+ connectHostname : upstreamDestination ?. hostname ,
468
+ connectPort : upstreamDestination ?. port . toString ( ) ,
463
469
clientAlpn : helloData . alpnProtocols ,
464
470
ja3Fingerprint : calculateJa3FromFingerprintData ( helloData . fingerprintData ) ,
465
471
ja4Fingerprint : calculateJa4FromHelloData ( helloData )
466
472
} ;
467
473
468
- if ( shouldPassThrough ( upstreamHostname , passThroughPatterns , interceptOnlyPatterns ) ) {
469
- passthroughListener ( socket , upstreamHostname , upstreamPort ) ;
474
+ if ( shouldPassThrough ( upstreamDestination ?. hostname , passThroughPatterns , interceptOnlyPatterns ) ) {
475
+ passthroughListener ( socket , upstreamDestination . hostname , upstreamDestination . port ) ;
470
476
return ; // Do not continue with TLS
471
477
} else if ( shouldPassThrough ( sniHostname , passThroughPatterns , interceptOnlyPatterns ) ) {
472
478
passthroughListener ( socket , sniHostname ! ) ; // Can't guess the port - not included in SNI
0 commit comments