diff --git a/EICAR/eicar b/EICAR/eicar new file mode 100644 index 0000000000000000000000000000000000000000..704cac859b4ce00807c504f005c26a76542b6201 --- /dev/null +++ b/EICAR/eicar @@ -0,0 +1 @@ +X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 0000000000000000000000000000000000000000..d458dba7b3a0de5643840768e2ce0e12819360f0 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1 @@ +Main-Class: org.slub.rosetta.dps.repository.plugin.SLUBVirusCheckClamAVPlugin diff --git a/README.1st b/README.1st index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..392ce2dbcddb2aed13e36e2ba2192358b6a4ca76 100644 --- a/README.1st +++ b/README.1st @@ -0,0 +1,17 @@ += EICAR = +in directory EICAR/ is a file called "eicar", which contains the standard +antivirus testsignature. This means that this file will ever ever trigger a +virus alert in all known antivirus programs. This inot a bug, but a feature to +ensure that "virus" files will be detected. + +Please test the plugin with this file: +$> java -cp $EXLIBRISCLASSPATH:SLUBVirusCheckPlugin.jar org.slub.rosetta.dps.repository.plugin.SLUBVirusCheckClamAVPlugin EICAR/eicar + + += Administer clamd = +* Ensure clamd.conf enables tcp-sockets, the java prog could only use +tcpsockets. Default is port 3310. The entry should look like: "TCPSocket 3310" + += Compile = +* make clean +* make diff --git a/java/org/slub/rosetta/dps/repository/plugin/SLUBVirusCheckClamAVPlugin.java b/java/org/slub/rosetta/dps/repository/plugin/SLUBVirusCheckClamAVPlugin.java index 5ada144c32fe6e907bbd1fb86a3f98c54cbf8132..1a2252a51181e0300e4492731c61ba0c9d5ece44 100644 --- a/java/org/slub/rosetta/dps/repository/plugin/SLUBVirusCheckClamAVPlugin.java +++ b/java/org/slub/rosetta/dps/repository/plugin/SLUBVirusCheckClamAVPlugin.java @@ -1,13 +1,23 @@ package org.slub.rosetta.dps.repository.plugin; + import java.io.File; import java.io.FileOutputStream; +import java.io.FileInputStream; import java.io.InputStream; import java.util.Calendar; import java.util.Date; import java.text.SimpleDateFormat; import java.util.List; import java.util.Iterator; +import java.io.ByteArrayInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; + import com.exlibris.core.infra.common.exceptions.logging.ExLogger; // import com.exlibris.dps.repository.plugin.virusCheck; @@ -27,21 +37,128 @@ import com.exlibris.dps.repository.plugin.virusChcek.VirusCheckPlugin; * @see */ public class SLUBVirusCheckClamAVPlugin implements VirusCheckPlugin { - // constructor - SLUBVirusCheckClamAVPlugin () { - } - // scans a given file for viruses - public void scan(String fileFullPath) { - } - // outcome of virus check - public String getOutput () { - return null; // dummy - } - public String getAgent () { - return null; // dummy - } - public boolean isVirusFree() { - return true; // dummy + //private static final ExLogger log = ExLogger.getExLogger(SLUBVirusCheckClamAVPlugin.class); + private static final int DEFAULT_CHUNK_SIZE = 2048; + private static final byte[] INSTREAM = "zINSTREAM\0".getBytes(); + private static final String RESPONSEOK = "stream: OK"; + private static final String FOUND_SUFFIX = "FOUND"; + private static final String STREAM_PREFIX = "stream: "; + private enum Status { PASSED, FAILED }; + + private int timeout; + private String host; + private int port; + private String response; + private Status status = Status.FAILED; + private String signature = ""; + + // constructor + SLUBVirusCheckClamAVPlugin (String host, int port, int timeout) { + this.host = host; + this.port = port; + this.timeout = timeout; + //log.info("SLUBVirusCheckPlugin instantiated with host=" + host + " port=" + port + " timeout=" + timeout); + System.out.println( "SLUBVirusCheckPlugin instantiated with host=" + host + " port=" + port + " timeout=" + timeout); + } + + // getter, ex.: get Host, port, timeout + protected String getHost() { return this.host; } + protected int getPort() { return this.port; } + protected int getTimeOut() { return this.timeout; } + protected String getSignature() { return this.signature; } + protected Status getStatus() { return status; } + + // setter + protected void setSignature(String signature) { + this.signature = signature; + } + protected void setStatus(Status status) { + this.status = status; + } + + // scans a given file for viruses + public void scan(String fileFullPath) { + try { + // create a socket + Socket socket = new Socket (); + //socket.connect( new InetSocketAddress(getHost())); + socket.connect( new InetSocketAddress(getHost(), getPort())); + try { + socket.setSoTimeout( getTimeOut() ); + } catch (SocketException e) { + System.out.println( "Could not set socket timeout to " + getTimeOut() + "ms " + e); + //log.error( "Could not set socket timeout to " + getTimeOut() + "ms", e); + } + InputStream in = new FileInputStream( fileFullPath ); + // send stream + DataOutputStream dos = null; + response = ""; + try { + dos = new DataOutputStream(socket.getOutputStream()); + dos.write(INSTREAM); + int read; + byte[] buffer = new byte[DEFAULT_CHUNK_SIZE]; + while ((read = in.read(buffer))> 0) { + dos.writeInt(read); + dos.write(buffer, 0, read); + } + dos.writeInt(0); + dos.flush(); + read = socket.getInputStream().read(buffer); + if (read > 0) response = new String(buffer, 0, read); + } finally { + if (dos != null) try { dos.close(); } catch (IOException e) { + // log.debug("exception closing DOS", e); + System.out.println( "exception closing DOS "+ e ); + } + try { socket.close(); } catch (IOException e) { + // log.debug("exception closing socket", e); + System.out.println( "exception closing socket "+ e); + } + } + //log.debug( "Response: " + response); + System.out.println("Response: " + response); + // parse return code + String result = response.trim(); + if (RESPONSEOK.equals( result ) ) { + setStatus(Status.PASSED); + } else if ( result.endsWith(FOUND_SUFFIX) ) { + setStatus(Status.FAILED); + setSignature(result.substring(STREAM_PREFIX.length(), result.lastIndexOf(FOUND_SUFFIX) - 1 )); + } else { + setStatus(Status.FAILED); + //log.warn("clamd protocol not fully implemented"); + System.out.println("clamd protocol not fully implemented"); + } + } catch (IOException e) { + //log.error("exception creation socket, clamd not available at host=" + host + "port=" + port, e); + System.out.println ("exception creation socket, clamd not available at host=" + host + "port=" + port + " " + e); + setStatus(Status.FAILED); + setSignature("ERROR: clamd not available"); + } + } + + // outcome of virus check + public String getOutput () { + return getSignature(); + } + + public String getAgent () { + return "clamd"; + } + + public boolean isVirusFree() { + //return true; // dummy + return (Status.PASSED == getStatus()); + } + + // stand alone check + public static void main(String [] args) { + SLUBVirusCheckClamAVPlugin plugin = new SLUBVirusCheckClamAVPlugin( "127.0.0.1", 3310, 60); + for (String file:args) { + plugin.scan( file ); + System.out.println("RESULT: " + plugin.isVirusFree() + " SIGNATURE: " + plugin.getOutput()); + } } }