Differences between revisions 5 and 6
Revision 5 as of 2005-03-16 11:24:23
Size: 5738
Revision 6 as of 2009-09-20 22:58:32
Size: 5738
Editor: localhost
Comment: converted to 1.6 markup
No differences found!

ClamAVScan - antivirus scan mailet using ClamAV's CLAMD daemon

ClamAVScan does an antivirus scan check using the ClamAV daemon CLAMD (see http://www.clamav.net/).

It interacts directly with the daemon using the "stream" method, which should have the lowest possible overhead. I've done tests getting less than 2.5 seconds of CPU per megabyte scanned on a 1.5 GHz CPU.

The CLAMD daemon will typically reside on localhost, but could reside on a different host. It may also consist on a set of multiple daemons, each residing on a different server and on different IP number. In such case a DNS host name with multiple IP adresses (round-robin load sharing) is supported by the mailet (but on the same port number).

ClamAV runs on Linux, but there are ports to other OS too. I have ClamAVScan working on Windows XP and 2k using ClamAV For Windows (see http://www.sosdg.org/clamav-win32)

Initialization parameters

The init parameters are as follows:

  • <debug>.

  • <host>: the host name of the server where CLAMD runs. It can either be a machine name, such as "java.sun.com", or a textual representation of its IP address. If a literal IP address is supplied, only the validity of the address format is checked. If the machine name resolves to multiple IP addresses, round-robin load sharing will be used. The default is localhost.

  • <port>: the port on which CLAMD listens. The default is 3310.

  • <maxPings>: the maximum number of connection retries during startup. If the value is 0 no startup test will be done. The default is 6.

  • <pingIntervalMilli>: the interval between each connection retry during startup. The default is 30000 (30 seconds).

  • <streamBufferSize>: the BufferedOutputStream buffer size to use when writing to the stream connection. The default is 8192.


The actions performed are as follows:

  • During initialization:
    1. Gets all config.xml parameters, handling the defaults;
    2. resolves the <host> parameter, creating the round-robin IP list;

    3. connects to CLAMD at the first IP in the round-robin list, on the specified <port>;

    4. if unsuccessful, retries every <pingIntervalMilli> milliseconds up to <maxPings> times;

    5. sends a "PING" request;
    6. waits for a "PONG" answer;
    7. repeats steps 3-6 for every other IP resolved.
  • For every mail
    1. connects to CLAMD at the "next" IP in the round-robin list, on the specified <port>, and increments the "next" index; if the connection request is not accepted, tries with the next one in the list unless all of them have failed;

    2. sends a "STREAM" request;
    3. parses the "PORT streamPort" answer obtaining the port number;

    4. makes a second connection (the stream connection) to CLAMD at the same host (or IP) on the streamPort just obtained;

    5. sends the MimeMessage to CLAMD (using MimeMessage#writeTo(OutputStream)) through the stream connection;

    6. closes the stream connection;

    7. gets the "OK" or "... FOUND" answer from the main connection;
    8. closes the main connection;
    9. sets the "org.apache.james.infected" mail attribute to either "true" or "false";

    10. adds the "X-MessageIsInfected" header to either "true" or "false", depending on the results of the scan.

ClamAV configuration notes

The following parameters are required in clamav.conf:

  • LocalSocket must be commented out

  • TCPSocket must be set to a port# (typically 3310)

  • StreamMaxLength must be >= the James config.xml parameter <maxmessagesize> in SMTP <handler>

  • MaxThreads should? be >= the James config.xml parameter <threads> in <spoolmanager>

  • ScanMail must be uncommented

A James config.xml example

Here follows an example of config.xml definitions deploying CLAMD on localhost, and handling the infected messages:


<!-- Do an antivirus scan -->
<mailet match="All" class="ClamAVScan" onMailetException="ignore"/>

<!-- If infected go to virus processor -->
<mailet match="HasMailAttributeWithValue=org.apache.james.infected, true" class="ToProcessor">
   <processor> virus </processor>

<!-- Check attachment extensions for possible viruses -->
<mailet match="AttachmentFileNameIs=-d -z *.exe *.com *.bat *.cmd *.pif *.scr *.vbs *.avi *.mp3 *.mpeg *.shs" class="ToProcessor" onMatchException="noMatch">
   <processor> bad-extensions </processor>


<!-- Messages containing viruses -->
<processor name="virus">

   <!-- To avoid a loop while bouncing -->
   <mailet match="All" class="SetMailAttribute">
      <org.apache.james.infected>true, bouncing</org.apache.james.infected>

   <mailet match="SMTPAuthSuccessful" class="Bounce">
      <notice>Warning: We were unable to deliver the message below because it was found infected by virus(es).</notice>
   <mailet match="All" class="ToRepository">
   <mailet match="All" class="Null"/>


The reason for the onMailetException="ignore" entry in the ClamAVScan mailet "call" is to avoid losing the message if an Exception is unlikely thrown, but it is just my choice, as I have a "second line" defense blocking "bad extensions" with an "AttachmentFileNameIs=" call.

ClamAVScan (last edited 2009-09-20 22:58:32 by localhost)