oscam-2.26.01-11942-802-wit.../module-anticasc.c
2026-02-23 17:40:08 +01:00

491 lines
12 KiB
C

#define MODULE_LOG_PREFIX "anticasc"
//FIXME Not checked on threadsafety yet; after checking please remove this line
#include "globals.h"
#ifdef CS_ANTICASC
#include "module-anticasc.h"
#include "oscam-conf.h"
#include "oscam-client.h"
#include "oscam-garbage.h"
#include "oscam-string.h"
#include "oscam-time.h"
#define cs_ac "oscam.ac"
static FILE *ac_log;
static uint8_t ac_ecmd5[CS_ECMSTORESIZE];
bool anticasc_logging(char *txt)
{
if (!ac_log)
return false;
if (strstr(txt, "acasc: "))
{
fprintf(ac_log, "%s\n", txt);
fflush(ac_log);
return true;
}
return false;
}
static int32_t ac_init_log(void)
{
if(ac_log)
{ return 1; }
if(!cfg.ac_logfile)
{
cs_log("ERROR: anti cascading is enabled but ac_logfile is not set.");
return 0;
}
ac_log = fopen(cfg.ac_logfile, "a+");
if(!ac_log)
{
cs_log("ERROR: Can't open anti cascading logfile: %s (errno=%d %s)",
cfg.ac_logfile, errno, strerror(errno));
return 0;
}
cs_log("anti cascading log initialized");
return 1;
}
void ac_clear(void)
{
struct s_client *client;
struct s_auth *account;
for(client = first_client; client; client = client->next)
{
if(client->typ != 'c') { continue; }
memset(&client->acasc, 0, sizeof(client->acasc));
}
for(account = cfg.account; account; account = account->next)
{ memset(&account->ac_stat, 0, sizeof(account->ac_stat)); }
}
void ac_init_stat(void)
{
if(!cfg.ac_enabled)
{ return; }
ac_clear();
ac_init_log();
}
void ac_do_stat(void)
{
int32_t j, idx, exceeds, maxval, prev_deny = 0;
struct s_client *client;
for(client = first_client; client; client = client->next)
{
if(client->typ != 'c') { continue; }
struct s_acasc *ac_stat = &client->account->ac_stat;
struct s_acasc_shm *acasc = &client->acasc;
idx = ac_stat->idx;
ac_stat->stat[idx] = acasc->ac_count;
acasc->ac_count = 0;
if(ac_stat->stat[idx])
{
if(client->ac_penalty == 2) // banned
{
cs_log_dbg(D_CLIENT, "acasc: user '%s' banned", client->account->usr);
acasc->ac_deny = 1;
}
else
{
for(j = exceeds = maxval = 0; j < cfg.ac_samples; j++)
{
if(ac_stat->stat[j] > maxval)
{ maxval = ac_stat->stat[j]; }
exceeds += (ac_stat->stat[j] > client->ac_limit);
}
prev_deny = acasc->ac_deny;
acasc->ac_deny = (exceeds >= cfg.ac_denysamples);
cs_log_dbg(D_CLIENT, "acasc: %s limit=%d, max=%d, samples=%d, dsamples=%d, [idx=%d]:",
client->account->usr, client->ac_limit, maxval,
cfg.ac_samples, cfg.ac_denysamples, idx);
cs_log_dbg(D_CLIENT, "acasc: %d %d %d %d %d %d %d %d %d %d ", ac_stat->stat[0],
ac_stat->stat[1], ac_stat->stat[2], ac_stat->stat[3],
ac_stat->stat[4], ac_stat->stat[5], ac_stat->stat[6],
ac_stat->stat[7], ac_stat->stat[8], ac_stat->stat[9]);
if(acasc->ac_deny)
{
cs_log("acasc: user '%s' exceeds limit", client->account->usr);
ac_stat->stat[idx] = 0;
}
else if(prev_deny)
{ cs_log("acasc: user '%s' restored access", client->account->usr); }
}
}
else if(acasc->ac_deny)
{
prev_deny = 1;
acasc->ac_deny = 0;
cs_log("acasc: restored access for inactive user '%s'", client->account->usr);
}
if(!acasc->ac_deny && !prev_deny)
{ ac_stat->idx = (ac_stat->idx + 1) % cfg.ac_samples; }
}
}
void ac_init_client(struct s_client *client, struct s_auth *account)
{
client->ac_limit = 0;
client->ac_penalty = account->ac_penalty == -1 ? cfg.ac_penalty : account->ac_penalty;
client->ac_fakedelay = account->ac_fakedelay == -1 ? cfg.ac_fakedelay : account->ac_fakedelay;
if(cfg.ac_enabled)
{
int32_t numusers = account->ac_users;
if(numusers == -1)
{ numusers = cfg.ac_users; }
if(numusers)
{
client->ac_limit = (numusers * 100 + 80) * cfg.ac_stime;
cs_log_dbg(D_CLIENT, "acasc: user '%s', users=%d, stime=%d min, dwlimit=%d per min, penalty=%d",
account->usr, numusers, cfg.ac_stime,
numusers * 100 + 80, client->ac_penalty);
}
else
{
cs_log_dbg(D_CLIENT, "acasc: anti-cascading not used for user '%s'", account->usr);
}
}
}
static int32_t ac_dw_weight(ECM_REQUEST *er)
{
struct s_cpmap *cpmap;
for(cpmap = cfg.cpmap; (cpmap) ; cpmap = cpmap->next)
if((cpmap->caid == 0 || cpmap->caid == er->caid) &&
(cpmap->provid == 0 || cpmap->provid == er->prid) &&
(cpmap->sid == 0 || cpmap->sid == er->srvid) &&
(cpmap->chid == 0 || cpmap->chid == er->chid))
{ return (cpmap->dwtime * 100 / 60); }
cs_log_dbg(D_CLIENT, "acasc: WARNING: CAID %04X, PROVID %06X, SID %04X, CHID %04X not found in oscam.ac",
er->caid, er->prid, er->srvid, er->chid);
cs_log_dbg(D_CLIENT, "acasc: set DW lifetime 10 sec");
return 16; // 10*100/60
}
void ac_chk(struct s_client *cl, ECM_REQUEST *er, int32_t level)
{
if(!cl->ac_limit || !cfg.ac_enabled) { return; }
struct s_acasc_shm *acasc = &cl->acasc;
if(level == 1)
{
if(er->rc == E_FAKE)
{ acasc->ac_count++; }
if(er->rc >= E_NOTFOUND)
{ return; } // not found
if(memcmp(ac_ecmd5, er->ecmd5, CS_ECMSTORESIZE) != 0)
{
acasc->ac_count += ac_dw_weight(er);
memcpy(ac_ecmd5, er->ecmd5, CS_ECMSTORESIZE);
}
return;
}
if(acasc->ac_deny)
{
if(cl->ac_penalty)
{
if(cl->ac_penalty == 3)
{
if(cl->ac_fakedelay > 0)
{ cs_log_dbg(D_CLIENT, "acasc: fake delay %d ms", cl->ac_fakedelay); }
}
else
{
cs_log_dbg(D_CLIENT, "acasc: send fake dw");
er->rc = E_FAKE; // fake
er->rcEx = 0;
}
if(cl->ac_fakedelay > 0)
{ cs_sleepms(cl->ac_fakedelay); }
}
}
}
static void ac_load_config(void)
{
FILE *fp = open_config_file(cs_ac);
if(!fp)
{ return; }
int32_t nr;
char *saveptr1 = NULL, *token;
if(!cs_malloc(&token, MAXLINESIZE))
{ return; }
struct s_cpmap *cur_cpmap, *first_cpmap = NULL, *last_cpmap = NULL;
for(nr = 0; fgets(token, MAXLINESIZE, fp);)
{
int32_t i, skip;
uint16_t caid, sid, chid, dwtime;
uint32_t provid;
char *ptr, *ptr1;
if(cs_strlen(token) < 4) { continue; }
caid = sid = chid = dwtime = 0;
provid = 0;
skip = 0;
ptr1 = 0;
for(i = 0, ptr = strtok_r(token, "=", &saveptr1); (i < 2) && (ptr); ptr = strtok_r(NULL, "=", &saveptr1), i++)
{
trim(ptr);
if(*ptr == ';' || *ptr == '#' || *ptr == '-')
{
skip = 1;
break;
}
switch(i)
{
case 0:
ptr1 = ptr;
break;
case 1:
dwtime = atoi(ptr);
break;
}
}
if(!skip)
{
for(i = 0, ptr = strtok_r(ptr1, ":", &saveptr1); (i < 4) && (ptr); ptr = strtok_r(NULL, ":", &saveptr1), i++)
{
trim(ptr);
switch(i)
{
case 0:
if(*ptr == '*') { caid = 0; }
else { caid = a2i(ptr, 4); }
break;
case 1:
if(*ptr == '*') { provid = 0; }
else { provid = a2i(ptr, 6); }
break;
case 2:
if(*ptr == '*') { sid = 0; }
else { sid = a2i(ptr, 4); }
break;
case 3:
if(*ptr == '*') { chid = 0; }
else { chid = a2i(ptr, 4); }
break;
}
}
if(!cs_malloc(&cur_cpmap, sizeof(struct s_cpmap)))
{
for(cur_cpmap = first_cpmap; cur_cpmap;)
{
last_cpmap = cur_cpmap;
cur_cpmap = cur_cpmap->next;
NULLFREE(last_cpmap);
}
NULLFREE(token);
return;
}
if(last_cpmap)
{ last_cpmap->next = cur_cpmap; }
else
{ first_cpmap = cur_cpmap; }
last_cpmap = cur_cpmap;
cur_cpmap->caid = caid;
cur_cpmap->provid = provid;
cur_cpmap->sid = sid;
cur_cpmap->chid = chid;
cur_cpmap->dwtime = dwtime;
cur_cpmap->next = 0;
cs_log_dbg(D_CLIENT, "nr=%d, caid=%04X, provid=%06X, sid=%04X, chid=%04X, dwtime=%d",
nr, caid, provid, sid, chid, dwtime);
nr++;
}
}
NULLFREE(token);
fclose(fp);
last_cpmap = cfg.cpmap;
cfg.cpmap = first_cpmap;
for(cur_cpmap = last_cpmap; cur_cpmap; cur_cpmap = cur_cpmap->next)
{ add_garbage(cur_cpmap); }
//cs_log("%d lengths for caid guessing loaded", nr);
return;
}
void ac_copy_vars(struct s_auth *src, struct s_auth *dst)
{
dst->ac_stat = src->ac_stat;
}
void ac_init(void)
{
if(!cfg.ac_enabled)
{
cs_log("anti cascading disabled");
return;
}
ac_load_config();
ac_init_stat();
}
int8_t get_caid_weight(ECM_REQUEST *er)
{
switch(er->caid)
{
case 0x0100:
switch (er->prid)
{
case 0x00003D:
return 20;
case 0x000065:
return 7;
default:
return 10;
}
case 0x0500:
switch(er->prid)
{
case 0x020910:
return 30;
case 0x024400:
case 0x032830:
return 10;
case 0x043800:
return 25;
default:
return 15;
}
case 0x0604:
return 11;
case 0x1702:
case 0x1722:
case 0x1833:
switch(er->srvid)
{
case 0x0022: // Disney Channel
case 0x0016: // Heimatkanal
case 0x0203: // MGM
case 0x0008: // Sky Comedy
case 0x002B: // Sky Cinema +24
case 0x000B: // Sky Cinema +1
case 0x0009: // Sky Action
case 0x000A: // Sky Cinema
case 0x0014: // Sky Emotion
case 0x0029: // Sky Hits
case 0x0204: // Sky Nostalgie
case 0x0011: // Sky Sport News
case 0x0024: // Syfy
return 10;
default:
return 7;
}
case 0x1830:
return 15;
case 0x1843:
case 0x1834:
case 0x09C7:
return 7;
case 0x4A70:
return 14;
case 0x183D:
return 13;
case 0x1810:
case 0x0D05:
case 0x0D95:
case 0x093B:
case 0x098C:
case 0x098D:
case 0x0B00:
default:
return 10;
}
}
void insert_zaplist(ECM_REQUEST *er, struct s_client *client)
{
bool new_zaplist_entry = false;
int8_t zap_caid_weight;
zap_caid_weight = get_caid_weight(er);
int8_t k = 0;
bool found = false;
time_t zaptime = time(NULL);
for(k=0; k<15 ; k++)
{
if(er->caid == client->client_zap_list[k].caid && er->prid == client->client_zap_list[k].provid && er->chid == client->client_zap_list[k].chid && er->srvid == client->client_zap_list[k].sid) //found
{
if(zaptime-zap_caid_weight*2 < client->client_zap_list[k].lasttime)
{
cs_log_dbg(D_TRACE, "[zaplist] update Entry [%i] for Client: %s %04X@%06X/%04X/%04X TIME: %" PRId64 " Diff: %" PRId64 " zcw: %i(%i)", k, username(client), er->caid, er->prid, er->chid, er->srvid, (int64_t)zaptime, (int64_t)(zaptime - client->client_zap_list[k].lasttime), zap_caid_weight, zap_caid_weight*2);
client->client_zap_list[k].lasttime = zaptime;
if(client->client_zap_list[k].request_stage < 10)
{
client->client_zap_list[k].request_stage ++;
}
found = true;
break;
}
}
}
if(!found)
{
for(k=0; k<15 ; k++)
{
if(zaptime-30 > client->client_zap_list[k].lasttime) //make a new Entry and use a memoryplace of a old entry
{
client->client_zap_list[k].caid = er->caid;
client->client_zap_list[k].provid = er->prid;
client->client_zap_list[k].chid = er->chid;
client->client_zap_list[k].sid = er->srvid;
client->client_zap_list[k].request_stage = 1; //need for ACoSC
client->client_zap_list[k].lasttime = zaptime;
cs_log_dbg(D_TRACE, "[zaplist] new Entry [%i] for Client: %s %04X@%06X/%04X/%04X TIME: %" PRId64, k, username(client), er->caid, er->prid, er->chid, er->srvid, (int64_t)zaptime);
new_zaplist_entry = true;
break;
}
}
if(!new_zaplist_entry)
{ cs_log_dbg(D_TRACE, "[zaplist] no free slot for client: %s", username(client)); }
if(client->account->acosc_user_zap_count_start_time+60 > zaptime)
{ client->account->acosc_user_zap_count ++; }
else
{
client->account->acosc_user_zap_count_start_time = zaptime;
client->account->acosc_user_zap_count = 0;
cs_log_dbg(D_TRACE, "[zaplist] Client: %s reset acosc_user_zap_count_start_time", username(client));
for(k=0; k<15 ; k++)
{
if(client->client_zap_list[k].lasttime > zaptime-60)
{
client->account->acosc_user_zap_count ++;
}
}
cs_log_dbg(D_TRACE, "[zaplist] Client: %s zap_count: %i", username(client), client->account->acosc_user_zap_count);
}
}
}
#endif