Files
apprepodbmgr/apprepodbmgr.Core/Workers/Clamd.cs

323 lines
13 KiB
C#
Raw Normal View History

2017-05-18 22:23:49 +01:00
//
// Author:
// Natalia Portillo claunia@claunia.com
//
// Copyright (c) 2017, © Claunia.com
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the distribution.
// * Neither the name of the [ORGANIZATION] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
2017-12-30 00:32:21 +00:00
2017-05-18 22:23:49 +01:00
using System;
2017-05-19 18:16:16 +01:00
using System.Collections.Generic;
2017-05-18 22:23:49 +01:00
using System.IO;
2017-12-30 00:32:21 +00:00
using System.Net.Sockets;
using System.Threading;
2017-05-19 18:16:16 +01:00
using System.Threading.Tasks;
using nClam;
2017-12-30 00:32:21 +00:00
using SharpCompress.Compressors;
2017-05-18 22:23:49 +01:00
using SharpCompress.Compressors.BZip2;
2017-05-19 18:16:16 +01:00
using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.LZMA;
2017-05-18 22:23:49 +01:00
namespace apprepodbmgr.Core
2017-05-18 22:23:49 +01:00
{
public static partial class Workers
{
static ClamClient clam;
public static void InitClamd()
{
2020-08-22 21:37:02 +01:00
if(!Settings.Current.UseClamd ||
!Settings.Current.UseAntivirus)
2017-05-18 22:23:49 +01:00
{
2017-12-30 00:32:21 +00:00
Context.ClamdVersion = null;
2020-08-22 21:37:02 +01:00
2017-05-18 22:23:49 +01:00
return;
}
TestClamd();
}
2020-08-22 21:37:02 +01:00
public static void TestClamd() => Task.Run(async () =>
2017-05-18 22:23:49 +01:00
{
2020-08-22 21:37:02 +01:00
try
2017-05-18 22:23:49 +01:00
{
2020-08-22 21:37:02 +01:00
clam = new ClamClient(Settings.Current.ClamdHost, Settings.Current.ClamdPort);
Context.ClamdVersion = await clam.GetVersionAsync();
}
catch(SocketException) {}
}).Wait();
2017-05-18 22:23:49 +01:00
2017-12-30 00:32:21 +00:00
public static void ClamScanFileFromRepo(DbFile file)
2017-05-18 22:23:49 +01:00
{
try
{
2017-12-30 00:32:21 +00:00
if(Context.ClamdVersion == null)
2017-05-18 22:23:49 +01:00
{
2017-12-30 00:32:21 +00:00
Failed?.Invoke("clamd is not usable");
2020-08-22 21:37:02 +01:00
2017-05-18 22:23:49 +01:00
return;
}
2020-08-22 21:37:02 +01:00
if(clam == null)
Failed?.Invoke("clamd is not initalized");
2017-05-18 22:23:49 +01:00
2017-12-30 00:32:21 +00:00
string repoPath;
2017-05-18 22:23:49 +01:00
AlgoEnum algorithm;
if(File.Exists(Path.Combine(Settings.Current.RepositoryPath, file.Sha256[0].ToString(),
file.Sha256[1].ToString(), file.Sha256[2].ToString(),
2017-12-30 00:32:21 +00:00
file.Sha256[3].ToString(), file.Sha256[4].ToString(), file.Sha256 + ".gz")))
2017-05-18 22:23:49 +01:00
{
repoPath = Path.Combine(Settings.Current.RepositoryPath, file.Sha256[0].ToString(),
file.Sha256[1].ToString(), file.Sha256[2].ToString(),
2017-12-30 00:32:21 +00:00
file.Sha256[3].ToString(), file.Sha256[4].ToString(), file.Sha256 + ".gz");
2020-08-22 21:37:02 +01:00
2017-05-18 22:23:49 +01:00
algorithm = AlgoEnum.GZip;
}
else if(File.Exists(Path.Combine(Settings.Current.RepositoryPath, file.Sha256[0].ToString(),
2017-12-30 00:32:21 +00:00
file.Sha256[1].ToString(), file.Sha256[2].ToString(),
file.Sha256[3].ToString(), file.Sha256[4].ToString(),
file.Sha256 + ".bz2")))
2017-05-18 22:23:49 +01:00
{
repoPath = Path.Combine(Settings.Current.RepositoryPath, file.Sha256[0].ToString(),
file.Sha256[1].ToString(), file.Sha256[2].ToString(),
2017-12-30 00:32:21 +00:00
file.Sha256[3].ToString(), file.Sha256[4].ToString(), file.Sha256 + ".bz2");
2020-08-22 21:37:02 +01:00
2017-05-18 22:23:49 +01:00
algorithm = AlgoEnum.BZip2;
}
else if(File.Exists(Path.Combine(Settings.Current.RepositoryPath, file.Sha256[0].ToString(),
2017-12-30 00:32:21 +00:00
file.Sha256[1].ToString(), file.Sha256[2].ToString(),
file.Sha256[3].ToString(), file.Sha256[4].ToString(),
file.Sha256 + ".lzma")))
2017-05-18 22:23:49 +01:00
{
repoPath = Path.Combine(Settings.Current.RepositoryPath, file.Sha256[0].ToString(),
file.Sha256[1].ToString(), file.Sha256[2].ToString(),
file.Sha256[3].ToString(), file.Sha256[4].ToString(),
file.Sha256 + ".lzma");
2020-08-22 21:37:02 +01:00
2017-05-18 22:23:49 +01:00
algorithm = AlgoEnum.LZMA;
}
2017-06-06 22:50:45 +01:00
else if(File.Exists(Path.Combine(Settings.Current.RepositoryPath, file.Sha256[0].ToString(),
2017-12-30 00:32:21 +00:00
file.Sha256[1].ToString(), file.Sha256[2].ToString(),
file.Sha256[3].ToString(), file.Sha256[4].ToString(),
file.Sha256 + ".lz")))
2017-06-06 22:50:45 +01:00
{
repoPath = Path.Combine(Settings.Current.RepositoryPath, file.Sha256[0].ToString(),
file.Sha256[1].ToString(), file.Sha256[2].ToString(),
2017-12-30 00:32:21 +00:00
file.Sha256[3].ToString(), file.Sha256[4].ToString(), file.Sha256 + ".lz");
2020-08-22 21:37:02 +01:00
2017-06-06 22:50:45 +01:00
algorithm = AlgoEnum.LZip;
}
2017-05-18 22:23:49 +01:00
else
{
2017-12-30 00:32:21 +00:00
Failed?.Invoke($"Cannot find file with hash {file.Sha256} in the repository");
2020-08-22 21:37:02 +01:00
2017-05-18 22:23:49 +01:00
return;
}
2017-12-30 00:32:21 +00:00
ClamScanResult result = null;
Stream zStream = null;
2017-05-18 22:23:49 +01:00
if(Settings.Current.ClamdIsLocal)
2020-08-22 21:37:02 +01:00
if(algorithm == AlgoEnum.LZMA ||
algorithm == AlgoEnum.LZip)
2017-05-18 22:23:49 +01:00
{
2020-08-22 21:37:02 +01:00
string tmpFile = Path.Combine(Settings.Current.TemporaryFolder, Path.GetTempFileName());
var outFs = new FileStream(tmpFile, FileMode.Create, FileAccess.Write);
var inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read);
2017-05-18 22:23:49 +01:00
2017-06-06 22:50:45 +01:00
if(algorithm == AlgoEnum.LZMA)
{
byte[] properties = new byte[5];
inFs.Read(properties, 0, 5);
inFs.Seek(8, SeekOrigin.Current);
zStream = new LzmaStream(properties, inFs, inFs.Length - 13, file.Length);
}
2020-08-22 21:37:02 +01:00
else
zStream = new LZipStream(inFs, CompressionMode.Decompress);
2017-05-18 22:23:49 +01:00
2017-12-30 00:32:21 +00:00
UpdateProgress?.Invoke("Uncompressing file...", null, 0, 0);
2017-05-19 01:27:05 +01:00
2020-08-22 21:37:02 +01:00
#if DEBUG
stopwatch.Restart();
2020-08-22 21:37:02 +01:00
#endif
2017-05-18 22:23:49 +01:00
zStream.CopyTo(outFs);
zStream.Close();
outFs.Close();
2020-08-22 21:37:02 +01:00
#if DEBUG
stopwatch.Stop();
2020-08-22 21:37:02 +01:00
2017-12-30 00:32:21 +00:00
Console.WriteLine("Core.ClamScanFileFromRepo({0}): Uncompressing took {1} seconds", file,
stopwatch.Elapsed.TotalSeconds);
2020-08-22 21:37:02 +01:00
#endif
2017-05-18 22:23:49 +01:00
2017-12-30 00:32:21 +00:00
UpdateProgress?.Invoke("Requesting local scan to clamd server...", null, 0, 0);
2017-05-19 01:27:05 +01:00
2020-08-22 21:37:02 +01:00
#if DEBUG
stopwatch.Restart();
2020-08-22 21:37:02 +01:00
#endif
Task.Run(async () =>
{
result = await clam.ScanFileOnServerMultithreadedAsync(tmpFile);
}).Wait();
#if DEBUG
stopwatch.Stop();
2020-08-22 21:37:02 +01:00
2017-12-30 00:32:21 +00:00
Console.WriteLine("Core.ClamScanFileFromRepo({0}): Clamd took {1} seconds to scan", file,
stopwatch.Elapsed.TotalSeconds);
2020-08-22 21:37:02 +01:00
#endif
2017-05-18 22:23:49 +01:00
File.Delete(tmpFile);
}
else
2017-05-19 01:27:05 +01:00
{
2017-12-30 00:32:21 +00:00
UpdateProgress?.Invoke("Requesting local scan to clamd server...", null, 0, 0);
2017-05-19 01:27:05 +01:00
2020-08-22 21:37:02 +01:00
#if DEBUG
stopwatch.Restart();
2020-08-22 21:37:02 +01:00
#endif
Task.Run(async () =>
{
result = await clam.ScanFileOnServerMultithreadedAsync(repoPath);
}).Wait();
#if DEBUG
stopwatch.Stop();
2020-08-22 21:37:02 +01:00
2017-12-30 00:32:21 +00:00
Console.WriteLine("Core.ClamScanFileFromRepo({0}): Clamd took {1} seconds to scan", file,
stopwatch.Elapsed.TotalSeconds);
2020-08-22 21:37:02 +01:00
#endif
2017-05-19 01:27:05 +01:00
}
2017-05-18 22:23:49 +01:00
else
{
2020-08-22 21:37:02 +01:00
var inFs = new FileStream(repoPath, FileMode.Open, FileAccess.Read);
2017-05-18 22:23:49 +01:00
switch(algorithm)
{
case AlgoEnum.GZip:
2017-12-30 00:32:21 +00:00
zStream = new GZipStream(inFs, CompressionMode.Decompress);
2020-08-22 21:37:02 +01:00
2017-05-18 22:23:49 +01:00
break;
case AlgoEnum.BZip2:
2017-12-30 00:32:21 +00:00
zStream = new BZip2Stream(inFs, CompressionMode.Decompress);
2020-08-22 21:37:02 +01:00
2017-05-18 22:23:49 +01:00
break;
case AlgoEnum.LZMA:
byte[] properties = new byte[5];
inFs.Read(properties, 0, 5);
inFs.Seek(8, SeekOrigin.Current);
zStream = new LzmaStream(properties, inFs, inFs.Length - 13, file.Length);
2020-08-22 21:37:02 +01:00
2017-06-06 22:50:45 +01:00
break;
case AlgoEnum.LZip:
2017-12-30 00:32:21 +00:00
zStream = new LZipStream(inFs, CompressionMode.Decompress);
2020-08-22 21:37:02 +01:00
2017-05-18 22:23:49 +01:00
break;
}
2017-12-30 00:32:21 +00:00
UpdateProgress?.Invoke("Uploading file to clamd server...", null, 0, 0);
2017-05-19 01:27:05 +01:00
2020-08-22 21:37:02 +01:00
#if DEBUG
stopwatch.Restart();
2020-08-22 21:37:02 +01:00
#endif
Task.Run(async () =>
{
result = await clam.SendAndScanFileAsync(zStream);
}).Wait();
#if DEBUG
stopwatch.Stop();
2020-08-22 21:37:02 +01:00
2017-12-30 00:32:21 +00:00
Console.WriteLine("Core.ClamScanFileFromRepo({0}): Clamd took {1} seconds to scan", file,
stopwatch.Elapsed.TotalSeconds);
2020-08-22 21:37:02 +01:00
#endif
2017-05-18 22:23:49 +01:00
zStream.Close();
}
2020-08-22 21:37:02 +01:00
if(result.InfectedFiles != null &&
result.InfectedFiles.Count > 0)
2017-05-18 22:23:49 +01:00
{
file.HasVirus = true;
2017-12-30 00:32:21 +00:00
file.Virus = result.InfectedFiles[0].VirusName;
2017-05-18 22:23:49 +01:00
}
else if(file.HasVirus == null)
{
// If no scan has been done, mark as false.
// If a positive has already existed don't overwrite it.
file.HasVirus = false;
2017-12-30 00:32:21 +00:00
file.Virus = null;
2017-05-18 22:23:49 +01:00
}
2017-12-30 00:32:21 +00:00
file.ClamTime = DateTime.UtcNow;
2017-05-18 22:23:49 +01:00
2017-12-30 00:32:21 +00:00
dbCore.DbOps.UpdateFile(file);
2017-05-18 22:23:49 +01:00
2017-12-30 00:32:21 +00:00
ScanFinished?.Invoke(file);
2017-05-18 22:23:49 +01:00
}
2020-08-22 21:37:02 +01:00
catch(ThreadAbortException) {}
2017-05-18 22:23:49 +01:00
catch(Exception ex)
{
2017-12-30 00:32:21 +00:00
Failed?.Invoke($"Exception {ex.Message} when calling clamd");
2020-08-22 21:37:02 +01:00
#if DEBUG
2017-06-13 18:21:12 +01:00
Console.WriteLine("Exception {0}\n{1}", ex.Message, ex.InnerException);
2020-08-22 21:37:02 +01:00
#endif
2017-05-18 22:23:49 +01:00
}
}
public static void ClamScanAllFiles()
{
2017-12-30 00:32:21 +00:00
UpdateProgress2?.Invoke("Asking database for files", null, 0, 0);
2020-08-22 21:37:02 +01:00
#if DEBUG
stopwatch.Restart();
2020-08-22 21:37:02 +01:00
#endif
2017-12-30 00:32:21 +00:00
if(!dbCore.DbOps.GetNotAvFiles(out List<DbFile> files))
Failed?.Invoke("Could not get files from database.");
2020-08-22 21:37:02 +01:00
#if DEBUG
stopwatch.Stop();
2020-08-22 21:37:02 +01:00
2017-12-30 00:32:21 +00:00
Console.WriteLine("Core.ClamScanAllFiles(): Took {0} seconds to get files from database",
stopwatch.Elapsed.TotalSeconds);
2020-08-22 21:37:02 +01:00
stopwatch.Restart();
2020-08-22 21:37:02 +01:00
#endif
int counter = 0;
2020-08-22 21:37:02 +01:00
2017-12-30 00:32:21 +00:00
foreach(DbFile file in files)
{
2017-12-30 00:32:21 +00:00
UpdateProgress2?.Invoke($"Scanning file {counter} of {files.Count}", null, counter, files.Count);
ClamScanFileFromRepo(file);
counter++;
}
2020-08-22 21:37:02 +01:00
#if DEBUG
stopwatch.Stop();
2020-08-22 21:37:02 +01:00
2017-12-30 00:32:21 +00:00
Console.WriteLine("Core.ClamScanAllFiles(): Took {0} seconds scan all pending files",
stopwatch.Elapsed.TotalSeconds);
2020-08-22 21:37:02 +01:00
#endif
2017-12-30 00:32:21 +00:00
Finished?.Invoke();
}
2017-05-18 22:23:49 +01:00
}
2017-12-30 00:32:21 +00:00
}