diff --git a/clamd/session.c b/clamd/session.c index e01359be5f..4b41fdca2f 100644 --- a/clamd/session.c +++ b/clamd/session.c @@ -475,8 +475,10 @@ static int dispatch_command(client_conn_t *conn, enum commands cmd, const char * static int print_ver(int desc, char term, const struct cl_engine *engine) { uint32_t ver; + long long num_sigs; ver = cl_engine_get_num(engine, CL_ENGINE_DB_VERSION, NULL); + num_sigs = cl_engine_get_num(engine, CL_ENGINE_NUM_SIGNATURES, NULL); if (ver) { char timestr[32]; const char *tstr; @@ -485,8 +487,14 @@ static int print_ver(int desc, char term, const struct cl_engine *engine) tstr = cli_ctime(&t, timestr, sizeof(timestr)); /* cut trailing \n */ timestr[strlen(tstr) - 1] = '\0'; + if (num_sigs > 0) { + return mdprintf(desc, "ClamAV %s/%u/%s/%llu%c", get_version(), (unsigned int)ver, tstr, num_sigs, term); + } return mdprintf(desc, "ClamAV %s/%u/%s%c", get_version(), (unsigned int)ver, tstr, term); } + if (num_sigs > 0) { + return mdprintf(desc, "ClamAV %s/%llu%c", get_version(), num_sigs, term); + } return mdprintf(desc, "ClamAV %s%c", get_version(), term); } diff --git a/clamdscan/clamdscan.c b/clamdscan/clamdscan.c index 108d6e191c..e9595162a3 100644 --- a/clamdscan/clamdscan.c +++ b/clamdscan/clamdscan.c @@ -174,6 +174,7 @@ int main(int argc, char **argv) /* TODO: Implement STATUS in clamd */ if (!optget(opts, "no-summary")->enabled) { struct tm tmp; + unsigned long long sigs = 0; date_end = time(NULL); gettimeofday(&t2, NULL); @@ -182,6 +183,9 @@ int main(int argc, char **argv) ds -= (dms < 0) ? (1) : (0); dms += (dms < 0) ? (1000000) : (0); logg(LOGG_INFO, "\n----------- SCAN SUMMARY -----------\n"); + if (get_clamd_signature_count(opts, &sigs) == 0) { + logg(LOGG_INFO, "Database: %llu signatures\n", sigs); + } logg(LOGG_INFO, "Infected files: %d\n", infected); if (err) logg(LOGG_INFO, "Total errors: %d\n", err); diff --git a/clamdscan/client.c b/clamdscan/client.c index 51f9744bee..461af58503 100644 --- a/clamdscan/client.c +++ b/clamdscan/client.c @@ -402,6 +402,70 @@ int reload_clamd_database(const struct optstruct *opts) return 0; } +int get_clamd_signature_count(const struct optstruct *opts, unsigned long long *sigs) +{ + char *buff; + int len, sockd; + struct RCVLN rcv; + const char zVERSION[] = "zVERSION"; + char *last_slash, *endptr; + + if (!sigs) { + return 2; + } + + *sigs = 0; + + isremote(opts); + if ((sockd = dconnect(clamdopts)) < 0) return 2; + recvlninit(&rcv, sockd); + + if (sendln(sockd, zVERSION, sizeof(zVERSION))) { + closesocket(sockd); + return 2; + } + + while ((len = recvln(&rcv, &buff, NULL))) { + if (len == -1) { + logg(LOGG_ERROR, "Error occurred while receiving version information.\n"); + break; + } + + /* Check if the response was "COMMAND UNAVAILABLE" */ + if (len >= 19 && memcmp(buff, "COMMAND UNAVAILABLE", 19) == 0) { + closesocket(sockd); + return 2; + } + + /* Parse the VERSION response format: + * "ClamAV version/dbversion/timestamp/signatures" (if ver and sigs > 0) + * "ClamAV version/dbversion/timestamp" (if ver but sigs == 0) + * "ClamAV version/signatures" (if no ver but sigs > 0) + * "ClamAV version" (if no ver and no sigs) + */ + /* Only parse lines that start with "ClamAV" */ + if (len >= 6 && memcmp(buff, "ClamAV", 6) == 0) { + /* Find the last '/' to get the signature count */ + last_slash = strrchr(buff, '/'); + if (last_slash) { + last_slash++; + /* Try to parse as number - if it's a number, it's the signature count */ + *sigs = strtoull(last_slash, &endptr, 10); + if (endptr == last_slash || (*endptr != '\0' && *endptr != '\n' && *endptr != '\r')) { + /* Last field is not a number, so no signature count in response */ + *sigs = 0; + } else if (*sigs > 0) { + /* Successfully parsed signature count, we're done */ + break; + } + } + } + } + + closesocket(sockd); + return (*sigs > 0) ? 0 : 2; +} + int client(const struct optstruct *opts, int *infected, int *err) { int remote, scantype, session = 0, errors = 0, scandash = 0, maxrec, flags = 0; diff --git a/clamdscan/client.h b/clamdscan/client.h index 80056b304b..0b9de20192 100644 --- a/clamdscan/client.h +++ b/clamdscan/client.h @@ -30,6 +30,7 @@ int client(const struct optstruct *opts, int *infected, int *err); int get_clamd_version(const struct optstruct *opts); int reload_clamd_database(const struct optstruct *opts); +int get_clamd_signature_count(const struct optstruct *opts, unsigned long long *sigs); int16_t ping_clamd(const struct optstruct *opts); #endif diff --git a/clamscan/clamscan.c b/clamscan/clamscan.c index 4d4e2c2465..ab4120edcb 100644 --- a/clamscan/clamscan.c +++ b/clamscan/clamscan.c @@ -194,7 +194,7 @@ int main(int argc, char **argv) ds -= (dms < 0) ? (1) : (0); dms += (dms < 0) ? (1000000) : (0); logg(LOGG_INFO, "\n----------- SCAN SUMMARY -----------\n"); - logg(LOGG_INFO, "Known viruses: %u\n", info.sigs); + logg(LOGG_INFO, "Database: %u signatures\n", info.sigs); logg(LOGG_INFO, "Engine version: %s\n", get_version()); logg(LOGG_INFO, "Scanned directories: %u\n", info.dirs); logg(LOGG_INFO, "Scanned files: %u\n", info.files); diff --git a/cmake/CheckFDPassing.cmake b/cmake/CheckFDPassing.cmake index 039d9eb4d1..f2ce66f03d 100644 --- a/cmake/CheckFDPassing.cmake +++ b/cmake/CheckFDPassing.cmake @@ -55,30 +55,63 @@ if(HAVE_CONTROL_IN_MSGHDR) set(EXTRA_COMPILE_DEFINITIONS "${EXTRA_COMPILE_DEFINITIONS} -DHAVE_SYS_UIO_H=1") endif() - # Try without _XOPEN_SOURCE first - try_run( - # Name of variable to store the run result (process exit status; number) in: - test_run_result - # Name of variable to store the compile result (TRUE or FALSE) in: - test_compile_result - # Binary directory: - ${CMAKE_CURRENT_BINARY_DIR} - # Source file to be compiled: - ${_selfdir_CheckFDPassing}/CheckFDPassing.c - # Extra -D Compile Definitions - COMPILE_DEFINITIONS ${EXTRA_COMPILE_DEFINITIONS} - # Where to store the output produced during compilation: - COMPILE_OUTPUT_VARIABLE test_compile_output - # Where to store the output produced by running the compiled executable: - RUN_OUTPUT_VARIABLE test_run_output ) - - # Did compilation succeed and process return 0 (success)? - if("${test_compile_result}" AND ("${test_run_result}" EQUAL 0)) - set(HAVE_FD_PASSING 1) - else() - # Try again, this time with: #define _XOPEN_SOURCE 500 + # OpenBSD requires _XOPEN_SOURCE for proper fdpassing support + if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(EXTRA_COMPILE_DEFINITIONS "${EXTRA_COMPILE_DEFINITIONS} -D_XOPEN_SOURCE=500") + set(FORCE_XOPEN_TEST 1) + else() + set(FORCE_XOPEN_TEST 0) + endif() + # Try without _XOPEN_SOURCE first (unless OpenBSD) + if(NOT FORCE_XOPEN_TEST) + try_run( + # Name of variable to store the run result (process exit status; number) in: + test_run_result + # Name of variable to store the compile result (TRUE or FALSE) in: + test_compile_result + # Binary directory: + ${CMAKE_CURRENT_BINARY_DIR} + # Source file to be compiled: + ${_selfdir_CheckFDPassing}/CheckFDPassing.c + # Extra -D Compile Definitions + COMPILE_DEFINITIONS ${EXTRA_COMPILE_DEFINITIONS} + # Where to store the output produced during compilation: + COMPILE_OUTPUT_VARIABLE test_compile_output + # Where to store the output produced by running the compiled executable: + RUN_OUTPUT_VARIABLE test_run_output ) + + # Did compilation succeed and process return 0 (success)? + if("${test_compile_result}" AND ("${test_run_result}" EQUAL 0)) + set(HAVE_FD_PASSING 1) + else() + # Try again, this time with: #define _XOPEN_SOURCE 500 + set(EXTRA_COMPILE_DEFINITIONS "${EXTRA_COMPILE_DEFINITIONS} -D_XOPEN_SOURCE=500") + + try_run( + # Name of variable to store the run result (process exit status; number) in: + test_run_result + # Name of variable to store the compile result (TRUE or FALSE) in: + test_compile_result + # Binary directory: + ${CMAKE_CURRENT_BINARY_DIR} + # Source file to be compiled: + ${_selfdir_CheckFDPassing}/CheckFDPassing.c + # Extra -D Compile Definitions + COMPILE_DEFINITIONS ${EXTRA_COMPILE_DEFINITIONS} + # Where to store the output produced during compilation: + COMPILE_OUTPUT_VARIABLE test_compile_output + # Where to store the output produced by running the compiled executable: + RUN_OUTPUT_VARIABLE test_run_output ) + + # Did compilation succeed and process return 0 (success)? + if("${test_compile_result}" AND ("${test_run_result}" EQUAL 0)) + set(HAVE_FD_PASSING 1) + set(FDPASS_NEED_XOPEN 1) + endif() + endif() + else() + # For OpenBSD, test directly with _XOPEN_SOURCE=500 try_run( # Name of variable to store the run result (process exit status; number) in: test_run_result diff --git a/common/clamdcom.c b/common/clamdcom.c index 2c7653efdd..3e8ca2eab1 100644 --- a/common/clamdcom.c +++ b/common/clamdcom.c @@ -23,6 +23,9 @@ #include "clamav-config.h" #endif +/* must be first because it may define _XOPEN_SOURCE */ +#include "fdpassing.h" + #include #include #include diff --git a/libclamav/clamav.h b/libclamav/clamav.h index b41d39e39b..ecfa32cd6d 100644 --- a/libclamav/clamav.h +++ b/libclamav/clamav.h @@ -345,6 +345,7 @@ enum cl_engine_field { CL_ENGINE_CVDCERTSDIR, /** (char *) */ CL_ENGINE_TMPDIR_RECURSION, /** uint32_t */ CL_ENGINE_FIPS_LIMITS, /** uint32_t */ + CL_ENGINE_NUM_SIGNATURES, /** size_t */ }; enum bytecode_security { diff --git a/libclamav/others.c b/libclamav/others.c index d14a6aecb6..520ea93361 100644 --- a/libclamav/others.c +++ b/libclamav/others.c @@ -954,6 +954,8 @@ long long cl_engine_get_num(const struct cl_engine *engine, enum cl_engine_field return engine->pcre_recmatch_limit; case CL_ENGINE_PCRE_MAX_FILESIZE: return engine->pcre_max_filesize; + case CL_ENGINE_NUM_SIGNATURES: + return (long long)engine->num_total_signatures; default: cli_errmsg("cl_engine_get: Incorrect field number\n"); if (err)