On Wed, 21 Apr 2010, Dave O'Neill wrote:

 On Wed, Apr 21, 2010 at 10:34:37AM -0700, Tom Brown wrote:
> The reason for this post is to ask if such patches would be likely to > make
>   it into the mainstream? Or possibly if others have already done this?

 At some point in the near future, I'd like to convert MIMEDefang to use
 File::VirusScan (see http://search.cpan.org/perldoc?File::VirusScan).  This
 is essentially the existing MIMEDefang antivirus integration code pulled out
 and modularized into something a bit more maintainable.

 Patches against File::VirusScan::Engine::Daemon::ClamAV::Clamd to use a
 host/port will definitely be accepted, and might be enough of a spur to get
 David or I to finally finish integrating the module back into MIMEDefang.

 If you feel like implementing this, take a look at the other
 File::VirusScan::Engine::Daemon subclasses for inspiration, as some of them
 already handle taking a host/port instead of a UNIX socket.

File-Virus wants perl 5.008. The boxes that are short of ram are by definition old boxes which aren't that current... and changing perl is a big change, whereas clamd is only used by mimedefang... So I went back to patching the code we have today.

oh, whoops, I seem to have a problem with zero byte messages. (fixed) I was aware I was risking that, when I wrote the defensive code to return an error if there was nothing to scan. Is there a variable somewhere I can use to see the message length?

requiring this from /etc/mail/mimedefang-filter, with an appropriate setting from ClamdSock seems to work just fine, e.g.

   $ClamdSock = "scanner.example.com:3310";

and the file. It does cause a subroutine redefined warning, but
otherwise it seems quiet and effective.

-Tom

#  rewrite of message_contains_virus_clamd from /usr/bin/mimedefang.pl
#  need to rewrite it so we can support offsite clamd

use File::Find qw(find);
use IO::Socket;

sub clamd_sock($) {  # we need to call this multiple times
     my $clamd_sock = shift;

     if ($clamd_sock =~ m/^(.*):(\d+)$/) {
             my ($host,$port) = ($1, $2);
             $sock = IO::Socket::INET->new(
                 PeerAddr => $host,
                 PeerPort => $port,
                 Proto => 'tcp',
                 Type => SOCK_STREAM,
                 Timeout => 10
            ) ;
             if (! $sock) {
                 md_syslog('err', "$MsgID: cannot connect to clamd
                 ($host:$port)");
                 return undef;
             }

     } else {
        $sock = IO::Socket::UNIX->new(Peer => $clamd_sock);
     }
     $sock;
}

#  NAH. returns undef on error, '' on no virus, else virus name
#  returns '' on no virus, else virus name or error message

sub clamd_instream_scan($$) {
    my ($sock, $filename) = @_;

    return '' unless -f $filename;
    open(FILE,"<$filename") or return "could not open $filename ($!)";
    $sock->print("zINSTREAM\0")
       or return "dead socket, can't send INSTREAM";
    my $buf = '';
    while (1) {
       my $num = sysread(FILE,$buf, 128*1024);
       $sock->print( pack("N", $num )) ;  # length
       $sock->print( $buf )  # data
          or return "dead socket, can't send data for INSTREAM";
       $sock->flush();
       last if ($num <= 0);  # we DO want to send the zero byte count to clamd
    }
    close FILE;

    $buf = ();
    while( defined( my $line = $sock->getline() )) {
        $buf .= $line;
    }
    $sock->close(); # encapsulated and terminated, but one transaction per sock
    return '' if ($buf eq "stream: OK\0");  # return '' if no virus
    return $buf;
}

# ***********************************************************************
# % PROCEDURE: message_contains_virus_clamd
# % ARGUMENTS:
#  clamd_sock (optional) -- clamd socket path
#  %RETURNS:
#  1 if any file in the working directory contains a virus
#  %DESCRIPTION:
#   Invokes the clamd daemon (http://clamav.elektrapro.com/)
#   on the entire message.
# ***********************************************************************
sub message_contains_virus_clamd (;$) {
     my ($clamd_sock) = $ClamdSock;
     $clamd_sock = shift if (@_ > 0);
     $clamd_sock = "/var/spool/MIMEDefang/clamd.sock" if
     (!defined($clamd_sock));
     my ($output,$sock);

     $sock = clamd_sock($clamd_sock); #
     # PING/PONG test to make sure clamd is alive
     if (defined $sock) {
         $sock->print("nPING\n");
         $sock->flush;
         $sock->sysread($output,256);
         chomp($output);
         if (! defined($output) || $output ne "PONG") {
           md_syslog('err', "$MsgID: clamd is not responding");
           return (wantarray ? (999, 'cannot-execute', 'tempfail') : 999);
        }
     }
     else {
         md_syslog('err', "$MsgID: Could not connect to clamd daemon at
         $clamd_sock");
         return (wantarray ? (999, 'cannot-execute', 'tempfail') : 999);
     }

    # open up a socket and scan each file in ./Work
    # $sock = clamd_sock($clamd_sock); #
     if (1) {
         my @files = ();

         # snarfed from matt seargants clamd.pm
         find(
             sub {
                 if (-f $File::Find::name) {
                     push @files, $File::Find::name;
                 }
             }, "$CWD/Work" );


        # let us be defensive:
# nope, fails for zero byte messages
#        return (wantarray ? (999, 'error no files to scan?', 'tempfail') : 999)
#          unless @files;

         $output = '';
         foreach my $f (@files) {
            $sock = clamd_sock($clamd_sock)
               or return (wantarray ? (999, 'can\'t connect to clamd',
            'tempfail') : 999);
            $output = clamd_instream_scan( $sock, $f );
            last if ($output);
         }
         if ($output =~ /: (.+) FOUND/) {
             $VirusScannerMessages .= "clamd found the $1 virus.\n";
             $VirusName = $1;
             return (wantarray ? (1, 'virus', 'quarantine') : 1);
         }
         if ($output) {
            # then we got an error...
            return (wantarray ? (999, "error while clamd scanning: $output",
            'tempfail') : 999)
        }
     }
     # No errors, no infected files were found
     return (wantarray ? (0, 'ok', 'ok') : 0);
}


1;


_______________________________________________
NOTE: If there is a disclaimer or other legal boilerplate in the above
message, it is NULL AND VOID.  You may ignore it.

Visit http://www.mimedefang.org and http://www.roaringpenguin.com
MIMEDefang mailing list [email protected]
http://lists.roaringpenguin.com/mailman/listinfo/mimedefang

Reply via email to