oscam-2.26.01-11942-802-wit.../oscam-emm-cache.c
2026-02-24 21:37:51 +00:00

535 lines
11 KiB
C
Executable File

#define MODULE_LOG_PREFIX "emmcache"
#include "globals.h"
#include "oscam-config.h"
#include "oscam-string.h"
#include "oscam-emm-cache.h"
#include "oscam-files.h"
#include "oscam-time.h"
#include "oscam-lock.h"
#include "cscrypt/md5.h"
#define LINESIZE 1024
#define DEFAULT_LOCK_TIMEOUT 1000000
static LLIST *emm_cache;
bool emm_cache_configured(void)
{
struct s_reader *rdr;
bool enable = false;
LL_ITER itr = ll_iter_create(configured_readers);
while((rdr = ll_iter_next(&itr)))
{
if(rdr->cachemm == 1)
{
enable = true;
}
}
return enable;
}
static char *get_emmcache_filename(char *dest, size_t destlen, const char *filename)
{
const char *slash = "";
if(cfg.emmlogdir[strlen(cfg.emmlogdir) - 1] != '/')
{
slash = "/";
}
snprintf(dest, destlen, "%s%s%s", cfg.emmlogdir, slash, filename);
return dest;
}
void emm_save_cache(void)
{
if(boxtype_is("dbox2")) return; // don't save emmcache on these boxes, they lack resources and will crash!
if(!emm_cache_configured()){
cs_log("saving emmcache disabled since no reader is using it!");
return;
}
char fname[256];
struct timeb ts, te;
if(cfg.emmlogdir)
{
get_emmcache_filename(fname, sizeof(fname), "oscam.emmcache");
}
else
{
get_config_filename(fname, sizeof(fname), "oscam.emmcache");
}
FILE *file = fopen(fname, "w");
if(!file)
{
cs_log("can't write emmcache to file %s", fname);
return;
}
cs_ftime(&ts);
int32_t count = 0, result = 0;
LL_ITER it = ll_iter_create(emm_cache);
struct s_emmcache *c;
while((c = ll_iter_next(&it)))
{
uint8_t tmp_emmd5[MD5_DIGEST_LENGTH * 2 + 1];
char_to_hex(c->emmd5, MD5_DIGEST_LENGTH, tmp_emmd5);
uint8_t tmp_emm[c->len * 2 + 1];
char_to_hex(c->emm, c->len, tmp_emm);
result = fprintf(file, "%s,%" PRId64 ",%" PRId64 ",%02X,%04X,%s\n", tmp_emmd5, (int64_t)c->firstseen.time, (int64_t)c->lastseen.time, c->type, c->len, tmp_emm);
if(result < 0)
{
fclose(file);
result = remove(fname);
if(!result)
{
cs_log("error writing cache -> cache file removed!");
}
else
{
cs_log("error writing cache -> cache file could not be removed either!");
}
return;
}
count++;
}
fclose(file);
cs_ftime(&te);
int64_t load_time = comp_timeb(&te, &ts);
cs_log("saved %d emmcache records to %s in %"PRId64" ms", count, fname, load_time);
}
void load_emmstat_from_file(void)
{
if(boxtype_is("dbox2")) return; // dont load emmstat on these boxes, they lack resources and will crash!
if(!emm_cache_configured()){
cs_log("loading emmstats disabled since no reader is using it!");
return;
}
char buf[256];
char fname[256];
char *line;
FILE *file;
if(cfg.emmlogdir)
{
get_emmcache_filename(fname, sizeof(fname), "oscam.emmstat");
}
else
{
get_config_filename(fname, sizeof(fname), "oscam.emmstat");
}
file = fopen(fname, "r");
if(!file)
{
cs_log_dbg(D_TRACE, "can't read emmstats from file %s", fname);
return;
}
if(!cs_malloc(&line, LINESIZE))
{
fclose(file);
return;
}
struct timeb ts, te;
cs_ftime(&ts);
struct s_reader *rdr = NULL;
struct s_emmstat *s;
int32_t i = 1;
int32_t valid = 0;
int32_t count = 0;
char *ptr, *saveptr1 = NULL;
char *split[7];
while(fgets(line, LINESIZE, file))
{
if(!line[0] || line[0] == '#' || line[0] == ';')
{ continue; }
if(!cs_malloc(&s, sizeof(struct s_emmstat)))
{ continue; }
for(i = 0, ptr = strtok_r(line, ",", &saveptr1); ptr && i < 7 ; ptr = strtok_r(NULL, ",", &saveptr1), i++)
{ split[i] = ptr; }
valid = (i == 6);
if(valid)
{
cs_strncpy(buf, split[0], sizeof(buf));
key_atob_l(split[1], s->emmd5, MD5_DIGEST_LENGTH * 2);
s->firstwritten.time = atol(split[2]);
s->lastwritten.time = atol(split[3]);
s->type = a2i(split[4], 2);
s->count = a2i(split[5], 4);
LL_ITER itr = ll_iter_create(configured_readers);
while((rdr = ll_iter_next(&itr)))
{
if(rdr->cachemm !=1) // skip: emmcache save is disabled
{
continue;
}
if(strcmp(rdr->label, buf) == 0)
{
break;
}
}
if(rdr != NULL)
{
if(!rdr->emmstat)
{
rdr->emmstat = ll_create("emmstat");
cs_lock_create(__func__, &rdr->emmstat_lock, rdr->label, DEFAULT_LOCK_TIMEOUT);
}
ll_append(rdr->emmstat, s);
count++;
}
else
{
cs_log("emmstats could not be loaded for %s", buf);
NULLFREE(s);
}
}
else
{
cs_log_dbg(D_EMM, "emmstat ERROR: %s count=%d type=%d", buf, s->count, s->type);
NULLFREE(s);
}
}
fclose(file);
NULLFREE(line);
cs_ftime(&te);
int64_t load_time = comp_timeb(&te, &ts);
cs_log("loaded %d emmstat records from %s in %"PRId64" ms", count, fname, load_time);
}
void save_emmstat_to_file(void)
{
if(boxtype_is("dbox2")) return; // don't save emmstat on these boxes, they lack resources and will crash!
if(!emm_cache_configured())
{
cs_log("saving emmstats disabled since no reader is using it!");
return;
}
char fname[256];
if(cfg.emmlogdir)
{
get_emmcache_filename(fname, sizeof(fname), "oscam.emmstat");
}
else
{
get_config_filename(fname, sizeof(fname), "oscam.emmstat");
}
FILE *file = fopen(fname, "w");
if(!file)
{
cs_log("can't write to file %s", fname);
return;
}
struct timeb ts, te;
cs_ftime(&ts);
int32_t count = 0, result = 0;
struct s_reader *rdr;
LL_ITER itr = ll_iter_create(configured_readers);
while((rdr = ll_iter_next(&itr)))
{
if(!rdr->cachemm || rdr->cachemm == 2)
{
cs_log("reader %s skipped since emmcache save is disabled", rdr->label);
continue;
}
if(rdr->emmstat)
{
cs_writelock(__func__, &rdr->emmstat_lock);
LL_ITER it = ll_iter_create(rdr->emmstat);
struct s_emmstat *s;
while((s = ll_iter_next(&it)))
{
uint8_t tmp_emmd5[MD5_DIGEST_LENGTH * 2 + 1];
char_to_hex(s->emmd5, MD5_DIGEST_LENGTH, tmp_emmd5);
result = fprintf(file, "%s,%s,%" PRId64 ",%" PRId64 ",%02X,%04X\n", rdr->label, tmp_emmd5, (int64_t)s->firstwritten.time, (int64_t)s->lastwritten.time, s->type, s->count);
if(result < 0)
{
cs_writeunlock(__func__, &rdr->emmstat_lock);
fclose(file);
result = remove(fname);
if(!result)
{
cs_log("error writing stats -> stat file removed!");
}
else
{
cs_log("error writing stats -> stat file could not be removed either!");
}
return;
}
count++;
}
cs_writeunlock(__func__, &rdr->emmstat_lock);
}
}
fclose(file);
cs_ftime(&te);
int64_t load_time = comp_timeb(&te, &ts);
cs_log("saved %d emmstat records to %s in %"PRId64" ms", count, fname, load_time);
}
void emm_load_cache(void)
{
if(boxtype_is("dbox2")) return; // don't load emmcache on these boxes, they lack resources and will crash!
if(!emm_cache_configured()){
cs_log("loading emmcache disabled since no reader is using it!");
return;
}
char fname[256];
char line[1024];
FILE *file;
struct s_emmcache *c;
if(cfg.emmlogdir)
{
get_emmcache_filename(fname, sizeof(fname), "oscam.emmcache");
}
else
{
get_config_filename(fname, sizeof(fname), "oscam.emmcache");
}
file = fopen(fname, "r");
if(!file)
{
cs_log_dbg(D_TRACE, "can't read emmcache from file %s", fname);
return;
}
struct timeb ts, te;
cs_ftime(&ts);
int32_t count = 0;
int32_t i = 1;
int32_t valid = 0;
char *ptr, *saveptr1 = NULL;
char *split[7];
memset(line, 0, sizeof(line));
while(fgets(line, sizeof(line), file))
{
if(!line[0] || line[0] == '#' || line[0] == ';')
{ continue; }
for(i = 0, ptr = strtok_r(line, ",", &saveptr1); ptr && i < 7 ; ptr = strtok_r(NULL, ",", &saveptr1), i++)
{
split[i] = ptr;
}
valid = (i == 6);
if(valid)
{
if(!cs_malloc(&c, sizeof(struct s_emmcache)))
{ continue; }
key_atob_l(split[0], c->emmd5, MD5_DIGEST_LENGTH*2);
c->firstseen.time = atol(split[1]);
c->lastseen.time = atol(split[2]);
c->type = a2i(split[3], 2);
c->len = a2i(split[4], 4);
key_atob_l(split[5], c->emm, c->len*2);
if(valid && c->len != 0)
{
if(!emm_cache)
{
emm_cache = ll_create("emm cache");
}
ll_append(emm_cache, c);
count++;
}
else
{
NULLFREE(c);
}
}
}
fclose(file);
cs_ftime(&te);
int64_t load_time = comp_timeb(&te, &ts);
cs_log("loaded %d emmcache records from %s in %"PRId64" ms", count, fname, load_time);
}
struct s_emmcache *find_emm_cache(uint8_t *emmd5)
{
struct s_emmcache *c;
LL_ITER it;
if(!emm_cache)
{ emm_cache = ll_create("emm cache"); }
it = ll_iter_create(emm_cache);
while((c = ll_iter_next(&it)))
{
if(!memcmp(emmd5, c->emmd5, MD5_DIGEST_LENGTH))
{
cs_log_dump_dbg(D_EMM, c->emmd5, MD5_DIGEST_LENGTH, "found emmcache match");
return c;
}
}
return NULL;
}
int32_t clean_stale_emm_cache_and_stat(uint8_t *emmd5, int64_t gone)
{
struct timeb now;
cs_ftime(&now);
int32_t count = 0;
struct s_emmcache *c;
LL_ITER it;
if(!emm_cache)
{ emm_cache = ll_create("emm cache"); }
it = ll_iter_create(emm_cache);
while((c = ll_iter_next(&it)))
{
if(comp_timeb(&now, &c->lastseen) > gone && memcmp(c->emmd5, emmd5, MD5_DIGEST_LENGTH)) // clean older than gone ms and dont clean if its the current emm!
{
struct s_reader *rdr;
LL_ITER rdr_itr = ll_iter_create(configured_readers);
while((rdr = ll_iter_next(&rdr_itr)))
{
if(rdr->emmstat && !(caid_is_irdeto(rdr->caid) || caid_is_videoguard(rdr->caid)))
{
remove_emm_stat(rdr, c->emmd5); // clean stale entry from stats
count++;
}
}
ll_iter_remove_data(&it); // clean stale entry from emmcache
}
}
return count;
}
int32_t emm_edit_cache(uint8_t *emmd5, EMM_PACKET *ep, bool add)
{
struct s_emmcache *c;
LL_ITER it;
int32_t count = 0;
if(!emm_cache)
{ emm_cache = ll_create("emm cache"); }
it = ll_iter_create(emm_cache);
while((c = ll_iter_next(&it)))
{
if(!memcmp(emmd5, c->emmd5, MD5_DIGEST_LENGTH))
{
if(add)
{
return 0; // already added
}
ll_iter_remove_data(&it);
count++;
}
}
if(add)
{
if(!cs_malloc(&c, sizeof(struct s_emmcache)))
{ return count; }
memcpy(c->emmd5, emmd5, MD5_DIGEST_LENGTH);
c->type = ep->type;
c->len = SCT_LEN(ep->emm);
cs_ftime(&c->firstseen);
c->lastseen = c->firstseen;
memcpy(c->emm, ep->emm, c->len);
ll_append(emm_cache, c);
#ifdef WITH_DEBUG
cs_log_dump_dbg(D_EMM, c->emmd5, MD5_DIGEST_LENGTH, "added emm to cache:");
#endif
count++;
}
return count;
}
int32_t remove_emm_stat(struct s_reader *rdr, uint8_t *emmd5)
{
int32_t count = 0;
if(rdr && rdr->emmstat)
{
cs_writelock(__func__, &rdr->emmstat_lock);
struct s_emmstat *c;
LL_ITER itr = ll_iter_create(rdr->emmstat);
while((c = ll_iter_next(&itr)))
{
if(!memcmp(emmd5, c->emmd5, MD5_DIGEST_LENGTH))
{
ll_iter_remove_data(&itr);
count++;
break;
}
}
cs_writeunlock(__func__, &rdr->emmstat_lock);
}
return count;
}
struct s_emmstat *get_emm_stat(struct s_reader *rdr, uint8_t *emmd5, uint8_t emmtype)
{
if(!rdr->cachemm) return NULL;
struct s_emmstat *c;
LL_ITER it;
if(!rdr->emmstat)
{ rdr->emmstat = ll_create("emm stat"); }
it = ll_iter_create(rdr->emmstat);
while((c = ll_iter_next(&it)))
{
if(!memcmp(emmd5, c->emmd5, MD5_DIGEST_LENGTH))
{
cs_log_dump_dbg(D_EMM, c->emmd5, MD5_DIGEST_LENGTH, "found emmstat match (reader:%s, count:%d)", rdr->label, c->count);
return c;
}
}
if(cs_malloc(&c, sizeof(struct s_emmstat)))
{
memcpy(c->emmd5, emmd5, MD5_DIGEST_LENGTH);
c->type = emmtype;
ll_append(rdr->emmstat, c);
cs_log_dump_dbg(D_EMM, c->emmd5, MD5_DIGEST_LENGTH, "added emmstat (reader:%s, count:%d)", rdr->label, c->count);
}
return c;
}