oscam-2.26.01-11942-802-wit.../oscam-config.c

1513 lines
33 KiB
C
Raw Permalink Normal View History

#define MODULE_LOG_PREFIX "config"
//FIXME Not checked on threadsafety yet; after checking please remove this line
#include "globals.h"
#include "oscam-conf.h"
#include "oscam-conf-chk.h"
#include "oscam-config.h"
#include "oscam-files.h"
#include "oscam-garbage.h"
#include "oscam-lock.h"
#include "oscam-string.h"
#include "oscam-time.h"
#define cs_srid "oscam.srvid"
#define cs_ratelimit "oscam.ratelimit"
#define cs_trid "oscam.tiers"
#define cs_sidt "oscam.services"
#define cs_whitelist "oscam.whitelist"
#define cs_provid "oscam.provid"
#define cs_fakecws "oscam.fakecws"
#define cs_twin "oscam.twin"
uint32_t cfg_sidtab_generation = 1;
uint32_t caid;
extern char cs_confdir[];
char *get_config_filename(char *dest, size_t destlen, const char *filename)
{
// cs_confdir is always terminated with /
snprintf(dest, destlen, "%s%s", cs_confdir, filename);
return dest;
}
int32_t write_services(void)
{
int32_t i;
struct s_sidtab *sidtab = cfg.sidtab;
char *ptr;
FILE *f = create_config_file(cs_sidt);
if(!f)
{ return 1; }
while(sidtab != NULL)
{
ptr = sidtab->label;
while(*ptr)
{
if(*ptr == ' ') { *ptr = '_'; }
ptr++;
}
fprintf(f, "[%s]\n", sidtab->label);
#ifdef CS_CACHEEX_AIO
fprintf_conf(f, "disablecrccws_only_for_exception", "%u", sidtab->disablecrccws_only_for_exception); // it should not have \n at the end
fputc((int)'\n', f);
fprintf_conf(f, "no_wait_time", "%u", sidtab->no_wait_time); // it should not have \n at the end
fputc((int)'\n', f);
fprintf_conf(f, "lg_only_exception", "%u", sidtab->lg_only_exception); // it should not have \n at the end
fputc((int)'\n', f);
#endif
fprintf_conf(f, "caid", "%s", ""); // it should not have \n at the end
for(i = 0; i < sidtab->num_caid; i++)
{
if(i == 0) { fprintf(f, "%04X", sidtab->caid[i]); }
else { fprintf(f, ",%04X", sidtab->caid[i]); }
}
fputc((int)'\n', f);
fprintf_conf(f, "provid", "%s", ""); // it should not have \n at the end
for(i = 0; i < sidtab->num_provid; i++)
{
if(i == 0) { fprintf(f, "%06X", sidtab->provid[i]); }
else { fprintf(f, ",%06X", sidtab->provid[i]); }
}
fputc((int)'\n', f);
fprintf_conf(f, "srvid", "%s", ""); // it should not have \n at the end
for(i = 0; i < sidtab->num_srvid; i++)
{
if(i == 0) { fprintf(f, "%04X", sidtab->srvid[i]); }
else { fprintf(f, ",%04X", sidtab->srvid[i]); }
}
fprintf(f, "\n\n");
sidtab = sidtab->next;
}
return flush_config_file(f, cs_sidt);
}
void free_sidtab(struct s_sidtab *ptr)
{
if(!ptr) { return; }
add_garbage(ptr->caid); //no need to check on NULL first, freeing NULL doesnt do anything
add_garbage(ptr->provid);
add_garbage(ptr->srvid);
add_garbage(ptr);
}
static void chk_entry4sidtab(char *value, struct s_sidtab *sidtab, int32_t what)
{
int32_t i, b;
char *ptr, *saveptr1 = NULL;
uint16_t *slist = (uint16_t *) 0;
uint32_t *llist = (uint32_t *) 0;
#ifdef CS_CACHEEX_AIO
uint8_t disablecrccws_only_for_exception = 0;
uint8_t no_wait_time = 0;
uint8_t lg_only_exception = 0;
#endif
char buf[cs_strlen(value) + 1];
cs_strncpy(buf, value, sizeof(buf));
#ifdef CS_CACHEEX_AIO
if(what == 5) // lg_only_exception
{
sidtab->lg_only_exception = a2i(buf, sizeof(lg_only_exception));
return;
}
if(what == 4) // no_wait_time
{
sidtab->no_wait_time = a2i(buf, sizeof(no_wait_time));
return;
}
if(what == 3) // disablecrccws_only_for_exception
{
sidtab->disablecrccws_only_for_exception = a2i(buf, sizeof(disablecrccws_only_for_exception));
return;
}
#endif
b = (what == 1) ? sizeof(uint32_t) : sizeof(uint16_t);
for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1))
{
caid = a2i(ptr, b);
if(!errno) { i++; }
}
//if (!i) return(0);
if(b == sizeof(uint16_t))
{
if(!cs_malloc(&slist, i * sizeof(uint16_t))) { return; }
}
else
{
if(!cs_malloc(&llist, i * sizeof(uint32_t))) { return; }
}
cs_strncpy(value, buf, sizeof(buf));
for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1))
{
caid = a2i(ptr, b);
if(errno) { continue; }
if(b == sizeof(uint16_t))
{ slist[i++] = (uint16_t) caid; }
else
{ llist[i++] = caid; }
}
switch(what)
{
case 0:
add_garbage(sidtab->caid);
sidtab->caid = slist;
sidtab->num_caid = i;
break;
case 1:
add_garbage(sidtab->provid);
sidtab->provid = llist;
sidtab->num_provid = i;
break;
case 2:
add_garbage(sidtab->srvid);
sidtab->srvid = slist;
sidtab->num_srvid = i;
break;
}
}
void chk_sidtab(char *token, char *value, struct s_sidtab *sidtab)
{
if(!strcmp(token, "caid"))
{
chk_entry4sidtab(value, sidtab, 0);
return;
}
if(!strcmp(token, "provid"))
{
chk_entry4sidtab(value, sidtab, 1);
return;
}
if(!strcmp(token, "ident"))
{
chk_entry4sidtab(value, sidtab, 1);
return;
}
if(!strcmp(token, "srvid"))
{
chk_entry4sidtab(value, sidtab, 2);
return;
}
#ifdef CS_CACHEEX_AIO
if(!strcmp(token, "disablecrccws_only_for_exception"))
{
chk_entry4sidtab(value, sidtab, 3);
return;
}
if(!strcmp(token, "no_wait_time"))
{
chk_entry4sidtab(value, sidtab, 4);
return;
}
if(!strcmp(token, "lg_only_exception"))
{
chk_entry4sidtab(value, sidtab, 5);
return;
}
#endif
if(token[0] != '#')
{ fprintf(stderr, "Warning: keyword '%s' in sidtab section not recognized\n", token); }
}
void init_free_sidtab(void)
{
struct s_sidtab *nxt, *ptr = cfg.sidtab;
while(ptr)
{
nxt = ptr->next;
free_sidtab(ptr);
ptr = nxt;
}
cfg.sidtab = NULL;
++cfg_sidtab_generation;
}
//#define DEBUG_SIDTAB 1
#ifdef DEBUG_SIDTAB
static void show_sidtab(struct s_sidtab *sidtab)
{
for(; sidtab; sidtab = sidtab->next)
{
int32_t i;
char buf[1024];
char *saveptr = buf;
cs_log("label=%s", sidtab->label);
#ifdef CS_CACHEEX_AIO
cs_log("disablecrccws_only_for_exception=%u", sidtab->disablecrccws_only_for_exception);
cs_log("no_wait_time=%u", sidtab->no_wait_time);
cs_log("lg_only_exception=%u", sidtab->lg_only_exception);
#endif
snprintf(buf, sizeof(buf), "caid(%d)=", sidtab->num_caid);
for(i = 0; i < sidtab->num_caid; i++)
{ snprintf(buf + cs_strlen(buf), 1024 - (buf - saveptr), "%04X ", sidtab->caid[i]); }
cs_log("%s", buf);
snprintf(buf, sizeof(buf), "provider(%d)=", sidtab->num_provid);
for(i = 0; i < sidtab->num_provid; i++)
{ snprintf(buf + cs_strlen(buf), 1024 - (buf - saveptr), "%08X ", sidtab->provid[i]); }
cs_log("%s", buf);
snprintf(buf, sizeof(buf), "services(%d)=", sidtab->num_srvid);
for(i = 0; i < sidtab->num_srvid; i++)
{ snprintf(buf + cs_strlen(buf), 1024 - (buf - saveptr), "%04X ", sidtab->srvid[i]); }
cs_log("%s", buf);
}
}
#else
static void show_sidtab(struct s_sidtab *UNUSED(sidtab)) { }
#endif
int32_t init_sidtab(void)
{
FILE *fp = open_config_file(cs_sidt);
if(!fp)
{ return 1; }
int32_t nr, nro, nrr;
char *value, *token;
if(!cs_malloc(&token, MAXLINESIZE))
{ return 1; }
struct s_sidtab *ptr;
struct s_sidtab *sidtab = (struct s_sidtab *)0;
for(nro = 0, ptr = cfg.sidtab; ptr; nro++)
{
struct s_sidtab *ptr_next;
ptr_next = ptr->next;
free_sidtab(ptr);
ptr = ptr_next;
}
nr = 0;
nrr = 0;
while(fgets(token, MAXLINESIZE, fp))
{
int32_t l;
if((l = cs_strlen(trim(token))) < 3) { continue; }
if((token[0] == '[') && (token[l - 1] == ']'))
{
token[l - 1] = 0;
if(nr > MAX_SIDBITS)
{
fprintf(stderr, "Warning: Service No.%d - '%s' ignored. Max allowed Services %d\n", nr, strtolower(token + 1), MAX_SIDBITS);
nr++;
nrr++;
}
else
{
if(!cs_malloc(&ptr, sizeof(struct s_sidtab)))
{
NULLFREE(token);
return (1);
}
if(sidtab)
{ sidtab->next = ptr; }
else
{ cfg.sidtab = ptr; }
sidtab = ptr;
nr++;
cs_strncpy(sidtab->label, strtolower(token + 1), sizeof(sidtab->label));
continue;
}
}
if(!sidtab) { continue; }
if(!(value = strchr(token, '='))) { continue; }
*value++ = '\0';
chk_sidtab(trim(strtolower(token)), trim(strtolower(value)), sidtab);
}
NULLFREE(token);
fclose(fp);
show_sidtab(cfg.sidtab);
++cfg_sidtab_generation;
cs_log("services reloaded: %d services freed, %d services loaded, rejected %d", nro, nr, nrr);
return (0);
}
int32_t init_provid(void)
{
FILE *fp = open_config_file(cs_provid);
if(!fp)
{ return 0; }
int32_t nr;
char *payload, *saveptr1 = NULL, *token;
if(!cs_malloc(&token, MAXLINESIZE))
{ return 0; }
struct s_provid *provid_ptr = NULL;
struct s_provid *new_cfg_provid = NULL, *last_provid;
nr = 0;
while(fgets(token, MAXLINESIZE, fp))
{
int32_t i;
struct s_provid *new_provid = NULL;
char *tmp, *ptr1;
tmp = trim(token);
if(tmp[0] == '#') { continue; }
if(cs_strlen(tmp) < 11) { continue; }
if(!(payload = strchr(token, '|'))) { continue; }
*payload++ = '\0';
if(!cs_malloc(&new_provid, sizeof(struct s_provid)))
{
NULLFREE(token);
fclose(fp);
return (1);
}
new_provid->nprovid = 0;
for(i = 0, ptr1 = strtok_r(token, ":@", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ":@", &saveptr1), i++)
{
if(i==0)
{
new_provid->caid = a2i(ptr1, 3);
continue;
}
new_provid->nprovid++;
}
if(!cs_malloc(&new_provid->provid, sizeof(uint32_t) * new_provid->nprovid))
{
NULLFREE(new_provid);
NULLFREE(token);
fclose(fp);
return (1);
}
ptr1 = token + cs_strlen(token) + 1;
for(i = 0; i < new_provid->nprovid ; i++)
{
new_provid->provid[i] = a2i(ptr1, 3);
ptr1 = ptr1 + cs_strlen(ptr1) + 1;
}
for(i = 0, ptr1 = strtok_r(payload, "|", &saveptr1); ptr1; ptr1 = strtok_r(NULL, "|", &saveptr1), i++)
{
switch(i)
{
case 0:
cs_strncpy(new_provid->prov, trim(ptr1), sizeof(new_provid->prov));
break;
case 1:
cs_strncpy(new_provid->sat, trim(ptr1), sizeof(new_provid->sat));
break;
case 2:
cs_strncpy(new_provid->lang, trim(ptr1), sizeof(new_provid->lang));
break;
}
}
if(cs_strlen(new_provid->prov) == 0)
{
NULLFREE(new_provid->provid);
NULLFREE(new_provid);
continue;
}
nr++;
if(provid_ptr)
{
provid_ptr->next = new_provid;
}
else
{
new_cfg_provid = new_provid;
}
provid_ptr = new_provid;
}
NULLFREE(token);
fclose(fp);
if(nr > 0)
{ cs_log("%d provid's loaded", nr); }
if(new_cfg_provid == NULL)
{
if(!cs_malloc(&new_cfg_provid, sizeof(struct s_provid)))
{
return (1);
}
}
cs_writelock(__func__, &config_lock);
// this allows reloading of provids, so cleanup of old data is needed:
last_provid = cfg.provid; // old data
cfg.provid = new_cfg_provid; // assign after loading, so everything is in memory
cs_writeunlock(__func__, &config_lock);
struct s_client *cl;
for(cl = first_client->next; cl ; cl = cl->next)
{ cl->last_providptr = NULL; }
struct s_provid *ptr, *nptr;
if(last_provid)
{
ptr = last_provid;
while(ptr) // cleanup old data:
{
add_garbage(ptr->provid);
nptr = ptr->next;
add_garbage(ptr);
ptr = nptr;
}
}
return (0);
}
int32_t init_srvid(void)
{
int8_t new_syntax = 1;
FILE *fp = open_config_file("oscam.srvid2");
if(!fp)
{
fp = open_config_file(cs_srid);
if(fp)
{
new_syntax = 0;
}
}
if(!fp)
{
fp = create_config_file("oscam.srvid2");
if(fp)
{
flush_config_file(fp, "oscam.srvid2");
}
return 0;
}
int32_t nr = 0, i, j;
char *payload, *saveptr1 = NULL, *saveptr2 = NULL, *token;
const char *tmp;
if(!cs_malloc(&token, MAXLINESIZE))
{ return 0; }
struct s_srvid *srvid = NULL, *new_cfg_srvid[16], *last_srvid[16];
// A cache for strings within srvids. A checksum is calculated which is the start point in the array (some kind of primitive hash algo).
// From this point, a sequential search is done. This greatly reduces the amount of string comparisons.
const char **stringcache[1024];
int32_t allocated[1024] = { 0 };
int32_t used[1024] = { 0 };
struct timeb ts, te;
cs_ftime(&ts);
memset(last_srvid, 0, sizeof(last_srvid));
memset(new_cfg_srvid, 0, sizeof(new_cfg_srvid));
while(fgets(token, MAXLINESIZE, fp))
{
int32_t len = 0, len2, srvidtmp;
uint32_t k;
uint32_t pos;
char *srvidasc, *prov;
tmp = trim(token);
if(tmp[0] == '#') { continue; }
if(cs_strlen(tmp) < 6) { continue; }
if(!(srvidasc = strchr(token, ':'))) { continue; }
if(!(payload = strchr(token, '|'))) { continue; }
*payload++ = '\0';
if(!cs_malloc(&srvid, sizeof(struct s_srvid)))
{
NULLFREE(token);
fclose(fp);
return (1);
}
char tmptxt[128];
int32_t offset[4] = { -1, -1, -1, -1 };
char *ptr1 = NULL, *ptr2 = NULL;
const char *searchptr[4] = { NULL, NULL, NULL, NULL };
const char **ptrs[4] = { &srvid->prov, &srvid->name, &srvid->type, &srvid->desc };
uint32_t max_payload_length = MAXLINESIZE - (payload - token);
if(new_syntax)
{
ptrs[0] = &srvid->name;
ptrs[1] = &srvid->type;
ptrs[2] = &srvid->desc;
ptrs[3] = &srvid->prov;
}
// allow empty strings as "||"
if(payload[0] == '|' && (cs_strlen(payload) + 2 < max_payload_length))
{
memmove(payload+1, payload, cs_strlen(payload)+1);
payload[0] = ' ';
}
for(k = 1; ((k < max_payload_length) && (payload[k] != '\0')); k++)
{
if(payload[k - 1] == '|' && payload[k] == '|')
{
if(cs_strlen(payload + k) + 2 < max_payload_length-k)
{
memmove(payload + k + 1, payload + k, cs_strlen(payload + k) + 1);
payload[k] = ' ';
}
else
{
break;
}
}
}
for(i = 0, ptr1 = strtok_r(payload, "|", &saveptr1); ptr1 && (i < 4) ; ptr1 = strtok_r(NULL, "|", &saveptr1), ++i)
{
// check if string is in cache
len2 = cs_strlen(ptr1);
pos = 0;
for(j = 0; j < len2; ++j) { pos += (uint8_t)ptr1[j]; }
pos = pos % 1024;
for(j = 0; j < used[pos]; ++j)
{
if(!strcmp(stringcache[pos][j], ptr1))
{
searchptr[i] = stringcache[pos][j];
break;
}
}
if(searchptr[i]) { continue; }
offset[i] = len;
cs_strncpy(tmptxt + len, trim(ptr1), sizeof(tmptxt) - len);
len += cs_strlen(ptr1) + 1;
}
char *tmpptr = NULL;
if(len > 0 && !cs_malloc(&tmpptr, len))
{ continue; }
srvid->data = tmpptr;
if(len > 0) { memcpy(tmpptr, tmptxt, len); }
for(i = 0; i < 4; i++)
{
if(searchptr[i])
{
*ptrs[i] = searchptr[i];
continue;
}
if(offset[i] > -1)
{
*ptrs[i] = tmpptr + offset[i];
// store string in stringcache
if (*ptrs[i])
{
tmp = *ptrs[i];
len2 = cs_strlen(tmp);
}
else
{
cs_log("FIXME! len2!");
len2 = 0;
}
pos = 0;
for(j = 0; j < len2; ++j) { pos += (uint8_t)tmp[j]; }
if (pos > 0)
{
pos = pos % 1024;
}
if(used[pos] >= allocated[pos])
{
if(allocated[pos] == 0)
{
if(!cs_malloc(&stringcache[pos], 16 * sizeof(char *)))
{ break; }
}
else
{
if(!cs_realloc(&stringcache[pos], (allocated[pos] + 16) * sizeof(char *)))
{ break; }
}
allocated[pos] += 16;
}
if (tmp[0])
{
stringcache[pos][used[pos]] = tmp;
used[pos] += 1;
}
}
}
*srvidasc++ = '\0';
if(new_syntax)
{ srvidtmp = dyn_word_atob(token) & 0xFFFF; }
else
{ srvidtmp = dyn_word_atob(srvidasc) & 0xFFFF; }
if(srvidtmp < 0)
{
NULLFREE(tmpptr);
NULLFREE(srvid);
continue;
}
else
{
srvid->srvid = srvidtmp;
}
srvid->ncaid = 0;
for(i = 0, ptr1 = strtok_r(new_syntax ? srvidasc : token, ",", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1), i++)
{
srvid->ncaid++;
}
if(!cs_malloc(&srvid->caid, sizeof(struct s_srvid_caid) * srvid->ncaid))
{
NULLFREE(tmpptr);
NULLFREE(srvid);
return 0;
}
ptr1 = new_syntax ? srvidasc : token;
for(i = 0; i < srvid->ncaid; i++)
{
prov = strchr(ptr1,'@');
srvid->caid[i].nprovid = 0;
if(prov)
{
if(prov[1] != '\0')
{
for(j = 0, ptr2 = strtok_r(prov+1, "@", &saveptr2); (ptr2); ptr2 = strtok_r(NULL, "@", &saveptr2), j++)
{
srvid->caid[i].nprovid++;
}
if(!cs_malloc(&srvid->caid[i].provid, sizeof(uint32_t) * srvid->caid[i].nprovid))
{
for(j = 0; j < i; j++)
{ NULLFREE(srvid->caid[j].provid); }
NULLFREE(srvid->caid);
NULLFREE(tmpptr);
NULLFREE(srvid);
return 0;
}
ptr2 = prov + 1;
for(j = 0; j < srvid->caid[i].nprovid; j++)
{
srvid->caid[i].provid[j] = dyn_word_atob(ptr2) & 0xFFFFFF;
ptr2 = ptr2 + cs_strlen(ptr2) + 1;
}
}
else
{
ptr2 = prov + 2;
}
prov[0] = '\0';
}
srvid->caid[i].caid = dyn_word_atob(ptr1) & 0xFFFF;
if(prov)
{ ptr1 = ptr2; }
else
{ ptr1 = ptr1 + cs_strlen(ptr1) + 1; }
}
nr++;
if(new_cfg_srvid[srvid->srvid >> 12])
{ last_srvid[srvid->srvid >> 12]->next = srvid; }
else
{ new_cfg_srvid[srvid->srvid >> 12] = srvid; }
last_srvid[srvid->srvid >> 12] = srvid;
}
for(i = 0; i < 1024; ++i)
{
if(allocated[i] > 0) { NULLFREE(stringcache[i]); }
}
NULLFREE(token);
cs_ftime(&te);
int64_t load_time = comp_timeb(&te, &ts);
fclose(fp);
if(nr > 0)
{
cs_log("%d service-id's loaded in %"PRId64" ms", nr, load_time);
if(nr > 2000)
{
cs_log("WARNING: You risk high CPU load and high ECM times with more than 2000 service-id's!");
cs_log("HINT: --> use optimized lists from %s/Srvid", WIKI_URL);
}
}
cs_writelock(__func__, &config_lock);
// this allows reloading of srvids, so cleanup of old data is needed:
memcpy(last_srvid, cfg.srvid, sizeof(last_srvid)); //old data
memcpy(cfg.srvid, new_cfg_srvid, sizeof(last_srvid)); //assign after loading, so everything is in memory
cs_writeunlock(__func__, &config_lock);
struct s_client *cl;
for(cl = first_client->next; cl ; cl = cl->next)
{ cl->last_srvidptr = NULL; }
struct s_srvid *ptr, *nptr;
for(i = 0; i < 16; i++)
{
ptr = last_srvid[i];
while(ptr) // cleanup old data:
{
for(j = 0; j < ptr->ncaid; j++)
{ add_garbage(ptr->caid[j].provid); }
add_garbage(ptr->caid);
add_garbage(ptr->data);
nptr = ptr->next;
add_garbage(ptr);
ptr = nptr;
}
}
return (0);
}
int32_t init_fakecws(void)
{
int32_t nr = 0, i, j, idx;
uint32_t alloccount[0x100], count[0x100], tmp, max_compares = 0, average_compares = 0;
char *token, cw_string[64];
2026-02-23 16:40:08 +00:00
uint8_t cw[16], wrong_checksum, c, have_fakecw = 0;
FILE *fp;
memset(alloccount, 0, sizeof(count));
memset(count, 0, sizeof(alloccount));
cs_writelock(__func__, &config_lock);
for(i = 0; i < 0x100; i++)
{
cfg.fakecws[i].count = 0;
NULLFREE(cfg.fakecws[i].data);
}
cs_writeunlock(__func__, &config_lock);
fp = open_config_file(cs_fakecws);
if(!fp)
{ return 0; }
if(!cs_malloc(&token, MAXLINESIZE))
{ return 0; }
while(fgets(token, MAXLINESIZE, fp))
{
if(sscanf(token, " %62s ", cw_string) == 1)
{
if(cs_strlen(cw_string) == 32)
{
if(cs_atob(cw, cw_string, 16) == 16)
{
wrong_checksum = 0;
2026-02-23 16:40:08 +00:00
for(i = 0; i < 16; i += 4)
{
c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff);
if(cw[i + 3] != c)
{
wrong_checksum = 1;
}
}
if(wrong_checksum)
{
cs_log("skipping fake cw %s because of wrong checksum!", cw_string);
}
else
{
idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF);
alloccount[idx]++;
have_fakecw = 1;
}
}
else
{
cs_log("skipping fake cw %s because it contains invalid characters!", cw_string);
}
}
else
{
cs_log("skipping fake cw %s because of wrong length (%u != 32)!", cw_string, (uint32_t)cs_strlen(cw_string));
}
}
}
if(!have_fakecw)
{
NULLFREE(token);
fclose(fp);
return 0;
}
for(i = 0; i < 0x100; i++)
{
if(alloccount[i] && !cs_malloc(&cfg.fakecws[i].data, sizeof(struct s_cw)*alloccount[i]))
{
alloccount[i] = 0;
}
}
fseek(fp, 0, SEEK_SET);
while(fgets(token, MAXLINESIZE, fp))
{
if(sscanf(token, " %62s ", cw_string) == 1)
{
if(cs_strlen(cw_string) == 32)
{
if(cs_atob(cw, cw_string, 16) == 16)
{
wrong_checksum = 0;
2026-02-23 16:40:08 +00:00
for(i = 0; i < 16; i += 4)
{
c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff);
if(cw[i + 3] != c)
{
wrong_checksum = 1;
}
}
if(!wrong_checksum)
{
idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF);
if(count[idx] < alloccount[idx])
{
memcpy(cfg.fakecws[idx].data[count[idx]].cw, cw, 16);
count[idx]++;
nr++;
}
}
}
}
}
}
NULLFREE(token);
fclose(fp);
if(nr > 0)
{ cs_log("%d fakecws's loaded", nr); }
cs_writelock(__func__, &config_lock);
for(i = 0; i < 0x100; i++)
{
cfg.fakecws[i].count = count[i];
}
cs_writeunlock(__func__, &config_lock);
for(i = 0; i < 0x100; i++)
{
if(count[i] > max_compares)
{ max_compares = count[i]; }
}
for(i = 0; i < (0x100 - 1); i++)
{
for(j = i + 1; j < 0x100; j++)
{
if(count[j] < count[i])
{
tmp = count[i];
count[i] = count[j];
count[j] = tmp;
}
}
}
average_compares = ((count[0x100 / 2] + count[0x100 / 2 - 1]) / 2);
cs_log("max %d fakecw compares required, on average: %d compares", max_compares, average_compares);
return 0;
}
static struct s_rlimit *ratelimit_read_int(void)
{
FILE *fp = open_config_file(cs_ratelimit);
if(!fp)
{ return NULL; }
char token[1024], str1[1024];
int32_t i, ret, count = 0;
struct s_rlimit *new_rlimit = NULL, *entry, *last = NULL;
while(fgets(token, sizeof(token), fp))
{
if(cs_strlen(token) <= 1) { continue; }
if(token[0] == '#' || token[0] == '/') { continue; }
if(cs_strlen(token) > 1024) { continue; }
for(i = 0; i < (int)cs_strlen(token); i++)
{
if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':')
{
memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1);
token[i + 1] = '0';
}
if(token[i] == '#' || token[i] == '/')
{
token[i] = '\0';
break;
}
}
caid = 0;
uint32_t provid = 0, srvid = 0, chid = 0, ratelimitecm = 0, ratelimittime = 0, srvidholdtime = 0;
memset(str1, 0, sizeof(str1));
ret = sscanf(token, "%4x:%6x:%4x:%4x:%d:%d:%d:%1023s", &caid, &provid, &srvid, &chid, &ratelimitecm, &ratelimittime, &srvidholdtime, str1);
if(ret < 1) {
continue;
}
if (!cs_strncat(str1, ",", sizeof(str1))) {
return new_rlimit;
}
if(!cs_malloc(&entry, sizeof(struct s_rlimit)))
{
fclose(fp);
return new_rlimit;
}
count++;
if (ratelimittime < 60) ratelimittime *= 1000;
if (srvidholdtime < 60) srvidholdtime *= 1000;
entry->rl.caid = caid;
entry->rl.provid = provid;
entry->rl.srvid = srvid;
entry->rl.chid = chid;
entry->rl.ratelimitecm = ratelimitecm;
entry->rl.ratelimittime = ratelimittime;
entry->rl.srvidholdtime = srvidholdtime;
cs_log_dbg(D_TRACE, "ratelimit: %04X@%06X:%04X:%04X:%d:%d:%d", entry->rl.caid, entry->rl.provid, entry->rl.srvid, entry->rl.chid,
entry->rl.ratelimitecm, entry->rl.ratelimittime, entry->rl.srvidholdtime);
if(!new_rlimit)
{
new_rlimit = entry;
last = new_rlimit;
}
else
{
last->next = entry;
last = entry;
}
}
if(count)
{ cs_log("%d entries read from %s", count, cs_ratelimit); }
fclose(fp);
return new_rlimit;
}
void ratelimit_read(void)
{
struct s_rlimit *entry, *old_list;
old_list = cfg.ratelimit_list;
cfg.ratelimit_list = ratelimit_read_int();
while(old_list)
{
entry = old_list->next;
NULLFREE(old_list);
old_list = entry;
}
}
struct ecmrl get_ratelimit(ECM_REQUEST *er)
{
struct ecmrl tmp;
memset(&tmp, 0, sizeof(tmp));
if(!cfg.ratelimit_list) { return tmp; }
struct s_rlimit *entry = cfg.ratelimit_list;
while(entry)
{
if(entry->rl.caid == er->caid && entry->rl.provid == er->prid && entry->rl.srvid == er->srvid && (!entry->rl.chid || entry->rl.chid == er->chid))
{
break;
}
entry = entry->next;
}
if(entry) { tmp = entry->rl; }
return (tmp);
}
int32_t init_tierid(void)
{
FILE *fp = open_config_file(cs_trid);
if(!fp)
{ return 0; }
int32_t nr;
char *payload, *saveptr1 = NULL, *token;
if(!cs_malloc(&token, MAXLINESIZE))
{ return 0; }
static struct s_tierid *tierid = NULL, *new_cfg_tierid = NULL;
nr = 0;
while(fgets(token, MAXLINESIZE, fp))
{
void *ptr;
char *tmp, *tieridasc;
tmp = trim(token);
if(tmp[0] == '#') { continue; }
if(cs_strlen(tmp) < 6) { continue; }
if(!(payload = strchr(token, '|'))) { continue; }
if(!(tieridasc = strchr(token, ':'))) { continue; }
*payload++ = '\0';
if(!cs_malloc(&ptr, sizeof(struct s_tierid)))
{
NULLFREE(token);
fclose(fp);
return (1);
}
if(tierid)
{ tierid->next = ptr; }
else
{ new_cfg_tierid = ptr; }
tierid = ptr;
int32_t i;
char *ptr1 = strtok_r(payload, "|", &saveptr1);
if(ptr1)
{ cs_strncpy(tierid->name, trim(ptr1), sizeof(tierid->name)); }
*tieridasc++ = '\0';
tierid->tierid = dyn_word_atob(tieridasc);
//printf("tierid %s - %d\n",tieridasc,tierid->tierid );
tierid->ncaid = 0;
for(i = 0, ptr1 = strtok_r(token, ",", &saveptr1); (ptr1) && (i < 10) ; ptr1 = strtok_r(NULL, ",", &saveptr1), i++)
{
tierid->caid[i] = dyn_word_atob(ptr1);
tierid->ncaid = i + 1;
//cs_log("ld caid: %04X tierid: %04X name: %s",tierid->caid[i],tierid->tierid,tierid->name);
}
nr++;
}
NULLFREE(token);
fclose(fp);
if(nr > 0)
{ cs_log("%d tier-id's loaded", nr); }
cs_writelock(__func__, &config_lock);
// reload function:
tierid = cfg.tierid;
cfg.tierid = new_cfg_tierid;
struct s_tierid *ptr;
while(tierid)
{
ptr = tierid->next;
NULLFREE(tierid);
tierid = ptr;
}
cs_writeunlock(__func__, &config_lock);
return (0);
}
int32_t match_whitelist(ECM_REQUEST *er, struct s_global_whitelist *entry)
{
return ((!entry->caid || entry->caid == er->caid)
&& (!entry->provid || entry->provid == er->prid)
&& (!entry->srvid || entry->srvid == er->srvid)
&& (!entry->chid || entry->chid == er->chid)
&& (!entry->pid || entry->pid == er->pid)
&& (!entry->ecmlen || entry->ecmlen == er->ecmlen));
}
int32_t chk_global_whitelist(ECM_REQUEST *er, uint32_t *line)
{
*line = -1;
if(!cfg.global_whitelist)
{ return 1; }
struct s_global_whitelist *entry;
// check mapping:
if(cfg.global_whitelist_use_m)
{
entry = cfg.global_whitelist;
while(entry)
{
if(entry->type == 'm')
{
if(match_whitelist(er, entry))
{
cs_log_dbg(D_TRACE, "whitelist: mapped %04X@%06X to %04X@%06X", er->caid, er->prid, entry->mapcaid, entry->mapprovid);
er->caid = entry->mapcaid;
er->prid = entry->mapprovid;
break;
}
}
entry = entry->next;
}
}
if(cfg.global_whitelist_use_l) // Check caid/prov/srvid etc matching, except ecm-len:
{
entry = cfg.global_whitelist;
int8_t caidprov_matches = 0;
while(entry)
{
if(entry->type == 'l')
{
if(match_whitelist(er, entry))
{
*line = entry->line;
return 1;
}
if((!entry->caid || entry->caid == er->caid)
&& (!entry->provid || entry->provid == er->prid)
&& (!entry->srvid || entry->srvid == er->srvid)
&& (!entry->chid || entry->chid == er->chid)
&& (!entry->pid || entry->pid == er->pid))
{
caidprov_matches = 1;
*line = entry->line;
}
}
entry = entry->next;
}
if(caidprov_matches) // ...but not ecm-len!
{ return 0; }
}
entry = cfg.global_whitelist;
while(entry)
{
if(match_whitelist(er, entry))
{
*line = entry->line;
if(entry->type == 'w')
{ return 1; }
else if(entry->type == 'i')
{ return 0; }
}
entry = entry->next;
}
return 0;
}
//Format:
//Whitelist-Entry:
//w:caid:prov:srvid:pid:chid:ecmlen
//Ignore-Entry:
//i:caid:prov:srvid:pid:chid:ecmlen
//ECM len check - Entry:
//l:caid:prov:srvid:pid:chid:ecmlen
//Mapping:
//m:caid:prov:srvid:pid:chid:ecmlen caidto:provto
static struct s_global_whitelist *global_whitelist_read_int(void)
{
FILE *fp = open_config_file(cs_whitelist);
if(!fp)
{ return NULL; }
char token[1024], str1[1024];
uint8_t type;
int32_t i, ret, count = 0;
struct s_global_whitelist *new_whitelist = NULL, *entry, *last = NULL;
uint32_t line = 0;
cfg.global_whitelist_use_l = 0;
cfg.global_whitelist_use_m = 0;
while(fgets(token, sizeof(token), fp))
{
line++;
if(cs_strlen(token) <= 1) { continue; }
if(token[0] == '#' || token[0] == '/') { continue; }
if(cs_strlen(token) > 1024) { continue; }
for(i = 0; i < (int)cs_strlen(token); i++)
{
if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':')
{
memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1);
token[i + 1] = '0';
}
if(token[i] == '#' || token[i] == '/')
{
token[i] = '\0';
break;
}
}
type = 'w';
caid = 0;
uint32_t provid = 0, srvid = 0, pid = 0, chid = 0, ecmlen = 0, mapcaid = 0, mapprovid = 0;
memset(str1, 0, sizeof(str1));
ret = sscanf(token, "%c:%4x:%6x:%4x:%4x:%4x:%1023s", &type, &caid, &provid, &srvid, &pid, &chid, str1);
type = tolower(type);
//w=whitelist
//i=ignore
//l=len-check
//m=map caid/prov
if(ret < 1 || (type != 'w' && type != 'i' && type != 'l' && type != 'm'))
{ continue; }
if(type == 'm')
{
char *p = strstr(token + 4, " ");
if(!p || sscanf(p + 1, "%4x:%6x", &mapcaid, &mapprovid) < 2)
{
cs_log_dbg(D_TRACE, "whitelist: wrong mapping: %s", token);
continue;
}
str1[0] = 0;
cfg.global_whitelist_use_m = 1;
}
if (!cs_strncat(str1, ",", sizeof(str1))) {
return new_whitelist;
}
char *p = str1, *p2 = str1;
while(*p)
{
if(*p == ',')
{
*p = 0;
ecmlen = 0;
sscanf(p2, "%4x", &ecmlen);
if(!cs_malloc(&entry, sizeof(struct s_global_whitelist)))
{
fclose(fp);
return new_whitelist;
}
count++;
entry->line = line;
entry->type = type;
entry->caid = caid;
entry->provid = provid;
entry->srvid = srvid;
entry->pid = pid;
entry->chid = chid;
entry->ecmlen = ecmlen;
entry->mapcaid = mapcaid;
entry->mapprovid = mapprovid;
if(entry->type == 'l')
{ cfg.global_whitelist_use_l = 1; }
if(type == 'm')
cs_log_dbg(D_TRACE, "whitelist: %c: %04X@%06X:%04X:%04X:%04X:%02X map to %04X@%06X",
entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen, entry->mapcaid, entry->mapprovid);
else
cs_log_dbg(D_TRACE, "whitelist: %c: %04X@%06X:%04X:%04X:%04X:%02X",
entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen);
if(!new_whitelist)
{
new_whitelist = entry;
last = new_whitelist;
}
else
{
last->next = entry;
last = entry;
}
p2 = p + 1;
}
p++;
}
}
if(count)
{ cs_log("%d entries read from %s", count, cs_whitelist); }
fclose(fp);
return new_whitelist;
}
void global_whitelist_read(void)
{
struct s_global_whitelist *entry, *old_list;
old_list = cfg.global_whitelist;
cfg.global_whitelist = global_whitelist_read_int();
while(old_list)
{
entry = old_list->next;
NULLFREE(old_list);
old_list = entry;
}
}
#ifdef MODULE_SERIAL
static struct s_twin *twin_read_int(void)
{
FILE *fp = open_config_file(cs_twin);
if(!fp)
{ return NULL; }
char token[1024], str1[1024];
int32_t i, ret, count = 0;
struct s_twin *new_twin = NULL, *entry, *last = NULL;
while(fgets(token, sizeof(token), fp))
{
if(cs_strlen(token) <= 1) { continue; }
if(token[0] == '#' || token[0] == '/') { continue; }
if(cs_strlen(token) > 1024) { continue; }
for(i = 0; i < (int)cs_strlen(token); i++)
{
if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':')
{
memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1);
token[i + 1] = '0';
}
if(token[i] == '#' || token[i] == '/' || token[i] == '"')
{
token[i] = '\0';
break;
}
}
caid = 0;
uint32_t provid = 0, srvid = 0, deg = 0, freq = 0;
//char hdeg[4], hfreq[4], hsrvid[4];
memset(str1, 0, sizeof(str1));
ret = sscanf(token, "%4x:%6x:%d:%d:%d", &caid, &provid, &deg, &freq, &srvid);
if(ret < 1) { continue; }
//snprintf(hdeg, 4, "%x", deg);
//sscanf(hdeg, "%4x", &deg);
//snprintf(hfreq, 4, "%x", freq);
//sscanf(hfreq, "%4x", &freq);
//snprintf(hsrvid, 4, "%x", srvid);
//sscanf(hsrvid, "%4x", &srvid);
if (!cs_strncat(str1, ",", sizeof(str1))) {
return new_twin;
}
if(!cs_malloc(&entry, sizeof(struct s_twin)))
{
fclose(fp);
return new_twin;
}
count++;
entry->tw.caid = caid;
entry->tw.provid = provid;
entry->tw.srvid = srvid;
entry->tw.deg = deg;
entry->tw.freq = freq;
cs_debug_mask(D_TRACE, "channel: %04X:%06X:%d:%d:%d", entry->tw.caid,
entry->tw.provid, entry->tw.deg, entry->tw.freq, entry->tw.srvid);
if(!new_twin)
{
new_twin = entry;
last = new_twin;
}
else
{
last->next = entry;
last = entry;
}
}
if(count)
{ cs_log("%d entries read from %s", count, cs_twin); }
fclose(fp);
return new_twin;
}
void twin_read(void)
{
struct s_twin *entry, *old_list;
old_list = cfg.twin_list;
cfg.twin_list = twin_read_int();
while(old_list)
{
entry = old_list->next;
free(old_list);
old_list = entry;
}
}
struct ecmtw get_twin(ECM_REQUEST *er)
{
struct ecmtw tmp;
memset(&tmp, 0, sizeof(tmp));
if(!cfg.twin_list)
{
cs_log("twin_list not found!");
return tmp;
}
struct s_twin *entry = cfg.twin_list;
while(entry)
{
if(entry->tw.caid == er->caid && entry->tw.provid == er->prid && entry->tw.srvid == er->srvid)
{
break;
}
entry = entry->next;
}
if(entry) { tmp = entry->tw; }
return (tmp);
}
#endif