The ClamAVScanner class is a Spring service that provides functionality to scan files for viruses using a ClamAV server.
It connects to a ClamAV server (configurable host/port, with optional SSL) and streams file data for virus scanning.
It is used to ensure uploaded files are safe and do not contain viruses
public VirusScanDto scan(final InputStream inputStream) throws IOException {
Socket socket = null;
InputStream inStream = null;
OutputStream outStream = null;
try {
if (secureScan)
socket = SSLSocketFactory.getDefault().createSocket();
else {
socket = new Socket();
}
socket.connect(new InetSocketAddress(host, port), connectionTimeout);
socket.setSoTimeout(readTimeout);
outStream = new BufferedOutputStream(socket.getOutputStream());
outStream.write("zINSTREAM\0".getBytes(StandardCharsets.UTF_8));
outStream.flush();
byte[] buffer = new byte[chunkSize];
inStream = socket.getInputStream();
int read = inputStream.read(buffer);
while (read >= 0) {
byte[] chunkSize = ByteBuffer.allocate(4).putInt(read).array();
outStream.write(chunkSize);
outStream.write(buffer, 0, read);
if (inStream.available() > 0) {
byte[] reply = IOUtils.toByteArray(inStream);
throw new IOException(
"Reply from server: " + new String(reply, StandardCharsets.UTF_8));
}
read = inputStream.read(buffer);
}
outStream.write(new byte[]{0, 0, 0, 0});
outStream.flush();
logger.debug("Time require to process file " + (System.currentTimeMillis() - startTime) + " milli sec");
return populateVirusScanResult(new String(IOUtils.toByteArray(inStream)).trim());
} catch (IOException e) {
return new VirusScanDto("CONNECTION_FAILED", e.getMessage());
} finally {
try {
if (Objects.nonNull(inStream)) {
inStream.close();
}
if (Objects.nonNull(outStream)) {
outStream.flush();
outStream.close();
}
if (socket != null && socket.isConnected()) {
socket.close();
}
} catch (Exception e) {
System.out.println("Error closing instance, " + e.getMessage());
}
}
}
private VirusScanDto populateVirusScanResult(final String result) {
VirusScanDto scanResult = new VirusScanDto();
scanResult.setStatus("FAILED");
scanResult.setResult(result);
if (result == null || result.isEmpty()) {
scanResult.setStatus("ERROR");
} else if (responseOk.equals(result)) {
scanResult.setStatus("PASSED");
} else if (result.endsWith(foundSuffix)) {
scanResult.setSignature(
result.substring(streamPrefix.length(), result.lastIndexOf(foundSuffix) - 1).trim());
} else if (result.endsWith(errorSuffix)) {
scanResult.setStatus("ERROR");
}
return scanResult;
}