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:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031:
032: /**
033: * Connects to a clamd daemon, chats with it to pass it the email data and
034: * a response of virus or not in it.
035: * @author Jean-Francois POUX
036: */
037: public class ClamAVChat {
038:
039: private String host;
040: private int port;
041: private byte[] toScan;
042:
043: private Socket chat = null;
044: private Socket data = null;
045: private String virus = "";
046:
047: private Log log = LogFactory.getLog(ClamAVChat.class);
048:
049: private int connectionTimeout = 90;
050:
051: public ClamAVChat(String host, int port, byte[] toScan,
052: int connectionTimeout) {
053: this .host = host;
054: this .port = port;
055: this .toScan = toScan;
056: this .connectionTimeout = connectionTimeout;
057: }
058:
059: /**
060: * check for viruses
061: * @return true if none found, false if found
062: */
063: public boolean doScan() {
064: chat = new Socket();
065: SocketAddress sockaddr = new InetSocketAddress(host, port);
066: try {
067: chat.setSoTimeout(connectionTimeout * 1000);
068: } catch (SocketException e1) {
069: }
070:
071: String responseValue = "";
072: try {
073: //First, try to connect to the clamd
074: chat.connect(sockaddr);
075: byte[] b = { 'S', 'T', 'R', 'E', 'A', 'M', '\n' };
076: chat.getOutputStream().write(b); // Write the initialisation command
077:
078: // Now, read byte per byte until we find a LF.
079: byte[] rec = new byte[1];
080: while (true) {
081: chat.getInputStream().read(rec);
082: if (rec[0] == '\n')
083: break;
084: responseValue += new String(rec);
085: }
086: log.debug("response: " + responseValue);
087:
088: // In the response value, there's an integer. It's the TCP port that the clamd has allocated for us for data stream.
089: int dataPort = -1;
090: if (responseValue.contains(" "))
091: dataPort = Integer
092: .parseInt(responseValue.split(" ")[1]);
093:
094: // Now, we connect to the data port obtained before.
095: data = new Socket();
096: SocketAddress sockaddrData = new InetSocketAddress(host,
097: dataPort);
098: try {
099: data.setSoTimeout(connectionTimeout * 1000); // we leave 1m30 before closing connection is clamd does not issue a response.
100: } catch (SocketException e1) {
101: }
102: try {
103: data.connect(sockaddrData);
104: data.getOutputStream().write(toScan); // We write to the data stream the content of the mail
105: data.close(); // Then close the stream, so that clamd knows it's the end of the stream.
106: } catch (IOException e2) {
107:
108: }
109: // Now that's we have send the body of the mail to clamd, we wait for the response on the chat stream.
110: responseValue = "";
111: while (true) {
112: try {
113: chat.getInputStream().read(rec);
114: } catch (IOException e3) {
115: break;
116: }
117: if (rec[0] == '\n')
118: break;
119: responseValue += new String(rec);
120: }
121: log.debug("response: " + responseValue);
122:
123: } catch (IOException e) {
124: log.debug("IO Error : " + e.getMessage());
125: } finally {
126: if (data != null) {
127: try {
128: data.close();
129: } catch (IOException e3) {
130: }
131: }
132:
133: if (chat != null) {
134: try {
135: chat.close();
136: } catch (IOException e3) {
137: }
138: }
139: }
140:
141: if (responseValue == null) {
142: log.error("response is null. Passing the mail anyway...");
143: return true;
144: }
145:
146: if (responseValue.contains("ERROR")) {
147: log.error("response is erroneous (" + responseValue
148: + "). Passing the mail anyway...");
149: }
150:
151: if (responseValue.equals("stream: OK")) // clamd writes this if the stream we sent does not contains viruses.
152: return true;
153:
154: virus = responseValue; // Else there is an error, the response contains the name of the identified virus
155: return false;
156:
157: }
158:
159: public String getVirus() {
160: return virus;
161: }
162: }
|