--- AnyEvent/lib/AnyEvent/Handle.pm 2009/06/29 21:00:32 1.130 +++ AnyEvent/lib/AnyEvent/Handle.pm 2009/07/03 00:09:04 1.134 @@ -65,9 +65,9 @@ =over 4 -=item B +=item $handle = B AnyEvent::TLS fh => $filehandle, key => value... -The constructor supports these arguments (all as key => value pairs). +The constructor supports these arguments (all as C<< key => value >> pairs). =over 4 @@ -97,7 +97,7 @@ If an EOF condition has been detected but no C callback has been set, then a fatal error will be raised with C<$!> set to <0>. -=item on_error => $cb->($handle, $fatal) +=item on_error => $cb->($handle, $fatal, $message) This is the error callback, which is called when, well, some error occured, such as not being able to resolve the hostname, failure to @@ -109,13 +109,19 @@ errors are an EOF condition with active (but unsatisifable) read watchers (C) or I/O errors. +AnyEvent::Handle tries to find an appropriate error code for you to check +against, but in some cases (TLS errors), this does not work well. It is +recommended to always output the C<$message> argument in human-readable +error messages (it's usually the same as C<"$!">). + Non-fatal errors can be retried by simply returning, but it is recommended to simply ignore this parameter and instead abondon the handle object when this callback is invoked. Examples of non-fatal errors are timeouts C) or badly-formatted data (C). On callback entrance, the value of C<$!> contains the operating system -error (or C, C, C or C). +error code (or C, C, C, C or +C). While not mandatory, it is I recommended to set this callback, as you will not be notified of errors otherwise. The default simply calls @@ -239,12 +245,23 @@ yet. This data will be lost. Calling the C method in time might help. +=item peername => $string + +A string used to identify the remote site - usually the DNS hostname +(I IDN!) used to create the connection, rarely the IP address. + +Apart from being useful in error messages, this string is also used in TLS +common name verification (see C in L). + =item tls => "accept" | "connect" | Net::SSLeay::SSL object When this parameter is given, it enables TLS (SSL) mode, that means AnyEvent will start a TLS handshake as soon as the conenction has been established and will transparently encrypt/decrypt data afterwards. +All TLS protocol errors will be signalled as C, with an +appropriate error message. + TLS mode requires Net::SSLeay to be installed (it will be loaded automatically when you try to create a TLS handle): this module doesn't have a dependency on that module, so if your module requires it, you have @@ -257,7 +274,12 @@ You can also provide your own TLS connection object, but you have to make sure that you call either C or C on it before you pass it to -AnyEvent::Handle. +AnyEvent::Handle. Also, this module will take ownership of this connection +object. + +At some future point, AnyEvent::Handle might switch to another TLS +implementation, then the option to use your own session object will go +away. B since Net::SSLeay "objects" are really only integers, passing in the wrong integer will lead to certain crash. This most often @@ -266,12 +288,16 @@ See the C<< ->starttls >> method for when need to start TLS negotiation later. -=item tls_ctx => $ssl_ctx +=item tls_ctx => $anyevent_tls -Use the given C object to create the new TLS connection +Use the given C object to create the new TLS connection (unless a connection object was specified directly). If this parameter is missing, then AnyEvent::Handle will use C. +Instead of an object, you can also specify a hash reference with C<< key +=> value >> pairs. Those will be passed to L to create a +new TLS context object. + =item json => JSON or JSON::XS object This is the json coder object used by the C read and write types. @@ -289,48 +315,50 @@ sub new { my $class = shift; - my $self = bless { @_ }, $class; $self->{fh} or Carp::croak "mandatory argument fh is missing"; AnyEvent::Util::fh_nonblocking $self->{fh}, 1; - $self->starttls (delete $self->{tls}, delete $self->{tls_ctx}) - if $self->{tls}; - $self->{_activity} = AnyEvent->now; $self->_timeout; - $self->on_drain (delete $self->{on_drain}) if exists $self->{on_drain}; $self->no_delay (delete $self->{no_delay}) if exists $self->{no_delay}; + $self->starttls (delete $self->{tls}, delete $self->{tls_ctx}) + if $self->{tls}; + + $self->on_drain (delete $self->{on_drain}) if exists $self->{on_drain}; + $self->start_read if $self->{on_read}; - $self + $self->{fh} && $self } sub _shutdown { my ($self) = @_; - delete @$self{qw(_tw _rw _ww fh rbuf wbuf on_read _queue)}; + delete @$self{qw(_tw _rw _ww fh wbuf on_read _queue)}; + $self->{_eof} = 1; # tell starttls et. al to stop trying &_freetls; } sub _error { - my ($self, $errno, $fatal) = @_; + my ($self, $errno, $fatal, $message) = @_; $self->_shutdown if $fatal; $! = $errno; + $message ||= "$!"; if ($self->{on_error}) { - $self->{on_error}($self, $fatal); + $self->{on_error}($self, $fatal, $message); } elsif ($self->{fh}) { - Carp::croak "AnyEvent::Handle uncaught error: $!"; + Carp::croak "AnyEvent::Handle uncaught error: $message"; } } @@ -653,6 +681,27 @@ =back +=item $handle->push_shutdown + +Sometimes you know you want to close the socket after writing your data +before it was actually written. One way to do that is to replace your +C handler by a callback that shuts down the socket. This method +is a shorthand for just that, and replaces the C callback with: + + sub { shutdown $_[0]{fh}, 1 } # for push_shutdown + +This simply shuts down the write side and signals an EOF condition to the +the peer. + +You can rely on the normal read queue and C handling +afterwards. This is the cleanest way to close a connection. + +=cut + +sub push_shutdown { + $_[0]->{on_drain} = sub { shutdown $_[0]{fh}, 1 }; +} + =item AnyEvent::Handle::register_write_type type => $coderef->($handle, @args) This function (not method) lets you add your own types to C. @@ -1328,7 +1377,26 @@ } } +our $ERROR_SYSCALL; +our $ERROR_WANT_READ; +our $ERROR_ZERO_RETURN; + +sub _tls_error { + my ($self, $err) = @_; + warn "$err,$!\n";#d# + + return $self->_error ($!, 1) + if $err == Net::SSLeay::ERROR_SYSCALL (); + + $self->_error (&Errno::EPROTO, 1, + Net::SSLeay::ERR_error_string (Net::SSLeay::ERR_get_error ())); +} + # poll the write BIO and send the data if applicable +# also decode read data if possible +# this is basiclaly our TLS state machine +# more efficient implementations are possible with openssl, +# but not with the buggy and incomplete Net::SSLeay. sub _dotls { my ($self) = @_; @@ -1338,6 +1406,12 @@ while (($tmp = Net::SSLeay::write ($self->{tls}, $self->{_tls_wbuf})) > 0) { substr $self->{_tls_wbuf}, 0, $tmp, ""; } + + $tmp = Net::SSLeay::get_error ($self->{tls}, $tmp); + return $self->_tls_error ($tmp) + if $tmp != $ERROR_WANT_READ + && ($tmp != $ERROR_SYSCALL || $!) + && $tmp != $ERROR_ZERO_RETURN; } while (defined ($tmp = Net::SSLeay::read ($self->{tls}))) { @@ -1354,16 +1428,10 @@ } $tmp = Net::SSLeay::get_error ($self->{tls}, -1); - - if ($tmp != Net::SSLeay::ERROR_WANT_READ ()) { - if ($tmp == Net::SSLeay::ERROR_SYSCALL ()) { - return $self->_error ($!, 1); - } elsif ($tmp == Net::SSLeay::ERROR_SSL ()) { - return $self->_error (&Errno::EIO, 1); - } - - # all other errors are fine for our purposes - } + return $self->_tls_error ($tmp) + if $tmp != $ERROR_WANT_READ + && ($tmp != $ERROR_SYSCALL || $!) + && $tmp != $ERROR_ZERO_RETURN; while (length ($tmp = Net::SSLeay::BIO_read ($self->{_wbio}))) { $self->{wbuf} .= $tmp; @@ -1380,12 +1448,15 @@ The first argument is the same as the C constructor argument (either C<"connect">, C<"accept"> or an existing Net::SSLeay object). -The second argument is the optional C object that is -used when AnyEvent::Handle has to create its own TLS connection object. - -The TLS connection object will end up in C<< $handle->{tls} >> after this -call and can be used or changed to your liking. Note that the handshake -might have already started when this function returns. +The second argument is the optional C object that is used +when AnyEvent::Handle has to create its own TLS connection object, or +a hash reference with C<< key => value >> pairs that will be used to +construct a new context. + +The TLS connection object will end up in C<< $handle->{tls} >>, the TLS +context in C<< $handle->{tls_ctx} >> after this call and can be used or +changed to your liking. Note that the handshake might have already started +when this function returns. If it an error to start a TLS handshake more than once per AnyEvent::Handle object (this is due to bugs in OpenSSL). @@ -1399,16 +1470,22 @@ Carp::croak "it is an error to call starttls more than once on an AnyEvent::Handle object" if $self->{tls}; - - if ($ssl eq "accept") { - $ssl = Net::SSLeay::new ($ctx || TLS_CTX ()); - Net::SSLeay::set_accept_state ($ssl); - } elsif ($ssl eq "connect") { - $ssl = Net::SSLeay::new ($ctx || TLS_CTX ()); - Net::SSLeay::set_connect_state ($ssl); - } - $self->{tls} = $ssl; + $ERROR_SYSCALL = Net::SSLeay::ERROR_SYSCALL (); + $ERROR_WANT_READ = Net::SSLeay::ERROR_WANT_READ (); + $ERROR_ZERO_RETURN = Net::SSLeay::ERROR_ZERO_RETURN (); + + $ctx ||= $self->{tls_ctx}; + + if ("HASH" eq ref $ctx) { + require AnyEvent::TLS; + + local $Carp::CarpLevel = 1; # skip ourselves when creating a new context + $ctx = new AnyEvent::TLS %$ctx; + } + + $self->{tls_ctx} = $ctx || TLS_CTX (); + $self->{tls} = $ssl = $self->{tls_ctx}->_get_session ($ssl, $self, $self->{peername}); # basically, this is deep magic (because SSL_read should have the same issues) # but the openssl maintainers basically said: "trust us, it just works". @@ -1422,9 +1499,10 @@ # we assume that most (but not all) of this insanity only applies to non-blocking cases, # and we drive openssl fully in blocking mode here. Or maybe we don't - openssl seems to # have identity issues in that area. - Net::SSLeay::CTX_set_mode ($self->{tls}, - (eval { local $SIG{__DIE__}; Net::SSLeay::MODE_ENABLE_PARTIAL_WRITE () } || 1) - | (eval { local $SIG{__DIE__}; Net::SSLeay::MODE_ACCEPT_MOVING_WRITE_BUFFER () } || 2)); +# Net::SSLeay::CTX_set_mode ($ssl, +# (eval { local $SIG{__DIE__}; Net::SSLeay::MODE_ENABLE_PARTIAL_WRITE () } || 1) +# | (eval { local $SIG{__DIE__}; Net::SSLeay::MODE_ACCEPT_MOVING_WRITE_BUFFER () } || 2)); + Net::SSLeay::CTX_set_mode ($ssl, 1|2); $self->{_rbio} = Net::SSLeay::BIO_new (Net::SSLeay::BIO_s_mem ()); $self->{_wbio} = Net::SSLeay::BIO_new (Net::SSLeay::BIO_s_mem ()); @@ -1463,7 +1541,7 @@ return unless $self->{tls}; - Net::SSLeay::free (delete $self->{tls}); + $self->{tls_ctx}->_put_session (delete $self->{tls}); delete @$self{qw(_rbio _wbio _tls_wbuf)}; } @@ -1523,36 +1601,20 @@ =item AnyEvent::Handle::TLS_CTX -This function creates and returns the Net::SSLeay::CTX object used by -default for TLS mode. +This function creates and returns the AnyEvent::TLS object used by default +for TLS mode. -The context is created like this: - - Net::SSLeay::load_error_strings; - Net::SSLeay::SSLeay_add_ssl_algorithms; - Net::SSLeay::randomize; - - my $CTX = Net::SSLeay::CTX_new; - - Net::SSLeay::CTX_set_options $CTX, Net::SSLeay::OP_ALL +The context is created by calling L without any arguments. =cut our $TLS_CTX; sub TLS_CTX() { - $TLS_CTX || do { - require Net::SSLeay; - - Net::SSLeay::load_error_strings (); - Net::SSLeay::SSLeay_add_ssl_algorithms (); - Net::SSLeay::randomize (); - - $TLS_CTX = Net::SSLeay::CTX_new (); - - Net::SSLeay::CTX_set_options ($TLS_CTX, Net::SSLeay::OP_ALL ()); + $TLS_CTX ||= do { + require AnyEvent::TLS; - $TLS_CTX + new AnyEvent::TLS } }