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});

Reply via email to