libclamav/events.c
ec30b57c
 /*
  *  (bytecode) events
  *
c442ca9c
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2010-2013 Sourcefire, Inc.
ec30b57c
  *
  *  Authors: Török Edvin
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
b2e7c931
 
 #ifndef _WIN32
 #include <sys/time.h>
 #endif
 
60d8d2c3
 #include "clamav.h"
ec30b57c
 #include "events.h"
 #include "others.h"
 #include "7z/7zCrc.h"
 #include "str.h"
 #include <string.h>
 
abd76fab
 struct cli_event
 {
ec30b57c
     const char *name;
     union ev_val u;
     uint32_t count;
abd76fab
     enum ev_type type : 8;
     enum multiple_handling multiple : 8;
ec30b57c
 };
 
abd76fab
 struct cli_events
 {
ec30b57c
     struct cli_event *events;
     struct cli_event errors;
     uint64_t oom_total;
     unsigned max;
     unsigned oom_count;
 };
 
 cli_events_t *cli_events_new(unsigned max_event)
 {
     struct cli_events *ev = cli_calloc(1, sizeof(*ev));
     if (!ev)
abd76fab
         return NULL;
ec30b57c
     ev->max = max_event;
e3a7c30b
     ev->events = cli_calloc(max_event, sizeof(*ev->events));
abd76fab
     if (!ev->events)
     {
         free(ev);
         return NULL;
ec30b57c
     }
     ev->errors.name = "errors";
     ev->errors.type = ev_string;
     ev->errors.multiple = multiple_chain;
     return ev;
 }
 
 void cli_events_free(cli_events_t *ev)
 {
abd76fab
     if (ev)
     {
         /* TODO: free components */
         free(ev->events);
         free(ev);
ec30b57c
     }
 }
 
 void cli_event_error_oom(cli_events_t *ctx, uint32_t amount)
 {
d7531f2a
     if (!ctx)
abd76fab
         return;
ec30b57c
     ctx->oom_total += amount;
     ctx->oom_count++;
7cd9337a
     /* amount == 0 means error already reported, just increment count */
ec30b57c
     if (amount)
abd76fab
         cli_errmsg("events: out of memory allocating %u bytes\n", amount);
ec30b57c
 }
 
 int cli_event_define(cli_events_t *ctx, unsigned id,
abd76fab
                      const char *name, enum ev_type type, enum multiple_handling multiple)
ec30b57c
 {
     struct cli_event *ev = &ctx->events[id];
abd76fab
     if (id >= ctx->max)
     {
         cli_event_error_str(ctx, "cli_event_define: event id out of range");
         return -1;
ec30b57c
     }
     if (multiple == multiple_sum &&
abd76fab
         (type != ev_int && type != ev_time && type != ev_data_fast))
     {
         cli_event_error_str(ctx, "cli_event_define: can only sum ev_int, ev_time, and ev_data_fast");
         return -1;
ec30b57c
     }
abd76fab
     if (type == ev_data_fast && multiple != multiple_sum)
     {
         cli_event_error_str(ctx, "cli_event_define: ev_data_fast can only be sumed");
         return -1;
ec30b57c
     }
abd76fab
     if (multiple == multiple_concat && type != ev_data)
     {
         cli_event_error_str(ctx, "cli_event_define: only ev_data can be concatenated");
         return -1;
ec30b57c
     }
     /* default was ev_none */
     ev->type = type;
     ev->name = name;
     ev->type = type;
     ev->multiple = multiple;
     if (type == ev_data_fast)
abd76fab
         ev->u.v_int = CRC_INIT_VAL;
ec30b57c
     return 0;
 }
 
 static inline struct cli_event *get_event(cli_events_t *ctx, unsigned id)
 {
     if (!ctx)
abd76fab
         return NULL;
     if (id >= ctx->max)
     {
         cli_event_error_str(ctx, "event id out of range");
         return NULL;
ec30b57c
     }
     return &ctx->events[id];
 }
 
 static inline void ev_chain(cli_events_t *ctx, struct cli_event *ev, union ev_val *val)
 {
     union ev_val *chain;
     uint32_t siz = sizeof(*chain) * (ev->count + 1);
 
     chain = cli_realloc(ev->u.v_chain, siz);
abd76fab
     if (!chain)
     {
         cli_event_error_oom(ctx, siz);
         return;
ec30b57c
     }
     ev->u.v_chain = chain;
     ev->u.v_chain[ev->count] = *val;
     ev->count++;
 }
 
