> 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