1*97e3c585Schristos# Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved. 253060421Schristos# 3b0d17251Schristos# Licensed under the Apache License 2.0 (the "License"). You may not use 453060421Schristos# this file except in compliance with the License. You can obtain a copy 553060421Schristos# in the file LICENSE in the source distribution or at 653060421Schristos# https://www.openssl.org/source/license.html 753060421Schristos 853060421Schristosuse strict; 953060421Schristosuse POSIX ":sys_wait_h"; 1053060421Schristos 1153060421Schristospackage TLSProxy::Proxy; 1253060421Schristos 1353060421Schristosuse File::Spec; 1453060421Schristosuse IO::Socket; 1553060421Schristosuse IO::Select; 1653060421Schristosuse TLSProxy::Record; 1753060421Schristosuse TLSProxy::Message; 1853060421Schristosuse TLSProxy::ClientHello; 1953060421Schristosuse TLSProxy::ServerHello; 2013d40330Schristosuse TLSProxy::EncryptedExtensions; 2113d40330Schristosuse TLSProxy::Certificate; 224ce06407Schristosuse TLSProxy::CertificateRequest; 2313d40330Schristosuse TLSProxy::CertificateVerify; 2453060421Schristosuse TLSProxy::ServerKeyExchange; 2553060421Schristosuse TLSProxy::NewSessionTicket; 26*97e3c585Schristosuse TLSProxy::NextProto; 2753060421Schristos 2813d40330Schristosmy $have_IPv6; 2953060421Schristosmy $IP_factory; 3053060421Schristos 3113d40330SchristosBEGIN 3253060421Schristos{ 3353060421Schristos # IO::Socket::IP is on the core module list, IO::Socket::INET6 isn't. 3453060421Schristos # However, IO::Socket::INET6 is older and is said to be more widely 3553060421Schristos # deployed for the moment, and may have less bugs, so we try the latter 3613d40330Schristos # first, then fall back on the core modules. Worst case scenario, we 3753060421Schristos # fall back to IO::Socket::INET, only supports IPv4. 3853060421Schristos eval { 3953060421Schristos require IO::Socket::INET6; 4053060421Schristos my $s = IO::Socket::INET6->new( 4153060421Schristos LocalAddr => "::1", 4253060421Schristos LocalPort => 0, 4353060421Schristos Listen=>1, 4453060421Schristos ); 4553060421Schristos $s or die "\n"; 4653060421Schristos $s->close(); 4753060421Schristos }; 4853060421Schristos if ($@ eq "") { 49b88c74d5Schristos $IP_factory = sub { IO::Socket::INET6->new(Domain => AF_INET6, @_); }; 5053060421Schristos $have_IPv6 = 1; 5153060421Schristos } else { 5253060421Schristos eval { 5353060421Schristos require IO::Socket::IP; 5453060421Schristos my $s = IO::Socket::IP->new( 5553060421Schristos LocalAddr => "::1", 5653060421Schristos LocalPort => 0, 5753060421Schristos Listen=>1, 5853060421Schristos ); 5953060421Schristos $s or die "\n"; 6053060421Schristos $s->close(); 6153060421Schristos }; 6253060421Schristos if ($@ eq "") { 6353060421Schristos $IP_factory = sub { IO::Socket::IP->new(@_); }; 6453060421Schristos $have_IPv6 = 1; 6553060421Schristos } else { 6653060421Schristos $IP_factory = sub { IO::Socket::INET->new(@_); }; 6713d40330Schristos $have_IPv6 = 0; 6853060421Schristos } 6953060421Schristos } 7013d40330Schristos} 7113d40330Schristos 7213d40330Schristosmy $is_tls13 = 0; 7313d40330Schristosmy $ciphersuite = undef; 7413d40330Schristos 7513d40330Schristossub new 7613d40330Schristos{ 7713d40330Schristos my $class = shift; 7813d40330Schristos my ($filter, 7913d40330Schristos $execute, 8013d40330Schristos $cert, 8113d40330Schristos $debug) = @_; 8213d40330Schristos 8313d40330Schristos my $self = { 8413d40330Schristos #Public read/write 8513d40330Schristos proxy_addr => $have_IPv6 ? "[::1]" : "127.0.0.1", 8613d40330Schristos filter => $filter, 8713d40330Schristos serverflags => "", 8813d40330Schristos clientflags => "", 8913d40330Schristos serverconnects => 1, 9013d40330Schristos reneg => 0, 9113d40330Schristos sessionfile => undef, 9213d40330Schristos 9313d40330Schristos #Public read 9413d40330Schristos proxy_port => 0, 9513d40330Schristos server_port => 0, 9613d40330Schristos serverpid => 0, 9713d40330Schristos clientpid => 0, 9813d40330Schristos execute => $execute, 9913d40330Schristos cert => $cert, 10013d40330Schristos debug => $debug, 10113d40330Schristos cipherc => "", 10213d40330Schristos ciphersuitesc => "", 10313d40330Schristos ciphers => "AES128-SHA", 10413d40330Schristos ciphersuitess => "TLS_AES_128_GCM_SHA256", 10513d40330Schristos flight => -1, 10613d40330Schristos direction => -1, 10713d40330Schristos partial => ["", ""], 10813d40330Schristos record_list => [], 10913d40330Schristos message_list => [], 11013d40330Schristos }; 11153060421Schristos 11253060421Schristos # Create the Proxy socket 11353060421Schristos my $proxaddr = $self->{proxy_addr}; 11453060421Schristos $proxaddr =~ s/[\[\]]//g; # Remove [ and ] 11553060421Schristos my @proxyargs = ( 11653060421Schristos LocalHost => $proxaddr, 11713d40330Schristos LocalPort => 0, 11853060421Schristos Proto => "tcp", 11953060421Schristos Listen => SOMAXCONN, 12053060421Schristos ); 12153060421Schristos 12213d40330Schristos if (my $sock = $IP_factory->(@proxyargs)) { 12313d40330Schristos $self->{proxy_sock} = $sock; 12413d40330Schristos $self->{proxy_port} = $sock->sockport(); 12513d40330Schristos $self->{proxy_addr} = $sock->sockhost(); 12613d40330Schristos $self->{proxy_addr} =~ s/(.*:.*)/[$1]/; 12713d40330Schristos print "Proxy started on port ", 12813d40330Schristos "$self->{proxy_addr}:$self->{proxy_port}\n"; 12913d40330Schristos # use same address for s_server 13013d40330Schristos $self->{server_addr} = $self->{proxy_addr}; 13153060421Schristos } else { 13213d40330Schristos warn "Failed creating proxy socket (".$proxaddr.",0): $!\n"; 13353060421Schristos } 13453060421Schristos 13553060421Schristos return bless $self, $class; 13653060421Schristos} 13753060421Schristos 13853060421Schristossub DESTROY 13953060421Schristos{ 14053060421Schristos my $self = shift; 14153060421Schristos 14253060421Schristos $self->{proxy_sock}->close() if $self->{proxy_sock}; 14353060421Schristos} 14453060421Schristos 14553060421Schristossub clearClient 14653060421Schristos{ 14753060421Schristos my $self = shift; 14853060421Schristos 14953060421Schristos $self->{cipherc} = ""; 15013d40330Schristos $self->{ciphersuitec} = ""; 15153060421Schristos $self->{flight} = -1; 15253060421Schristos $self->{direction} = -1; 15353060421Schristos $self->{partial} = ["", ""]; 15453060421Schristos $self->{record_list} = []; 15553060421Schristos $self->{message_list} = []; 15653060421Schristos $self->{clientflags} = ""; 15713d40330Schristos $self->{sessionfile} = undef; 15853060421Schristos $self->{clientpid} = 0; 15913d40330Schristos $is_tls13 = 0; 16013d40330Schristos $ciphersuite = undef; 16153060421Schristos 16253060421Schristos TLSProxy::Message->clear(); 16353060421Schristos TLSProxy::Record->clear(); 16453060421Schristos} 16553060421Schristos 16653060421Schristossub clear 16753060421Schristos{ 16853060421Schristos my $self = shift; 16953060421Schristos 17053060421Schristos $self->clearClient; 17153060421Schristos $self->{ciphers} = "AES128-SHA"; 17213d40330Schristos $self->{ciphersuitess} = "TLS_AES_128_GCM_SHA256"; 17353060421Schristos $self->{serverflags} = ""; 17453060421Schristos $self->{serverconnects} = 1; 17553060421Schristos $self->{serverpid} = 0; 17653060421Schristos $self->{reneg} = 0; 17753060421Schristos} 17853060421Schristos 17953060421Schristossub restart 18053060421Schristos{ 18153060421Schristos my $self = shift; 18253060421Schristos 18353060421Schristos $self->clear; 18453060421Schristos $self->start; 18553060421Schristos} 18653060421Schristos 18753060421Schristossub clientrestart 18853060421Schristos{ 18953060421Schristos my $self = shift; 19053060421Schristos 19153060421Schristos $self->clear; 19253060421Schristos $self->clientstart; 19353060421Schristos} 19453060421Schristos 19513d40330Schristossub connect_to_server 19613d40330Schristos{ 19713d40330Schristos my $self = shift; 19813d40330Schristos my $servaddr = $self->{server_addr}; 19913d40330Schristos 20013d40330Schristos $servaddr =~ s/[\[\]]//g; # Remove [ and ] 20113d40330Schristos 20213d40330Schristos my $sock = $IP_factory->(PeerAddr => $servaddr, 20313d40330Schristos PeerPort => $self->{server_port}, 20413d40330Schristos Proto => 'tcp'); 20513d40330Schristos if (!defined($sock)) { 20613d40330Schristos my $err = $!; 20713d40330Schristos kill(3, $self->{real_serverpid}); 20813d40330Schristos die "unable to connect: $err\n"; 20913d40330Schristos } 21013d40330Schristos 21113d40330Schristos $self->{server_sock} = $sock; 21213d40330Schristos} 21313d40330Schristos 21453060421Schristossub start 21553060421Schristos{ 21653060421Schristos my ($self) = shift; 21753060421Schristos my $pid; 21853060421Schristos 21953060421Schristos if ($self->{proxy_sock} == 0) { 22053060421Schristos return 0; 22153060421Schristos } 22253060421Schristos 22353060421Schristos my $execcmd = $self->execute 22413d40330Schristos ." s_server -max_protocol TLSv1.3 -no_comp -rev -engine ossltest" 22513d40330Schristos #In TLSv1.3 we issue two session tickets. The default session id 22613d40330Schristos #callback gets confused because the ossltest engine causes the same 22713d40330Schristos #session id to be created twice due to the changed random number 22813d40330Schristos #generation. Using "-ext_cache" replaces the default callback with a 22913d40330Schristos #different one that doesn't get confused. 23013d40330Schristos ." -ext_cache" 23113d40330Schristos ." -accept $self->{server_addr}:0" 23213d40330Schristos ." -cert ".$self->cert." -cert2 ".$self->cert 23313d40330Schristos ." -naccept ".$self->serverconnects; 23453060421Schristos if ($self->ciphers ne "") { 23553060421Schristos $execcmd .= " -cipher ".$self->ciphers; 23653060421Schristos } 23713d40330Schristos if ($self->ciphersuitess ne "") { 23813d40330Schristos $execcmd .= " -ciphersuites ".$self->ciphersuitess; 23913d40330Schristos } 24053060421Schristos if ($self->serverflags ne "") { 24153060421Schristos $execcmd .= " ".$self->serverflags; 24253060421Schristos } 24353060421Schristos if ($self->debug) { 24453060421Schristos print STDERR "Server command: $execcmd\n"; 24553060421Schristos } 24613d40330Schristos 24713d40330Schristos open(my $savedin, "<&STDIN"); 24813d40330Schristos 24913d40330Schristos # Temporarily replace STDIN so that sink process can inherit it... 25013d40330Schristos $pid = open(STDIN, "$execcmd 2>&1 |") or die "Failed to $execcmd: $!\n"; 25113d40330Schristos $self->{real_serverpid} = $pid; 25213d40330Schristos 25313d40330Schristos # Process the output from s_server until we find the ACCEPT line, which 25413d40330Schristos # tells us what the accepting address and port are. 25513d40330Schristos while (<>) { 25613d40330Schristos print; 25713d40330Schristos s/\R$//; # Better chomp 25813d40330Schristos next unless (/^ACCEPT\s.*:(\d+)$/); 25913d40330Schristos $self->{server_port} = $1; 26013d40330Schristos last; 26153060421Schristos } 26213d40330Schristos 26313d40330Schristos if ($self->{server_port} == 0) { 26413d40330Schristos # This actually means that s_server exited, because otherwise 26513d40330Schristos # we would still searching for ACCEPT... 26613d40330Schristos waitpid($pid, 0); 26713d40330Schristos die "no ACCEPT detected in '$execcmd' output: $?\n"; 26813d40330Schristos } 26913d40330Schristos 27013d40330Schristos # Just make sure everything else is simply printed [as separate lines]. 27113d40330Schristos # The sub process simply inherits our STD* and will keep consuming 27213d40330Schristos # server's output and printing it as long as there is anything there, 27313d40330Schristos # out of our way. 27413d40330Schristos my $error; 27513d40330Schristos $pid = undef; 27613d40330Schristos if (eval { require Win32::Process; 1; }) { 27713d40330Schristos if (Win32::Process::Create(my $h, $^X, "perl -ne print", 0, 0, ".")) { 27813d40330Schristos $pid = $h->GetProcessID(); 27913d40330Schristos $self->{proc_handle} = $h; # hold handle till next round [or exit] 28013d40330Schristos } else { 28113d40330Schristos $error = Win32::FormatMessage(Win32::GetLastError()); 28213d40330Schristos } 28313d40330Schristos } else { 28413d40330Schristos if (defined($pid = fork)) { 28513d40330Schristos $pid or exec("$^X -ne print") or exit($!); 28613d40330Schristos } else { 28713d40330Schristos $error = $!; 28813d40330Schristos } 28913d40330Schristos } 29013d40330Schristos 29113d40330Schristos # Change back to original stdin 29213d40330Schristos open(STDIN, "<&", $savedin); 29313d40330Schristos close($savedin); 29413d40330Schristos 29513d40330Schristos if (!defined($pid)) { 29613d40330Schristos kill(3, $self->{real_serverpid}); 29713d40330Schristos die "Failed to capture s_server's output: $error\n"; 29813d40330Schristos } 29913d40330Schristos 30013d40330Schristos $self->{serverpid} = $pid; 30113d40330Schristos 30213d40330Schristos print STDERR "Server responds on ", 30313d40330Schristos "$self->{server_addr}:$self->{server_port}\n"; 30413d40330Schristos 30513d40330Schristos # Connect right away... 30613d40330Schristos $self->connect_to_server(); 30753060421Schristos 30853060421Schristos return $self->clientstart; 30953060421Schristos} 31053060421Schristos 31153060421Schristossub clientstart 31253060421Schristos{ 31353060421Schristos my ($self) = shift; 31453060421Schristos 31553060421Schristos if ($self->execute) { 31613d40330Schristos my $pid; 31713d40330Schristos my $execcmd = $self->execute 31813d40330Schristos ." s_client -max_protocol TLSv1.3 -engine ossltest" 31913d40330Schristos ." -connect $self->{proxy_addr}:$self->{proxy_port}"; 32053060421Schristos if ($self->cipherc ne "") { 32153060421Schristos $execcmd .= " -cipher ".$self->cipherc; 32253060421Schristos } 32313d40330Schristos if ($self->ciphersuitesc ne "") { 32413d40330Schristos $execcmd .= " -ciphersuites ".$self->ciphersuitesc; 32513d40330Schristos } 32653060421Schristos if ($self->clientflags ne "") { 32753060421Schristos $execcmd .= " ".$self->clientflags; 32853060421Schristos } 32913d40330Schristos if ($self->clientflags !~ m/-(no)?servername/) { 33013d40330Schristos $execcmd .= " -servername localhost"; 33113d40330Schristos } 33213d40330Schristos if (defined $self->sessionfile) { 33313d40330Schristos $execcmd .= " -ign_eof"; 33413d40330Schristos } 33553060421Schristos if ($self->debug) { 33653060421Schristos print STDERR "Client command: $execcmd\n"; 33753060421Schristos } 33813d40330Schristos 33913d40330Schristos open(my $savedout, ">&STDOUT"); 34013d40330Schristos # If we open pipe with new descriptor, attempt to close it, 34113d40330Schristos # explicitly or implicitly, would incur waitpid and effectively 34213d40330Schristos # dead-lock... 34313d40330Schristos if (!($pid = open(STDOUT, "| $execcmd"))) { 34413d40330Schristos my $err = $!; 34513d40330Schristos kill(3, $self->{real_serverpid}); 34613d40330Schristos die "Failed to $execcmd: $err\n"; 34753060421Schristos } 34813d40330Schristos $self->{clientpid} = $pid; 34913d40330Schristos 35013d40330Schristos # queue [magic] input 35113d40330Schristos print $self->reneg ? "R" : "test"; 35213d40330Schristos 35313d40330Schristos # this closes client's stdin without waiting for its pid 35413d40330Schristos open(STDOUT, ">&", $savedout); 35513d40330Schristos close($savedout); 35653060421Schristos } 35753060421Schristos 35853060421Schristos # Wait for incoming connection from client 35913d40330Schristos my $fdset = IO::Select->new($self->{proxy_sock}); 36013d40330Schristos if (!$fdset->can_read(60)) { 36113d40330Schristos kill(3, $self->{real_serverpid}); 36213d40330Schristos die "s_client didn't try to connect\n"; 36313d40330Schristos } 36413d40330Schristos 36553060421Schristos my $client_sock; 36653060421Schristos if(!($client_sock = $self->{proxy_sock}->accept())) { 36753060421Schristos warn "Failed accepting incoming connection: $!\n"; 36853060421Schristos return 0; 36953060421Schristos } 37053060421Schristos 37153060421Schristos print "Connection opened\n"; 37253060421Schristos 37313d40330Schristos my $server_sock = $self->{server_sock}; 37453060421Schristos my $indata; 37553060421Schristos 37653060421Schristos #Wait for either the server socket or the client socket to become readable 37713d40330Schristos $fdset = IO::Select->new($server_sock, $client_sock); 37853060421Schristos my @ready; 37913d40330Schristos my $ctr = 0; 38053060421Schristos local $SIG{PIPE} = "IGNORE"; 38113d40330Schristos $self->{saw_session_ticket} = undef; 38213d40330Schristos while($fdset->count && $ctr < 10) { 38313d40330Schristos if (defined($self->{sessionfile})) { 38413d40330Schristos # s_client got -ign_eof and won't be exiting voluntarily, so we 38513d40330Schristos # look for data *and* session ticket... 38613d40330Schristos last if TLSProxy::Message->success() 38713d40330Schristos && $self->{saw_session_ticket}; 38813d40330Schristos } 38913d40330Schristos if (!(@ready = $fdset->can_read(1))) { 39013d40330Schristos $ctr++; 39113d40330Schristos next; 39213d40330Schristos } 39353060421Schristos foreach my $hand (@ready) { 39453060421Schristos if ($hand == $server_sock) { 39513d40330Schristos if ($server_sock->sysread($indata, 16384)) { 39613d40330Schristos if ($indata = $self->process_packet(1, $indata)) { 39713d40330Schristos $client_sock->syswrite($indata) or goto END; 39813d40330Schristos } 39913d40330Schristos $ctr = 0; 40053060421Schristos } else { 40113d40330Schristos $fdset->remove($server_sock); 40213d40330Schristos $client_sock->shutdown(SHUT_WR); 40313d40330Schristos } 40413d40330Schristos } elsif ($hand == $client_sock) { 40513d40330Schristos if ($client_sock->sysread($indata, 16384)) { 40613d40330Schristos if ($indata = $self->process_packet(0, $indata)) { 40713d40330Schristos $server_sock->syswrite($indata) or goto END; 40813d40330Schristos } 40913d40330Schristos $ctr = 0; 41013d40330Schristos } else { 41113d40330Schristos $fdset->remove($client_sock); 41213d40330Schristos $server_sock->shutdown(SHUT_WR); 41313d40330Schristos } 41413d40330Schristos } else { 41513d40330Schristos kill(3, $self->{real_serverpid}); 41613d40330Schristos die "Unexpected handle"; 41753060421Schristos } 41853060421Schristos } 41953060421Schristos } 42053060421Schristos 42113d40330Schristos if ($ctr >= 10) { 42213d40330Schristos kill(3, $self->{real_serverpid}); 42313d40330Schristos die "No progress made"; 42413d40330Schristos } 42513d40330Schristos 42653060421Schristos END: 42753060421Schristos print "Connection closed\n"; 42853060421Schristos if($server_sock) { 42953060421Schristos $server_sock->close(); 43013d40330Schristos $self->{server_sock} = undef; 43153060421Schristos } 43253060421Schristos if($client_sock) { 43353060421Schristos #Closing this also kills the child process 43453060421Schristos $client_sock->close(); 43553060421Schristos } 43613d40330Schristos 43713d40330Schristos my $pid; 43813d40330Schristos if (--$self->{serverconnects} == 0) { 43913d40330Schristos $pid = $self->{serverpid}; 44013d40330Schristos print "Waiting for 'perl -ne print' process to close: $pid...\n"; 44113d40330Schristos $pid = waitpid($pid, 0); 44213d40330Schristos if ($pid > 0) { 44313d40330Schristos die "exit code $? from 'perl -ne print' process\n" if $? != 0; 44413d40330Schristos } elsif ($pid == 0) { 44513d40330Schristos kill(3, $self->{real_serverpid}); 44613d40330Schristos die "lost control over $self->{serverpid}?"; 44753060421Schristos } 44813d40330Schristos $pid = $self->{real_serverpid}; 44913d40330Schristos print "Waiting for s_server process to close: $pid...\n"; 45013d40330Schristos # it's done already, just collect the exit code [and reap]... 45113d40330Schristos waitpid($pid, 0); 45213d40330Schristos die "exit code $? from s_server process\n" if $? != 0; 45353060421Schristos } else { 45413d40330Schristos # It's a bit counter-intuitive spot to make next connection to 45513d40330Schristos # the s_server. Rationale is that established connection works 4567d004720Schristos # as synchronization point, in sense that this way we know that 45713d40330Schristos # s_server is actually done with current session... 45813d40330Schristos $self->connect_to_server(); 45953060421Schristos } 46013d40330Schristos $pid = $self->{clientpid}; 46113d40330Schristos print "Waiting for s_client process to close: $pid...\n"; 46213d40330Schristos waitpid($pid, 0); 46353060421Schristos 46453060421Schristos return 1; 46553060421Schristos} 46653060421Schristos 46753060421Schristossub process_packet 46853060421Schristos{ 46953060421Schristos my ($self, $server, $packet) = @_; 47053060421Schristos my $len_real; 47153060421Schristos my $decrypt_len; 47253060421Schristos my $data; 47353060421Schristos my $recnum; 47453060421Schristos 47553060421Schristos if ($server) { 47653060421Schristos print "Received server packet\n"; 47753060421Schristos } else { 47853060421Schristos print "Received client packet\n"; 47953060421Schristos } 48053060421Schristos 48153060421Schristos if ($self->{direction} != $server) { 48253060421Schristos $self->{flight} = $self->{flight} + 1; 48353060421Schristos $self->{direction} = $server; 48453060421Schristos } 48553060421Schristos 48653060421Schristos print "Packet length = ".length($packet)."\n"; 48753060421Schristos print "Processing flight ".$self->flight."\n"; 48853060421Schristos 48953060421Schristos #Return contains the list of record found in the packet followed by the 49053060421Schristos #list of messages in those records and any partial message 49113d40330Schristos my @ret = TLSProxy::Record->get_records($server, $self->flight, 49213d40330Schristos $self->{partial}[$server].$packet); 49353060421Schristos $self->{partial}[$server] = $ret[2]; 49413d40330Schristos push @{$self->{record_list}}, @{$ret[0]}; 49553060421Schristos push @{$self->{message_list}}, @{$ret[1]}; 49653060421Schristos 49753060421Schristos print "\n"; 49853060421Schristos 49953060421Schristos if (scalar(@{$ret[0]}) == 0 or length($ret[2]) != 0) { 50053060421Schristos return ""; 50153060421Schristos } 50253060421Schristos 50353060421Schristos #Finished parsing. Call user provided filter here 50453060421Schristos if (defined $self->filter) { 50553060421Schristos $self->filter->($self); 50653060421Schristos } 50753060421Schristos 50813d40330Schristos #Take a note on NewSessionTicket 50913d40330Schristos foreach my $message (reverse @{$self->{message_list}}) { 51013d40330Schristos if ($message->{mt} == TLSProxy::Message::MT_NEW_SESSION_TICKET) { 51113d40330Schristos $self->{saw_session_ticket} = 1; 51213d40330Schristos last; 51313d40330Schristos } 51413d40330Schristos } 51513d40330Schristos 51653060421Schristos #Reconstruct the packet 51753060421Schristos $packet = ""; 51853060421Schristos foreach my $record (@{$self->record_list}) { 51913d40330Schristos $packet .= $record->reconstruct_record($server); 52053060421Schristos } 52153060421Schristos 52253060421Schristos print "Forwarded packet length = ".length($packet)."\n\n"; 52353060421Schristos 52453060421Schristos return $packet; 52553060421Schristos} 52653060421Schristos 52753060421Schristos#Read accessors 52853060421Schristossub execute 52953060421Schristos{ 53053060421Schristos my $self = shift; 53153060421Schristos return $self->{execute}; 53253060421Schristos} 53353060421Schristossub cert 53453060421Schristos{ 53553060421Schristos my $self = shift; 53653060421Schristos return $self->{cert}; 53753060421Schristos} 53853060421Schristossub debug 53953060421Schristos{ 54053060421Schristos my $self = shift; 54153060421Schristos return $self->{debug}; 54253060421Schristos} 54353060421Schristossub flight 54453060421Schristos{ 54553060421Schristos my $self = shift; 54653060421Schristos return $self->{flight}; 54753060421Schristos} 54853060421Schristossub record_list 54953060421Schristos{ 55053060421Schristos my $self = shift; 55153060421Schristos return $self->{record_list}; 55253060421Schristos} 55353060421Schristossub success 55453060421Schristos{ 55553060421Schristos my $self = shift; 55653060421Schristos return $self->{success}; 55753060421Schristos} 55853060421Schristossub end 55953060421Schristos{ 56053060421Schristos my $self = shift; 56153060421Schristos return $self->{end}; 56253060421Schristos} 56353060421Schristossub supports_IPv6 56453060421Schristos{ 56553060421Schristos my $self = shift; 56653060421Schristos return $have_IPv6; 56753060421Schristos} 56853060421Schristossub proxy_addr 56953060421Schristos{ 57053060421Schristos my $self = shift; 57153060421Schristos return $self->{proxy_addr}; 57253060421Schristos} 57353060421Schristossub proxy_port 57453060421Schristos{ 57553060421Schristos my $self = shift; 57653060421Schristos return $self->{proxy_port}; 57753060421Schristos} 57853060421Schristossub server_addr 57953060421Schristos{ 58053060421Schristos my $self = shift; 58153060421Schristos return $self->{server_addr}; 58253060421Schristos} 58353060421Schristossub server_port 58453060421Schristos{ 58553060421Schristos my $self = shift; 58653060421Schristos return $self->{server_port}; 58753060421Schristos} 58813d40330Schristossub serverpid 58913d40330Schristos{ 59013d40330Schristos my $self = shift; 59113d40330Schristos return $self->{serverpid}; 59213d40330Schristos} 59313d40330Schristossub clientpid 59413d40330Schristos{ 59513d40330Schristos my $self = shift; 59613d40330Schristos return $self->{clientpid}; 59713d40330Schristos} 59813d40330Schristos 59913d40330Schristos#Read/write accessors 60053060421Schristossub filter 60153060421Schristos{ 60253060421Schristos my $self = shift; 60353060421Schristos if (@_) { 60453060421Schristos $self->{filter} = shift; 60553060421Schristos } 60653060421Schristos return $self->{filter}; 60753060421Schristos} 60853060421Schristossub cipherc 60953060421Schristos{ 61053060421Schristos my $self = shift; 61153060421Schristos if (@_) { 61253060421Schristos $self->{cipherc} = shift; 61353060421Schristos } 61453060421Schristos return $self->{cipherc}; 61553060421Schristos} 61613d40330Schristossub ciphersuitesc 61713d40330Schristos{ 61813d40330Schristos my $self = shift; 61913d40330Schristos if (@_) { 62013d40330Schristos $self->{ciphersuitesc} = shift; 62113d40330Schristos } 62213d40330Schristos return $self->{ciphersuitesc}; 62313d40330Schristos} 62453060421Schristossub ciphers 62553060421Schristos{ 62653060421Schristos my $self = shift; 62753060421Schristos if (@_) { 62853060421Schristos $self->{ciphers} = shift; 62953060421Schristos } 63053060421Schristos return $self->{ciphers}; 63153060421Schristos} 63213d40330Schristossub ciphersuitess 63313d40330Schristos{ 63413d40330Schristos my $self = shift; 63513d40330Schristos if (@_) { 63613d40330Schristos $self->{ciphersuitess} = shift; 63713d40330Schristos } 63813d40330Schristos return $self->{ciphersuitess}; 63913d40330Schristos} 64053060421Schristossub serverflags 64153060421Schristos{ 64253060421Schristos my $self = shift; 64353060421Schristos if (@_) { 64453060421Schristos $self->{serverflags} = shift; 64553060421Schristos } 64653060421Schristos return $self->{serverflags}; 64753060421Schristos} 64853060421Schristossub clientflags 64953060421Schristos{ 65053060421Schristos my $self = shift; 65153060421Schristos if (@_) { 65253060421Schristos $self->{clientflags} = shift; 65353060421Schristos } 65453060421Schristos return $self->{clientflags}; 65553060421Schristos} 65653060421Schristossub serverconnects 65753060421Schristos{ 65853060421Schristos my $self = shift; 65953060421Schristos if (@_) { 66053060421Schristos $self->{serverconnects} = shift; 66153060421Schristos } 66253060421Schristos return $self->{serverconnects}; 66353060421Schristos} 66453060421Schristos# This is a bit ugly because the caller is responsible for keeping the records 66553060421Schristos# in sync with the updated message list; simply updating the message list isn't 66653060421Schristos# sufficient to get the proxy to forward the new message. 66753060421Schristos# But it does the trick for the one test (test_sslsessiontick) that needs it. 66853060421Schristossub message_list 66953060421Schristos{ 67053060421Schristos my $self = shift; 67153060421Schristos if (@_) { 67253060421Schristos $self->{message_list} = shift; 67353060421Schristos } 67453060421Schristos return $self->{message_list}; 67553060421Schristos} 67653060421Schristos 67753060421Schristossub fill_known_data 67853060421Schristos{ 67953060421Schristos my $length = shift; 68053060421Schristos my $ret = ""; 68153060421Schristos for (my $i = 0; $i < $length; $i++) { 68253060421Schristos $ret .= chr($i); 68353060421Schristos } 68453060421Schristos return $ret; 68553060421Schristos} 68653060421Schristos 68713d40330Schristossub is_tls13 68813d40330Schristos{ 68913d40330Schristos my $class = shift; 69013d40330Schristos if (@_) { 69113d40330Schristos $is_tls13 = shift; 69213d40330Schristos } 69313d40330Schristos return $is_tls13; 69413d40330Schristos} 69513d40330Schristos 69653060421Schristossub reneg 69753060421Schristos{ 69853060421Schristos my $self = shift; 69953060421Schristos if (@_) { 70053060421Schristos $self->{reneg} = shift; 70153060421Schristos } 70253060421Schristos return $self->{reneg}; 70353060421Schristos} 70453060421Schristos 70513d40330Schristos#Setting a sessionfile means that the client will not close until the given 70613d40330Schristos#file exists. This is useful in TLSv1.3 where otherwise s_client will close 70713d40330Schristos#immediately at the end of the handshake, but before the session has been 70813d40330Schristos#received from the server. A side effect of this is that s_client never sends 70913d40330Schristos#a close_notify, so instead we consider success to be when it sends application 71013d40330Schristos#data over the connection. 71113d40330Schristossub sessionfile 71213d40330Schristos{ 71313d40330Schristos my $self = shift; 71413d40330Schristos if (@_) { 71513d40330Schristos $self->{sessionfile} = shift; 71613d40330Schristos TLSProxy::Message->successondata(1); 71713d40330Schristos } 71813d40330Schristos return $self->{sessionfile}; 71913d40330Schristos} 72013d40330Schristos 72113d40330Schristossub ciphersuite 72213d40330Schristos{ 72313d40330Schristos my $class = shift; 72413d40330Schristos if (@_) { 72513d40330Schristos $ciphersuite = shift; 72613d40330Schristos } 72713d40330Schristos return $ciphersuite; 72813d40330Schristos} 72913d40330Schristos 73053060421Schristos1; 731