abd76fab
 const char *cli_event_get_name(cli_events_t *ctx, unsigned id)
54402320
 {
     struct cli_event *ev = get_event(ctx, id);
     if (!ev)
abd76fab
         return NULL;
54402320
     return ev->name;
 }
 
ec30b57c
 void cli_event_int(cli_events_t *ctx, unsigned id, uint64_t arg)
 {
     struct cli_event *ev = get_event(ctx, id);
     if (!ev)
abd76fab
         return;
     if (ev->type != ev_int)
     {
         cli_event_error_str(ctx, "cli_event_int must be called with ev_int type");
         return;
     }
     switch (ev->multiple)
     {
     case multiple_last:
         ev->u.v_int = arg;
         ev->count++;
         break;
     case multiple_sum:
         ev->count++;
         ev->u.v_int += arg;
         break;
     case multiple_chain:
     {
         union ev_val val;
         val.v_int = arg;
         ev_chain(ctx, ev, &val);
         break;
     }
ce2dcb53
     default:
         // TODO: Consider if we should handle multiple_concat cases.
         break;
ec30b57c
     }
 }
 
 void cli_event_time_start(cli_events_t *ctx, unsigned id)
 {
     struct timeval tv;
     struct cli_event *ev = get_event(ctx, id);
     if (!ev)
abd76fab
         return;
     if (ev->type != ev_time)
     {
         cli_event_error_str(ctx, "cli_event_time* must be called with ev_time type");
         return;
ec30b57c
     }
     gettimeofday(&tv, NULL);
     ev->u.v_int -= ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
63feb6cd
     ev->count++;
 }
 
 void cli_event_time_nested_start(cli_events_t *ctx, unsigned id, unsigned nestedid)
 {
     struct timeval tv;
     struct cli_event *ev = get_event(ctx, id);
     struct cli_event *evnested = get_event(ctx, nestedid);
     if (!ev || !evnested)
abd76fab
         return;
     if (ev->type != ev_time || evnested->type != ev_time)
     {
         cli_event_error_str(ctx, "cli_event_time* must be called with ev_time type");
         return;
63feb6cd
     }
     gettimeofday(&tv, NULL);
     ev->u.v_int -= ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
     ev->u.v_int += evnested->u.v_int;
     ev->count++;
ec30b57c
 }
 
 void cli_event_time_stop(cli_events_t *ctx, unsigned id)
 {
     struct timeval tv;
     struct cli_event *ev = get_event(ctx, id);
     if (!ev)
abd76fab
         return;
     if (ev->type != ev_time)
     {
         cli_event_error_str(ctx, "cli_event_time* must be called with ev_time type");
         return;
ec30b57c
     }
     gettimeofday(&tv, NULL);
     ev->u.v_int += ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
 }
 
63feb6cd
 void cli_event_time_nested_stop(cli_events_t *ctx, unsigned id, unsigned nestedid)
 {
     struct timeval tv;
     struct cli_event *ev = get_event(ctx, id);
     struct cli_event *evnested = get_event(ctx, nestedid);
     if (!ev || !evnested)
abd76fab
         return;
     if (ev->type != ev_time || evnested->type != ev_time)
     {
         cli_event_error_str(ctx, "cli_event_time* must be called with ev_time type");
         return;
63feb6cd
     }
     gettimeofday(&tv, NULL);
     ev->u.v_int += ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
     ev->u.v_int -= evnested->u.v_int;
 }
 
