ClamAV is an open source (GPLv2) anti-virus toolkit.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
clamav/libclamav/stats.c

461 lines
12 KiB

#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#if !defined(_WIN32)
#include <sys/sysctl.h>
#include <dlfcn.h>
#else
#include <Windows.h>
#include <tchar.h>
#endif
#ifdef CL_THREAD_SAFE
#include <pthread.h>
#endif
#include <errno.h>
#include "libclamav/others.h"
#include "libclamav/clamav.h"
#include "libclamav/json.h"
#include "libclamav/stats.h"
#include "libclamav/hostid.h"
#include "libclamav/www.h"
static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type);
void free_sample(cli_flagged_sample_t *sample);
void clamav_stats_add_sample(const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type, void *cbdata)
{
cli_intel_t *intel;
cli_flagged_sample_t *sample;
size_t i;
char **p;
int err, submit=0;
if (!(cbdata))
return;
intel = (cli_intel_t *)cbdata;
if (!(intel->engine))
return;
/* First check if we need to submit stats based on memory/number limits */
if ((intel->engine->cb_stats_get_size))
submit = (intel->engine->cb_stats_get_size(cbdata) >= intel->maxmem);
else
submit = (clamav_stats_get_size(cbdata) >= intel->maxmem);
if (submit == 0) {
if ((intel->engine->cb_stats_get_num))
submit = (intel->engine->cb_stats_get_num(cbdata) >= intel->maxsamples);
else
submit = (clamav_stats_get_num(cbdata) >= intel->maxsamples);
}
if (submit) {
if ((intel->engine->cb_stats_submit)) {
intel->engine->cb_stats_submit(intel->engine, cbdata);
} else {
if ((intel->engine->cb_stats_flush))
intel->engine->cb_stats_flush(intel->engine, intel);
return;
}
}
#ifdef CL_THREAD_SAFE
err = pthread_mutex_lock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_add_sample: locking mutex failed (err: %d): %s\n", err, strerror(err));
return;
}
#endif
sample = find_sample(intel, virname, md5, size, type);
if (!(sample)) {
if (!(intel->samples)) {
sample = intel->samples = calloc(1, sizeof(cli_flagged_sample_t));
if (!(sample))
goto end;
} else {
sample = calloc(1, sizeof(cli_flagged_sample_t));
if (!(sample))
goto end;
sample->next = intel->samples;
intel->samples->prev = sample;
intel->samples = sample;
}
if ((sample->virus_name)) {
for (i=0; sample->virus_name[i] != NULL; i++)
;
} else {
i=0;
sample->virus_name = calloc(1, sizeof(char **));
if (!(sample->virus_name)) {
free(sample);
if (sample == intel->samples)
intel->samples = NULL;
goto end;
}
}
sample->virus_name[i] = strdup((virname != NULL) ? virname : "[unknown]");
if (!(sample->virus_name[i])) {
free(sample);
free(sample->virus_name);
if (sample == intel->samples)
intel->samples = NULL;
goto end;
}
12 years ago
p = realloc(sample->virus_name, sizeof(char **) * (i == 0 ? 2 : i+1));
if (!(p)) {
free(sample->virus_name);
free(sample);
if (sample == intel->samples)
intel->samples = NULL;
goto end;
}
sample->virus_name = p;
sample->virus_name[i+1] = NULL;
memcpy(sample->md5, md5, sizeof(sample->md5));
sample->type = type;
sample->size = size;
intel->nsamples++;
}
cli_warnmsg("Added %s to the stats cache\n", (virname != NULL) ? virname: "[unknown]");
sample->hits++;
end:
#ifdef CL_THREAD_SAFE
err = pthread_mutex_unlock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_add_sample: unlcoking mutex failed (err: %d): %s\n", err, strerror(err));
}
#endif
}
void clamav_stats_flush(struct cl_engine *engine, void *cbdata)
{
cli_intel_t *intel;
cli_flagged_sample_t *sample, *next;
int err;
if (!(cbdata) || !(engine))
return;
intel = (cli_intel_t *)cbdata;
#ifdef CL_THREAD_SAFE
err = pthread_mutex_lock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_flush: locking mutex failed (err: %d): %s\n", err, strerror(err));
return;
}
#endif
for (sample=intel->samples; sample != NULL; sample = next) {
next = sample->next;
free_sample(sample);
}
intel->samples = NULL;
intel->nsamples = 0;
#ifdef CL_THREAD_SAFE
err = pthread_mutex_unlock(&(intel->mutex));
if (err)
cli_warnmsg("clamav_stats_flush: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
#endif
}
void free_sample(cli_flagged_sample_t *sample)
{
size_t i;
if ((sample->virus_name)) {
for (i=0; sample->virus_name[i] != NULL; i++)
free(sample->virus_name[i]);
free(sample->virus_name);
}
free(sample);
}
void clamav_stats_submit(struct cl_engine *engine, void *cbdata)
{
char *json;
cli_intel_t *intel, myintel;
cli_flagged_sample_t *sample, *next;
int err;
intel = (cli_intel_t *)cbdata;
if (!(intel) || !(engine))
return;
#ifdef CL_THREAD_SAFE
err = pthread_mutex_lock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_submit: locking mutex failed (err: %d): %s\n", err, strerror(err));
if ((intel->engine) && (intel->engine->cb_stats_flush))
intel->engine->cb_stats_flush(intel->engine, cbdata);
return;
}
#endif
/* Empty out the cached intelligence data so that other threads don't sit waiting to add data to the cache */
memcpy(&myintel, intel, sizeof(cli_intel_t));
intel->samples = NULL;
intel->nsamples = 0;
json = export_stats_to_json(engine, &myintel);
#ifdef CL_THREAD_SAFE
err = pthread_mutex_unlock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_submit: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
}
#endif
for (sample=myintel.samples; sample != NULL; sample = next) {
next = sample->next;
free_sample(sample);
}
if (json) {
cli_warnmsg("====\tSUBMITTING STATS\t====\n");
submit_post(STATS_HOST, STATS_PORT, "/enginestats.php", json);
free(json);
}
}
void clamav_stats_remove_sample(const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type, void *cbdata)
{
cli_intel_t *intel;
cli_flagged_sample_t *sample;
int err;
intel = (cli_intel_t *)cbdata;
if (!(intel))
return;
#ifdef CL_THREAD_SAFE
err = pthread_mutex_lock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_remove_sample: locking mutex failed (err: %d): %s\n", err, strerror(err));
return;
}
#endif
sample = find_sample(intel, virname, md5, size, type);
if (!(sample))
return;
if (sample->prev)
sample->prev->next = sample->next;
if (sample->next)
sample->next->prev = sample;
if (sample == intel->samples)
intel->samples = sample->next;
free_sample(sample);
intel->nsamples--;
#ifdef CL_THREAD_SAFE
err = pthread_mutex_unlock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_remove_sample: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
}
#endif
}
void clamav_stats_decrement_count(const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type, void *cbdata)
{
cli_intel_t *intel;
cli_flagged_sample_t *sample;
int err;
intel = (cli_intel_t *)cbdata;
if (!(intel))
return;
#ifdef CL_THREAD_SAFE
err = pthread_mutex_lock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_decrement_count: locking mutex failed (err: %d): %s\n", err, strerror(err));
return;
}
#endif
sample = find_sample(intel, virname, md5, size, type);
if (!(sample))
return;
if (sample->hits == 1) {
if ((intel->engine->cb_stats_remove_sample))
intel->engine->cb_stats_remove_sample(virname, md5, size, type, intel);
else
clamav_stats_remove_sample(virname, md5, size, type, intel);
return;
}
sample->hits--;
#ifdef CL_THREAD_SAFE
err = pthread_mutex_unlock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_decrement_count: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
}
#endif
}
size_t clamav_stats_get_num(void *cbdata)
{
cli_intel_t *intel;
intel = (cli_intel_t *)cbdata;
if (!(intel))
return 0;
return intel->nsamples;
}
size_t clamav_stats_get_size(void *cbdata)
{
cli_intel_t *intel;
cli_flagged_sample_t *sample;
size_t sz, i;
int err;
intel = (cli_intel_t *)cbdata;
if (!(intel))
return 0;
sz = sizeof(cli_intel_t);
#ifdef CL_THREAD_SAFE
err = pthread_mutex_lock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_get_size: locking mutex failed (err: %d): %s\n", err, strerror(err));
return sz;
}
#endif
for (sample = intel->samples; sample != NULL; sample = sample->next) {
sz += sizeof(cli_flagged_sample_t);
if ((sample->virus_name)) {
for (i=0; sample->virus_name[i] != NULL; i++)
sz += strlen(sample->virus_name[i]);
sz += sizeof(char **) * i;
}
}
#ifdef CL_THREAD_SAFE
err = pthread_mutex_unlock(&(intel->mutex));
if (err) {
cli_warnmsg("clamav_stats_get_size: unlocking mutex failed (err: %d): %s\n", err, strerror(err));
}
#endif
return sz;
}
#if defined(_WIN32)
char *clamav_stats_get_hostid(void *cbdata)
{
HW_PROFILE_INFO HwProfInfo;
if (!GetCurrentHwProfile(&HwProfInfo))
return strdup(STATS_ANON_UUID);
return strdup(HwProfInfo.szHwProfileGuid);
}
#else
char *clamav_stats_get_hostid(void *cbdata)
{
char *sysctls[] = {
"kern.hostuuid",
NULL
};
size_t bufsz, i;
char *buf;
#if HAVE_SYSCTLBYNAME
/* FreeBSD-landia */
for (i=0; sysctls[i] != NULL; i++) {
if (sysctlbyname(sysctls[i], NULL, &bufsz, NULL, 0))
continue;
break; /* Got one */
}
if (sysctls[i] != NULL) {
buf = calloc(1, bufsz+1);
if (sysctlbyname(sysctls[i], buf, &bufsz, NULL, 0))
return strdup(STATS_ANON_UUID); /* Not sure why this would happen, but we'll just default to the anon uuid on error */
return buf;
}
#else
buf = internal_get_host_id();
if (!(buf))
return strdup(STATS_ANON_UUID);
return buf;
#endif
return strdup(STATS_ANON_UUID);
}
#endif
static cli_flagged_sample_t *find_sample(cli_intel_t *intel, const char *virname, const unsigned char *md5, size_t size, cli_intel_sample_type_t type)
{
cli_flagged_sample_t *sample;
size_t i;
for (sample = intel->samples; sample != NULL; sample = sample->next) {
if (sample->type != type)
continue;
if (sample->size != size)
continue;
if (memcmp(sample->md5, md5, sizeof(sample->md5)))
continue;
if (!(virname))
return sample;
for (i=0; sample->virus_name[i] != NULL; i++)
if (!strcmp(sample->virus_name[i], virname))
return sample;
}
return NULL;
}