sketch out interface and impl
This commit is contained in:
parent
d2c9de4992
commit
9236a26d92
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
123
src/solanaceae/clamav/clamav_module.cpp
Normal file
123
src/solanaceae/clamav/clamav_module.cpp
Normal 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";
|
||||||
|
}
|
||||||
|
|
22
src/solanaceae/clamav/clamav_module.hpp
Normal file
22
src/solanaceae/clamav/clamav_module.hpp
Normal 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);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
25
src/solanaceae/clamav/clamav_module_interface.hpp
Normal file
25
src/solanaceae/clamav/clamav_module_interface.hpp
Normal 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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user