1e1051a39Sopenharmony_ci# Copyright 2016-2018 The OpenSSL Project Authors. All Rights Reserved. 2e1051a39Sopenharmony_ci# 3e1051a39Sopenharmony_ci# Licensed under the Apache License 2.0 (the "License"). You may not use 4e1051a39Sopenharmony_ci# this file except in compliance with the License. You can obtain a copy 5e1051a39Sopenharmony_ci# in the file LICENSE in the source distribution or at 6e1051a39Sopenharmony_ci# https://www.openssl.org/source/license.html 7e1051a39Sopenharmony_ci 8e1051a39Sopenharmony_ciuse strict; 9e1051a39Sopenharmony_ciuse POSIX ":sys_wait_h"; 10e1051a39Sopenharmony_ci 11e1051a39Sopenharmony_cipackage TLSProxy::Proxy; 12e1051a39Sopenharmony_ci 13e1051a39Sopenharmony_ciuse File::Spec; 14e1051a39Sopenharmony_ciuse IO::Socket; 15e1051a39Sopenharmony_ciuse IO::Select; 16e1051a39Sopenharmony_ciuse TLSProxy::Record; 17e1051a39Sopenharmony_ciuse TLSProxy::Message; 18e1051a39Sopenharmony_ciuse TLSProxy::ClientHello; 19e1051a39Sopenharmony_ciuse TLSProxy::ServerHello; 20e1051a39Sopenharmony_ciuse TLSProxy::EncryptedExtensions; 21e1051a39Sopenharmony_ciuse TLSProxy::Certificate; 22e1051a39Sopenharmony_ciuse TLSProxy::CertificateRequest; 23e1051a39Sopenharmony_ciuse TLSProxy::CertificateVerify; 24e1051a39Sopenharmony_ciuse TLSProxy::ServerKeyExchange; 25e1051a39Sopenharmony_ciuse TLSProxy::NewSessionTicket; 26e1051a39Sopenharmony_ci 27e1051a39Sopenharmony_cimy $have_IPv6; 28e1051a39Sopenharmony_cimy $IP_factory; 29e1051a39Sopenharmony_ci 30e1051a39Sopenharmony_ciBEGIN 31e1051a39Sopenharmony_ci{ 32e1051a39Sopenharmony_ci # IO::Socket::IP is on the core module list, IO::Socket::INET6 isn't. 33e1051a39Sopenharmony_ci # However, IO::Socket::INET6 is older and is said to be more widely 34e1051a39Sopenharmony_ci # deployed for the moment, and may have less bugs, so we try the latter 35e1051a39Sopenharmony_ci # first, then fall back on the core modules. Worst case scenario, we 36e1051a39Sopenharmony_ci # fall back to IO::Socket::INET, only supports IPv4. 37e1051a39Sopenharmony_ci eval { 38e1051a39Sopenharmony_ci require IO::Socket::INET6; 39e1051a39Sopenharmony_ci my $s = IO::Socket::INET6->new( 40e1051a39Sopenharmony_ci LocalAddr => "::1", 41e1051a39Sopenharmony_ci LocalPort => 0, 42e1051a39Sopenharmony_ci Listen=>1, 43e1051a39Sopenharmony_ci ); 44e1051a39Sopenharmony_ci $s or die "\n"; 45e1051a39Sopenharmony_ci $s->close(); 46e1051a39Sopenharmony_ci }; 47e1051a39Sopenharmony_ci if ($@ eq "") { 48e1051a39Sopenharmony_ci $IP_factory = sub { IO::Socket::INET6->new(Domain => AF_INET6, @_); }; 49e1051a39Sopenharmony_ci $have_IPv6 = 1; 50e1051a39Sopenharmony_ci } else { 51e1051a39Sopenharmony_ci eval { 52e1051a39Sopenharmony_ci require IO::Socket::IP; 53e1051a39Sopenharmony_ci my $s = IO::Socket::IP->new( 54e1051a39Sopenharmony_ci LocalAddr => "::1", 55e1051a39Sopenharmony_ci LocalPort => 0, 56e1051a39Sopenharmony_ci Listen=>1, 57e1051a39Sopenharmony_ci ); 58e1051a39Sopenharmony_ci $s or die "\n"; 59e1051a39Sopenharmony_ci $s->close(); 60e1051a39Sopenharmony_ci }; 61e1051a39Sopenharmony_ci if ($@ eq "") { 62e1051a39Sopenharmony_ci $IP_factory = sub { IO::Socket::IP->new(@_); }; 63e1051a39Sopenharmony_ci $have_IPv6 = 1; 64e1051a39Sopenharmony_ci } else { 65e1051a39Sopenharmony_ci $IP_factory = sub { IO::Socket::INET->new(@_); }; 66e1051a39Sopenharmony_ci $have_IPv6 = 0; 67e1051a39Sopenharmony_ci } 68e1051a39Sopenharmony_ci } 69e1051a39Sopenharmony_ci} 70e1051a39Sopenharmony_ci 71e1051a39Sopenharmony_cimy $is_tls13 = 0; 72e1051a39Sopenharmony_cimy $ciphersuite = undef; 73e1051a39Sopenharmony_ci 74e1051a39Sopenharmony_cisub new 75e1051a39Sopenharmony_ci{ 76e1051a39Sopenharmony_ci my $class = shift; 77e1051a39Sopenharmony_ci my ($filter, 78e1051a39Sopenharmony_ci $execute, 79e1051a39Sopenharmony_ci $cert, 80e1051a39Sopenharmony_ci $debug) = @_; 81e1051a39Sopenharmony_ci 82e1051a39Sopenharmony_ci my $self = { 83e1051a39Sopenharmony_ci #Public read/write 84e1051a39Sopenharmony_ci proxy_addr => $have_IPv6 ? "[::1]" : "127.0.0.1", 85e1051a39Sopenharmony_ci filter => $filter, 86e1051a39Sopenharmony_ci serverflags => "", 87e1051a39Sopenharmony_ci clientflags => "", 88e1051a39Sopenharmony_ci serverconnects => 1, 89e1051a39Sopenharmony_ci reneg => 0, 90e1051a39Sopenharmony_ci sessionfile => undef, 91e1051a39Sopenharmony_ci 92e1051a39Sopenharmony_ci #Public read 93e1051a39Sopenharmony_ci proxy_port => 0, 94e1051a39Sopenharmony_ci server_port => 0, 95e1051a39Sopenharmony_ci serverpid => 0, 96e1051a39Sopenharmony_ci clientpid => 0, 97e1051a39Sopenharmony_ci execute => $execute, 98e1051a39Sopenharmony_ci cert => $cert, 99e1051a39Sopenharmony_ci debug => $debug, 100e1051a39Sopenharmony_ci cipherc => "", 101e1051a39Sopenharmony_ci ciphersuitesc => "", 102e1051a39Sopenharmony_ci ciphers => "AES128-SHA", 103e1051a39Sopenharmony_ci ciphersuitess => "TLS_AES_128_GCM_SHA256", 104e1051a39Sopenharmony_ci flight => -1, 105e1051a39Sopenharmony_ci direction => -1, 106e1051a39Sopenharmony_ci partial => ["", ""], 107e1051a39Sopenharmony_ci record_list => [], 108e1051a39Sopenharmony_ci message_list => [], 109e1051a39Sopenharmony_ci }; 110e1051a39Sopenharmony_ci 111e1051a39Sopenharmony_ci # Create the Proxy socket 112e1051a39Sopenharmony_ci my $proxaddr = $self->{proxy_addr}; 113e1051a39Sopenharmony_ci $proxaddr =~ s/[\[\]]//g; # Remove [ and ] 114e1051a39Sopenharmony_ci my @proxyargs = ( 115e1051a39Sopenharmony_ci LocalHost => $proxaddr, 116e1051a39Sopenharmony_ci LocalPort => 0, 117e1051a39Sopenharmony_ci Proto => "tcp", 118e1051a39Sopenharmony_ci Listen => SOMAXCONN, 119e1051a39Sopenharmony_ci ); 120e1051a39Sopenharmony_ci 121e1051a39Sopenharmony_ci if (my $sock = $IP_factory->(@proxyargs)) { 122e1051a39Sopenharmony_ci $self->{proxy_sock} = $sock; 123e1051a39Sopenharmony_ci $self->{proxy_port} = $sock->sockport(); 124e1051a39Sopenharmony_ci $self->{proxy_addr} = $sock->sockhost(); 125e1051a39Sopenharmony_ci $self->{proxy_addr} =~ s/(.*:.*)/[$1]/; 126e1051a39Sopenharmony_ci print "Proxy started on port ", 127e1051a39Sopenharmony_ci "$self->{proxy_addr}:$self->{proxy_port}\n"; 128e1051a39Sopenharmony_ci # use same address for s_server 129e1051a39Sopenharmony_ci $self->{server_addr} = $self->{proxy_addr}; 130e1051a39Sopenharmony_ci } else { 131e1051a39Sopenharmony_ci warn "Failed creating proxy socket (".$proxaddr.",0): $!\n"; 132e1051a39Sopenharmony_ci } 133e1051a39Sopenharmony_ci 134e1051a39Sopenharmony_ci return bless $self, $class; 135e1051a39Sopenharmony_ci} 136e1051a39Sopenharmony_ci 137e1051a39Sopenharmony_cisub DESTROY 138e1051a39Sopenharmony_ci{ 139e1051a39Sopenharmony_ci my $self = shift; 140e1051a39Sopenharmony_ci 141e1051a39Sopenharmony_ci $self->{proxy_sock}->close() if $self->{proxy_sock}; 142e1051a39Sopenharmony_ci} 143e1051a39Sopenharmony_ci 144e1051a39Sopenharmony_cisub clearClient 145e1051a39Sopenharmony_ci{ 146e1051a39Sopenharmony_ci my $self = shift; 147e1051a39Sopenharmony_ci 148e1051a39Sopenharmony_ci $self->{cipherc} = ""; 149e1051a39Sopenharmony_ci $self->{ciphersuitec} = ""; 150e1051a39Sopenharmony_ci $self->{flight} = -1; 151e1051a39Sopenharmony_ci $self->{direction} = -1; 152e1051a39Sopenharmony_ci $self->{partial} = ["", ""]; 153e1051a39Sopenharmony_ci $self->{record_list} = []; 154e1051a39Sopenharmony_ci $self->{message_list} = []; 155e1051a39Sopenharmony_ci $self->{clientflags} = ""; 156e1051a39Sopenharmony_ci $self->{sessionfile} = undef; 157e1051a39Sopenharmony_ci $self->{clientpid} = 0; 158e1051a39Sopenharmony_ci $is_tls13 = 0; 159e1051a39Sopenharmony_ci $ciphersuite = undef; 160e1051a39Sopenharmony_ci 161e1051a39Sopenharmony_ci TLSProxy::Message->clear(); 162e1051a39Sopenharmony_ci TLSProxy::Record->clear(); 163e1051a39Sopenharmony_ci} 164e1051a39Sopenharmony_ci 165e1051a39Sopenharmony_cisub clear 166e1051a39Sopenharmony_ci{ 167e1051a39Sopenharmony_ci my $self = shift; 168e1051a39Sopenharmony_ci 169e1051a39Sopenharmony_ci $self->clearClient; 170e1051a39Sopenharmony_ci $self->{ciphers} = "AES128-SHA"; 171e1051a39Sopenharmony_ci $self->{ciphersuitess} = "TLS_AES_128_GCM_SHA256"; 172e1051a39Sopenharmony_ci $self->{serverflags} = ""; 173e1051a39Sopenharmony_ci $self->{serverconnects} = 1; 174e1051a39Sopenharmony_ci $self->{serverpid} = 0; 175e1051a39Sopenharmony_ci $self->{reneg} = 0; 176e1051a39Sopenharmony_ci} 177e1051a39Sopenharmony_ci 178e1051a39Sopenharmony_cisub restart 179e1051a39Sopenharmony_ci{ 180e1051a39Sopenharmony_ci my $self = shift; 181e1051a39Sopenharmony_ci 182e1051a39Sopenharmony_ci $self->clear; 183e1051a39Sopenharmony_ci $self->start; 184e1051a39Sopenharmony_ci} 185e1051a39Sopenharmony_ci 186e1051a39Sopenharmony_cisub clientrestart 187e1051a39Sopenharmony_ci{ 188e1051a39Sopenharmony_ci my $self = shift; 189e1051a39Sopenharmony_ci 190e1051a39Sopenharmony_ci $self->clear; 191e1051a39Sopenharmony_ci $self->clientstart; 192e1051a39Sopenharmony_ci} 193e1051a39Sopenharmony_ci 194e1051a39Sopenharmony_cisub connect_to_server 195e1051a39Sopenharmony_ci{ 196e1051a39Sopenharmony_ci my $self = shift; 197e1051a39Sopenharmony_ci my $servaddr = $self->{server_addr}; 198e1051a39Sopenharmony_ci 199e1051a39Sopenharmony_ci $servaddr =~ s/[\[\]]//g; # Remove [ and ] 200e1051a39Sopenharmony_ci 201e1051a39Sopenharmony_ci my $sock = $IP_factory->(PeerAddr => $servaddr, 202e1051a39Sopenharmony_ci PeerPort => $self->{server_port}, 203e1051a39Sopenharmony_ci Proto => 'tcp'); 204e1051a39Sopenharmony_ci if (!defined($sock)) { 205e1051a39Sopenharmony_ci my $err = $!; 206e1051a39Sopenharmony_ci kill(3, $self->{real_serverpid}); 207e1051a39Sopenharmony_ci die "unable to connect: $err\n"; 208e1051a39Sopenharmony_ci } 209e1051a39Sopenharmony_ci 210e1051a39Sopenharmony_ci $self->{server_sock} = $sock; 211e1051a39Sopenharmony_ci} 212e1051a39Sopenharmony_ci 213e1051a39Sopenharmony_cisub start 214e1051a39Sopenharmony_ci{ 215e1051a39Sopenharmony_ci my ($self) = shift; 216e1051a39Sopenharmony_ci my $pid; 217e1051a39Sopenharmony_ci 218e1051a39Sopenharmony_ci if ($self->{proxy_sock} == 0) { 219e1051a39Sopenharmony_ci return 0; 220e1051a39Sopenharmony_ci } 221e1051a39Sopenharmony_ci 222e1051a39Sopenharmony_ci my $execcmd = $self->execute 223e1051a39Sopenharmony_ci ." s_server -max_protocol TLSv1.3 -no_comp -rev -engine ossltest" 224e1051a39Sopenharmony_ci #In TLSv1.3 we issue two session tickets. The default session id 225e1051a39Sopenharmony_ci #callback gets confused because the ossltest engine causes the same 226e1051a39Sopenharmony_ci #session id to be created twice due to the changed random number 227e1051a39Sopenharmony_ci #generation. Using "-ext_cache" replaces the default callback with a 228e1051a39Sopenharmony_ci #different one that doesn't get confused. 229e1051a39Sopenharmony_ci ." -ext_cache" 230e1051a39Sopenharmony_ci ." -accept $self->{server_addr}:0" 231e1051a39Sopenharmony_ci ." -cert ".$self->cert." -cert2 ".$self->cert 232e1051a39Sopenharmony_ci ." -naccept ".$self->serverconnects; 233e1051a39Sopenharmony_ci if ($self->ciphers ne "") { 234e1051a39Sopenharmony_ci $execcmd .= " -cipher ".$self->ciphers; 235e1051a39Sopenharmony_ci } 236e1051a39Sopenharmony_ci if ($self->ciphersuitess ne "") { 237e1051a39Sopenharmony_ci $execcmd .= " -ciphersuites ".$self->ciphersuitess; 238e1051a39Sopenharmony_ci } 239e1051a39Sopenharmony_ci if ($self->serverflags ne "") { 240e1051a39Sopenharmony_ci $execcmd .= " ".$self->serverflags; 241e1051a39Sopenharmony_ci } 242e1051a39Sopenharmony_ci if ($self->debug) { 243e1051a39Sopenharmony_ci print STDERR "Server command: $execcmd\n"; 244e1051a39Sopenharmony_ci } 245e1051a39Sopenharmony_ci 246e1051a39Sopenharmony_ci open(my $savedin, "<&STDIN"); 247e1051a39Sopenharmony_ci 248e1051a39Sopenharmony_ci # Temporarily replace STDIN so that sink process can inherit it... 249e1051a39Sopenharmony_ci $pid = open(STDIN, "$execcmd 2>&1 |") or die "Failed to $execcmd: $!\n"; 250e1051a39Sopenharmony_ci $self->{real_serverpid} = $pid; 251e1051a39Sopenharmony_ci 252e1051a39Sopenharmony_ci # Process the output from s_server until we find the ACCEPT line, which 253e1051a39Sopenharmony_ci # tells us what the accepting address and port are. 254e1051a39Sopenharmony_ci while (<>) { 255e1051a39Sopenharmony_ci print; 256e1051a39Sopenharmony_ci s/\R$//; # Better chomp 257e1051a39Sopenharmony_ci next unless (/^ACCEPT\s.*:(\d+)$/); 258e1051a39Sopenharmony_ci $self->{server_port} = $1; 259e1051a39Sopenharmony_ci last; 260e1051a39Sopenharmony_ci } 261e1051a39Sopenharmony_ci 262e1051a39Sopenharmony_ci if ($self->{server_port} == 0) { 263e1051a39Sopenharmony_ci # This actually means that s_server exited, because otherwise 264e1051a39Sopenharmony_ci # we would still searching for ACCEPT... 265e1051a39Sopenharmony_ci waitpid($pid, 0); 266e1051a39Sopenharmony_ci die "no ACCEPT detected in '$execcmd' output: $?\n"; 267e1051a39Sopenharmony_ci } 268e1051a39Sopenharmony_ci 269e1051a39Sopenharmony_ci # Just make sure everything else is simply printed [as separate lines]. 270e1051a39Sopenharmony_ci # The sub process simply inherits our STD* and will keep consuming 271e1051a39Sopenharmony_ci # server's output and printing it as long as there is anything there, 272e1051a39Sopenharmony_ci # out of our way. 273e1051a39Sopenharmony_ci my $error; 274e1051a39Sopenharmony_ci $pid = undef; 275e1051a39Sopenharmony_ci if (eval { require Win32::Process; 1; }) { 276e1051a39Sopenharmony_ci if (Win32::Process::Create(my $h, $^X, "perl -ne print", 0, 0, ".")) { 277e1051a39Sopenharmony_ci $pid = $h->GetProcessID(); 278e1051a39Sopenharmony_ci $self->{proc_handle} = $h; # hold handle till next round [or exit] 279e1051a39Sopenharmony_ci } else { 280e1051a39Sopenharmony_ci $error = Win32::FormatMessage(Win32::GetLastError()); 281e1051a39Sopenharmony_ci } 282e1051a39Sopenharmony_ci } else { 283e1051a39Sopenharmony_ci if (defined($pid = fork)) { 284e1051a39Sopenharmony_ci $pid or exec("$^X -ne print") or exit($!); 285e1051a39Sopenharmony_ci } else { 286e1051a39Sopenharmony_ci $error = $!; 287e1051a39Sopenharmony_ci } 288e1051a39Sopenharmony_ci } 289e1051a39Sopenharmony_ci 290e1051a39Sopenharmony_ci # Change back to original stdin 291e1051a39Sopenharmony_ci open(STDIN, "<&", $savedin); 292e1051a39Sopenharmony_ci close($savedin); 293e1051a39Sopenharmony_ci 294e1051a39Sopenharmony_ci if (!defined($pid)) { 295e1051a39Sopenharmony_ci kill(3, $self->{real_serverpid}); 296e1051a39Sopenharmony_ci die "Failed to capture s_server's output: $error\n"; 297e1051a39Sopenharmony_ci } 298e1051a39Sopenharmony_ci 299e1051a39Sopenharmony_ci $self->{serverpid} = $pid; 300e1051a39Sopenharmony_ci 301e1051a39Sopenharmony_ci print STDERR "Server responds on ", 302e1051a39Sopenharmony_ci "$self->{server_addr}:$self->{server_port}\n"; 303e1051a39Sopenharmony_ci 304e1051a39Sopenharmony_ci # Connect right away... 305e1051a39Sopenharmony_ci $self->connect_to_server(); 306e1051a39Sopenharmony_ci 307e1051a39Sopenharmony_ci return $self->clientstart; 308e1051a39Sopenharmony_ci} 309e1051a39Sopenharmony_ci 310e1051a39Sopenharmony_cisub clientstart 311e1051a39Sopenharmony_ci{ 312e1051a39Sopenharmony_ci my ($self) = shift; 313e1051a39Sopenharmony_ci 314e1051a39Sopenharmony_ci if ($self->execute) { 315e1051a39Sopenharmony_ci my $pid; 316e1051a39Sopenharmony_ci my $execcmd = $self->execute 317e1051a39Sopenharmony_ci ." s_client -max_protocol TLSv1.3 -engine ossltest" 318e1051a39Sopenharmony_ci ." -connect $self->{proxy_addr}:$self->{proxy_port}"; 319e1051a39Sopenharmony_ci if ($self->cipherc ne "") { 320e1051a39Sopenharmony_ci $execcmd .= " -cipher ".$self->cipherc; 321e1051a39Sopenharmony_ci } 322e1051a39Sopenharmony_ci if ($self->ciphersuitesc ne "") { 323e1051a39Sopenharmony_ci $execcmd .= " -ciphersuites ".$self->ciphersuitesc; 324e1051a39Sopenharmony_ci } 325e1051a39Sopenharmony_ci if ($self->clientflags ne "") { 326e1051a39Sopenharmony_ci $execcmd .= " ".$self->clientflags; 327e1051a39Sopenharmony_ci } 328e1051a39Sopenharmony_ci if ($self->clientflags !~ m/-(no)?servername/) { 329e1051a39Sopenharmony_ci $execcmd .= " -servername localhost"; 330e1051a39Sopenharmony_ci } 331e1051a39Sopenharmony_ci if (defined $self->sessionfile) { 332e1051a39Sopenharmony_ci $execcmd .= " -ign_eof"; 333e1051a39Sopenharmony_ci } 334e1051a39Sopenharmony_ci if ($self->debug) { 335e1051a39Sopenharmony_ci print STDERR "Client command: $execcmd\n"; 336e1051a39Sopenharmony_ci } 337e1051a39Sopenharmony_ci 338e1051a39Sopenharmony_ci open(my $savedout, ">&STDOUT"); 339e1051a39Sopenharmony_ci # If we open pipe with new descriptor, attempt to close it, 340e1051a39Sopenharmony_ci # explicitly or implicitly, would incur waitpid and effectively 341e1051a39Sopenharmony_ci # dead-lock... 342e1051a39Sopenharmony_ci if (!($pid = open(STDOUT, "| $execcmd"))) { 343e1051a39Sopenharmony_ci my $err = $!; 344e1051a39Sopenharmony_ci kill(3, $self->{real_serverpid}); 345e1051a39Sopenharmony_ci die "Failed to $execcmd: $err\n"; 346e1051a39Sopenharmony_ci } 347e1051a39Sopenharmony_ci $self->{clientpid} = $pid; 348e1051a39Sopenharmony_ci 349e1051a39Sopenharmony_ci # queue [magic] input 350e1051a39Sopenharmony_ci print $self->reneg ? "R" : "test"; 351e1051a39Sopenharmony_ci 352e1051a39Sopenharmony_ci # this closes client's stdin without waiting for its pid 353e1051a39Sopenharmony_ci open(STDOUT, ">&", $savedout); 354e1051a39Sopenharmony_ci close($savedout); 355e1051a39Sopenharmony_ci } 356e1051a39Sopenharmony_ci 357e1051a39Sopenharmony_ci # Wait for incoming connection from client 358e1051a39Sopenharmony_ci my $fdset = IO::Select->new($self->{proxy_sock}); 359e1051a39Sopenharmony_ci if (!$fdset->can_read(60)) { 360e1051a39Sopenharmony_ci kill(3, $self->{real_serverpid}); 361e1051a39Sopenharmony_ci die "s_client didn't try to connect\n"; 362e1051a39Sopenharmony_ci } 363e1051a39Sopenharmony_ci 364e1051a39Sopenharmony_ci my $client_sock; 365e1051a39Sopenharmony_ci if(!($client_sock = $self->{proxy_sock}->accept())) { 366e1051a39Sopenharmony_ci warn "Failed accepting incoming connection: $!\n"; 367e1051a39Sopenharmony_ci return 0; 368e1051a39Sopenharmony_ci } 369e1051a39Sopenharmony_ci 370e1051a39Sopenharmony_ci print "Connection opened\n"; 371e1051a39Sopenharmony_ci 372e1051a39Sopenharmony_ci my $server_sock = $self->{server_sock}; 373e1051a39Sopenharmony_ci my $indata; 374e1051a39Sopenharmony_ci 375e1051a39Sopenharmony_ci #Wait for either the server socket or the client socket to become readable 376e1051a39Sopenharmony_ci $fdset = IO::Select->new($server_sock, $client_sock); 377e1051a39Sopenharmony_ci my @ready; 378e1051a39Sopenharmony_ci my $ctr = 0; 379e1051a39Sopenharmony_ci local $SIG{PIPE} = "IGNORE"; 380e1051a39Sopenharmony_ci $self->{saw_session_ticket} = undef; 381e1051a39Sopenharmony_ci while($fdset->count && $ctr < 10) { 382e1051a39Sopenharmony_ci if (defined($self->{sessionfile})) { 383e1051a39Sopenharmony_ci # s_client got -ign_eof and won't be exiting voluntarily, so we 384e1051a39Sopenharmony_ci # look for data *and* session ticket... 385e1051a39Sopenharmony_ci last if TLSProxy::Message->success() 386e1051a39Sopenharmony_ci && $self->{saw_session_ticket}; 387e1051a39Sopenharmony_ci } 388e1051a39Sopenharmony_ci if (!(@ready = $fdset->can_read(1))) { 389e1051a39Sopenharmony_ci $ctr++; 390e1051a39Sopenharmony_ci next; 391e1051a39Sopenharmony_ci } 392e1051a39Sopenharmony_ci foreach my $hand (@ready) { 393e1051a39Sopenharmony_ci if ($hand == $server_sock) { 394e1051a39Sopenharmony_ci if ($server_sock->sysread($indata, 16384)) { 395e1051a39Sopenharmony_ci if ($indata = $self->process_packet(1, $indata)) { 396e1051a39Sopenharmony_ci $client_sock->syswrite($indata) or goto END; 397e1051a39Sopenharmony_ci } 398e1051a39Sopenharmony_ci $ctr = 0; 399e1051a39Sopenharmony_ci } else { 400e1051a39Sopenharmony_ci $fdset->remove($server_sock); 401e1051a39Sopenharmony_ci $client_sock->shutdown(SHUT_WR); 402e1051a39Sopenharmony_ci } 403e1051a39Sopenharmony_ci } elsif ($hand == $client_sock) { 404e1051a39Sopenharmony_ci if ($client_sock->sysread($indata, 16384)) { 405e1051a39Sopenharmony_ci if ($indata = $self->process_packet(0, $indata)) { 406e1051a39Sopenharmony_ci $server_sock->syswrite($indata) or goto END; 407e1051a39Sopenharmony_ci } 408e1051a39Sopenharmony_ci $ctr = 0; 409e1051a39Sopenharmony_ci } else { 410e1051a39Sopenharmony_ci $fdset->remove($client_sock); 411e1051a39Sopenharmony_ci $server_sock->shutdown(SHUT_WR); 412e1051a39Sopenharmony_ci } 413e1051a39Sopenharmony_ci } else { 414e1051a39Sopenharmony_ci kill(3, $self->{real_serverpid}); 415e1051a39Sopenharmony_ci die "Unexpected handle"; 416e1051a39Sopenharmony_ci } 417e1051a39Sopenharmony_ci } 418e1051a39Sopenharmony_ci } 419e1051a39Sopenharmony_ci 420e1051a39Sopenharmony_ci if ($ctr >= 10) { 421e1051a39Sopenharmony_ci kill(3, $self->{real_serverpid}); 422e1051a39Sopenharmony_ci die "No progress made"; 423e1051a39Sopenharmony_ci } 424e1051a39Sopenharmony_ci 425e1051a39Sopenharmony_ci END: 426e1051a39Sopenharmony_ci print "Connection closed\n"; 427e1051a39Sopenharmony_ci if($server_sock) { 428e1051a39Sopenharmony_ci $server_sock->close(); 429e1051a39Sopenharmony_ci $self->{server_sock} = undef; 430e1051a39Sopenharmony_ci } 431e1051a39Sopenharmony_ci if($client_sock) { 432e1051a39Sopenharmony_ci #Closing this also kills the child process 433e1051a39Sopenharmony_ci $client_sock->close(); 434e1051a39Sopenharmony_ci } 435e1051a39Sopenharmony_ci 436e1051a39Sopenharmony_ci my $pid; 437e1051a39Sopenharmony_ci if (--$self->{serverconnects} == 0) { 438e1051a39Sopenharmony_ci $pid = $self->{serverpid}; 439e1051a39Sopenharmony_ci print "Waiting for 'perl -ne print' process to close: $pid...\n"; 440e1051a39Sopenharmony_ci $pid = waitpid($pid, 0); 441e1051a39Sopenharmony_ci if ($pid > 0) { 442e1051a39Sopenharmony_ci die "exit code $? from 'perl -ne print' process\n" if $? != 0; 443e1051a39Sopenharmony_ci } elsif ($pid == 0) { 444e1051a39Sopenharmony_ci kill(3, $self->{real_serverpid}); 445e1051a39Sopenharmony_ci die "lost control over $self->{serverpid}?"; 446e1051a39Sopenharmony_ci } 447e1051a39Sopenharmony_ci $pid = $self->{real_serverpid}; 448e1051a39Sopenharmony_ci print "Waiting for s_server process to close: $pid...\n"; 449e1051a39Sopenharmony_ci # it's done already, just collect the exit code [and reap]... 450e1051a39Sopenharmony_ci waitpid($pid, 0); 451e1051a39Sopenharmony_ci die "exit code $? from s_server process\n" if $? != 0; 452e1051a39Sopenharmony_ci } else { 453e1051a39Sopenharmony_ci # It's a bit counter-intuitive spot to make next connection to 454e1051a39Sopenharmony_ci # the s_server. Rationale is that established connection works 455e1051a39Sopenharmony_ci # as synchronization point, in sense that this way we know that 456e1051a39Sopenharmony_ci # s_server is actually done with current session... 457e1051a39Sopenharmony_ci $self->connect_to_server(); 458e1051a39Sopenharmony_ci } 459e1051a39Sopenharmony_ci $pid = $self->{clientpid}; 460e1051a39Sopenharmony_ci print "Waiting for s_client process to close: $pid...\n"; 461e1051a39Sopenharmony_ci waitpid($pid, 0); 462e1051a39Sopenharmony_ci 463e1051a39Sopenharmony_ci return 1; 464e1051a39Sopenharmony_ci} 465e1051a39Sopenharmony_ci 466e1051a39Sopenharmony_cisub process_packet 467e1051a39Sopenharmony_ci{ 468e1051a39Sopenharmony_ci my ($self, $server, $packet) = @_; 469e1051a39Sopenharmony_ci my $len_real; 470e1051a39Sopenharmony_ci my $decrypt_len; 471e1051a39Sopenharmony_ci my $data; 472e1051a39Sopenharmony_ci my $recnum; 473e1051a39Sopenharmony_ci 474e1051a39Sopenharmony_ci if ($server) { 475e1051a39Sopenharmony_ci print "Received server packet\n"; 476e1051a39Sopenharmony_ci } else { 477e1051a39Sopenharmony_ci print "Received client packet\n"; 478e1051a39Sopenharmony_ci } 479e1051a39Sopenharmony_ci 480e1051a39Sopenharmony_ci if ($self->{direction} != $server) { 481e1051a39Sopenharmony_ci $self->{flight} = $self->{flight} + 1; 482e1051a39Sopenharmony_ci $self->{direction} = $server; 483e1051a39Sopenharmony_ci } 484e1051a39Sopenharmony_ci 485e1051a39Sopenharmony_ci print "Packet length = ".length($packet)."\n"; 486e1051a39Sopenharmony_ci print "Processing flight ".$self->flight."\n"; 487e1051a39Sopenharmony_ci 488e1051a39Sopenharmony_ci #Return contains the list of record found in the packet followed by the 489e1051a39Sopenharmony_ci #list of messages in those records and any partial message 490e1051a39Sopenharmony_ci my @ret = TLSProxy::Record->get_records($server, $self->flight, 491e1051a39Sopenharmony_ci $self->{partial}[$server].$packet); 492e1051a39Sopenharmony_ci $self->{partial}[$server] = $ret[2]; 493e1051a39Sopenharmony_ci push @{$self->{record_list}}, @{$ret[0]}; 494e1051a39Sopenharmony_ci push @{$self->{message_list}}, @{$ret[1]}; 495e1051a39Sopenharmony_ci 496e1051a39Sopenharmony_ci print "\n"; 497e1051a39Sopenharmony_ci 498e1051a39Sopenharmony_ci if (scalar(@{$ret[0]}) == 0 or length($ret[2]) != 0) { 499e1051a39Sopenharmony_ci return ""; 500e1051a39Sopenharmony_ci } 501e1051a39Sopenharmony_ci 502e1051a39Sopenharmony_ci #Finished parsing. Call user provided filter here 503e1051a39Sopenharmony_ci if (defined $self->filter) { 504e1051a39Sopenharmony_ci $self->filter->($self); 505e1051a39Sopenharmony_ci } 506e1051a39Sopenharmony_ci 507e1051a39Sopenharmony_ci #Take a note on NewSessionTicket 508e1051a39Sopenharmony_ci foreach my $message (reverse @{$self->{message_list}}) { 509e1051a39Sopenharmony_ci if ($message->{mt} == TLSProxy::Message::MT_NEW_SESSION_TICKET) { 510e1051a39Sopenharmony_ci $self->{saw_session_ticket} = 1; 511e1051a39Sopenharmony_ci last; 512e1051a39Sopenharmony_ci } 513e1051a39Sopenharmony_ci } 514e1051a39Sopenharmony_ci 515e1051a39Sopenharmony_ci #Reconstruct the packet 516e1051a39Sopenharmony_ci $packet = ""; 517e1051a39Sopenharmony_ci foreach my $record (@{$self->record_list}) { 518e1051a39Sopenharmony_ci $packet .= $record->reconstruct_record($server); 519e1051a39Sopenharmony_ci } 520e1051a39Sopenharmony_ci 521e1051a39Sopenharmony_ci print "Forwarded packet length = ".length($packet)."\n\n"; 522e1051a39Sopenharmony_ci 523e1051a39Sopenharmony_ci return $packet; 524e1051a39Sopenharmony_ci} 525e1051a39Sopenharmony_ci 526e1051a39Sopenharmony_ci#Read accessors 527e1051a39Sopenharmony_cisub execute 528e1051a39Sopenharmony_ci{ 529e1051a39Sopenharmony_ci my $self = shift; 530e1051a39Sopenharmony_ci return $self->{execute}; 531e1051a39Sopenharmony_ci} 532e1051a39Sopenharmony_cisub cert 533e1051a39Sopenharmony_ci{ 534e1051a39Sopenharmony_ci my $self = shift; 535e1051a39Sopenharmony_ci return $self->{cert}; 536e1051a39Sopenharmony_ci} 537e1051a39Sopenharmony_cisub debug 538e1051a39Sopenharmony_ci{ 539e1051a39Sopenharmony_ci my $self = shift; 540e1051a39Sopenharmony_ci return $self->{debug}; 541e1051a39Sopenharmony_ci} 542e1051a39Sopenharmony_cisub flight 543e1051a39Sopenharmony_ci{ 544e1051a39Sopenharmony_ci my $self = shift; 545e1051a39Sopenharmony_ci return $self->{flight}; 546e1051a39Sopenharmony_ci} 547e1051a39Sopenharmony_cisub record_list 548e1051a39Sopenharmony_ci{ 549e1051a39Sopenharmony_ci my $self = shift; 550e1051a39Sopenharmony_ci return $self->{record_list}; 551e1051a39Sopenharmony_ci} 552e1051a39Sopenharmony_cisub success 553e1051a39Sopenharmony_ci{ 554e1051a39Sopenharmony_ci my $self = shift; 555e1051a39Sopenharmony_ci return $self->{success}; 556e1051a39Sopenharmony_ci} 557e1051a39Sopenharmony_cisub end 558e1051a39Sopenharmony_ci{ 559e1051a39Sopenharmony_ci my $self = shift; 560e1051a39Sopenharmony_ci return $self->{end}; 561e1051a39Sopenharmony_ci} 562e1051a39Sopenharmony_cisub supports_IPv6 563e1051a39Sopenharmony_ci{ 564e1051a39Sopenharmony_ci my $self = shift; 565e1051a39Sopenharmony_ci return $have_IPv6; 566e1051a39Sopenharmony_ci} 567e1051a39Sopenharmony_cisub proxy_addr 568e1051a39Sopenharmony_ci{ 569e1051a39Sopenharmony_ci my $self = shift; 570e1051a39Sopenharmony_ci return $self->{proxy_addr}; 571e1051a39Sopenharmony_ci} 572e1051a39Sopenharmony_cisub proxy_port 573e1051a39Sopenharmony_ci{ 574e1051a39Sopenharmony_ci my $self = shift; 575e1051a39Sopenharmony_ci return $self->{proxy_port}; 576e1051a39Sopenharmony_ci} 577e1051a39Sopenharmony_cisub server_addr 578e1051a39Sopenharmony_ci{ 579e1051a39Sopenharmony_ci my $self = shift; 580e1051a39Sopenharmony_ci return $self->{server_addr}; 581e1051a39Sopenharmony_ci} 582e1051a39Sopenharmony_cisub server_port 583e1051a39Sopenharmony_ci{ 584e1051a39Sopenharmony_ci my $self = shift; 585e1051a39Sopenharmony_ci return $self->{server_port}; 586e1051a39Sopenharmony_ci} 587e1051a39Sopenharmony_cisub serverpid 588e1051a39Sopenharmony_ci{ 589e1051a39Sopenharmony_ci my $self = shift; 590e1051a39Sopenharmony_ci return $self->{serverpid}; 591e1051a39Sopenharmony_ci} 592e1051a39Sopenharmony_cisub clientpid 593e1051a39Sopenharmony_ci{ 594e1051a39Sopenharmony_ci my $self = shift; 595e1051a39Sopenharmony_ci return $self->{clientpid}; 596e1051a39Sopenharmony_ci} 597e1051a39Sopenharmony_ci 598e1051a39Sopenharmony_ci#Read/write accessors 599e1051a39Sopenharmony_cisub filter 600e1051a39Sopenharmony_ci{ 601e1051a39Sopenharmony_ci my $self = shift; 602e1051a39Sopenharmony_ci if (@_) { 603e1051a39Sopenharmony_ci $self->{filter} = shift; 604e1051a39Sopenharmony_ci } 605e1051a39Sopenharmony_ci return $self->{filter}; 606e1051a39Sopenharmony_ci} 607e1051a39Sopenharmony_cisub cipherc 608e1051a39Sopenharmony_ci{ 609e1051a39Sopenharmony_ci my $self = shift; 610e1051a39Sopenharmony_ci if (@_) { 611e1051a39Sopenharmony_ci $self->{cipherc} = shift; 612e1051a39Sopenharmony_ci } 613e1051a39Sopenharmony_ci return $self->{cipherc}; 614e1051a39Sopenharmony_ci} 615e1051a39Sopenharmony_cisub ciphersuitesc 616e1051a39Sopenharmony_ci{ 617e1051a39Sopenharmony_ci my $self = shift; 618e1051a39Sopenharmony_ci if (@_) { 619e1051a39Sopenharmony_ci $self->{ciphersuitesc} = shift; 620e1051a39Sopenharmony_ci } 621e1051a39Sopenharmony_ci return $self->{ciphersuitesc}; 622e1051a39Sopenharmony_ci} 623e1051a39Sopenharmony_cisub ciphers 624e1051a39Sopenharmony_ci{ 625e1051a39Sopenharmony_ci my $self = shift; 626e1051a39Sopenharmony_ci if (@_) { 627e1051a39Sopenharmony_ci $self->{ciphers} = shift; 628e1051a39Sopenharmony_ci } 629e1051a39Sopenharmony_ci return $self->{ciphers}; 630e1051a39Sopenharmony_ci} 631e1051a39Sopenharmony_cisub ciphersuitess 632e1051a39Sopenharmony_ci{ 633e1051a39Sopenharmony_ci my $self = shift; 634e1051a39Sopenharmony_ci if (@_) { 635e1051a39Sopenharmony_ci $self->{ciphersuitess} = shift; 636e1051a39Sopenharmony_ci } 637e1051a39Sopenharmony_ci return $self->{ciphersuitess}; 638e1051a39Sopenharmony_ci} 639e1051a39Sopenharmony_cisub serverflags 640e1051a39Sopenharmony_ci{ 641e1051a39Sopenharmony_ci my $self = shift; 642e1051a39Sopenharmony_ci if (@_) { 643e1051a39Sopenharmony_ci $self->{serverflags} = shift; 644e1051a39Sopenharmony_ci } 645e1051a39Sopenharmony_ci return $self->{serverflags}; 646e1051a39Sopenharmony_ci} 647e1051a39Sopenharmony_cisub clientflags 648e1051a39Sopenharmony_ci{ 649e1051a39Sopenharmony_ci my $self = shift; 650e1051a39Sopenharmony_ci if (@_) { 651e1051a39Sopenharmony_ci $self->{clientflags} = shift; 652e1051a39Sopenharmony_ci } 653e1051a39Sopenharmony_ci return $self->{clientflags}; 654e1051a39Sopenharmony_ci} 655e1051a39Sopenharmony_cisub serverconnects 656e1051a39Sopenharmony_ci{ 657e1051a39Sopenharmony_ci my $self = shift; 658e1051a39Sopenharmony_ci if (@_) { 659e1051a39Sopenharmony_ci $self->{serverconnects} = shift; 660e1051a39Sopenharmony_ci } 661e1051a39Sopenharmony_ci return $self->{serverconnects}; 662e1051a39Sopenharmony_ci} 663e1051a39Sopenharmony_ci# This is a bit ugly because the caller is responsible for keeping the records 664e1051a39Sopenharmony_ci# in sync with the updated message list; simply updating the message list isn't 665e1051a39Sopenharmony_ci# sufficient to get the proxy to forward the new message. 666e1051a39Sopenharmony_ci# But it does the trick for the one test (test_sslsessiontick) that needs it. 667e1051a39Sopenharmony_cisub message_list 668e1051a39Sopenharmony_ci{ 669e1051a39Sopenharmony_ci my $self = shift; 670e1051a39Sopenharmony_ci if (@_) { 671e1051a39Sopenharmony_ci $self->{message_list} = shift; 672e1051a39Sopenharmony_ci } 673e1051a39Sopenharmony_ci return $self->{message_list}; 674e1051a39Sopenharmony_ci} 675e1051a39Sopenharmony_ci 676e1051a39Sopenharmony_cisub fill_known_data 677e1051a39Sopenharmony_ci{ 678e1051a39Sopenharmony_ci my $length = shift; 679e1051a39Sopenharmony_ci my $ret = ""; 680e1051a39Sopenharmony_ci for (my $i = 0; $i < $length; $i++) { 681e1051a39Sopenharmony_ci $ret .= chr($i); 682e1051a39Sopenharmony_ci } 683e1051a39Sopenharmony_ci return $ret; 684e1051a39Sopenharmony_ci} 685e1051a39Sopenharmony_ci 686e1051a39Sopenharmony_cisub is_tls13 687e1051a39Sopenharmony_ci{ 688e1051a39Sopenharmony_ci my $class = shift; 689e1051a39Sopenharmony_ci if (@_) { 690e1051a39Sopenharmony_ci $is_tls13 = shift; 691e1051a39Sopenharmony_ci } 692e1051a39Sopenharmony_ci return $is_tls13; 693e1051a39Sopenharmony_ci} 694e1051a39Sopenharmony_ci 695e1051a39Sopenharmony_cisub reneg 696e1051a39Sopenharmony_ci{ 697e1051a39Sopenharmony_ci my $self = shift; 698e1051a39Sopenharmony_ci if (@_) { 699e1051a39Sopenharmony_ci $self->{reneg} = shift; 700e1051a39Sopenharmony_ci } 701e1051a39Sopenharmony_ci return $self->{reneg}; 702e1051a39Sopenharmony_ci} 703e1051a39Sopenharmony_ci 704e1051a39Sopenharmony_ci#Setting a sessionfile means that the client will not close until the given 705e1051a39Sopenharmony_ci#file exists. This is useful in TLSv1.3 where otherwise s_client will close 706e1051a39Sopenharmony_ci#immediately at the end of the handshake, but before the session has been 707e1051a39Sopenharmony_ci#received from the server. A side effect of this is that s_client never sends 708e1051a39Sopenharmony_ci#a close_notify, so instead we consider success to be when it sends application 709e1051a39Sopenharmony_ci#data over the connection. 710e1051a39Sopenharmony_cisub sessionfile 711e1051a39Sopenharmony_ci{ 712e1051a39Sopenharmony_ci my $self = shift; 713e1051a39Sopenharmony_ci if (@_) { 714e1051a39Sopenharmony_ci $self->{sessionfile} = shift; 715e1051a39Sopenharmony_ci TLSProxy::Message->successondata(1); 716e1051a39Sopenharmony_ci } 717e1051a39Sopenharmony_ci return $self->{sessionfile}; 718e1051a39Sopenharmony_ci} 719e1051a39Sopenharmony_ci 720e1051a39Sopenharmony_cisub ciphersuite 721e1051a39Sopenharmony_ci{ 722e1051a39Sopenharmony_ci my $class = shift; 723e1051a39Sopenharmony_ci if (@_) { 724e1051a39Sopenharmony_ci $ciphersuite = shift; 725e1051a39Sopenharmony_ci } 726e1051a39Sopenharmony_ci return $ciphersuite; 727e1051a39Sopenharmony_ci} 728e1051a39Sopenharmony_ci 729e1051a39Sopenharmony_ci1; 730