Timer support added to Danga::Socket
check_earlytalker updated to use timers Few other code cleanups to make sure check-earlytalker is fully working git-svn-id: https://svn.perl.org/qpsmtpd/branches/high_perf@441 958fd67b-6ff1-0310-b445-bb7760255be9
This commit is contained in:
parent
a4517bdfa4
commit
cb047d9aa9
@ -33,7 +33,7 @@ sub get_line {
|
|||||||
#warn("get_line PRE\n");
|
#warn("get_line PRE\n");
|
||||||
$self->EventLoop();
|
$self->EventLoop();
|
||||||
#warn("get_line POST\n");
|
#warn("get_line POST\n");
|
||||||
$self->watch_read(0);
|
$self->disable_read();
|
||||||
}
|
}
|
||||||
return if $self->{closing};
|
return if $self->{closing};
|
||||||
# now have a line.
|
# now have a line.
|
||||||
@ -49,8 +49,7 @@ sub can_read {
|
|||||||
# warn("Calling can-read\n");
|
# warn("Calling can-read\n");
|
||||||
$self->{can_read_mode} = 1;
|
$self->{can_read_mode} = 1;
|
||||||
if (!length($self->{line})) {
|
if (!length($self->{line})) {
|
||||||
my $old = $self->watch_read();
|
$self->disable_read();
|
||||||
$self->watch_read(1);
|
|
||||||
# loop because any callback, not just ours, can make EventLoop return
|
# loop because any callback, not just ours, can make EventLoop return
|
||||||
while( !(length($self->{line}) || (Time::HiRes::time > $end)) ) {
|
while( !(length($self->{line}) || (Time::HiRes::time > $end)) ) {
|
||||||
$self->SetPostLoopCallback(sub { (length($self->{line}) ||
|
$self->SetPostLoopCallback(sub { (length($self->{line}) ||
|
||||||
@ -59,7 +58,7 @@ sub can_read {
|
|||||||
$self->EventLoop();
|
$self->EventLoop();
|
||||||
#warn("get_line POST\n");
|
#warn("get_line POST\n");
|
||||||
}
|
}
|
||||||
$self->watch_read($old);
|
$self->enable_read();
|
||||||
}
|
}
|
||||||
$self->{can_read_mode} = 0;
|
$self->{can_read_mode} = 0;
|
||||||
$self->SetPostLoopCallback(undef);
|
$self->SetPostLoopCallback(undef);
|
||||||
|
@ -39,25 +39,25 @@ sub new {
|
|||||||
if ($options{type}) {
|
if ($options{type}) {
|
||||||
if ($options{type} eq 'TXT') {
|
if ($options{type} eq 'TXT') {
|
||||||
if (!$resolver->query_txt($self, @{$self->{hosts}})) {
|
if (!$resolver->query_txt($self, @{$self->{hosts}})) {
|
||||||
$client->watch_read(1) if $client;
|
$client->enable_read() if $client;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($options{type} eq 'A') {
|
elsif ($options{type} eq 'A') {
|
||||||
if (!$resolver->query($self, @{$self->{hosts}})) {
|
if (!$resolver->query($self, @{$self->{hosts}})) {
|
||||||
$client->watch_read(1) if $client;
|
$client->enable_read() if $client;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($options{type} eq 'PTR') {
|
elsif ($options{type} eq 'PTR') {
|
||||||
if (!$resolver->query($self, @{$self->{hosts}})) {
|
if (!$resolver->query($self, @{$self->{hosts}})) {
|
||||||
$client->watch_read(1) if $client;
|
$client->enable_read() if $client;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($options{type} eq 'MX') {
|
elsif ($options{type} eq 'MX') {
|
||||||
if (!$resolver->query_mx($self, @{$self->{hosts}})) {
|
if (!$resolver->query_mx($self, @{$self->{hosts}})) {
|
||||||
$client->watch_read(1) if $client;
|
$client->enable_read() if $client;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ sub new {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!$resolver->query($self, @{$self->{hosts}})) {
|
if (!$resolver->query($self, @{$self->{hosts}})) {
|
||||||
$client->watch_read(1) if $client;
|
$client->enable_read() if $client;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@ our (
|
|||||||
# descriptors for the event loop to track.
|
# descriptors for the event loop to track.
|
||||||
$PostLoopCallback, # subref to call at the end of each loop, if defined
|
$PostLoopCallback, # subref to call at the end of each loop, if defined
|
||||||
%PLCMap, # fd (num) -> PostLoopCallback
|
%PLCMap, # fd (num) -> PostLoopCallback
|
||||||
|
@Timers, # timers
|
||||||
);
|
);
|
||||||
|
|
||||||
%OtherFds = ();
|
%OtherFds = ();
|
||||||
@ -110,6 +111,30 @@ sub OtherFds {
|
|||||||
return wantarray ? %OtherFds : \%OtherFds;
|
return wantarray ? %OtherFds : \%OtherFds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub AddTimer {
|
||||||
|
my $class = shift;
|
||||||
|
my ($secs, $coderef) = @_;
|
||||||
|
my $timeout = time + $secs;
|
||||||
|
|
||||||
|
use Data::Dumper; $Data::Dumper::Indent=1;
|
||||||
|
|
||||||
|
if (!@Timers || ($timeout > $Timers[-1][0])) {
|
||||||
|
push @Timers, [$timeout, $coderef];
|
||||||
|
print STDERR Dumper(\@Timers);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Now where do we insert...
|
||||||
|
for (my $i = 0; $i < @Timers; $i++) {
|
||||||
|
if ($Timers[$i][0] > $timeout) {
|
||||||
|
splice(@Timers, $i, 0, [$timeout, $coderef]);
|
||||||
|
print STDERR Dumper(\@Timers);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
die "Shouldn't get here spank matt.";
|
||||||
|
}
|
||||||
|
|
||||||
### (CLASS) METHOD: DescriptorMap()
|
### (CLASS) METHOD: DescriptorMap()
|
||||||
### Get the hash of Danga::Socket objects keyed by the file descriptor they are
|
### Get the hash of Danga::Socket objects keyed by the file descriptor they are
|
||||||
@ -169,7 +194,16 @@ sub KQueueEventLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
my @ret = $KQueue->kevent(1000);
|
my $now = time;
|
||||||
|
# Run expired timers
|
||||||
|
while (@Timers && $Timers[0][0] <= $now) {
|
||||||
|
my $to_run = shift(@Timers);
|
||||||
|
$to_run->[1]->($now);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get next timeout
|
||||||
|
my $timeout = @Timers ? ($Timers[0][0] - $now) : 1;
|
||||||
|
my @ret = $KQueue->kevent($timeout * 1000);
|
||||||
|
|
||||||
if (!@ret) {
|
if (!@ret) {
|
||||||
foreach my $fd ( keys %DescriptorMap ) {
|
foreach my $fd ( keys %DescriptorMap ) {
|
||||||
@ -233,11 +267,21 @@ sub EpollEventLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
my $now = time;
|
||||||
|
# Run expired timers
|
||||||
|
while (@Timers && $Timers[0][0] <= $now) {
|
||||||
|
my $to_run = shift(@Timers);
|
||||||
|
$to_run->[1]->($now);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get next timeout
|
||||||
|
my $timeout = @Timers ? ($Timers[0][0] - $now) : 1;
|
||||||
|
|
||||||
my @events;
|
my @events;
|
||||||
my $i;
|
my $i;
|
||||||
my $evcount;
|
my $evcount;
|
||||||
# get up to 1000 events, 1000ms timeout
|
# get up to 1000 events, 1000ms timeout
|
||||||
while ($evcount = epoll_wait($Epoll, 1000, 1000, \@events)) {
|
while ($evcount = epoll_wait($Epoll, 1000, $timeout * 1000, \@events)) {
|
||||||
my @objs;
|
my @objs;
|
||||||
EVENT:
|
EVENT:
|
||||||
for ($i=0; $i<$evcount; $i++) {
|
for ($i=0; $i<$evcount; $i++) {
|
||||||
@ -300,6 +344,16 @@ sub PollEventLoop {
|
|||||||
my Danga::Socket $pob;
|
my Danga::Socket $pob;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
my $now = time;
|
||||||
|
# Run expired timers
|
||||||
|
while (@Timers && $Timers[0][0] <= $now) {
|
||||||
|
my $to_run = shift(@Timers);
|
||||||
|
$to_run->[1]->($now);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get next timeout
|
||||||
|
my $timeout = @Timers ? ($Timers[0][0] - $now) : 1;
|
||||||
|
|
||||||
# the following sets up @poll as a series of ($poll,$event_mask)
|
# the following sets up @poll as a series of ($poll,$event_mask)
|
||||||
# items, then uses IO::Poll::_poll, implemented in XS, which
|
# items, then uses IO::Poll::_poll, implemented in XS, which
|
||||||
# modifies the array in place with the even elements being
|
# modifies the array in place with the even elements being
|
||||||
@ -314,7 +368,7 @@ sub PollEventLoop {
|
|||||||
}
|
}
|
||||||
return 0 unless @poll;
|
return 0 unless @poll;
|
||||||
|
|
||||||
my $count = IO::Poll::_poll(1000, @poll);
|
my $count = IO::Poll::_poll($timeout * 1000, @poll);
|
||||||
if (!$count) {
|
if (!$count) {
|
||||||
foreach my $fd ( keys %DescriptorMap ) {
|
foreach my $fd ( keys %DescriptorMap ) {
|
||||||
my Danga::Socket $sock = $DescriptorMap{$fd};
|
my Danga::Socket $sock = $DescriptorMap{$fd};
|
||||||
@ -481,6 +535,7 @@ sub close {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete $PLCMap{$fd};
|
||||||
delete $DescriptorMap{$fd};
|
delete $DescriptorMap{$fd};
|
||||||
delete $PushBackSet{$fd};
|
delete $PushBackSet{$fd};
|
||||||
|
|
||||||
|
@ -135,11 +135,8 @@ sub _process_line {
|
|||||||
if ($self->{mode} eq 'connect') {
|
if ($self->{mode} eq 'connect') {
|
||||||
$self->{mode} = 'cmd';
|
$self->{mode} = 'cmd';
|
||||||
my $rc = $self->start_conversation;
|
my $rc = $self->start_conversation;
|
||||||
if ($rc != DONE) {
|
|
||||||
$self->close;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
elsif ($self->{mode} eq 'cmd') {
|
elsif ($self->{mode} eq 'cmd') {
|
||||||
$line =~ s/\r?\n//;
|
$line =~ s/\r?\n//;
|
||||||
return $self->process_cmd($line);
|
return $self->process_cmd($line);
|
||||||
|
@ -102,10 +102,12 @@ sub connect_respond {
|
|||||||
my ($self, $rc, $msg) = @_;
|
my ($self, $rc, $msg) = @_;
|
||||||
if ($rc == DENY) {
|
if ($rc == DENY) {
|
||||||
$self->respond(550, ($msg || 'Connection from you denied, bye bye.'));
|
$self->respond(550, ($msg || 'Connection from you denied, bye bye.'));
|
||||||
|
$self->disconnect;
|
||||||
return $rc;
|
return $rc;
|
||||||
}
|
}
|
||||||
elsif ($rc == DENYSOFT) {
|
elsif ($rc == DENYSOFT) {
|
||||||
$self->respond(450, ($msg || 'Connection from you temporarily denied, bye bye.'));
|
$self->respond(450, ($msg || 'Connection from you temporarily denied, bye bye.'));
|
||||||
|
$self->disconnect;
|
||||||
return $rc;
|
return $rc;
|
||||||
}
|
}
|
||||||
elsif ($rc == DONE) {
|
elsif ($rc == DONE) {
|
||||||
|
@ -44,9 +44,6 @@ and terminating the SMTP connection.
|
|||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
use warnings;
|
|
||||||
use strict;
|
|
||||||
|
|
||||||
sub register {
|
sub register {
|
||||||
my ($self, $qp, @args) = @_;
|
my ($self, $qp, @args) = @_;
|
||||||
|
|
||||||
@ -61,29 +58,49 @@ sub register {
|
|||||||
@args,
|
@args,
|
||||||
};
|
};
|
||||||
$self->register_hook('connect', 'connect_handler');
|
$self->register_hook('connect', 'connect_handler');
|
||||||
|
$self->register_hook('connect', 'connect_post_handler');
|
||||||
$self->register_hook('mail', 'mail_handler')
|
$self->register_hook('mail', 'mail_handler')
|
||||||
if $self->{_args}->{'defer-reject'};
|
if $self->{_args}->{'defer-reject'};
|
||||||
|
warn("check_earlytalker registered\n");
|
||||||
1;
|
1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub connect_handler {
|
sub connect_handler {
|
||||||
my ($self, $transaction) = @_;
|
my ($self, $transaction) = @_;
|
||||||
|
|
||||||
if ($self->qp->can_read($self->{_args}->{'wait'})) {
|
warn("check early talker");
|
||||||
$self->log(LOGNOTICE, 'remote host started talking before we said hello');
|
my $qp = $self->qp;
|
||||||
if ($self->{_args}->{'defer-reject'}) {
|
my $conn = $qp->connection;
|
||||||
$self->connection->notes('earlytalker', 1);
|
$qp->AddTimer($self->{_args}{'wait'}, sub { read_now($qp, $conn) });
|
||||||
|
$qp->disable_read();
|
||||||
|
return CONTINUATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub read_now {
|
||||||
|
my ($qp, $conn) = @_;
|
||||||
|
|
||||||
|
warn("read now");
|
||||||
|
$qp->enable_read();
|
||||||
|
if (my $data = $qp->read(1024)) {
|
||||||
|
if (length($$data)) {
|
||||||
|
$qp->log(LOGNOTICE, 'remote host started talking before we said hello');
|
||||||
|
$qp->push_back_read($data);
|
||||||
|
$conn->notes('earlytalker', 1);
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
|
$qp->finish_continuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub connect_post_handler {
|
||||||
|
my ($self, $transaction) = @_;
|
||||||
|
|
||||||
|
my $conn = $self->qp->connection;
|
||||||
|
return DECLINED unless $conn->notes('earlytalker');
|
||||||
|
return DECLINED if $self->{'defer-reject'};
|
||||||
my $msg = 'Connecting host started transmitting before SMTP greeting';
|
my $msg = 'Connecting host started transmitting before SMTP greeting';
|
||||||
return (DENY,$msg) if $self->{_args}->{'action'} eq 'deny';
|
return (DENY,$msg) if $self->{_args}->{'action'} eq 'deny';
|
||||||
return (DENYSOFT,$msg) if $self->{_args}->{'action'} eq 'denysoft';
|
return (DENYSOFT,$msg) if $self->{_args}->{'action'} eq 'denysoft';
|
||||||
}
|
return DECLINED; # assume action eq 'log'
|
||||||
}
|
|
||||||
else {
|
|
||||||
$self->log(LOGINFO, 'remote host said nothing spontaneous, proceeding');
|
|
||||||
}
|
|
||||||
return DECLINED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mail_handler {
|
sub mail_handler {
|
||||||
@ -91,6 +108,7 @@ sub mail_handler {
|
|||||||
my $msg = 'Connecting host started transmitting before SMTP greeting';
|
my $msg = 'Connecting host started transmitting before SMTP greeting';
|
||||||
|
|
||||||
return DECLINED unless $self->connection->notes('earlytalker');
|
return DECLINED unless $self->connection->notes('earlytalker');
|
||||||
|
my $msg = 'Connecting host started transmitting before SMTP greeting';
|
||||||
return (DENY,$msg) if $self->{_args}->{'action'} eq 'deny';
|
return (DENY,$msg) if $self->{_args}->{'action'} eq 'deny';
|
||||||
return (DENYSOFT,$msg) if $self->{_args}->{'action'} eq 'denysoft';
|
return (DENYSOFT,$msg) if $self->{_args}->{'action'} eq 'denysoft';
|
||||||
return DECLINED;
|
return DECLINED;
|
||||||
|
Loading…
Reference in New Issue
Block a user