Hi M,
Here's a test program which negotiates an SSL connection and sends
data in a single process (no threads, all async IO).
I've also included a patch for Net-SSLeay-1.06. I had to add a few
macros to Net::SSLeay to support the async error returns. I also
fixed the makefile to detect openssl.exe under Windows.
Hope it helps.
--Noel
From: [EMAIL PROTECTED]
To: [EMAIL PROTECTED]
Subject: non-blocking example for Net::SSLeay
Date: Sun, 29 Apr 2001 17:59:46 -0700 (PDT)
I'm trying to write a tiny SSL webserver. It would really
help to have an example of how to set up and use non-blocking
SSL calls with a select statement or similar. The problem
here is implementing CGI: the CGI script wants normal I/O
through normal file descriptiors, and I see that it's my
job to do SSL_read and copy the result into that file
descriptor, and copy output from the CGI script into SSL_write.
I need to select() on the CGI processes output pipe, and the
SSL input pipe, and service requests as they come in.
I'm writing this in perl, using Net::SSLeay. I've patched
the echo example code into my tiny webserver, so if I'm
doing the SSL_reads and SSL_writes in the main process, it works,
but of course, the cgi part fails miserably.
Does the underlying BIO interface mean if I do a fcntl
O_NONBLOCK on the NS (network socket) that SSL will somehow
figure that out? I don't see a BIO_set sort of thing
to set nonblocking behavior for the BIO or SSL. The
docs are a bit sparse, here.
Maybe a new call: SSL_select($ssl,$readfds,$writefds,$exceptfds,$timeout)
what it might do internally is do a select on the ssl network
socket read and the rest of the $readfs, but loop around and
try again if the network socket read didn't complete a pending
SSL_read? Can SSL writes not be similarly monitored,
they can just be set as nonblocking?
#! /usr/bin/perl -w
use lib '../lib';
use Socket;
use QV::OS;
use Net::SSLeay qw(1.07 die_now die_if_ssl_error);
Net::SSLeay::load_error_strings();
Net::SSLeay::SSLeay_add_ssl_algorithms();
Net::SSLeay::randomize();
use strict;
sub ssl_ctx {
my($io) = @_;
my($ctx, $ssl);
$ctx = Net::SSLeay::CTX_new() or die_now(Failed to create SSL_CTX $!);
Net::SSLeay::CTX_set_options($ctx, Net::SSLeay::OP_ALL)
and die_if_ssl_error(CTX_set_options);
$ssl = Net::SSLeay::new($ctx) or die_now(Failed to create SSL $!);
Net::SSLeay::set_fd($ssl, fileno($io))
and die_if_ssl_error(set_fd);
return $ssl;
}
sub ssl_err_select {
my($ret, $ssl, $io, $sel_read, $sel_write) = @_;
my($i);
my($fileno) = fileno($io);
vec($$sel_read, $fileno, 1) = 0;
vec($$sel_write, $fileno, 1) = 0;
if( $ret 0 ) {
$i = Net::SSLeay::get_error($ssl, $ret);
if( $i == Net::SSLeay::ERROR_WANT_READ ) {
# debug
print(ERROR_WANT_READ: fileno= . fileno($io) . \n);
vec($$sel_read, $fileno, 1) = 1;
$ret = undef;
}
elsif( $i == Net::SSLeay::ERROR_WANT_WRITE ) {
# debug
print(ERROR_WANT_WRITE: fileno= . fileno($io) . \n);
vec($$sel_write, $fileno, 1) = 1;
$ret = undef;
}
}
return $ret;
}
#-
my($buf, $off);
$off = 0;
if( @ARGV || ! -t STDIN ) {
local($/) = undef;
$buf = ;
}
else {
$buf = Hello from Alice;
}
my($ssl_A, $connect_A,
$ssl_B, $accept_B,
$rfds, $wfds, $sel_read, $sel_write,
$s, $i, $x);
($a, $b) = QV::OS-socket_pair();
select($a); $|=1;
select($b); $|=1;
select(STDOUT);
QV::OS-io_nonblock($a, 1);
QV::OS-io_nonblock($b, 1);
# debug
print(fileno(A)=, fileno($a), \n);
print(fileno(B)=, fileno($b), \n);
$ssl_A = ssl_ctx($a);
#Net::SSLeay::set_connect_state($ssl_A);
Net::SSLeay::use_RSAPrivateKey_file($ssl_A, 'b-rsa.pem',
Net::SSLeay::FILETYPE_PEM);
die_if_ssl_error(private key);
Net::SSLeay::use_certificate_file($ssl_A, 'b-cert.pem',
Net::SSLeay::FILETYPE_PEM);
die_if_ssl_error(certificate);
$ssl_B = ssl_ctx($b);
#Net::SSLeay::set_connect_state($ssl_B);
Net::SSLeay::use_RSAPrivateKey_file($ssl_B, 'b-rsa.pem',
Net::SSLeay::FILETYPE_PEM);
die_if_ssl_error(private key);
Net::SSLeay::use_certificate_file($ssl_B, 'b-cert.pem',
Net::SSLeay::FILETYPE_PEM);
die_if_ssl_error(certificate);
$accept_B = $connect_A = undef;
$rfds = $wfds = '';
vec($rfds, fileno($a), 1) = 1;
vec($rfds, fileno($b), 1) = 1;
$sel_read = $sel_write = '';
while(1) {
if( vec($rfds, fileno($a), 1)
|| vec($wfds, fileno($a), 1)
) {
if( !$connect_A ) {
$i = Net::SSLeay::connect($ssl_A);
$i = ssl_err_select($i, $ssl_A, $a, \$sel_read, \$sel_write);
if( !defined($i) ) {
# no error, just continue