cvs commit: qpsmtpd/plugins rhsbl

[email protected] (Ask "Bj?rn" Hansen)
Newsgroups perl.cvs.qpsmtpd
Message-ID <[email protected]>
cvsuser     04/07/31 18:54:16

  Modified:    .        Changes
               plugins  rhsbl
  Log:
    Make the rhsbl plugin do DNS lookups in the background. (Mark Powell)
  
  Revision  Changes    Path
  1.73      +2 -0      qpsmtpd/Changes
  
  Index: Changes
  ===================================================================
  RCS file: /cvs/public/qpsmtpd/Changes,v
  retrieving revision 1.72
  retrieving revision 1.73
  diff -u -w -r1.72 -r1.73
  --- Changes	28 Jul 2004 17:06:45 -0000	1.72
  +++ Changes	1 Aug 2004 01:54:15 -0000	1.73
  @@ -2,6 +2,8 @@
   
     [ many changes from cvs logs, gah ]
   
  +  Make the rhsbl plugin do DNS lookups in the background. (Mark Powell)
  +
     Fix warning in count_unrecognized_commands plugin (thanks to spaze
     and Roger Walker)
   
  
  
  
  1.5       +97 -21    qpsmtpd/plugins/rhsbl
  
  Index: rhsbl
  ===================================================================
  RCS file: /cvs/public/qpsmtpd/plugins/rhsbl,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -w -r1.4 -r1.5
  --- rhsbl	5 Mar 2004 12:46:24 -0000	1.4
  +++ rhsbl	1 Aug 2004 01:54:16 -0000	1.5
  @@ -1,43 +1,119 @@
  -
   sub register {
     my ($self, $qp) = @_;
  -  $self->register_hook("mail", "mail_handler");
  -  $self->register_hook("rcpt", "rcpt_handler");
  +
  +  $self->register_hook('mail', 'mail_handler');
  +  $self->register_hook('rcpt', 'rcpt_handler');
  +  $self->register_hook('disconnect', 'disconnect_handler');
   }
   
   sub mail_handler {
     my ($self, $transaction, $sender) = @_;
  -  # lookup the address here; but always just return DECLINED
  -  # we will store the state for rejection when rcpt is being run, some
  +
  +  my $res = new Net::DNS::Resolver;
  +  my $sel = IO::Select->new();
  +  my %rhsbl_zones_map = ();
  +
  +  # Perform any RHS lookups in the background. We just send the query packets here
  +  # and pick up any results in the RCPT handler.
     # MTAs gets confused when you reject mail during MAIL FROM:
  -  #
  -  # If we were really clever we would do the lookup in the background
  -  # but that must wait for another day.  (patches welcome! :-) )
  -  if ($sender->format ne "<>" and $self->qp->config('rhsbl_zones')) {
  +
       my %rhsbl_zones = map { (split /\s+/, $_, 2)[0,1] } $self->qp->config('rhsbl_zones');
  -    my $host = $sender->host;
  +
  +  if ($sender->format ne '<>' and %rhsbl_zones) {
  +    my $helo = $self->qp->connection->hello_host;
  +    push(my @hosts, $sender->host);
  +    push(@hosts, $helo) if $helo && $helo ne $sender->host;
  +    for my $host (@hosts) {
       for my $rhsbl (keys %rhsbl_zones) {
  -      $transaction->notes('rhsbl', "Mail from $host rejected because it $rhsbl_zones{$rhsbl}")
  -	if check_rhsbl($self, $rhsbl, $host);
  +        $self->log(LOGDEBUG, "Checking $host.$rhsbl for A record in the background");
  +        $sel->add($res->bgsend("$host.$rhsbl"));
  +        $rhsbl_zones_map{"$host.$rhsbl"} = $rhsbl_zones{$rhsbl};
       }
     }
  +
  +    %{$self->{_rhsbl_zones_map}} = %rhsbl_zones_map;
  +    $transaction->notes('rhsbl_sockets', $sel);
  +  } else {
  +    $self->log(LOGDEBUG, 'no RHS checks necessary');
  +  }
  +
     return DECLINED;
   }
   
   sub rcpt_handler {
     my ($self, $transaction, $rcpt) = @_;
  -  my $note = $transaction->notes('rhsbl');
  -  return (DENY, $note) if $note;
  +  my $host = $transaction->sender->host;
  +  my $hello = $self->qp->connection->hello_host;
  +
  +  my $result = $self->process_sockets;
  +  if ($result && defined($self->{_rhsbl_zones_map}{$result})) {
  +    if ($result =~ /^$host\./ ) {
  +      return (DENY, "Mail from $host rejected because it " . $self->{_rhsbl_zones_map}{$result});
  +    } else {
  +      return (DENY, "Mail from HELO $hello rejected because it " . $self->{_rhsbl_zones_map}{$result});
  +    }
  +  }
  +  return (DENY, $result) if $result;
     return DECLINED;
   }
   
  -sub check_rhsbl {
  -  my ($self, $rhsbl, $host) = @_;
  -  return 0 unless $host;
  -  $self->log(LOGDEBUG, "checking $host in $rhsbl");
  -  return 1 if ((gethostbyname("$host.$rhsbl"))[4]);
  -  return 0;
  +sub process_sockets {
  +  my ($self) = @_;
  +  my $trans = $self->transaction;
  +  my $result = '';
  +
  +  return $trans->notes('rhsbl') if $trans->notes('rhsbl');
  +
  +  my $res = new Net::DNS::Resolver;
  +  my $sel = $trans->notes('rhsbl_sockets') or return '';
  +
  +  $self->log(LOGDEBUG, 'waiting for rhsbl dns');
  +
  +  # don't wait more than 8 seconds here
  +  my @ready = $sel->can_read(8);
  +
  +  $self->log(LOGDEBUG, 'DONE waiting for rhsbl dns, got ' , scalar @ready, ' answers ...') ;
  +  return '' unless @ready;
  +
  +  for my $socket (@ready) {
  +    my $query = $res->bgread($socket);
  +    $sel->remove($socket);
  +    undef $socket;
  +
  +    if ($query) {
  +      foreach my $rr ($query->answer) {
  +        $self->log(LOGDEBUG, 'got an ' . $rr->type . ' record ' . $rr->name);
  +        if ($rr->type eq 'A') {
  +          $result = $rr->name;
  +          $self->log(LOGDEBUG, "A record found for $result with IP " . $rr->address);
  +          last;
  +        }
  +      }
  +    } else {
  +      $self->log(LOGCRIT, "query failed: ", $res->errorstring) unless $res->errorstring eq 'NXDOMAIN';
  +    }
  +
  +    if ($result) {
  +      #kill any other pending I/O
  +      $trans->notes('rhsbl_sockets', undef);
  +      return $trans->notes('rhsbl', $result);
  +    }
  +  }
  +
  +  if ($sel->count) {
  +    # loop around if we have dns results left
  +    return $self->process_sockets();
   }
   
  +  # if there was more to read; then forget it
  +  $trans->notes('rhsbl_sockets', undef);
   
  +  return $trans->notes('rhsbl', $result);
  +}
   
  +sub disconnect_handler {
  +  my ($self, $transaction) = @_;
  +
  +  $transaction->notes('rhsbl_sockets', undef);
  +  return DECLINED;
  +}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.