From 9236a26d9242acd8fac8f8044ccecaccea3cefca Mon Sep 17 00:00:00 2001 From: Green Sky Date: Wed, 25 Oct 2023 02:23:39 +0200 Subject: [PATCH] sketch out interface and impl --- src/CMakeLists.txt | 7 +- src/solanaceae/clamav/clamav_module.cpp | 123 ++++++++++++++++++ src/solanaceae/clamav/clamav_module.hpp | 22 ++++ .../clamav/clamav_module_interface.hpp | 25 ++++ src/solanaceae/clamav/test_exe.cpp | 73 +---------- 5 files changed, 181 insertions(+), 69 deletions(-) create mode 100644 src/solanaceae/clamav/clamav_module.cpp create mode 100644 src/solanaceae/clamav/clamav_module.hpp create mode 100644 src/solanaceae/clamav/clamav_module_interface.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d0daff2..906abf3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,10 @@ cmake_minimum_required(VERSION 3.24 FATAL_ERROR) 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_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_compile_features(solanaceae_clamav_test PUBLIC cxx_std_17) target_link_libraries(solanaceae_clamav_test PUBLIC - EXT_SOL::libclamav + solanaceae_clamav #solanaceae_util ) diff --git a/src/solanaceae/clamav/clamav_module.cpp b/src/solanaceae/clamav/clamav_module.cpp new file mode 100644 index 0000000..87abefe --- /dev/null +++ b/src/solanaceae/clamav/clamav_module.cpp @@ -0,0 +1,123 @@ +#include "./clamav_module.hpp" + +#include + +#include +#include + +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"; +} + diff --git a/src/solanaceae/clamav/clamav_module.hpp b/src/solanaceae/clamav/clamav_module.hpp new file mode 100644 index 0000000..f952f1e --- /dev/null +++ b/src/solanaceae/clamav/clamav_module.hpp @@ -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); + +}; + diff --git a/src/solanaceae/clamav/clamav_module_interface.hpp b/src/solanaceae/clamav/clamav_module_interface.hpp new file mode 100644 index 0000000..7ee2a8c --- /dev/null +++ b/src/solanaceae/clamav/clamav_module_interface.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +// 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 scanFilePath(std::string_view path) = 0; + +}; + diff --git a/src/solanaceae/clamav/test_exe.cpp b/src/solanaceae/clamav/test_exe.cpp index 31320f3..9b64d35 100644 --- a/src/solanaceae/clamav/test_exe.cpp +++ b/src/solanaceae/clamav/test_exe.cpp @@ -1,79 +1,18 @@ -#include +#include "./clamav_module.hpp" #include int main(void) { - if (cl_init(CL_INIT_DEFAULT) != CL_SUCCESS) { - return 1; - } - auto* clamav_engine = cl_engine_new(); - if (clamav_engine == nullptr) { - return 2; - } + ClamAVModule clamav; - 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"; - return 3; - } else { - db_dir = "/var/lib/clamav"; - } - } else { - db_dir = cl_retdbdir(); - } - // if db_dir changed, save to config? + auto [is_virus, virus_string] = clamav.scanFilePath("/home/green/Downloads/mal/f8c08d00ff6e8c6adb1a93cd133b19302d0b651afd73ccb54e3b6ac6c60d99c6"); + if (is_virus) { + std::cout << "is virus: " << virus_string << "\n"; } 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 << "not virus: " << virus_string << "\n"; } - 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; }