491 lines
12 KiB
C
Executable File
491 lines
12 KiB
C
Executable File
#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
|