Introducing zombies and a reaper. 24184 Accepted connection 0/15 from 64.185.226.20 / mx.<snip>olnews.com 24184 Connection from mx.<snip>olnews.com [64.185.226.20] 24184 (connect) ident::geoip: US, United States 24184 (connect) ident::p0f: Windows 7 or 8 24184 (connect) karma: fail, ZOMBIFIED, 1 naughty, 0 nice, 8 connects 24184 (connect) relay: skip: no match 24184 (connect) dnsbl: skip, zombie 24184 (connect) earlytalker: skip, zombie 24184 220 mail.theartfarm.com ESMTP qpsmtpd 0.84 ready; send us your mail, but not your spam. 24184 dispatching EHLO mx.<snip>olnews.com 24184 (ehlo) helo: skip, zombie 24184 250-mail.theartfarm.com Hi mx.<snip>olnews.com [64.185.226.20] 24184 250-PIPELINING 24184 250-8BITMIME 24184 250-SIZE 25000000 24184 250 STARTTLS 24184 dispatching MAIL FROM:<DIY_Energy@<snip>olnews.com> BODY=8BITMIME 24184 (mail) resolvable_fromhost: skip, zombie 24184 (mail) sender_permitted_from: skip, zombie 24184 250 <DIY_Energy@<snip>olnews.com>, sender OK - how exciting to get mail from you! 24184 dispatching RCPT TO:<user@example> 24184 (rcpt) reaper: disconnecting zombie 24184 550 You were naughty. You are penalized for 0.98 more days. 24184 click, disconnecting 24184 (post-connection) connection_time: 0.102 s. 13489 cleaning up after 24184
Notice the bolded lines. Instead of rejecting the message early, plugins like dnsbl and karma can zombify it. To increase efficiency, other plugins detect the zombie state and skip processing. As you can see from the connection time, the transaction zipped through to completion with no appreciable delay. The zombified connection was harvested by my companion plugin, reaper. It's a very simple plugin. I'll list the code before the POD, because its so short: sub register { my ($self, $qp ) = shift, shift; $self->log(LOGERROR, "Bad arguments") if @_ % 2; $self->{_args} = { @_ }; $self->{_args}{reject} ||= 'rcpt'; $self->{_args}{reject_type} ||= 'disconnect'; my @hooks = qw/ connect mail rcpt data data_post /; my %hooks = map { $_ => 1 } @hooks; my $reject = lc $self->{_args}{reject}; if ( ! $hooks{$reject} ) { $self->log( LOGERROR, "invalid hook $reject" ); }; foreach my $hook ( @hooks ) { next if $hook ne $reject; $self->register_hook($hook, 'reaper'); }; } sub reaper { my $self = shift; my $zombie = $self->connection->notes('zombie') or do { $self->log(LOGINFO, "alive"); return DECLINED; }; $self->log(LOGINFO, "disconnecting zombie"); return ( $self->get_reject_type(), $zombie ); }; NAME reaper - dispose of zombie connections BACKGROUND Rather than immediately terminating naughty connections, plugins often mark the connections and dispose of them later. Examples are dnsbl, karma, greylisting, require_resolvable_fromhost and SPF. This practice is based on RFC standards and the belief that malware will retry less if we disconnect after RCPT. This may have been true, and may still be, but my observations in 2012 suggest it makes no measurable difference whether I disconnect during connect or rcpt. YMMV. Disconnecting later is inefficient because other plugins continue to do their work, oblivious to the fact that the connection is destined for the bit bucket. DESCRIPTION Reaper provides the following: efficiency Reaper provides a way to tell other plugins that a connection is undead. For efficiency, most plugins should skip processing undead connections. Plugins like SpamAssassin and DSPAM can benefit from using these undead connections to train their filters. I believe disconnecting 80% of connections after one DNS query (dnsbl or one DB query (karma) and 0.01s of compute time is an easy win. Even if it did increase the number of connections. zombie cleanup Instead of each plugin having to mop up the undead, reaper does it. Set *reject* to the hook you prefer to reject in and reaper will reject all zombie connections, regardless of who zombified them, exactly when you choose. simplicity Rather than having plugins split processing across hooks, they can run to completion when they have the information they need, issue a *reject zombie* if warranted, and be done. This may help reduce the code divergence between the sync and async deployment models. zombies, aka undead <reaper> provides a a consistent way for plugins to mark connections as zombies or undead. Set the connection note *zombie* to the message you wish to send the naughty sender when reaper rejects them. $self->connection->notes('zombie', $message); This happens for plugins automatically if they use the $self->get_reject() method and have set *reject zombie* in the plugin configuration. CONFIGURATION reject karma reject [ connect | mail | rcpt | data | data_post ] reject_type [ temp | perm | disconnect ] What type of rejection should be sent? See docs/config.pod loglevel Adjust the quantity of logging for this plugin. See docs/logging.pod EXAMPLES Here's how to use zombies and get_reject in your plugin: sub register { my ($self,$qp) = shift, shift; $self->{_args} = { @_ }; $self->{_args}{reject} ||= 'zombie'; }; sub connect_handler { my ($self, $transaction) = @_; ... do a bunch of stuff ... return DECLINED if is_okay(); return $self->get_reject( $message ); }; AUTHOR 2012 - Matt Simerson - msimer...@cpan.org ````````````````````````````````````````````````````````````````````````` Matt Simerson http://matt.simerson.net/ Systems Engineer http://www.tnpi.net/ Mail::Toaster - http://mail-toaster.org/ NicTool - http://www.nictool.com/ `````````````````````````````````````````````````````````````````````````