sketch out interface and impl

This commit is contained in:
Green Sky 2023-10-25 02:23:39 +02:00
parent d2c9de4992
commit 9236a26d92
No known key found for this signature in database
5 changed files with 181 additions and 69 deletions

View File

@ -1,7 +1,10 @@
cmake_minimum_required(VERSION 3.24 FATAL_ERROR) cmake_minimum_required(VERSION 3.24 FATAL_ERROR)
add_library(solanaceae_clamav add_library(solanaceae_clamav
./solanaceae/clamav/test_lib.cpp # TODO: seperate interface lib
./solanaceae/clamav/clamav_module_interface.hpp
./solanaceae/clamav/clamav_module.hpp
./solanaceae/clamav/clamav_module.cpp
) )
target_include_directories(solanaceae_clamav PUBLIC .) target_include_directories(solanaceae_clamav PUBLIC .)
target_compile_features(solanaceae_clamav PUBLIC cxx_std_17) target_compile_features(solanaceae_clamav PUBLIC cxx_std_17)
@ -16,7 +19,7 @@ add_executable(solanaceae_clamav_test
target_include_directories(solanaceae_clamav_test PUBLIC .) target_include_directories(solanaceae_clamav_test PUBLIC .)
target_compile_features(solanaceae_clamav_test PUBLIC cxx_std_17) target_compile_features(solanaceae_clamav_test PUBLIC cxx_std_17)
target_link_libraries(solanaceae_clamav_test PUBLIC target_link_libraries(solanaceae_clamav_test PUBLIC
EXT_SOL::libclamav solanaceae_clamav
#solanaceae_util #solanaceae_util
) )

View File

@ -0,0 +1,123 @@
#include "./clamav_module.hpp"
#include <clamav.h>
#include <iostream>
#include <stdexcept>
extern "C" {
void sol_cl_msg_cb(enum cl_msg severity, const char *fullmsg, const char *msg, void *context);
};
ClamAVModule::ClamAVModule(void) {
if (cl_init(CL_INIT_DEFAULT) != CL_SUCCESS) {
throw std::domain_error("clamav cl_init() failed");
}
cl_set_clcb_msg(sol_cl_msg_cb);
}
ClamAVModule::~ClamAVModule(void) {
if (_clamav_engine != nullptr) {
cl_engine_free(_clamav_engine);
_clamav_engine = nullptr;
}
}
bool ClamAVModule::startEngine(void) {
if (_clamav_engine != nullptr) {
// already loaded
return true;
}
_clamav_engine = cl_engine_new();
if (_clamav_engine == nullptr) {
return false;
}
std::string db_dir;
// TODO: load from config
unsigned int signo = 0;
if (db_dir.empty()) {
std::cout << "default db dir: " << cl_retdbdir() << "\n";
if (cl_load(cl_retdbdir(), _clamav_engine, &signo, CL_DB_STDOPT) != CL_SUCCESS) {
std::cerr << "default db dir load failed, falling back to '/var/lib/clamav'\n";
if (cl_load("/var/lib/clamav", _clamav_engine, &signo, CL_DB_STDOPT) != CL_SUCCESS) {
std::cerr << "db dir load failed, exiting\n";
cl_engine_free(_clamav_engine);
_clamav_engine = nullptr;
return false;
} else {
db_dir = "/var/lib/clamav";
}
} else {
db_dir = cl_retdbdir();
}
// if db_dir changed, save to config?
} else {
if (cl_load(db_dir.c_str(), _clamav_engine, &signo, CL_DB_STDOPT) != CL_SUCCESS) {
std::cerr << "config db dir load failed, exiting (" << db_dir << ")\n";
cl_engine_free(_clamav_engine);
_clamav_engine = nullptr;
return false;
}
}
std::cout << "signatures loaded: " << signo << "\n";
if (cl_engine_compile(_clamav_engine) != CL_SUCCESS) {
cl_engine_free(_clamav_engine);
_clamav_engine = nullptr;
return false;
}
return true;
}
ClamAVModule::ScanResult ClamAVModule::scanFilePath(std::string_view path) {
// makeing sure engine is running
if (!startEngine()) {
return ScanResult{};
}
const char* virname = nullptr;
unsigned long int scanned = 0;
struct cl_scan_options scan_opts {
/*CL_SCAN_GENERAL_ALLMATCHES |*/ CL_SCAN_GENERAL_HEURISTICS | CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE,
~0u,
~0u,
0u,
0u
};
std::string path_cpy {path};
if (
auto ret = cl_scanfile(
path_cpy.c_str(),
&virname,
&scanned,
_clamav_engine,
&scan_opts
)
;
ret != CL_CLEAN && ret != CL_VIRUS) {
// error
return ScanResult{false, std::string{"error: "} + cl_strerror(ret)};
} else if (ret == CL_VIRUS) {
std::cout << "file virus\n";
return ScanResult{true, virname};
} else { // clean
std::cout << "file clean\n";
return ScanResult{false, "no virus detected"};
}
}
void sol_cl_msg_cb(enum cl_msg severity, const char *fullmsg, const char *msg, void *context) {
(void)msg;
(void)context;
std::cout << severity << " " << fullmsg << "\n";
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "./clamav_module_interface.hpp"
extern "C" {
struct cl_engine;
}
// blocking
class ClamAVModule : public ClamAVModuleInterface {
cl_engine* _clamav_engine {nullptr};
bool startEngine(void);
public:
ClamAVModule(void);
~ClamAVModule(void);
ScanResult scanFilePath(std::string_view path);
};

View File

@ -0,0 +1,25 @@
#pragma once
#include <string_view>
#include <string>
#include <future>
// blocking
struct ClamAVModuleInterface {
struct ScanResult {
bool virus {false};
std::string resultText;
};
virtual ScanResult scanFilePath(std::string_view path) = 0;
};
// non-blocking
struct ClamAVModuleAsyncInterface {
using ScanResult = ClamAVModuleInterface::ScanResult;
virtual std::promise<ScanResult> scanFilePath(std::string_view path) = 0;
};

View File

@ -1,79 +1,18 @@
#include <clamav.h> #include "./clamav_module.hpp"
#include <iostream> #include <iostream>
int main(void) { int main(void) {
if (cl_init(CL_INIT_DEFAULT) != CL_SUCCESS) {
return 1;
}
auto* clamav_engine = cl_engine_new(); ClamAVModule clamav;
if (clamav_engine == nullptr) {
return 2;
}
std::string db_dir; auto [is_virus, virus_string] = clamav.scanFilePath("/home/green/Downloads/mal/f8c08d00ff6e8c6adb1a93cd133b19302d0b651afd73ccb54e3b6ac6c60d99c6");
// TODO: load from config if (is_virus) {
std::cout << "is virus: " << virus_string << "\n";
unsigned int signo = 0;
if (db_dir.empty()) {
std::cout << "default db dir: " << cl_retdbdir() << "\n";
if (cl_load(cl_retdbdir(), clamav_engine, &signo, CL_DB_STDOPT) != CL_SUCCESS) {
std::cerr << "default db dir load failed, falling back to '/var/lib/clamav'\n";
if (cl_load("/var/lib/clamav", clamav_engine, &signo, CL_DB_STDOPT) != CL_SUCCESS) {
std::cerr << "db dir load failed, exiting\n";
return 3;
} else { } else {
db_dir = "/var/lib/clamav"; std::cout << "not virus: " << virus_string << "\n";
}
} else {
db_dir = cl_retdbdir();
}
// if db_dir changed, save to config?
} else {
if (cl_load(db_dir.c_str(), clamav_engine, &signo, CL_DB_STDOPT) != CL_SUCCESS) {
std::cerr << "config db dir load failed, exiting (" << db_dir << ")\n";
return 3;
}
} }
std::cout << "signatures loaded: " << signo << "\n";
if (cl_engine_compile(clamav_engine) != CL_SUCCESS) {
cl_engine_free(clamav_engine);
return 4;
}
// TODO: database update watcher
const char* filename = "~/Downloads/cubic-paper.pdf";
const char* virname = nullptr;
unsigned long int scanned = 0;
struct cl_scan_options scan_opts {
/*CL_SCAN_GENERAL_ALLMATCHES |*/ CL_SCAN_GENERAL_HEURISTICS | CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE,
~0u,
~0u,
0u,
0u
};
if (auto ret = cl_scanfile(
filename,
&virname,
&scanned,
clamav_engine,
&scan_opts
); ret != CL_CLEAN && ret != CL_VIRUS) {
// error
} else if (ret == CL_VIRUS) {
std::cout << "file virus\n";
} else { // clean
std::cout << "file clean\n";
}
cl_engine_free(clamav_engine);
return 0; return 0;
} }