001: /*
002: *
003: * Jsmtpd, Java SMTP daemon
004: * Copyright (C) 2005 Jean-Francois POUX, jf.poux@laposte.net
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021: package org.jsmtpd.plugins.filters.ClamAV;
022:
023: import java.io.IOException;
024: import java.net.InetSocketAddress;
025: import java.net.Socket;
026: import java.net.SocketAddress;
027: import java.net.SocketException;
028: import java.util.LinkedList;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.jsmtpd.core.common.PluginInitException;
033: import org.jsmtpd.core.common.filter.FilterTreeFailureException;
034: import org.jsmtpd.core.common.filter.IFilter;
035: import org.jsmtpd.core.mail.Email;
036: import org.jsmtpd.core.send.QueueService;
037:
038: /**
039:
040: *
041: * This plugin is a client to the ClamAV antivirus
042: * daemon.<br>
043: *
044: *
045: * It connects to a remote daemon (via TCP), passes
046: * it the stream to check. Any virus detection
047: * makes the filter fail and drop the mail.
048: * It generates a new mail to inform the sender.<br>
049: *
050: * You need to set up a ClamAV daemon, by example
051: * on your favourite Linux distro :
052: * apt-get install clamav-daemon should do the job
053: * chose a port number to bind it to and edit the
054: * clamav daemon config file clamd.conf :<br><br>
055: * TCPSocket xxx<br>
056: * where xxx is your port number
057: * <br><br>
058: * you should read http://www.clamav.net/doc/
059: * to get info on ClamAV
060: * <br>
061: * <br><br>
062: * 7/3/2005<br>
063: * changed Email type, so adapted here
064: * @author Jean-Francois POUX
065: */
066: public class ClamAVFilter implements IFilter {
067:
068: private String clamdHost;
069: private int clamdPort;
070: private int socketTimeout;
071: private Log log = LogFactory.getLog(ClamAVFilter.class);
072: private int connectionTimeout = 90;
073: /**
074: * Will cause a virus detection to automaticly break the chain
075: * and generate an error message to the sender
076: */
077: private boolean failOnError = false;
078:
079: public boolean doFilter(Email input)
080: throws FilterTreeFailureException {
081: /**
082: * Connect to clamd
083: * send STREAM
084: * read the socket
085: * connect to this socket
086: * send the email data as bytes
087: * close the stream socket
088: * read the response
089: */
090: log.debug("Starting ClamAV Scan of " + input.getDiskName());
091: long time = System.currentTimeMillis();
092: ClamAVChat chat = new ClamAVChat(clamdHost, clamdPort, input
093: .getDataAsByte(), connectionTimeout);
094: boolean response = chat.doScan();
095: log.debug("Scanned " + input.getDiskName() + ", "
096: + input.getSize() + " octs in "
097: + (System.currentTimeMillis() - time) + " ms");
098: if (response == true) {
099: return true;
100: }
101:
102: if (failOnError) {
103: if (!input.getFrom().toString().equals("<>")) // is it an internal mail already ?
104: {
105: LinkedList<String> messages = new LinkedList<String>();
106: messages.add("");
107: messages.add("Hello, this is the Jsmtpd mailer daemon");
108: messages.add("");
109: messages
110: .add("I'm affraid I can't deliver your email to "
111: + input.getRcptAsString());
112: messages.add("");
113: messages.add(getPluginName()
114: + " has detected the virus : "
115: + chat.getVirus());
116: messages.add("");
117: messages.add("This is a fatal error, giving up");
118: Email error = Email.createInternalMail(input.getFrom(),
119: "Mailer-daemon error, virus found", messages,
120: input);
121: QueueService.getInstance().queueMail(error);
122: }
123: throw new FilterTreeFailureException();
124: }
125: return false;
126: }
127:
128: /* (non-Javadoc)
129: * @see jsmtpd.common.IGenericPlugin#getPluginName()
130: */
131: public String getPluginName() {
132:
133: return "Jsmtpd-ClamAV antivirus filter";
134: }
135:
136: /* (non-Javadoc)
137: * @see jsmtpd.common.IGenericPlugin#initPlugin()
138: */
139: public void initPlugin() throws PluginInitException {
140: /**
141: * Read config
142: * connect to tcp port
143: * check answer of pong
144: */
145: log.debug(getPluginName() + " initing");
146:
147: Socket sock = new Socket();
148: SocketAddress sockaddr = new InetSocketAddress(clamdHost,
149: clamdPort);
150: try {
151: sock.setSoTimeout(socketTimeout * 1000);
152: } catch (SocketException e1) {
153: e1.printStackTrace();
154: }
155:
156: try {
157: sock.connect(sockaddr);
158: byte[] b = { 'P', 'I', 'N', 'G', '\n' };
159: sock.getOutputStream().write(b);
160: byte[] c = new byte[4];
161: sock.getInputStream().read(c);
162: String d = new String(c);
163: if (!d.equals("PONG"))
164: throw new PluginInitException();
165:
166: } catch (IOException e2) {
167: throw new PluginInitException();
168: }
169: try {
170: if (sock != null)
171: sock.close();
172: } catch (Exception e3) {
173:
174: }
175: log.debug(getPluginName() + " initialized");
176: }
177:
178: public void shutdownPlugin() {
179:
180: }
181:
182: public void setClamdHost(String host) {
183: clamdHost = host;
184: }
185:
186: public void setClamdPort(int port) {
187: clamdPort = port;
188: }
189:
190: public void setSocketTimeout(int time) {
191: socketTimeout = time;
192: }
193:
194: public void setFailOnError(boolean pa) {
195: failOnError = pa;
196: }
197:
198: public void setConnectionTimeout(int connectionTimeout) {
199: this.connectionTimeout = connectionTimeout;
200: }
201: }
|