ec30b57c
 static void event_string(cli_events_t *ctx, struct cli_event *ev, const char *str)
 {
     if (!str)
abd76fab
         str = "";
     switch (ev->multiple)
     {
     case multiple_last:
         ev->u.v_string = str;
         ev->count++;
         break;
     case multiple_chain:
     {
         union ev_val val;
         val.v_string = str;
         ev_chain(ctx, ev, &val);
         break;
     }
ce2dcb53
     default:
         // TODO: Consider if we should handle multiple_sum, multiple_concat cases.
         break;
ec30b57c
     }
 }
 
 void cli_event_error_str(cli_events_t *ctx, const char *str)
 {
     if (!ctx)
abd76fab
         return;
ec30b57c
     cli_warnmsg("events: %s\n", str);
     event_string(ctx, &ctx->errors, str);
 }
 
 void cli_event_string(cli_events_t *ctx, unsigned id, const char *str)
 {
     struct cli_event *ev = get_event(ctx, id);
     if (!ev)
abd76fab
         return;
     if (ev->type != ev_string)
     {
         cli_event_error_str(ctx, "cli_event_string must be called with ev_string type");
         return;
ec30b57c
     }
     event_string(ctx, ev, str);
 }
 
 void cli_event_data(cli_events_t *ctx, unsigned id, const void *data, uint32_t len)
 {
     struct cli_event *ev = get_event(ctx, id);
     if (!ev)
abd76fab
         return;
     if (ev->type != ev_data)
     {
         cli_event_error_str(ctx, "cli_event_string must be called with ev_data type");
         return;
     }
     switch (ev->multiple)
     {
     case multiple_last:
     {
         void *v_data = cli_realloc2(ev->u.v_data, len);
         if (v_data)
         {
             ev->u.v_data = v_data;
             memcpy(v_data, data, len);
             ev->count = len;
         }
         else
         {
             cli_event_error_oom(ctx, len);
         }
         break;
     }
     case multiple_concat:
     {
         void *v_data = cli_realloc2(ev->u.v_data, ev->count + len);
         if (v_data)
         {
             ev->u.v_data = v_data;
             memcpy((char *)v_data + ev->count, data, len);
             ev->count += len;
         }
         else
         {
             cli_event_error_oom(ctx, ev->count + len);
         }
         break;
     }
ce2dcb53
     default:
         // TODO: Consider if we should handle multiple_sum, multiple_chain cases.
         break;
ec30b57c
     }
 }
 
 void cli_event_fastdata(cli_events_t *ctx, unsigned id, const void *data, uint32_t len)
 {
     struct cli_event *ev = get_event(ctx, id);
     if (!ev)
abd76fab
         return;
     if (ev->type != ev_data_fast)
     {
         cli_event_error_str(ctx, "cli_event_fastdata must be called with ev_data_fast");
         return;
ec30b57c
     }
     ev->u.v_int = CrcUpdate(ev->u.v_int, data, len);
     ev->count += len;
     /* when we are done we should invert all bits, but since we are just
      * comparing it doesn't matter */
 }
 
 void cli_event_count(cli_events_t *ctx, unsigned id)
 {
     cli_event_int(ctx, id, 1);
 }
 
abd76fab
 void cli_event_get(cli_events_t *ctx, unsigned id, union ev_val *val, uint32_t *count)
