1 # Written by Matt Caswell for the OpenSSL project.
2 # ====================================================================
3 # Copyright (c) 1998-2015 The OpenSSL Project. All rights reserved.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in
14 # the documentation and/or other materials provided with the
17 # 3. All advertising materials mentioning features or use of this
18 # software must display the following acknowledgment:
19 # "This product includes software developed by the OpenSSL Project
20 # for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
22 # 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
23 # endorse or promote products derived from this software without
24 # prior written permission. For written permission, please contact
25 # openssl-core@openssl.org.
27 # 5. Products derived from this software may not be called "OpenSSL"
28 # nor may "OpenSSL" appear in their names without prior written
29 # permission of the OpenSSL Project.
31 # 6. Redistributions of any form whatsoever must retain the following
33 # "This product includes software developed by the OpenSSL Project
34 # for use in the OpenSSL Toolkit (http://www.openssl.org/)"
36 # THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
37 # EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
40 # ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
42 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
43 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
45 # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
47 # OF THE POSSIBILITY OF SUCH DAMAGE.
48 # ====================================================================
50 # This product includes cryptographic software written by Eric Young
51 # (eay@cryptsoft.com). This product includes software written by Tim
52 # Hudson (tjh@cryptsoft.com).
55 use POSIX ":sys_wait_h";
57 package TLSProxy::Proxy;
63 use TLSProxy::Message;
64 use TLSProxy::ClientHello;
65 use TLSProxy::ServerHello;
66 use TLSProxy::ServerKeyExchange;
67 use TLSProxy::NewSessionTicket;
82 proxy_addr => "localhost",
84 server_addr => "localhost",
97 ciphers => "AES128-SHA",
103 # IO::Socket::IP is on the core module list, IO::Socket::INET6 isn't.
104 # However, IO::Socket::INET6 is older and is said to be more widely
105 # deployed for the moment, and may have less bugs, so we try the latter
106 # first, then fall back on the code modules. Worst case scenario, we
107 # fall back to IO::Socket::INET, only supports IPv4.
109 require IO::Socket::INET6;
110 my $s = IO::Socket::INET6->new(
119 $IP_factory = sub { IO::Socket::INET6->new(@_); };
123 require IO::Socket::IP;
124 my $s = IO::Socket::IP->new(
133 $IP_factory = sub { IO::Socket::IP->new(@_); };
136 $IP_factory = sub { IO::Socket::INET->new(@_); };
140 return bless $self, $class;
147 $self->{cipherc} = "";
149 $self->{record_list} = [];
150 $self->{message_list} = [];
151 $self->{clientflags} = "";
153 TLSProxy::Message->clear();
154 TLSProxy::Record->clear();
162 $self->{ciphers} = "AES128-SHA";
163 $self->{serverflags} = "";
164 $self->{serverconnects} = 1;
165 $self->{serverpid} = 0;
192 open(STDOUT, ">", File::Spec->devnull())
193 or die "Failed to redirect stdout: $!";
194 open(STDERR, ">&STDOUT");
196 my $execcmd = $self->execute
197 ." s_server -no_comp -rev -engine ossltest -accept "
198 .($self->server_port)
199 ." -cert ".$self->cert." -naccept ".$self->serverconnects;
200 if ($self->ciphers ne "") {
201 $execcmd .= " -cipher ".$self->ciphers;
203 if ($self->serverflags ne "") {
204 $execcmd .= " ".$self->serverflags;
208 $self->serverpid($pid);
219 open DEVNULL, ">", File::Spec->devnull();
220 $oldstdout = select(DEVNULL);
223 # Create the Proxy socket
224 my $proxaddr = $self->proxy_addr;
225 $proxaddr =~ s/[\[\]]//g; # Remove [ and ]
226 my $proxy_sock = $IP_factory->(
227 LocalHost => $proxaddr,
228 LocalPort => $self->proxy_port,
235 print "Proxy started on port ".$self->proxy_port."\n";
237 die "Failed creating proxy socket (".$proxaddr.",".$self->proxy_port."): $!\n";
240 if ($self->execute) {
244 open(STDOUT, ">", File::Spec->devnull())
245 or die "Failed to redirect stdout: $!";
246 open(STDERR, ">&STDOUT");
248 my $execcmd = "echo test | ".$self->execute
249 ." s_client -engine ossltest -connect "
250 .($self->proxy_addr).":".($self->proxy_port);
251 if ($self->cipherc ne "") {
252 $execcmd .= " -cipher ".$self->cipherc;
254 if ($self->clientflags ne "") {
255 $execcmd .= " ".$self->clientflags;
261 # Wait for incoming connection from client
262 my $client_sock = $proxy_sock->accept()
263 or die "Failed accepting incoming connection: $!\n";
265 print "Connection opened\n";
267 # Now connect to the server
270 #We loop over this a few times because sometimes s_server can take a while
273 my $servaddr = $self->server_addr;
274 $servaddr =~ s/[\[\]]//g; # Remove [ and ]
275 $server_sock = $IP_factory->(
276 PeerAddr => $servaddr,
277 PeerPort => $self->server_port,
283 if ($@ || !defined($server_sock)) {
284 $server_sock->close() if defined($server_sock);
287 #Sleep for a short while
288 select(undef, undef, undef, 0.1);
290 die "Failed to start up server (".$servaddr.",".$self->server_port."): $!\n";
293 } while (!$server_sock);
295 my $sel = IO::Select->new($server_sock, $client_sock);
297 my @handles = ($server_sock, $client_sock);
299 #Wait for either the server socket or the client socket to become readable
301 while(!(TLSProxy::Message->end) && (@ready = $sel->can_read)) {
302 foreach my $hand (@ready) {
303 if ($hand == $server_sock) {
304 $server_sock->sysread($indata, 16384) or goto END;
305 $indata = $self->process_packet(1, $indata);
306 $client_sock->syswrite($indata);
307 } elsif ($hand == $client_sock) {
308 $client_sock->sysread($indata, 16384) or goto END;
309 $indata = $self->process_packet(0, $indata);
310 $server_sock->syswrite($indata);
319 print "Connection closed\n";
321 $server_sock->close();
324 #Closing this also kills the child process
325 $client_sock->close();
328 $proxy_sock->close();
333 $self->serverconnects($self->serverconnects - 1);
334 if ($self->serverconnects == 0) {
335 die "serverpid is zero\n" if $self->serverpid == 0;
336 print "Waiting for server process to close: "
337 .$self->serverpid."\n";
338 waitpid( $self->serverpid, 0);
344 my ($self, $server, $packet) = @_;
351 print "Received server packet\n";
353 print "Received client packet\n";
356 print "Packet length = ".length($packet)."\n";
357 print "Processing flight ".$self->flight."\n";
359 #Return contains the list of record found in the packet followed by the
360 #list of messages in those records
361 my @ret = TLSProxy::Record->get_records($server, $self->flight, $packet);
362 push @{$self->record_list}, @{$ret[0]};
363 push @{$self->{message_list}}, @{$ret[1]};
367 #Finished parsing. Call user provided filter here
368 if(defined $self->filter) {
369 $self->filter->($self);
372 #Reconstruct the packet
374 foreach my $record (@{$self->record_list}) {
375 #We only replay the records for the current flight
376 if ($record->flight != $self->flight) {
379 $packet .= $record->reconstruct_record();
382 $self->{flight} = $self->{flight} + 1;
384 print "Forwarded packet length = ".length($packet)."\n\n";
393 return $self->{execute};
398 return $self->{cert};
403 return $self->{debug};
408 return $self->{flight};
413 return $self->{record_list};
418 return $self->{success};
431 #Read/write accessors
436 $self->{proxy_addr} = shift;
438 return $self->{proxy_addr};
444 $self->{proxy_port} = shift;
446 return $self->{proxy_port};
452 $self->{server_addr} = shift;
454 return $self->{server_addr};
460 $self->{server_port} = shift;
462 return $self->{server_port};
468 $self->{filter} = shift;
470 return $self->{filter};
476 $self->{cipherc} = shift;
478 return $self->{cipherc};
484 $self->{ciphers} = shift;
486 return $self->{ciphers};
492 $self->{serverflags} = shift;
494 return $self->{serverflags};
500 $self->{clientflags} = shift;
502 return $self->{clientflags};
508 $self->{serverconnects} = shift;
510 return $self->{serverconnects};
512 # This is a bit ugly because the caller is responsible for keeping the records
513 # in sync with the updated message list; simply updating the message list isn't
514 # sufficient to get the proxy to forward the new message.
515 # But it does the trick for the one test (test_sslsessiontick) that needs it.
520 $self->{message_list} = shift;
522 return $self->{message_list};
528 $self->{serverpid} = shift;
530 return $self->{serverpid};