change the communication protocol to RMQ

This commit is contained in:
Volodymyr Smirnov 2020-10-26 16:20:47 +02:00
parent b6c3cb4131
commit 29156b7f10
25 changed files with 439 additions and 173 deletions

View File

@ -8,11 +8,11 @@ namespace MalwareMultiScan.Api.Controllers
[Route("download")] [Route("download")]
public class DownloadController : Controller public class DownloadController : Controller
{ {
private readonly ScanResultsService _scanResultsService; private readonly ScanResultService _scanResultService;
public DownloadController(ScanResultsService scanResultsService) public DownloadController(ScanResultService scanResultService)
{ {
_scanResultsService = scanResultsService; _scanResultService = scanResultService;
} }
[HttpGet("{id}")] [HttpGet("{id}")]
@ -20,7 +20,7 @@ namespace MalwareMultiScan.Api.Controllers
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Index(string id) public async Task<IActionResult> Index(string id)
{ {
var fileStream = await _scanResultsService.ObtainFile(id); var fileStream = await _scanResultService.ObtainFile(id);
if (fileStream == null) if (fileStream == null)
return NotFound(); return NotFound();

View File

@ -1,3 +1,10 @@
using System;
using System.IO;
using System.Threading.Tasks;
using MalwareMultiScan.Api.Services;
using MalwareMultiScan.Shared.Attributes;
using MalwareMultiScan.Shared.Data.Responses;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace MalwareMultiScan.Api.Controllers namespace MalwareMultiScan.Api.Controllers
@ -7,9 +14,45 @@ namespace MalwareMultiScan.Api.Controllers
[Produces("application/json")] [Produces("application/json")]
public class QueueController : Controller public class QueueController : Controller
{ {
public IActionResult Index() private readonly ScanResultService _scanResultService;
public QueueController( ScanResultService scanResultService)
{ {
return Ok(); _scanResultService = scanResultService;
}
[HttpPost("file")]
[ProducesResponseType(typeof(ResultResponse), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> ScanFile([FromForm] IFormFile file)
{
var result = await _scanResultService.CreateScanResult();
string storedFileId;
await using (var uploadFileStream = file.OpenReadStream())
storedFileId = await _scanResultService.StoreFile(file.Name, uploadFileStream);
await _scanResultService.QueueUrlScan(result, Url.Action("Index", "Download", new {id = storedFileId},
Request.Scheme, Request.Host.Value));
return Created(Url.Action("Index", "ScanResults", new {id = result.Id},
Request.Scheme, Request.Host.Value), result);
}
[HttpPost("url")]
[ProducesResponseType(typeof(ResultResponse), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> ScanUrl([FromForm] [UrlValidation] Uri url)
{
var result = await _scanResultService.CreateScanResult();
var resultUrl = Url.Action("Index", "ScanResults", new {id = result.Id},
Request.Scheme, Request.Host.Value);
await _scanResultService.QueueUrlScan(result, url.ToString());
return Created(resultUrl, result);
} }
} }
} }

View File

@ -1,41 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using MalwareMultiScan.Api.Data.Configuration;
using MalwareMultiScan.Api.Data.Response;
using MalwareMultiScan.Api.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace MalwareMultiScan.Api.Controllers
{
[ApiController]
[Route("backends")]
[Produces("application/json")]
public class ScanBackendsController : Controller
{
private readonly ScanBackendService _scanBackendService;
public ScanBackendsController(ScanBackendService scanBackendService)
{
_scanBackendService = scanBackendService;
}
private async Task<ScanBackendResponse> GetScanBackendResponse(ScanBackend backend)
{
return new ScanBackendResponse
{
Id = backend.Id,
Name = backend.Name,
Online = await _scanBackendService.Ping(backend)
};
}
[HttpGet]
[ProducesResponseType(typeof(ScanBackendResponse[]), StatusCodes.Status200OK)]
public async Task<IActionResult> Index()
{
return Ok(await Task.WhenAll(
_scanBackendService.List.Select(GetScanBackendResponse).ToArray()));
}
}
}

View File

@ -1,4 +1,5 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using MalwareMultiScan.Api.Data.Models;
using MalwareMultiScan.Api.Services; using MalwareMultiScan.Api.Services;
using MalwareMultiScan.Shared.Data.Responses; using MalwareMultiScan.Shared.Data.Responses;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -11,21 +12,23 @@ namespace MalwareMultiScan.Api.Controllers
[Produces("application/json")] [Produces("application/json")]
public class ScanResultsController : Controller public class ScanResultsController : Controller
{ {
private readonly ScanResultsService _scanResultsService; private readonly ScanResultService _scanResultService;
public ScanResultsController(ScanResultsService scanResultsService) public ScanResultsController(ScanResultService scanResultService)
{ {
_scanResultsService = scanResultsService; _scanResultService = scanResultService;
} }
[HttpPost("{id}")] [HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(typeof(ScanResult), StatusCodes.Status200OK)]
public async Task<IActionResult> Index(string id, [FromBody] ResultResponse result) public async Task<IActionResult> Index(string id)
{ {
await _scanResultsService.UpdateScanResultForBackend( var scanResult = await _scanResultService.GetScanResult(id);
id, result.Backend, true, result.Success, result.Threats);
return NoContent(); if (scanResult == null)
return NotFound();
return Ok(scanResult);
} }
} }
} }

View File

@ -4,6 +4,5 @@ namespace MalwareMultiScan.Api.Data.Configuration
{ {
public string Id { get; set; } public string Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Endpoint { get; set; }
} }
} }

View File

@ -11,8 +11,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="EasyNetQ" Version="5.6.0" />
<PackageReference Include="MongoDB.Driver" Version="2.11.3" /> <PackageReference Include="MongoDB.Driver" Version="2.11.3" />
<PackageReference Include="MongoDB.Driver.GridFS" Version="2.11.3" /> <PackageReference Include="MongoDB.Driver.GridFS" Version="2.11.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<PackageReference Include="YamlDotNet" Version="8.1.2" /> <PackageReference Include="YamlDotNet" Version="8.1.2" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,41 @@
using System.Threading;
using System.Threading.Tasks;
using EasyNetQ;
using MalwareMultiScan.Shared.Data.Messages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace MalwareMultiScan.Api.Services
{
public class ReceiverHostedService : IHostedService
{
private readonly IBus _bus;
private readonly IConfiguration _configuration;
private readonly ScanResultService _scanResultService;
public ReceiverHostedService(IBus bus, IConfiguration configuration, ScanResultService scanResultService)
{
_bus = bus;
_configuration = configuration;
_scanResultService = scanResultService;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_bus.Receive<ScanResultMessage>(_configuration.GetValue<string>("ResultsSubscriptionId"), async message =>
{
await _scanResultService.UpdateScanResultForBackend(
message.Id, message.Backend, true, true, message.Threats);
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_bus.Dispose();
return Task.CompletedTask;
}
}
}

View File

@ -1,12 +1,11 @@
using System; using System;
using System.IO; using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using EasyNetQ;
using MalwareMultiScan.Api.Data.Configuration; using MalwareMultiScan.Api.Data.Configuration;
using MalwareMultiScan.Shared.Data.Requests; using MalwareMultiScan.Api.Data.Models;
using MalwareMultiScan.Shared.Data.Messages;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NamingConventions;
@ -14,14 +13,12 @@ namespace MalwareMultiScan.Api.Services
{ {
public class ScanBackendService public class ScanBackendService
{ {
private readonly IHttpClientFactory _httpClientFactory; private readonly IBus _bus;
private readonly ILogger<ScanBackendService> _logger;
public ScanBackendService(IConfiguration configuration, IHttpClientFactory httpClientFactory, public ScanBackendService(IConfiguration configuration, IBus bus)
ILogger<ScanBackendService> logger)
{ {
_httpClientFactory = httpClientFactory; _bus = bus;
_logger = logger;
var configurationPath = configuration.GetValue<string>("BackendsConfiguration"); var configurationPath = configuration.GetValue<string>("BackendsConfiguration");
if (!File.Exists(configurationPath)) if (!File.Exists(configurationPath))
@ -38,73 +35,12 @@ namespace MalwareMultiScan.Api.Services
public ScanBackend[] List { get; } public ScanBackend[] List { get; }
public async Task<bool> Ping(ScanBackend backend) public async Task QueueUrlScan(ScanResult result, ScanBackend backend, string fileUrl)
{ {
var cancellationTokenSource = new CancellationTokenSource( await _bus.SendAsync(backend.Id, new ScanRequestMessage
TimeSpan.FromSeconds(1));
try
{ {
using var httpClient = _httpClientFactory.CreateClient(); Id = result.Id,
Uri = new Uri(fileUrl)
var pingResponse = await httpClient.GetAsync(
new Uri(new Uri(backend.Endpoint), "/ping"), cancellationTokenSource.Token);
pingResponse.EnsureSuccessStatusCode();
}
catch (Exception exception)
{
_logger.LogError(
exception, $"Failed to ping {backend.Id}");
return false;
}
return true;
}
private async Task<bool> QueueScan(ScanBackend backend, string uri, HttpContent content)
{
var cancellationTokenSource = new CancellationTokenSource(
TimeSpan.FromSeconds(5));
using var httpClient = _httpClientFactory.CreateClient();
try
{
var response = await httpClient.PostAsync(
new Uri(new Uri(backend.Endpoint), uri), content, cancellationTokenSource.Token);
response.EnsureSuccessStatusCode();
}
catch (Exception exception)
{
_logger.LogError(
exception, $"Failed to initiate scan against {backend.Id}");
return false;
}
return true;
}
public async Task<bool> QueueFileScan(
ScanBackend backend, string fileName, Stream fileStream, string callbackUrl)
{
return await QueueScan(backend, "/scan/file", new MultipartFormDataContent
{
{new StringContent(callbackUrl), nameof(FileRequest.CallbackUrl)},
{new StreamContent(fileStream), nameof(FileRequest.InputFile), fileName}
});
}
public async Task<bool> QueueUrlScan(
ScanBackend backend, string url, string callbackUrl)
{
return await QueueScan(backend, "/scan/url", new MultipartFormDataContent
{
{new StringContent(callbackUrl), nameof(UrlRequest.CallbackUrl)},
{new StringContent(url), nameof(UrlRequest.InputUrl)}
}); });
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -13,7 +14,7 @@ using MongoDB.Driver.GridFS;
namespace MalwareMultiScan.Api.Services namespace MalwareMultiScan.Api.Services
{ {
public class ScanResultsService public class ScanResultService
{ {
private const string CollectionName = "ScanResults"; private const string CollectionName = "ScanResults";
@ -22,7 +23,7 @@ namespace MalwareMultiScan.Api.Services
private readonly ScanBackendService _scanBackendService; private readonly ScanBackendService _scanBackendService;
public ScanResultsService(IMongoDatabase db, ScanBackendService scanBackendService) public ScanResultService(IMongoDatabase db, ScanBackendService scanBackendService)
{ {
_scanBackendService = scanBackendService; _scanBackendService = scanBackendService;
@ -32,15 +33,27 @@ namespace MalwareMultiScan.Api.Services
public async Task<ScanResult> CreateScanResult() public async Task<ScanResult> CreateScanResult()
{ {
var scanResult = new ScanResult(); var scanResult = new ScanResult
{
Results = _scanBackendService.List.ToDictionary(
k => k.Id, v => new ScanResultEntry())
};
await _collection.InsertOneAsync(scanResult); await _collection.InsertOneAsync(scanResult);
return scanResult; return scanResult;
} }
public async Task UpdateScanResultForBackend( public async Task<ScanResult> GetScanResult(string id)
string resultId, string backendId, bool completed, bool succeeded, string[] threats = null) {
var result = await _collection.FindAsync(
Builders<ScanResult>.Filter.Where(r => r.Id == id));
return await result.FirstOrDefaultAsync();
}
public async Task UpdateScanResultForBackend(string resultId, string backendId,
bool completed = false, bool succeeded = false, string[] threats = null)
{ {
var filterScanResult = Builders<ScanResult>.Filter.Where(r => r.Id == resultId); var filterScanResult = Builders<ScanResult>.Filter.Where(r => r.Id == resultId);
@ -54,31 +67,16 @@ namespace MalwareMultiScan.Api.Services
await _collection.UpdateOneAsync(filterScanResult, updateScanResult); await _collection.UpdateOneAsync(filterScanResult, updateScanResult);
} }
public async Task QueueFileScan(ScanResult result, string fileName, Stream fileStream, string callbackUrl) public async Task QueueUrlScan(ScanResult result, string fileUrl)
{ {
foreach (var backend in _scanBackendService.List) foreach (var backend in _scanBackendService.List)
{ await _scanBackendService.QueueUrlScan(result, backend, fileUrl);
var queueResult = await _scanBackendService.QueueFileScan(
backend, fileName, fileStream, callbackUrl);
await UpdateScanResultForBackend(result.Id, backend.Id, !queueResult, queueResult);
}
}
public async Task QueueUrlScan(ScanResult result, string url, string callbackUrl)
{
foreach (var backend in _scanBackendService.List)
{
var queueResult = await _scanBackendService.QueueUrlScan(
backend, url, callbackUrl);
await UpdateScanResultForBackend(result.Id, backend.Id, !queueResult, queueResult);
}
} }
public async Task<string> StoreFile(string fileName, Stream fileStream) public async Task<string> StoreFile(string fileName, Stream fileStream)
{ {
var objectId = await _bucket.UploadFromStreamAsync(fileName, fileStream); var objectId = await _bucket.UploadFromStreamAsync(
fileName, fileStream);
return objectId.ToString(); return objectId.ToString();
} }

View File

@ -1,8 +1,11 @@
using EasyNetQ;
using MalwareMultiScan.Api.Services; using MalwareMultiScan.Api.Services;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using MongoDB.Driver; using MongoDB.Driver;
namespace MalwareMultiScan.Api namespace MalwareMultiScan.Api
@ -18,8 +21,11 @@ namespace MalwareMultiScan.Api
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
services.AddSingleton(x =>
RabbitHutch.CreateBus(_configuration.GetConnectionString("RabbitMQ")));
services.AddSingleton<ScanBackendService>(); services.AddSingleton<ScanBackendService>();
services.AddSingleton<ScanResultsService>(); services.AddSingleton<ScanResultService>();
services.AddSingleton( services.AddSingleton(
serviceProvider => serviceProvider =>
@ -32,13 +38,45 @@ namespace MalwareMultiScan.Api
services.AddControllers(); services.AddControllers();
services.AddHttpClient(); services.AddHttpClient();
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("MalwareMultiScan",
new OpenApiInfo
{
Title = "MalwareMultiScan",
Version = "1.0.0"
});
});
services.AddHostedService<ReceiverHostedService>();
} }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ {
app.UseRouting(); app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); var forwardingOptions = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
};
forwardingOptions.KnownNetworks.Clear();
forwardingOptions.KnownProxies.Clear();
app.UseForwardedHeaders(forwardingOptions);
app.UseEndpoints(endpoints => endpoints.MapControllers());
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.DocumentTitle = "MalwareMultiScan";
options.SwaggerEndpoint(
$"/swagger/{options.DocumentTitle}/swagger.json", options.DocumentTitle);
});
} }
} }
} }

View File

@ -9,11 +9,13 @@
"AllowedHosts": "*", "AllowedHosts": "*",
"ConnectionStrings": { "ConnectionStrings": {
"Mongo": "mongodb://localhost:27017" "Mongo": "mongodb://localhost:27017",
"RabbitMQ": "host=localhost"
}, },
"DatabaseName": "MalwareMultiScan", "DatabaseName": "MalwareMultiScan",
"BackendsConfiguration": "backends.yaml", "ResultsSubscriptionId": "mms.results",
"StoreFileUploads": true
"BackendsConfiguration": "backends.yaml"
} }

View File

@ -1,7 +1,2 @@
- id: windows-defender - id: dummy
name: Windows Defender name: Dummy Backend
endpoint: http://localhost:9902
- id: clamav
name: ClamAV
endpoint: http://localhost:9901

View File

@ -17,6 +17,8 @@ namespace MalwareMultiScan.Backends.Backends.Implementations
protected override Regex MatchRegex { get; } = protected override Regex MatchRegex { get; } =
new Regex(@"(\S+): (?<threat>[\S]+) FOUND", RegexOptions.Compiled | RegexOptions.Multiline); new Regex(@"(\S+): (?<threat>[\S]+) FOUND", RegexOptions.Compiled | RegexOptions.Multiline);
protected override bool ThrowOnNonZeroExitCode { get; } = false;
protected override string GetBackendArguments(string path) protected override string GetBackendArguments(string path)
{ {
return $"--no-summary {path}"; return $"--no-summary {path}";

View File

@ -0,0 +1,39 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MalwareMultiScan.Shared.Interfaces;
using Microsoft.AspNetCore.Http;
namespace MalwareMultiScan.Backends.Backends.Implementations
{
public class DummyScanBackend : IScanBackend
{
public string Id { get; } = "dummy";
public Task<string[]> ScanAsync(string path, CancellationToken cancellationToken)
{
return Scan();
}
public Task<string[]> ScanAsync(Uri uri, CancellationToken cancellationToken)
{
return Scan();
}
public Task<string[]> ScanAsync(IFormFile file, CancellationToken cancellationToken)
{
return Scan();
}
public Task<string[]> ScanAsync(Stream stream, CancellationToken cancellationToken)
{
return Scan();
}
private static Task<string[]> Scan()
{
return Task.FromResult(new[] {"Malware.Dummy.Result"});
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using MalwareMultiScan.Backends.Backends.Implementations;
using MalwareMultiScan.Shared.Data.Enums;
using MalwareMultiScan.Shared.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace MalwareMultiScan.Scanner.Extensions
{
public static class ServiceCollectionExtensions
{
public static void AddScanningBackend(this IServiceCollection services, BackendType type)
{
using var provider = services.BuildServiceProvider();
var logger = provider.GetService<ILogger>();
services.AddSingleton<IScanBackend>(type switch
{
BackendType.Dummy => new DummyScanBackend(),
BackendType.Defender => new WindowsDefenderScanBackend(logger),
BackendType.Clamav => new ClamavScanBackend(logger),
BackendType.DrWeb => new DrWebScanBackend(logger),
BackendType.Kes => new KesScanBackend(logger),
BackendType.Comodo => new ComodoScanBackend(logger),
BackendType.Sophos => new SophosScanBackend(logger),
BackendType.McAfee => new McAfeeScanBackend(logger),
_ => throw new NotImplementedException()
});
}
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EasyNetQ" Version="5.6.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MalwareMultiScan.Backends\MalwareMultiScan.Backends.csproj" />
<ProjectReference Include="..\MalwareMultiScan.Shared\MalwareMultiScan.Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,31 @@
using System.Threading.Tasks;
using MalwareMultiScan.Scanner.Extensions;
using MalwareMultiScan.Shared.Data.Enums;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace MalwareMultiScan.Scanner
{
public static class Program
{
public static async Task Main(string[] args)
{
await Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(configure =>
{
configure.AddJsonFile("settings.json");
configure.AddEnvironmentVariables();
})
.ConfigureServices((context, services) =>
{
services.AddLogging();
services.AddScanningBackend(
context.Configuration.GetValue<BackendType>("BackendType"));
services.AddHostedService<ScanHostedService>();
}).RunConsoleAsync();
}
}
}

View File

@ -0,0 +1,10 @@
{
"profiles": {
"MalwareMultiScan.Scanner": {
"commandName": "Project",
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using EasyNetQ;
using MalwareMultiScan.Shared.Data.Messages;
using MalwareMultiScan.Shared.Interfaces;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace MalwareMultiScan.Scanner
{
public class ScanHostedService : IHostedService
{
private readonly IConfiguration _configuration;
private readonly ILogger<ScanHostedService> _logger;
private readonly IScanBackend _scanBackend;
private IBus _bus;
public ScanHostedService(ILogger<ScanHostedService> logger, IConfiguration configuration, IScanBackend scanBackend)
{
_logger = logger;
_configuration = configuration;
_scanBackend = scanBackend;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_bus = RabbitHutch.CreateBus(
_configuration.GetConnectionString("RabbitMQ"));
_bus.Receive<ScanRequestMessage>(
_scanBackend.Id, Scan);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_bus.Dispose();
return Task.CompletedTask;
}
private async Task Scan(ScanRequestMessage message)
{
_logger.LogInformation(
$"Starting scan of {message.Uri} via backend {_scanBackend.Id}");
var cancellationTokenSource = new CancellationTokenSource(
TimeSpan.FromSeconds(_configuration.GetValue<int>("MaxScanningTime")));
try
{
await _bus.SendAsync(_configuration.GetValue<string>("ResultsSubscriptionId"), new ScanResultMessage
{
Id = message.Id,
Backend = _scanBackend.Id,
Threats = await _scanBackend.ScanAsync(
message.Uri, cancellationTokenSource.Token)
});
}
catch (Exception exception)
{
_logger.LogError(
exception, "Scanning failed with exception");
}
}
}
}

View File

@ -0,0 +1,20 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"BackendType": "Dummy",
"MaxWorkers": 5,
"MaxScanningTime": 60,
"ResultsSubscriptionId": "mms.results",
"ConnectionStrings": {
"RabbitMQ": "host=localhost;prefetchcount=1"
}
}

View File

@ -9,8 +9,8 @@ namespace MalwareMultiScan.Shared.Attributes
{ {
var uri = (Uri) value; var uri = (Uri) value;
if (uri == null || uri.Scheme != "http" && uri.Scheme != "https") if (uri == null || !uri.IsAbsoluteUri || uri.Scheme != "http" && uri.Scheme != "https")
return new ValidationResult("Only http(s) URLs are supported"); return new ValidationResult("Only http(s) absolute URLs are supported");
return ValidationResult.Success; return ValidationResult.Success;
} }

View File

@ -2,6 +2,7 @@ namespace MalwareMultiScan.Shared.Data.Enums
{ {
public enum BackendType public enum BackendType
{ {
Dummy,
Defender, Defender,
Clamav, Clamav,
DrWeb, DrWeb,

View File

@ -0,0 +1,11 @@
using System;
namespace MalwareMultiScan.Shared.Data.Messages
{
public class ScanRequestMessage
{
public string Id { get; set; }
public Uri Uri { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace MalwareMultiScan.Shared.Data.Messages
{
public class ScanResultMessage
{
public string Id { get; set; }
public string Backend { get; set; }
public string[] Threats { get; set; }
}
}

View File

@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Backends",
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Api", "MalwareMultiScan.Api\MalwareMultiScan.Api.csproj", "{7B63B897-D390-4617-821F-F96799CBA2F4}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Api", "MalwareMultiScan.Api\MalwareMultiScan.Api.csproj", "{7B63B897-D390-4617-821F-F96799CBA2F4}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Scanner", "MalwareMultiScan.Scanner\MalwareMultiScan.Scanner.csproj", "{8A16A3C4-2AE3-4F63-8280-635FF7878080}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -30,5 +32,9 @@ Global
{7B63B897-D390-4617-821F-F96799CBA2F4}.Debug|Any CPU.Build.0 = Debug|Any CPU {7B63B897-D390-4617-821F-F96799CBA2F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B63B897-D390-4617-821F-F96799CBA2F4}.Release|Any CPU.ActiveCfg = Release|Any CPU {7B63B897-D390-4617-821F-F96799CBA2F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B63B897-D390-4617-821F-F96799CBA2F4}.Release|Any CPU.Build.0 = Release|Any CPU {7B63B897-D390-4617-821F-F96799CBA2F4}.Release|Any CPU.Build.0 = Release|Any CPU
{8A16A3C4-2AE3-4F63-8280-635FF7878080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A16A3C4-2AE3-4F63-8280-635FF7878080}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A16A3C4-2AE3-4F63-8280-635FF7878080}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A16A3C4-2AE3-4F63-8280-635FF7878080}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal