Handle localhost being either 127.0.0.1 or ::1
[openssl.git] / util / TLSProxy / Proxy.pm
index aaeea28c9850aa31de361e8b1f8b1b3cf1ae8160..283c76564f8b79f828dfb774356dde0b512abceb 100644 (file)
@@ -65,6 +65,9 @@ use TLSProxy::ServerHello;
 use TLSProxy::ServerKeyExchange;
 use TLSProxy::NewSessionTicket;
 
+my $have_IPv6 = 0;
+my $IP_factory;
+
 sub new
 {
     my $class = shift;
@@ -93,10 +96,45 @@ sub new
         flight => 0,
         record_list => [],
         message_list => [],
+    };
 
-        #Private
-        message_rec_list => []
+    eval {
+        require IO::Socket::IP;
+        my $s = IO::Socket::IP->new(
+            LocalAddr => "::1",
+            LocalPort => 0,
+            Listen=>1,
+            );
+        $s or die "\n";
+        $s->close();
     };
+    if ($@ eq "") {
+        # IO::Socket::IP supports IPv6 and is in the core modules list
+        $IP_factory = sub { IO::Socket::IP->new(@_); };
+        $have_IPv6 = 1;
+    } else {
+        eval {
+            require IO::Socket::INET6;
+            my $s = IO::Socket::INET6->new(
+                LocalAddr => "::1",
+                LocalPort => 0,
+                Listen=>1,
+                );
+            $s or die "\n";
+            $s->close();
+        };
+        if ($@ eq "") {
+            # IO::Socket::INET6 supports IPv6 but isn't on the core modules list
+            # However, it's a bit older and said to be more widely deployed
+            # at the time of writing this comment.
+            $IP_factory = sub { IO::Socket::INET6->new(@_); };
+            $have_IPv6 = 1;
+        } else {
+            # IO::Socket::INET doesn't support IPv6 but is a fallback in case
+            # we have no other.
+            $IP_factory = sub { IO::Socket::INET->new(@_); };
+        }
+    }
 
     return bless $self, $class;
 }
@@ -110,7 +148,6 @@ sub clear
     $self->{flight} = 0;
     $self->{record_list} = [];
     $self->{message_list} = [];
-    $self->{message_rec_list} = [];
     $self->{serverflags} = "";
     $self->{clientflags} = "";
     $self->{serverconnects} = 1;
@@ -143,10 +180,10 @@ sub start
     $pid = fork();
     if ($pid == 0) {
         open(STDOUT, ">", File::Spec->devnull())
-            or die "Failed to redirect stdout";
+            or die "Failed to redirect stdout: $!";
         open(STDERR, ">&STDOUT");
         my $execcmd = $self->execute
-            ." s_server -no_comp -rev -engine ossltest -accept "
+            ." s_server -rev -engine ossltest -accept "
             .($self->server_port)
             ." -cert ".$self->cert." -naccept ".$self->serverconnects;
         if ($self->ciphers ne "") {
@@ -172,25 +209,27 @@ sub clientstart
     }
 
     # Create the Proxy socket
-    my $proxy_sock = new IO::Socket::INET(
-        LocalHost   => $self->proxy_addr,
+    my $proxaddr = $self->proxy_addr;
+    $proxaddr =~ s/[\[\]]//g; # Remove [ and ]
+    my $proxy_sock = $IP_factory->(
+        LocalHost   => $proxaddr,
         LocalPort   => $self->proxy_port,
         Proto       => "tcp",
         Listen      => SOMAXCONN,
-        Reuse       => 1
+        ReuseAddr   => 1
     );
 
     if ($proxy_sock) {
         print "Proxy started on port ".$self->proxy_port."\n";
     } else {
-        die "Failed creating proxy socket\n";
+        die "Failed creating proxy socket (".$proxaddr.",".$self->proxy_port."): $!\n";
     }
 
     if ($self->execute) {
         my $pid = fork();
         if ($pid == 0) {
             open(STDOUT, ">", File::Spec->devnull())
-                or die "Failed to redirect stdout";
+                or die "Failed to redirect stdout: $!";
             open(STDERR, ">&STDOUT");
             my $execcmd = "echo test | ".$self->execute
                  ." s_client -engine ossltest -connect "
@@ -206,8 +245,8 @@ sub clientstart
     }
 
     # Wait for incoming connection from client
-    my $client_sock = $proxy_sock->accept() 
-        or die "Failed accepting incoming connection\n";
+    my $client_sock = $proxy_sock->accept()
+        or die "Failed accepting incoming connection: $!\n";
 
     print "Connection opened\n";
 
@@ -217,11 +256,14 @@ sub clientstart
     #We loop over this a few times because sometimes s_server can take a while
     #to start up
     do {
-        $server_sock = new IO::Socket::INET(
-            PeerAddr => $self->server_addr,
+        my $servaddr = $self->server_addr;
+        $servaddr =~ s/[\[\]]//g; # Remove [ and ]
+        $server_sock = $IP_factory->(
+            PeerAddr => $servaddr,
             PeerPort => $self->server_port,
+            MultiHomed => 1,
             Proto => 'tcp'
-        ); 
+        );
 
         $retry--;
         if (!$server_sock) {
@@ -229,7 +271,7 @@ sub clientstart
                 #Sleep for a short while
                 select(undef, undef, undef, 0.1);
             } else {
-                die "Failed to start up server\n";
+                die "Failed to start up server (".$servaddr.",".$self->server_port."): $!\n";
             }
         }
     } while (!$server_sock);
@@ -274,7 +316,6 @@ sub clientstart
     }
 }
 
-
 sub process_packet
 {
     my ($self, $server, $packet) = @_;
@@ -296,7 +337,6 @@ sub process_packet
     #list of messages in those records
     my @ret = TLSProxy::Record->get_records($server, $self->flight, $packet);
     push @{$self->record_list}, @{$ret[0]};
-    $self->{message_rec_list} = $ret[0];
     push @{$self->{message_list}}, @{$ret[1]};
 
     print "\n";
@@ -349,11 +389,6 @@ sub record_list
     my $self = shift;
     return $self->{record_list};
 }
-sub message_list
-{
-    my $self = shift;
-    return $self->{message_list};
-}
 sub success
 {
     my $self = shift;
@@ -364,6 +399,11 @@ sub end
     my $self = shift;
     return $self->{end};
 }
+sub supports_IPv6
+{
+    my $self = shift;
+    return $have_IPv6;
+}
 
 #Read/write accessors
 sub proxy_addr
@@ -446,4 +486,16 @@ sub serverconnects
     }
     return $self->{serverconnects};
 }
+# This is a bit ugly because the caller is responsible for keeping the records
+# in sync with the updated message list; simply updating the message list isn't
+# sufficient to get the proxy to forward the new message.
+# But it does the trick for the one test (test_sslsessiontick) that needs it.
+sub message_list
+{
+    my $self = shift;
+    if (@_) {
+        $self->{message_list} = shift;
+    }
+    return $self->{message_list};
+}
 1;