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