On 4-May-08, at 7:28 PM, Matt Sergeant wrote:
Yeah this makes a lot more sense... Though I wonder if we shouldn't
modify Danga::Client to just have some sort of "reader" entry, so
that anything else (not just TLS) can hook into the event_read
stream. I'll have a poke.
Can you check if this works:
Index: plugins/tls
===================================================================
--- plugins/tls (revision 876)
+++ plugins/tls (working copy)
@@ -150,7 +150,7 @@
return DECLINED unless $local_port == 465; # SMTPS
unless ( _convert_to_ssl($self) ) {
- return (DENY_DISCONNECT, "Cannot establish SSL session");
+ return (DENY_DISCONNECT, "Cannot establish SSL session");
}
$self->log(LOGWARN, "Connected via SMTPS");
return DECLINED;
@@ -159,6 +159,10 @@
sub _convert_to_ssl {
my ($self) = @_;
+ if ($self->qp->isa('Qpsmtpd::PollServer')) {
+ return _convert_to_ssl_async($self);
+ }
+
eval {
my $tlssocket = IO::Socket::SSL->new_from_fd(
fileno(STDIN), '+>',
@@ -178,13 +182,21 @@
$self->connection->notes('tls_enabled', 1);
};
if ($@) {
- return 0;
+ return 0;
}
else {
- return 1;
+ return 1;
}
}
+sub _convert_to_ssl_async {
+ my ($self) = @_;
+ my $upgrader = $self->connection
+ ->notes( 'tls_upgrader', UpgradeClientSSL-
>new($self) );
+ $upgrader->upgrade_socket();
+ return 1;
+}
+
sub can_do_tls {
my ($self) = @_;
$self->tls_cert && -r $self->tls_cert;
@@ -238,3 +250,81 @@
$self->log(LOGWARN, "Exiting because 'tls_enabled' was true.");
exit;
}
+
+package UpgradeClientSSL;
+
+# borrowed heavily from Perlbal::SocketSSL
+
+use strict;
+use warnings;
+no warnings qw(deprecated);
+
+use Danga::Socket 1.44;
+use IO::Socket::SSL 0.98;
+use Errno qw( EAGAIN );
+
+use fields qw( _stashed_qp _stashed_plugin _ssl_started );
+
+sub new {
+ my UpgradeClientSSL $self = shift;
+ $self = fields::new($self) unless ref $self;
+ $self->{_stashed_plugin} = shift;
+ $self->{_stashed_qp} = $self->{_stashed_plugin}->qp;
+ return $self;
+}
+
+sub upgrade_socket {
+ my UpgradeClientSSL $self = shift;
+
+ unless ( $self->{_ssl_started} ) {
+ IO::Socket::SSL->start_SSL(
+ $self->{_stashed_qp}->{sock}, {
+ SSL_use_cert => 1,
+ SSL_cert_file => $self->{_stashed_plugin}->tls_cert,
+ SSL_key_file => $self->{_stashed_plugin}->tls_key,
+ SSL_ca_file => $self->{_stashed_plugin}->tls_ca,
+ SSL_cipher_list => $self->{_stashed_plugin}-
>tls_ciphers,
+ SSL_startHandshake => 0,
+ SSL_server => 1,
+ SSL_reuse_ctx => $self->{_stashed_plugin}->ssl_context,
+ }
+ ) or die "Could not upgrade socket to SSL: $!";
+ $self->{_ssl_started} = 1;
+ }
+
+ $self->event_read($self->{_stashed_qp});
+}
+
+sub event_read {
+ my UpgradeClientSSL $self = shift;
+ my $qp = shift;
+
+ $qp->watch_read( 0 );
+
+ my $sock = $qp->{sock}->accept_SSL;
+
+ if (defined $sock) {
+ $qp->connection( $qp->connection->clone );
+ $qp->reset_transaction;
+ $qp->connection->notes('tls_socket', $sock);
+ $qp->connection->notes('tls_enabled', 1);
+ $qp->watch_read(1);
+ return 1;
+ }
+
+ # nope, let's see if we can continue the process
+ if ($! == EAGAIN) {
+ $qp->set_reader_object($self);
+ if ($SSL_ERROR == SSL_WANT_READ) {
+ $qp->watch_read(1);
+ } elsif ($SSL_ERROR == SSL_WANT_WRITE) {
+ $qp->watch_write(1);
+ } else {
+ $qp->disconnect();
+ }
+ } else {
+ $qp->disconnect();
+ }
+}
+
+1;
Index: lib/Danga/Client.pm
===================================================================
--- lib/Danga/Client.pm (revision 881)
+++ lib/Danga/Client.pm (working copy)
@@ -2,7 +2,15 @@
package Danga::Client;
use base 'Danga::TimeoutSocket';
-use fields qw(line pause_count read_bytes data_bytes callback
get_chunks);
+use fields qw(
+ line
+ pause_count
+ read_bytes
+ data_bytes
+ callback
+ get_chunks
+ reader_object
+ );
use Time::HiRes ();
use bytes;
@@ -26,6 +34,7 @@
$self->{pause_count} = 0;
$self->{read_bytes} = 0;
$self->{callback} = undef;
+ $self->{reader_object} = undef;
$self->{data_bytes} = '';
$self->{get_chunks} = 0;
return $self;
@@ -96,9 +105,18 @@
}
}
+sub set_reader_object {
+ my Danga::Client $self = shift;
+ $self->{reader_object} = shift;
+}
+
sub event_read {
my Danga::Client $self = shift;
- if ($self->{callback}) {
+ if (my $obj = $self->{reader_object}) {
+ $self->{reader_object} = undef;
+ $obj->event_read($self);
+ }
+ elsif ($self->{callback}) {
$self->{alive_time} = time;
if ($self->{get_chunks}) {
my $bref = $self->read($self->{read_bytes});