ec30b57c
 {
     struct cli_event *ev = get_event(ctx, id);
     if (!ev)
abd76fab
         return;
ec30b57c
     memcpy(val, &ev->u, sizeof(*val));
     *count = ev->count;
 }
 
 static inline void ev_debug(enum ev_type type, union ev_val *val, uint32_t count)
 {
abd76fab
     switch (type)
     {
     case ev_string:
         cli_dbgmsg("\t(%u): %s\n", count, val->v_string);
         break;
     case ev_data:
     {
         char *d = cli_str2hex(val->v_data, count);
         cli_dbgmsg("\t%d bytes\n", count);
         cli_dbgmsg("\t%s\n", d);
         free(d);
         break;
     }
     case ev_data_fast:
         cli_dbgmsg("\t%08x checksum, %u bytes\n", (uint32_t)val->v_int, count);
         break;
     case ev_int:
         cli_dbgmsg("\t(%u): 0x%llx\n", count, (long long)val->v_int);
         break;
     case ev_time:
         cli_dbgmsg("\t(%u): %d.%06us\n", count, (signed)(val->v_int / 1000000),
                    (unsigned)(val->v_int % 1000000));
         break;
ce2dcb53
     default:
         // TODO: Consider if we should handle ev_none cases.
         break;
ec30b57c
     }
 }
 
 static inline const char *evtype(enum ev_type type)
 {
abd76fab
     switch (type)
     {
     case ev_string:
         return "ev_string";
     case ev_data:
         return "ev_data";
     case ev_data_fast:
         return "ev_data_fast";
     case ev_int:
         return "ev_data_int";
     case ev_time:
         return "ev_time";
     default:
         return "";
ec30b57c
     }
 }
 
 void cli_event_debug(cli_events_t *ctx, unsigned id)
 {
     const char *tstr;
     struct cli_event *ev = get_event(ctx, id);
     if (!ev)
abd76fab
         return;
ec30b57c
     tstr = evtype(ev->type);
abd76fab
     if (ev->multiple == multiple_chain && ev->type != ev_data)
     {
         unsigned i;
         cli_dbgmsg("%s: ev_chain %u %s\n", ev->name, ev->count, tstr);
         for (i = 0; i < ev->count; i++)
             ev_debug(ev->type, &ev->u.v_chain[i], i);
     }
     else
     {
         cli_dbgmsg("%s: %s\n", ev->name, tstr);
         ev_debug(ev->type, &ev->u, ev->count);
ec30b57c
     }
 }
 
 void cli_event_debug_all(cli_events_t *ctx)
 {
     unsigned i;
abd76fab
     for (i = 0; i < ctx->max; i++)
     {
         if (ctx->events[i].count)
             cli_event_debug(ctx, i);
ec30b57c
     }
 }
 
 static int ev_diff(enum ev_type type, union ev_val *v1, union ev_val *v2, uint32_t count)
 {
abd76fab
     switch (type)
     {
     case ev_data_fast:
     case ev_int:
         return v1->v_int != v2->v_int;
     case ev_string:
         return strcmp(v1->v_string, v2->v_string);
     case ev_data:
         return memcmp(v1->v_data, v2->v_data, count);
     case ev_time:
         return 0;
ce2dcb53
     default:
         // TODO: Consider if we should handle ev_none cases.
         break;
ec30b57c
     }
     return 0;
 }
 
 int cli_event_diff(cli_events_t *ctx1, cli_events_t *ctx2, unsigned id)
 {
     int diff = 0;
     struct cli_event *ev1, *ev2;
     ev1 = get_event(ctx1, id);
     ev2 = get_event(ctx2, id);
     if (!ev1 || !ev2)
abd76fab
         return 1;
ec30b57c
     if (ev1->type != ev2->type ||
abd76fab
         ev1->multiple != ev2->multiple ||
         ev1->name != ev2->name)
     {
         cli_warnmsg("cli_event_diff: comparing incompatible events");
         return 1;
ec30b57c
     }
abd76fab
     if (ev1->count != ev2->count)
     {
         cli_dbgmsg("diff: %s count %u vs %u\n", ev1->name, ev1->count, ev2->count);
         return 1;
ec30b57c
     }
     diff = 0;
abd76fab
     if (ev1->multiple == multiple_chain && ev1->type != ev_data)
     {
         unsigned i;
         for (i = 0; i < ev1->count; i++)
         {
             unsigned di = ev_diff(ev1->type, &ev1->u.v_chain[i], &ev2->u.v_chain[i], ev1->count);
             if (di)
             {
                 if (!diff)
                     cli_dbgmsg("diff: %s\n", ev1->name);
                 ev_debug(ev1->type, &ev1->u.v_chain[i], i);
                 ev_debug(ev2->type, &ev2->u.v_chain[i], i);
             }
             diff += di;
         }
     }
     else
     {
         diff = ev_diff(ev1->type, &ev1->u, &ev2->u, ev1->count);
         if (diff)
         {
             cli_dbgmsg("diff: %s\n", ev1->name);
             ev_debug(ev1->type, &ev1->u, ev1->count);
             ev_debug(ev2->type, &ev2->u, ev2->count);
         }
ec30b57c
     }
     if (!diff)
abd76fab
         return 0;
ec30b57c
     return 1;
 }
 
 int cli_event_diff_all(cli_events_t *ctx1, cli_events_t *ctx2, compare_filter_t filter)
 {
     unsigned i, diff = 0;
abd76fab
     if (ctx1->max != ctx2->max)
     {
         cli_dbgmsg("diffall: incompatible event maximums %u vs %u\n",
                    ctx1->max, ctx2->max);
         return 1;
ec30b57c
     }
abd76fab
     for (i = 0; i < ctx1->max; i++)
     {
         struct cli_event *ev1 = &ctx1->events[i];
         if (filter && filter(i, ev1->type))
             continue;
         diff += cli_event_diff(ctx1, ctx2, i);
ec30b57c
     }
     return diff ? 1 : 0;
 }
 
 int cli_event_errors(cli_events_t *ctx)
 {
d7531f2a
     if (!ctx)
abd76fab
         return 0;
ec30b57c
     return ctx->errors.count + ctx->oom_count;
 }