> Can you investigate this some more?  I originally tried to use
> Net::SSLeay's accept() and connect() in non-blocking mode, but that
> broke the SSL session negotiation.
>
> While accept() and connect() follow the BSD sockets interface,
> Net::SSLeay expects them to send and receive data before they return.
> In O_NONBLOCK mode, accept() and connect() return before the SSL session
> is fully established, and I don't know how to resume that.

I just tried with nonblocking and it worked fine with your testsuite
(app-{server|client}.perl)..

I just check the error returned from accept() and call it again. Seems
like the internal buffers in OpenSSL does the right thing..


--- SslServer.pm.orig   Wed Jan 14 17:58:55 2004
+++ SslServer.pm        Tue Jan 20 15:06:28 2004
@@ -15,11 +15,9 @@
 use vars qw(@ISA);
 @ISA = qw(SslClient);
 
-use Net::SSLeay qw(die_if_ssl_error);
+use Net::SSLeay qw(die_if_ssl_error ERROR_WANT_READ);
 use POSIX qw(F_GETFL F_SETFL O_NONBLOCK EAGAIN EWOULDBLOCK);
 
-sub TIMEOUT () { 5 }
-
 sub TIEHANDLE {
   my ($class, $socket, $key, $cert) = @_;
 
@@ -29,7 +27,7 @@
 
   # Net::SSLeay needs nonblocking for setup.
   my $flags = fcntl($socket, F_GETFL, 0) or die $!;
-  until (fcntl($socket, F_SETFL, $flags & ~O_NONBLOCK)) {
+  until (fcntl($socket, F_SETFL, $flags | O_NONBLOCK)) {
     die $! unless $! == EAGAIN or $! == EWOULDBLOCK;
   }
 
@@ -53,23 +51,22 @@
                                    );
   die_if_ssl_error("certificate");
 
-  eval {
-    local $SIG{ALRM} = sub { die "alarm\n" };
-    alarm TIMEOUT;
-    my $resp = Net::SSLeay::accept($ssl);
-    alarm 0;
-    if ($resp == -1) {
-      # handshake could not complete
-      die ("handshake failed");
+  my $accepted = 0;
+  my $resp = Net::SSLeay::accept($ssl);
+  if ($resp <= 0) { # 0 is really controlled shutdown but we signal error
+    if (Net::SSLeay::get_error($ssl, $resp) == ERROR_WANT_READ) {
+      # we try again next time in READ
     }
-  };
-  if ($@) {
-    die unless $@ eq "alarm\n";
-    # timed out
-    die("SSL handshake timed out, maybe increase timeout in SslServer.pm");
+    else {
+      # handshake failed
+      return undef;
+    }
   }
+  else {
+    $accepted = 1;
+  }
 
-  $class->_set_filenum_obj($fileno, $ssl, $ctx, $socket);
+  $class->_set_filenum_obj($fileno, $ssl, $ctx, $socket, $accepted);
 
   return bless $socket, $class;
 }


--- SslClient.pm.orig   Sat Oct 25 03:10:58 2003
+++ SslClient.pm        Tue Jan 20 15:06:31 2004
@@ -10,6 +10,7 @@
 
 use POSIX qw(F_GETFL F_SETFL O_NONBLOCK EAGAIN EWOULDBLOCK);
 
+use Net::SSLeay qw(ERROR_WANT_READ ERROR_WANT_WRITE);
 use Net::SSLeay::Handle;
 use vars qw(@ISA);
 @ISA = qw(Net::SSLeay::Handle);
@@ -26,12 +27,13 @@
 }
 
 sub _set_filenum_obj {
-  my ($self, $fileno, $ssl, $ctx, $socket) = @_;
+  my ($self, $fileno, $ssl, $ctx, $socket, $accepted) = @_;
   $Filenum_Object{$fileno} =
   { ssl    => $ssl,
     ctx    => $ctx,
     socket => $socket,
     fileno => $fileno,
+    _is_accepted => $accepted,
   };
 }
 
@@ -40,7 +42,7 @@
 
   # Net::SSLeay needs nonblocking for setup.
   my $flags = fcntl($socket, F_GETFL, 0) or die $!;
-  until (fcntl($socket, F_SETFL, $flags & ~O_NONBLOCK)) {
+  until (fcntl($socket, F_SETFL, $flags | O_NONBLOCK)) {
     die $! unless $! == EAGAIN or $! == EWOULDBLOCK;
   }
 
@@ -55,7 +57,20 @@
 
   Net::SSLeay::set_fd($ssl, $fileno);   # Must use fileno
 
+  my $connected = 0;
   my $resp = Net::SSLeay::connect($ssl);
+  if ($resp <= 0) { # 0 is really controlled shutdown but we signal error
+    if (Net::SSLeay::get_error($ssl, $resp) == ERROR_WANT_WRITE) {
+      # we try again next time in WRITE
+    }
+    else {
+      # handshake failed
+      return undef;
+    }
+  }
+  else {
+    $connected = 1;
+  }
 
   $Filenum_Object{$fileno} =
   { ssl    => $ssl,
@@ -62,6 +77,7 @@
     ctx    => $ctx,
     socket => $socket,
     fileno => $fileno,
+    _is_connected => $connected,
   };
 
   return bless $socket, $class;
@@ -70,7 +86,22 @@
 sub READ {
   my ($socket, $buf, $len, $offset) = \ (@_);
   my $ssl = $$socket->_get_ssl();
+  my $self = $$socket->_get_self();
 
+  if (exists $self->{_is_accepted} && $self->{_is_accepted} == 0) {
+    my $resp = Net::SSLeay::accept($ssl);
+    if ($resp <= 0) {
+      if (Net::SSLeay::get_error($ssl, $resp) == ERROR_WANT_READ) {
+       return $$len;
+      }
+      else {
+       die ("handshake failed");
+      }
+    }
+    $self->{_is_accepted} = 1;
+    return $$len;
+  }
+
   # No offset.  Replace the buffer.
   unless (defined $$offset) {
     $$buf = Net::SSLeay::read($ssl, $$len);
@@ -92,9 +123,23 @@
   my $socket = shift;
   my ($buf, $len, $offset) = @_;
   $offset = 0 unless defined $offset;
+  my $ssl  = $socket->_get_ssl();
+  my $self = $socket->_get_self();
 
+  if (exists $self->{_is_connected} && $self->{_is_connected} == 0) {
+    my $resp = Net::SSLeay::connect($ssl);
+    if ($resp <= 0) {
+      if (Net::SSLeay::get_error($ssl, $resp) == ERROR_WANT_WRITE) {
+       return 0;
+      }
+      else {
+       die ("handshake failed");
+      }
+    }
+    $self->{_is_connected} = 1;
+  }
+
   # Return number of characters written.
-  my $ssl  = $socket->_get_ssl();
   my $wrote_len = Net::SSLeay::write($ssl, substr($buf, $offset, $len));
 
   # Net::SSLeay::write() returns the number of bytes written, or -1 on

Reply via email to