View Javadoc

1   package org.eparapher.core.tools;
2   
3   import java.io.BufferedReader;
4   import java.io.BufferedWriter;
5   import java.io.IOException;
6   import java.io.InputStreamReader;
7   import java.io.OutputStreamWriter;
8   import java.io.PrintWriter;
9   import java.net.Socket;
10  import java.net.UnknownHostException;
11  
12  import org.apache.log4j.Logger;
13  
14  /**
15   * <pre>
16   * Nom de la classe : ClamavUtils.java.
17   * Description : Client java pour ClamAV
18   * 
19   * Commandes reconnues par ClamD (daemon) :
20   * 
21   * PING : voir etat du demon
22   * VERSION : connaitre la version
23   * RELOAD : recharge les bases de donnees
24   * SHUTDOWN : arret du service
25   * SCAN file/directory : scan le fichier ou le repertoire avec le support des archives actives
26   * RAWSCAN file/directory : scan le fichier ou le repertoire avec le support des archives desactives
27   * CONTSCAN : scan le fichier ou le repertoire avec le support des archives actives (pas d'arret si detection de virus)
28   * STREAM scan stream : clamd retourne un nouveau numero de port
29   * SESSION / END : demarre/arrete une session clamd
30   *  
31   * </pre>
32   * 
33   * @date 19/11/2007
34   */
35  public final class ClamavUtils {
36  
37    /** serialVersionUID. */
38    private static final long serialVersionUID = -721510070948622509L;
39  
40    /** Base name for loggers. */
41    private static final String LOGGER_ROOTNAME = ClamavUtils.class.getName();
42  
43    /** Port d'ecoute du demon. */
44    private int port = 0;
45    
46    /** IP du serveur sur lequel se situe le demon ClamAV. */
47    private String server = null;
48    
49    /** Socket. */
50    private Socket skt = null;
51  
52    /**
53     * CONSTANTES
54     */
55    
56    /*
57     * Commandes envoyees a ClamD
58     */
59  
60    /** Commande permettant de verifier l'etat du demon. */
61    public static final String COMMANDE_PING = "PING";
62  
63    /** Commande permettant de recharger les bases de donnees. */
64    public static final String COMMANDE_RELOAD = "RELOAD";
65    
66    /** Commande permettant de scanner un fichier ou un repertoire. */
67    public static final String COMMANDE_SCAN = "SCAN";
68    
69    /** Commande permettant d'arreter le demon. */
70    public static final String COMMANDE_SHUTDOWN = "SHUTDOWN";
71  
72    /** Commande permettant de recuperer la version de ClamAV. */
73    public static final String COMMANDE_VERSION = "VERSION";
74    
75    /*
76     * Codes de retour
77     */
78    
79    /** Code de retour pour un fichier sain (ne contenant pas de virus). */
80    public static final byte CODE_RETOUR_FICHIER_SAIN = 0;
81    
82    /** Code de retour pour un fichier verole (contenant un virus). */
83    public static final byte CODE_RETOUR_FICHIER_VEROLE = 1;
84    
85    /** Code de retour pour un fichier non traite. */
86    public static final byte CODE_RETOUR_FICHIER_NON_TRAITE = 2;
87    
88    /** Code de retour pour une erreur de scan. */
89    public static final byte CODE_RETOUR_ERREUR_SCAN = 3;
90    
91    /*
92     * Reponse du demon ClamD
93     */
94    
95    /** La reponse de ClamD contient OK s'il n'y a pas de virus dans le fichier. */
96    private static final String CLAMAV_REPONSE_OK = "OK";
97    
98    /** La reponse de ClamD contient FOUND si un virus est trouve dans le fichier. */
99    private static final String CLAMAV_REPONSE_FOUND = "FOUND";
100   
101   /** La reponse de ClamD pour un PING. */
102   private static final String REPONSE_PING = "PONG";
103   
104   /**
105    * FIN CONSTANTES
106    */
107   
108   /**
109    * Constructeur.
110    * 
111    * @param pServeur : IP du serveur sur lequel se situe le demon ClamAV
112    * @param pPort : port d'ecoute du demon
113    */
114   public ClamavUtils(final String pServeur, final int pPort) {
115     
116     this.server = pServeur;
117     this.port = pPort;
118   } // ClamavUtils
119   
120   /**
121    * Se connecte au serveur.
122    * 
123    * @return Boolean : true=connected, false=not connected
124    */
125   private Boolean connect() {
126     
127     final Logger logger = Logger.getLogger(LOGGER_ROOTNAME + "#connect");
128     if (logger.isDebugEnabled()) {
129       logger.debug("Connecting to ClamAV server " + server + ":" + port );
130     }
131     
132     // Valeur de retour
133     Boolean retour = Boolean.FALSE;
134     
135     try {
136       
137       // Creation du socket
138       skt = new Socket(server, port);
139       
140       // Verification de la connectivite
141       retour = isConnected();
142       
143       if (logger.isDebugEnabled()) {
144         if (retour) {
145           logger.debug("connexion to ClamAV is [OK]");
146         } else {
147           logger.debug("connexion to ClamAV is [KO]");
148         }
149       }
150 
151     } catch (UnknownHostException uhe) {
152       logger.error("Bad IP address : " + uhe.getMessage());
153       
154     } catch (IOException ioe) {
155       logger.error("input/output error : " + ioe.getMessage());
156     }
157     
158     return retour;
159   } // connect
160   
161   /**
162    * Se deconnecte du serveur et libere toutes les ressources.
163    */
164   private void disconnect() {
165     
166     final Logger logger = Logger.getLogger(LOGGER_ROOTNAME + "#disconnect");
167     if (logger.isDebugEnabled()) {
168       logger.debug("tentative de deconnexion du serveur [" + server + "] sur le port [" + port + "]");
169     }
170 
171     // Fermeture du socket
172     try {
173       
174       if (isConnected()) {
175         skt.close();
176         
177         if (skt.isClosed()) {
178           logger.debug("deconnexion du serveur [OK]");
179         } else {
180           logger.debug("deconnexion du serveur [KO]");
181         }
182       }
183 
184     } catch (IOException ioe) {
185       logger.error("erreur d'entree/sortie a la fermeture du socket [" + ioe.getMessage() + "]");
186     }
187 
188   } // disconnect
189 
190   /**
191    * Verifie que la socket est connecte.
192    * 
193    * @return Boolean : true=connected, false=not connected.
194    */
195   private Boolean isConnected() {
196     return new Boolean(skt.isConnected());
197   } // isConnected
198   
199   /**
200    * Envoi une commande au serveur et retourne la reponse.
201    * 
202    * @param pCommande : commande a envoyer
203    * @return String : resultat de la commande
204    */
205   public String sendCommand(final String pCommande) {
206     
207     final Logger logger = Logger.getLogger(LOGGER_ROOTNAME + "#sendCommand");
208     
209     // Valeur de retour
210     String retour = null;
211     
212     // Si commande renseignee
213     if (!isEmptyString(pCommande)) {
214       
215       if (logger.isDebugEnabled()) {
216         logger.debug("envoi de la commande [" + pCommande + "]");
217       }
218       
219       // Creation d'une socket
220       if (connect()) {
221         
222         // Flux d'entree et de sortie
223         PrintWriter out = null;
224         BufferedReader in = null;
225 
226         try {
227 
228           // Envoi de la commande
229           out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(skt.getOutputStream())), Boolean.TRUE);
230           out.println(pCommande);
231 
232           // Lecture du resultat de la commande
233           in = new BufferedReader(new InputStreamReader(skt.getInputStream()));
234           retour = in.readLine();
235           
236           if (logger.isDebugEnabled()) {
237             logger.debug("reponse de la commande [" + retour + "]");
238           }
239           
240         } catch (IOException ioe) {
241           logger.error("IOException sur l'envoi de la commande ou la reception du resultat [" + ioe.getMessage() + "]");
242         
243         } finally {
244           
245           // Fermeture du flux d'entree
246           try {
247             in.close();
248             logger.debug("fermeture des ressources du flux d'entree [OK]");
249 
250           } catch (IOException ioe) {
251             logger.error("IOException a la fermeture du flux d'entree [" + ioe.getMessage() + "]");
252           }
253           
254           // Fermeture du flux de sortie
255           out.close();
256           logger.debug("fermeture des ressources du flux de sortie [OK]");
257           
258           // Fermeture de la socket
259           disconnect();
260         }
261 
262       } else {
263         logger.debug("pas de connexion");
264       }
265       
266     } else {
267       logger.debug("la commande est vide");
268     }
269     
270     return retour;
271   } // sendCommand
272   
273   //From import org.apache.commons.lang.StringUtils
274   private boolean isEmptyString(String str) {
275 	if (str == null)
276 		return true;
277 	if (str.equals(""))
278 		return true;
279 	return false;
280 }
281 
282 /**
283    * Verifie si un fichier contient un virus.
284    * 
285    * @param pFile : chemin complet du fichier a scanner
286    * @return byte : code d'erreur : 0=fichier sain, 1=fichier verole, 2=fichier non traite, 3=erreur de scan
287    */
288   public byte scanFile(final String pFile) {
289 
290     final Logger logger = Logger.getLogger(LOGGER_ROOTNAME + "#scanFile");
291 
292     // Valeur de retour
293     byte retour = -1;
294 
295     // Le fichier ne doit pas etre vide
296     if (!isEmptyString(pFile)) {
297     
298       // Construction de la commande
299       String command = COMMANDE_SCAN + " " + pFile;
300 
301       // Envoi de la commande
302       String result = sendCommand(command);
303       if (result != null) {
304 
305         // Decoupage du resultat
306         int pos = result.lastIndexOf(":") + 2;
307         result = result.substring(pos);
308 
309         // Traitement du resultat
310         if (result.equals(CLAMAV_REPONSE_OK)) {
311           retour = CODE_RETOUR_FICHIER_SAIN;
312           
313         } else if (result.contains(CLAMAV_REPONSE_FOUND)) {
314           retour = CODE_RETOUR_FICHIER_VEROLE;
315           
316         } else {
317           retour = CODE_RETOUR_ERREUR_SCAN;
318         }
319 
320       } else {
321         retour = CODE_RETOUR_ERREUR_SCAN;
322       }
323       
324     } else {
325       retour = CODE_RETOUR_FICHIER_NON_TRAITE;
326       logger.error("aucun fichier a verifier");
327     }
328 
329     if (logger.isDebugEnabled()) {
330       logger.debug("code de retour [" + retour + "]");
331     }
332     
333     return retour;
334   } // scanFile
335   
336   /**
337    * Verifie l'etat du demon.
338    * 
339    * @return Boolean : true=daemon started, false=daemon stop
340    */
341   public Boolean ping() {
342     
343     final Logger logger = Logger.getLogger(LOGGER_ROOTNAME + "#ping");
344 
345     // Valeur de retour
346     Boolean retour = null;
347     
348     // Construction de la commande
349     String command = COMMANDE_PING;
350 
351     // Envoi de la commande
352     String result = sendCommand(command);
353     if (result != null) {
354 
355       // Traitement du resultat
356       retour = new Boolean(result.equals(REPONSE_PING));
357       if (logger.isDebugEnabled()) {
358         logger.debug("ping [" + retour + "]");
359       }
360       
361     } else {
362       
363       if (logger.isDebugEnabled()) {
364         logger.debug("ping [error]");
365       }
366     }
367 
368     return retour;
369   } // ping
370   
371   /**
372    * Recupere la version de ClamAV.
373    * 
374    * @return String : version de ClamAV
375    */
376   public String getVersion() {
377     
378     final Logger logger = Logger.getLogger(LOGGER_ROOTNAME + "#getVersion");
379 
380     // Valeur de retour
381     String retour = null;
382 
383     // Construction de la commande
384     String command = COMMANDE_VERSION;
385 
386     // Envoi de la commande
387     retour = sendCommand(command);
388     if (retour != null) {
389 
390       if (logger.isDebugEnabled()) {
391         logger.debug("version de ClamAV [" + retour + "]");
392       }
393       
394     } else {
395       
396       if (logger.isDebugEnabled()) {
397         logger.debug("version de ClamAV [error]");
398       }
399     }
400     
401     return retour;
402   } // getVersion
403   
404 } // ClamavUtils
405