#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; }