1830 lines
50 KiB
C
Executable File
1830 lines
50 KiB
C
Executable File
#define MODULE_LOG_PREFIX "cacheex"
|
||
|
||
#include "globals.h"
|
||
|
||
#ifdef CS_CACHEEX
|
||
|
||
#include "cscrypt/md5.h"
|
||
#include "module-cacheex.h"
|
||
#include "module-cw-cycle-check.h"
|
||
#include "oscam-cache.h"
|
||
#include "oscam-chk.h"
|
||
#include "oscam-client.h"
|
||
#include "oscam-conf.h"
|
||
#include "oscam-ecm.h"
|
||
#include "oscam-hashtable.h"
|
||
#include "oscam-lock.h"
|
||
#include "oscam-net.h"
|
||
#include "oscam-string.h"
|
||
#include "oscam-time.h"
|
||
#include "oscam-work.h"
|
||
#ifdef CS_CACHEEX_AIO
|
||
#include "oscam-array.h"
|
||
#endif
|
||
|
||
#define cs_cacheex_matcher "oscam.cacheex"
|
||
|
||
extern uint8_t cc_node_id[8];
|
||
extern uint8_t camd35_node_id[8];
|
||
|
||
uint8_t cacheex_peer_id[8];
|
||
|
||
extern CS_MUTEX_LOCK ecm_pushed_deleted_lock;
|
||
extern struct ecm_request_t *ecm_pushed_deleted;
|
||
extern CS_MUTEX_LOCK ecmcache_lock;
|
||
extern struct ecm_request_t *ecmcwcache;
|
||
|
||
/* 48/64 learning structure (defined in oscam-ecm.c) */
|
||
#define CW_DETECT_MAX 8192
|
||
#define CW_LEARN_THRESHOLD 5
|
||
#define CW_CACHEEX_NODEID_MAX 5
|
||
|
||
typedef struct
|
||
{
|
||
uint16_t caid;
|
||
uint32_t prid;
|
||
uint16_t srvid;
|
||
|
||
uint8_t is64;
|
||
uint8_t is48;
|
||
|
||
uint8_t learn64_count;
|
||
uint8_t learn48_count;
|
||
|
||
uint8_t strict_logged;
|
||
|
||
/* CacheEX learning: track unique nodeid (64-bit) */
|
||
uint64_t cacheex_nodeid_48[CW_CACHEEX_NODEID_MAX];
|
||
uint8_t cacheex_nodeid_48_count;
|
||
|
||
uint64_t cacheex_nodeid_64[CW_CACHEEX_NODEID_MAX];
|
||
uint8_t cacheex_nodeid_64_count;
|
||
|
||
/* finalized flags (no more learning) */
|
||
uint8_t cacheex_48_finalized;
|
||
uint8_t cacheex_64_finalized;
|
||
|
||
} cw_detect_entry;
|
||
|
||
/* Forward declares for 48/64 learning (from oscam-ecm.c) */
|
||
typedef struct ecm_request_t ECM_REQUEST;
|
||
extern cw_detect_entry *cw_detect_get(ECM_REQUEST *er);
|
||
extern int cw_checksum_ok(uint8_t *cw);
|
||
extern int cacheex_learn_add_nodeid(uint64_t *nodeid_list, uint8_t *count, uint64_t new_nodeid);
|
||
|
||
// HIT CACHE functions **************************************************************
|
||
|
||
typedef struct hit_key_t {
|
||
uint16_t caid;
|
||
uint32_t prid;
|
||
uint16_t srvid;
|
||
} HIT_KEY;
|
||
|
||
typedef struct cache_hit_t {
|
||
HIT_KEY key;
|
||
struct timeb time;
|
||
struct timeb max_hitcache_time;
|
||
uint64_t grp;
|
||
uint64_t grp_last_max_hitcache_time;
|
||
#ifdef CS_CACHEEX_AIO
|
||
int32_t waittime_block;
|
||
#endif
|
||
node ht_node;
|
||
node ll_node;
|
||
} CACHE_HIT;
|
||
|
||
static pthread_rwlock_t hitcache_lock;
|
||
static hash_table ht_hitcache;
|
||
static list ll_hitcache;
|
||
static bool cacheex_running;
|
||
|
||
void cacheex_init_hitcache(void)
|
||
{
|
||
init_hash_table(&ht_hitcache, &ll_hitcache);
|
||
if (pthread_rwlock_init(&hitcache_lock,NULL) != 0)
|
||
cs_log("Error creating lock hitcache_lock!");
|
||
cacheex_running = true;
|
||
}
|
||
|
||
void cacheex_free_hitcache(void)
|
||
{
|
||
cacheex_running = false;
|
||
cacheex_cleanup_hitcache(true);
|
||
deinitialize_hash_table(&ht_hitcache);
|
||
pthread_rwlock_destroy(&hitcache_lock);
|
||
}
|
||
|
||
static int cacheex_compare_hitkey(const void *arg, const void *obj)
|
||
{
|
||
if(((const HIT_KEY*)arg)->caid==((const CACHE_HIT*)obj)->key.caid
|
||
&& ((const HIT_KEY*)arg)->prid==((const CACHE_HIT*)obj)->key.prid
|
||
&& ((const HIT_KEY*)arg)->srvid==((const CACHE_HIT*)obj)->key.srvid)
|
||
{
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int32_t cacheex_check_hitcache(ECM_REQUEST *er, struct s_client *cl)
|
||
{
|
||
CACHE_HIT *result;
|
||
HIT_KEY search;
|
||
memset(&search, 0, sizeof(HIT_KEY));
|
||
search.caid = er->caid;
|
||
search.prid = er->prid;
|
||
search.srvid = er->srvid;
|
||
SAFE_RWLOCK_RDLOCK(&hitcache_lock);
|
||
result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
|
||
if(result){
|
||
struct timeb now;
|
||
cs_ftime(&now);
|
||
int64_t gone = comp_timeb(&now, &result->time);
|
||
uint64_t grp = cl?cl->grp:0;
|
||
|
||
if(
|
||
gone <= (cfg.max_hitcache_time*1000)
|
||
&&
|
||
(!grp || !result->grp || (grp & result->grp))
|
||
#ifdef CS_CACHEEX_AIO
|
||
&&
|
||
result->waittime_block <= cfg.waittime_block_start
|
||
#endif
|
||
)
|
||
{
|
||
SAFE_RWLOCK_UNLOCK(&hitcache_lock);
|
||
return 1;
|
||
}
|
||
}
|
||
SAFE_RWLOCK_UNLOCK(&hitcache_lock);
|
||
return 0;
|
||
}
|
||
|
||
static void cacheex_add_hitcache(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
if (!cfg.max_hitcache_time) // we don't want check/save hitcache
|
||
return;
|
||
if (!cfg.cacheex_wait_timetab.cevnum)
|
||
return;
|
||
uint32_t cacheex_wait_time = get_cacheex_wait_time(er,NULL);
|
||
if (!cacheex_wait_time)
|
||
return;
|
||
|
||
CACHE_HIT *result;
|
||
HIT_KEY search;
|
||
|
||
memset(&search, 0, sizeof(HIT_KEY));
|
||
search.caid = er->caid;
|
||
search.prid = er->prid;
|
||
search.srvid = er->srvid;
|
||
|
||
SAFE_RWLOCK_WRLOCK(&hitcache_lock);
|
||
|
||
result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
|
||
if(!result) // not found, add it!
|
||
{
|
||
if(cs_malloc(&result, sizeof(CACHE_HIT)))
|
||
{
|
||
memset(result, 0, sizeof(CACHE_HIT));
|
||
result->key.caid = er->caid;
|
||
result->key.prid = er->prid;
|
||
result->key.srvid = er->srvid;
|
||
cs_ftime(&result->max_hitcache_time);
|
||
#ifdef CS_CACHEEX_AIO
|
||
result->waittime_block = 0;
|
||
#endif
|
||
add_hash_table(&ht_hitcache, &result->ht_node, &ll_hitcache, &result->ll_node, result, &result->key, sizeof(HIT_KEY));
|
||
}
|
||
}
|
||
|
||
if(result)
|
||
{
|
||
if(cl)
|
||
{
|
||
result->grp |= cl->grp;
|
||
result->grp_last_max_hitcache_time |= cl->grp;
|
||
}
|
||
cs_ftime(&result->time); //always update time;
|
||
}
|
||
|
||
SAFE_RWLOCK_UNLOCK(&hitcache_lock);
|
||
}
|
||
|
||
static void cacheex_del_hitcache(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
HIT_KEY search;
|
||
CACHE_HIT *result;
|
||
|
||
memset(&search, 0, sizeof(HIT_KEY));
|
||
search.caid = er->caid;
|
||
search.prid = er->prid;
|
||
search.srvid = er->srvid;
|
||
|
||
if(cl && cl->grp)
|
||
{
|
||
result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
|
||
while(result)
|
||
{
|
||
result->grp &= ~cl->grp;
|
||
result->grp_last_max_hitcache_time &= ~cl->grp;
|
||
result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
|
||
}
|
||
}
|
||
|
||
SAFE_RWLOCK_WRLOCK(&hitcache_lock);
|
||
search_remove_elem_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
|
||
SAFE_RWLOCK_UNLOCK(&hitcache_lock);
|
||
}
|
||
|
||
void cacheex_cleanup_hitcache(bool force)
|
||
{
|
||
CACHE_HIT *cachehit;
|
||
node *i,*i_next;
|
||
struct timeb now;
|
||
int64_t gone, gone_max_hitcache_time;
|
||
int32_t timeout = (cfg.max_hitcache_time + (cfg.max_hitcache_time / 2)) * 1000; // 1,5
|
||
int32_t clean_grp = (cfg.max_hitcache_time * 1000);
|
||
|
||
SAFE_RWLOCK_WRLOCK(&hitcache_lock);
|
||
i = get_first_node_list(&ll_hitcache);
|
||
while (i)
|
||
{
|
||
i_next = i->next;
|
||
cachehit = get_data_from_node(i);
|
||
|
||
if(!cachehit)
|
||
{
|
||
i = i_next;
|
||
continue;
|
||
}
|
||
|
||
cs_ftime(&now);
|
||
gone = comp_timeb(&now, &cachehit->time);
|
||
gone_max_hitcache_time = comp_timeb(&now, &cachehit->max_hitcache_time);
|
||
|
||
if(force || gone>timeout
|
||
#ifdef CS_CACHEEX_AIO
|
||
|| (cachehit->waittime_block > (cfg.waittime_block_time / 3 + 1))
|
||
#endif
|
||
)
|
||
{
|
||
remove_elem_list(&ll_hitcache, &cachehit->ll_node);
|
||
remove_elem_hash_table(&ht_hitcache, &cachehit->ht_node);
|
||
NULLFREE(cachehit);
|
||
}
|
||
else if(gone_max_hitcache_time >= clean_grp){
|
||
cachehit->grp = cachehit->grp_last_max_hitcache_time;
|
||
cachehit->grp_last_max_hitcache_time = 0;
|
||
cs_ftime(&cachehit->max_hitcache_time);
|
||
}
|
||
|
||
#ifdef CS_CACHEEX_AIO
|
||
if(cfg.waittime_block_start && (cachehit && cachehit->waittime_block >= cfg.waittime_block_start))
|
||
{
|
||
cachehit->waittime_block++;
|
||
}
|
||
#endif
|
||
i = i_next;
|
||
}
|
||
SAFE_RWLOCK_UNLOCK(&hitcache_lock);
|
||
}
|
||
|
||
static int32_t cacheex_ecm_hash_calc(uint8_t *buf, int32_t n)
|
||
{
|
||
int32_t i, h = 0;
|
||
for(i = 0; i < n; i++)
|
||
{
|
||
h = 31 * h + buf[i];
|
||
}
|
||
return h;
|
||
}
|
||
|
||
void cacheex_update_hash(ECM_REQUEST *er)
|
||
{
|
||
er->csp_hash = cacheex_ecm_hash_calc(er->ecm + 3, er->ecmlen - 3);
|
||
}
|
||
|
||
void cacheex_free_csp_lastnodes(ECM_REQUEST *er)
|
||
{
|
||
ll_destroy_free_data(&er->csp_lastnodes);
|
||
}
|
||
|
||
void cacheex_set_csp_lastnode(ECM_REQUEST *er)
|
||
{
|
||
er->csp_lastnodes = NULL;
|
||
}
|
||
|
||
void cacheex_set_cacheex_src(ECM_REQUEST *ecm, struct s_client *cl)
|
||
{
|
||
if(ecm->cacheex_src == cl)
|
||
ecm->cacheex_src = NULL;
|
||
}
|
||
|
||
void cacheex_init_cacheex_src(ECM_REQUEST *ecm, ECM_REQUEST *er)
|
||
{
|
||
if(!ecm->cacheex_src)
|
||
ecm->cacheex_src = er->cacheex_src;
|
||
}
|
||
|
||
static void *chkcache_process(void)
|
||
{
|
||
set_thread_name(__func__);
|
||
|
||
time_t timeout;
|
||
struct ecm_request_t *er, *ecm;
|
||
uint8_t add_hitcache_er;
|
||
struct s_reader *cl_rdr;
|
||
struct s_reader *rdr;
|
||
struct s_ecm_answer *ea;
|
||
struct s_client *cex_src = NULL;
|
||
struct s_write_from_cache *wfc = NULL;
|
||
|
||
while(cacheex_running)
|
||
{
|
||
cs_readlock(__func__, &ecmcache_lock);
|
||
for(er = ecmcwcache; er; er = er->next)
|
||
{
|
||
timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1);
|
||
if(er->tps.time < timeout)
|
||
{ break; }
|
||
|
||
if(er->rc < E_UNHANDLED || er->readers_timeout_check) // already answered
|
||
{ continue; }
|
||
|
||
// CHECK IF FOUND ECM IN CACHE
|
||
ecm = check_cache(er, er->client);
|
||
if(ecm) // found in cache
|
||
{
|
||
// check for add_hitcache
|
||
if(ecm->cacheex_src) // cw from cacheex
|
||
{
|
||
// only when no wait_time expires (or not wait_time)
|
||
if((er->cacheex_wait_time && !er->cacheex_wait_time_expired) || !er->cacheex_wait_time)
|
||
{
|
||
// add_hitcache already called, but we check if we have to call it for these (er) caid|prid|srvid
|
||
if(ecm->prid!=er->prid || ecm->srvid!=er->srvid)
|
||
{
|
||
// here we should be sure cex client has not been freed!
|
||
cex_src = ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill ? ecm->cacheex_src : NULL;
|
||
|
||
if(cex_src) // add_hitcache only if client is really active
|
||
{
|
||
add_hitcache_er = 1;
|
||
cl_rdr = cex_src->reader;
|
||
|
||
if(cl_rdr && cl_rdr->cacheex.mode == 2)
|
||
{
|
||
for(ea = er->matching_rdr; ea; ea = ea->next)
|
||
{
|
||
rdr = ea->reader;
|
||
if(cl_rdr == rdr && ((ea->status & REQUEST_ANSWERED) == REQUEST_ANSWERED))
|
||
{
|
||
cs_log_dbg(D_CACHEEX | D_CSP | D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [CACHEEX] skip ADD self request!",
|
||
(check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid);
|
||
|
||
add_hitcache_er=0; // don't add hit cache, reader requested self
|
||
}
|
||
}
|
||
}
|
||
|
||
// USE cacheex client (to get correct group) and ecm
|
||
// from requesting client (to get correct caid|prid|srvid)!!!
|
||
if(add_hitcache_er)
|
||
{
|
||
cacheex_add_hitcache(cex_src, er);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
// add_hitcache already called, but we have to remove it because cacheex not coming before wait_time
|
||
if(ecm->prid == er->prid && ecm->srvid == er->srvid)
|
||
{ cacheex_del_hitcache(er->client, ecm); }
|
||
}
|
||
}
|
||
// END check for add_hitcache
|
||
|
||
if(check_client(er->client))
|
||
{
|
||
wfc = NULL;
|
||
if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache)))
|
||
{
|
||
NULLFREE(ecm);
|
||
continue;
|
||
}
|
||
|
||
wfc->er_new = er;
|
||
wfc->er_cache = ecm;
|
||
|
||
if(!add_job(er->client, ACTION_ECM_ANSWER_CACHE, wfc, sizeof(struct s_write_from_cache))) // write_ecm_answer_fromcache
|
||
{
|
||
NULLFREE(ecm);
|
||
continue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
NULLFREE(ecm);
|
||
}
|
||
}
|
||
}
|
||
cs_readunlock(__func__, &ecmcache_lock);
|
||
cs_sleepms(10);
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
void checkcache_process_thread_start(void)
|
||
{
|
||
start_thread("chkcache_process", (void *)&chkcache_process, NULL, NULL, 1, 1);
|
||
}
|
||
|
||
void cacheex_init(void)
|
||
{
|
||
// Init random node id
|
||
get_random_bytes(cacheex_peer_id, 8);
|
||
#ifdef MODULE_CCCAM
|
||
memcpy(cacheex_peer_id, cc_node_id, 8);
|
||
#endif
|
||
#ifdef MODULE_CAMD35_TCP
|
||
memcpy(camd35_node_id, cacheex_peer_id, 8);
|
||
#endif
|
||
}
|
||
|
||
void cacheex_clear_account_stats(struct s_auth *account)
|
||
{
|
||
account->cwcacheexgot = 0;
|
||
account->cwcacheexpush = 0;
|
||
account->cwcacheexhit = 0;
|
||
#ifdef CS_CACHEEX_AIO
|
||
account->cwcacheexgotlg = 0;
|
||
account->cwcacheexpushlg = 0;
|
||
#endif
|
||
}
|
||
|
||
void cacheex_clear_client_stats(struct s_client *client)
|
||
{
|
||
client->cwcacheexgot = 0;
|
||
client->cwcacheexpush = 0;
|
||
client->cwcacheexhit = 0;
|
||
#ifdef CS_CACHEEX_AIO
|
||
client->cwcacheexgotlg = 0;
|
||
client->cwcacheexpushlg = 0;
|
||
#endif
|
||
}
|
||
|
||
int32_t cacheex_add_stats(struct s_client *cl, uint16_t caid, uint16_t srvid, uint32_t prid, uint8_t direction
|
||
#ifdef CS_CACHEEX_AIO
|
||
, uint8_t localgenerated
|
||
#endif
|
||
)
|
||
{
|
||
if(!cfg.cacheex_enable_stats)
|
||
{ return -1; }
|
||
|
||
// create list if doesn't exist
|
||
if(!cl->ll_cacheex_stats)
|
||
{ cl->ll_cacheex_stats = ll_create("ll_cacheex_stats"); }
|
||
|
||
time_t now = time((time_t *)0);
|
||
LL_ITER itr = ll_iter_create(cl->ll_cacheex_stats);
|
||
S_CACHEEX_STAT_ENTRY *cacheex_stats_entry;
|
||
|
||
// check for existing entry
|
||
while((cacheex_stats_entry = ll_iter_next(&itr)))
|
||
{
|
||
if(cacheex_stats_entry->cache_srvid == srvid &&
|
||
cacheex_stats_entry->cache_caid == caid &&
|
||
cacheex_stats_entry->cache_prid == prid &&
|
||
cacheex_stats_entry->cache_direction == direction)
|
||
{
|
||
// we already have this entry - just add count and time
|
||
cacheex_stats_entry->cache_count++;
|
||
#ifdef CS_CACHEEX_AIO
|
||
if(localgenerated)
|
||
cacheex_stats_entry->cache_count_lg++;
|
||
#endif
|
||
cacheex_stats_entry->cache_last = now;
|
||
return cacheex_stats_entry->cache_count;
|
||
}
|
||
}
|
||
|
||
// if we land here we have to add a new entry
|
||
if(cs_malloc(&cacheex_stats_entry, sizeof(S_CACHEEX_STAT_ENTRY)))
|
||
{
|
||
cacheex_stats_entry->cache_caid = caid;
|
||
cacheex_stats_entry->cache_srvid = srvid;
|
||
cacheex_stats_entry->cache_prid = prid;
|
||
cacheex_stats_entry->cache_count = 1;
|
||
#ifdef CS_CACHEEX_AIO
|
||
if(localgenerated)
|
||
cacheex_stats_entry->cache_count_lg = 1;
|
||
#endif
|
||
cacheex_stats_entry->cache_last = now;
|
||
cacheex_stats_entry->cache_direction = direction;
|
||
ll_iter_insert(&itr, cacheex_stats_entry);
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
const int8_t default_maxhop = 10;
|
||
uint16_t caid = er ? er->caid : 0;
|
||
|
||
// Apply ONLY for CacheEX readers
|
||
if(cl && cl->reader && cl->reader->cacheex.mode > 0)
|
||
{
|
||
// GLOBAL override ON
|
||
if(cfg.cacheex_global_maxhop == 1)
|
||
{
|
||
// Priority 1: global percaid
|
||
if(caid > 0)
|
||
{
|
||
uint16_t per = caidvaluetab_get_value(
|
||
&cfg.cacheex_maxhop_percaid,
|
||
caid,
|
||
0
|
||
);
|
||
|
||
if(per > 0)
|
||
return (int8_t)per;
|
||
}
|
||
|
||
// Priority 2: global maxhop
|
||
if(cfg.cacheex_maxhop > 0)
|
||
return cfg.cacheex_maxhop;
|
||
|
||
return default_maxhop;
|
||
}
|
||
// GLOBAL override OFF <20> reader only
|
||
else
|
||
{
|
||
// Priority 1: reader percaid
|
||
if(caid > 0)
|
||
{
|
||
uint16_t per = caidvaluetab_get_value(
|
||
&cl->reader->cacheex.maxhop_percaid,
|
||
caid,
|
||
0
|
||
);
|
||
|
||
if(per > 0)
|
||
return (int8_t)per;
|
||
}
|
||
|
||
// Priority 2: reader maxhop
|
||
if(cl->reader->cacheex.maxhop > 0)
|
||
return cl->reader->cacheex.maxhop;
|
||
|
||
return default_maxhop;
|
||
}
|
||
}
|
||
|
||
// NOT a cacheex reader <20> default
|
||
return default_maxhop;
|
||
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
#ifdef CS_CACHEEX_AIO
|
||
int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
const int8_t default_maxhop = 10;
|
||
uint16_t caid = er ? er->caid : 0;
|
||
|
||
// Apply ONLY for CacheEX readers
|
||
if(cl && cl->reader && cl->reader->cacheex.mode > 0)
|
||
{
|
||
// GLOBAL override ON
|
||
if(cfg.cacheex_global_maxhop == 1)
|
||
{
|
||
// Priority 1: global percaid LG
|
||
if(caid > 0)
|
||
{
|
||
uint16_t per = caidvaluetab_get_value(
|
||
&cfg.cacheex_maxhop_lg_percaid,
|
||
caid,
|
||
0
|
||
);
|
||
|
||
if(per > 0)
|
||
return (int8_t)per;
|
||
}
|
||
|
||
// Priority 2: global maxhop LG
|
||
if(cfg.cacheex_maxhop_lg > 0)
|
||
return cfg.cacheex_maxhop_lg;
|
||
|
||
return default_maxhop;
|
||
}
|
||
// GLOBAL override OFF <20> reader only
|
||
else
|
||
{
|
||
// Priority 1: reader percaid LG
|
||
if(caid > 0)
|
||
{
|
||
uint16_t per = caidvaluetab_get_value(
|
||
&cl->reader->cacheex.maxhop_lg_percaid,
|
||
caid,
|
||
0
|
||
);
|
||
|
||
if(per > 0)
|
||
return (int8_t)per;
|
||
}
|
||
|
||
// Priority 2: reader maxhop LG
|
||
if(cl->reader->cacheex.maxhop_lg > 0)
|
||
return cl->reader->cacheex.maxhop_lg;
|
||
|
||
return default_maxhop;
|
||
}
|
||
}
|
||
|
||
return default_maxhop;
|
||
}
|
||
#endif
|
||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
/**
|
||
* Check if ECM hop count is within reader's configured maxhop limits
|
||
* Returns 1 if hop is allowed, 0 if hop exceeds limit
|
||
*/
|
||
static int8_t cacheex_check_maxhop(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
// Get the hop count from csp_lastnodes list
|
||
uint8_t hop = er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0;
|
||
|
||
// Get the maximum allowed hop for this reader/ECM
|
||
int8_t max_hop;
|
||
#ifdef CS_CACHEEX_AIO
|
||
if(er->localgenerated)
|
||
{
|
||
max_hop = cacheex_maxhop_lg(cl, er);
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
max_hop = cacheex_maxhop(cl, er);
|
||
}
|
||
|
||
// Check if hop exceeds the limit
|
||
if(hop > max_hop)
|
||
{
|
||
debug_ecm(D_CACHEEX, "cacheex: reject ECM %04X@%06X/%04X hop %d > max_hop %d for reader %s",
|
||
er->caid, er->prid, er->srvid, hop, max_hop, username(cl));
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
static void cacheex_cache_push_to_client(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
add_job(cl, ACTION_CACHE_PUSH_OUT, er, 0);
|
||
}
|
||
|
||
/**
|
||
* Check for NULL ecmd5
|
||
**/
|
||
static uint8_t checkECMD5(ECM_REQUEST *er)
|
||
{
|
||
int8_t i;
|
||
for(i = 0; i < CS_ECMSTORESIZE; i++)
|
||
if(er->ecmd5[i]) { return 1; }
|
||
return 0;
|
||
}
|
||
|
||
#ifdef CS_CACHEEX_AIO
|
||
static uint8_t chk_cwcheck(ECM_REQUEST *er, uint8_t cw_check_for_push)
|
||
{
|
||
if(!cw_check_for_push)
|
||
return 1;
|
||
|
||
CWCHECK check_cw;
|
||
check_cw = get_cwcheck(er);
|
||
|
||
if(check_cw.mode && check_cw.counter > 1)
|
||
{
|
||
if(er->cw_count >= check_cw.counter)
|
||
{
|
||
return 1;
|
||
}
|
||
else
|
||
{
|
||
cs_log_dbg(D_CACHEEX, "push denied - cacheex_check_cw.counter: %u > er->cw_count: %u", check_cw.counter, er->cw_count);
|
||
return 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return 1;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/**
|
||
* cacheex modes:
|
||
*
|
||
* cacheex=1 CACHE PULL:
|
||
* Situation: oscam A reader1 has cacheex=1, oscam B account1 has cacheex=1
|
||
* oscam A gets a ECM request, reader1 send this request to oscam B, oscam B checks his cache
|
||
* a. not found in cache: return NOK
|
||
* a. found in cache: return OK+CW
|
||
* b. not found in cache, but found pending request: wait max cacheexwaittime and check again
|
||
* oscam B never requests new ECMs
|
||
*
|
||
* CW-flow: B->A
|
||
*
|
||
* cacheex=2 CACHE PUSH:
|
||
* Situation: oscam A reader1 has cacheex=2, oscam B account1 has cacheex=2
|
||
* if oscam B gets a CW, its pushed to oscam A
|
||
* reader has normal functionality and can request ECMs
|
||
*
|
||
* Problem: oscam B can only push if oscam A is connected
|
||
* Problem or feature?: oscam A reader can request ecms from oscam B
|
||
*
|
||
* CW-flow: B->A
|
||
*
|
||
*/
|
||
void cacheex_cache_push(ECM_REQUEST *er)
|
||
{
|
||
if(er->rc >= E_NOTFOUND) { return; }
|
||
|
||
//cacheex=2 mode: push (server->remote)
|
||
struct s_client *cl;
|
||
cs_readlock(__func__, &clientlist_lock);
|
||
for(cl = first_client->next; cl; cl = cl->next)
|
||
{
|
||
if(check_client(cl) && er->cacheex_src != cl)
|
||
{
|
||
if(get_module(cl)->num == R_CSP) // always send to csp cl
|
||
{
|
||
if(!er->cacheex_src || cfg.csp.allow_reforward) { cacheex_cache_push_to_client(cl, er); } // but not if the origin was cacheex (might loop)
|
||
}
|
||
else if(cl->typ == 'c' && !cl->dup && cl->account && cl->account->cacheex.mode == 2) // send cache over user
|
||
{
|
||
if(get_module(cl)->c_cache_push // cache-push able
|
||
&& (!er->grp || (cl->grp & er->grp)
|
||
#ifdef CS_CACHEEX_AIO
|
||
|| (er->localgenerated && ((cl->grp & cfg.cacheex_push_lg_groups) && strcmp(username(cl), username(er->cacheex_src))))
|
||
#endif
|
||
) // Group-check
|
||
/**** OUTGOING FILTER CHECK ***/
|
||
&& (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_name || strcmp(username(cl), er->selected_reader->label)) // check reader mode-1 loopback by same name
|
||
&& (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_ip || (check_client(er->selected_reader->client) && !IP_EQUAL(cl->ip, er->selected_reader->client->ip))) // check reader mode-1 loopback by same ip
|
||
&& (!cl->account->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check
|
||
&& chk_ctab(er->caid, &cl->ctab) // Caid-check
|
||
&& (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->ftab)) // Ident-check (not for csp: prid=0 always!)
|
||
&& chk_srvid(cl, er) // Service-check
|
||
&& chk_csp_ctab(er, &cl->account->cacheex.filter_caidtab) // cacheex_ecm_filter
|
||
#ifdef CS_CACHEEX_AIO
|
||
&& (er->localgenerated // lg-flag-check
|
||
|| chk_srvid_localgenerated_only_exception(er) // lg-only-service-exception
|
||
|| !(cl->account->cacheex.localgenerated_only // usr-lg-only
|
||
|| (
|
||
(cl->account->cacheex.feature_bitfield & 64) // cx-aio >= 9.2.6 => check ftab
|
||
&& (chk_lg_only(er, &cl->account->cacheex.lg_only_tab) // usr-lg-only-ftab (feature 64)
|
||
|| chk_lg_only(er, &cfg.cacheex_lg_only_tab)) // global-lg-only-ftab (feature 64)
|
||
)
|
||
)
|
||
)
|
||
&& (chk_cwcheck(er, cl->account->cacheex.cw_check_for_push)) // check cw_check-counter if enabled
|
||
&& chk_nopushafter(er->caid, &cl->account->cacheex.cacheex_nopushafter_tab, er->ecm_time) // no push after check
|
||
#endif
|
||
)
|
||
{
|
||
cacheex_cache_push_to_client(cl, er);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
cs_readunlock(__func__, &clientlist_lock);
|
||
|
||
//cacheex=3 mode: reverse push (reader->server)
|
||
cs_readlock(__func__, &readerlist_lock);
|
||
cs_readlock(__func__, &clientlist_lock);
|
||
struct s_reader *rdr;
|
||
for(rdr = first_active_reader; rdr; rdr = rdr->next)
|
||
{
|
||
cl = rdr->client;
|
||
if(check_client(cl) && er->cacheex_src != cl && rdr->cacheex.mode == 3) // send cache over reader
|
||
{
|
||
if(rdr->ph.c_cache_push // cache-push able
|
||
&& (!er->grp || (rdr->grp & er->grp)
|
||
#ifdef CS_CACHEEX_AIO
|
||
|| (er->localgenerated && ((rdr->grp & cfg.cacheex_push_lg_groups) && strcmp(username(cl), username(er->cacheex_src))))
|
||
#endif
|
||
) // Group-check
|
||
/**** OUTGOING FILTER CHECK ***/
|
||
&& (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_name || strcmp(username(cl), er->selected_reader->label)) // check reader mode-1 loopback by same name
|
||
&& (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_ip || (check_client(er->selected_reader->client) && !IP_EQUAL(cl->ip, er->selected_reader->client->ip))) // check reader mode-1 loopback by same ip
|
||
&& (!rdr->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check
|
||
&& chk_ctab(er->caid, &rdr->ctab) // Caid-check
|
||
&& (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &rdr->ftab)) // Ident-check (not for csp: prid=0 always!)
|
||
&& chk_srvid(cl, er) // Service-check
|
||
&& chk_csp_ctab(er, &rdr->cacheex.filter_caidtab) // cacheex_ecm_filter
|
||
#ifdef CS_CACHEEX_AIO
|
||
&& (er->localgenerated // lg-only-check
|
||
|| chk_srvid_localgenerated_only_exception(er) // service-exception
|
||
|| !(rdr->cacheex.localgenerated_only // rdr-lg-only
|
||
|| (
|
||
(rdr->cacheex.feature_bitfield & 64) // cx-aio >= 9.2.6 => check ftab
|
||
&& (chk_lg_only(er, &rdr->cacheex.lg_only_tab) // rdr-lg-only-ftab (feature 64)
|
||
|| chk_lg_only(er, &cfg.cacheex_lg_only_tab)) // global-lg-only-ftab (feature 64)
|
||
)
|
||
)
|
||
)
|
||
&& (chk_cwcheck(er, rdr->cacheex.cw_check_for_push)) // check cw_check-counter if enabled
|
||
&& chk_nopushafter(er->caid, &rdr->cacheex.cacheex_nopushafter_tab, er->ecm_time)
|
||
#endif
|
||
) // no push after check
|
||
{
|
||
cacheex_cache_push_to_client(cl, er);
|
||
}
|
||
}
|
||
}
|
||
cs_readunlock(__func__, &clientlist_lock);
|
||
cs_readunlock(__func__, &readerlist_lock);
|
||
}
|
||
|
||
/**** INCOMING FILTER CHECK ***/
|
||
uint8_t check_cacheex_filter(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
|
||
if(check_client(cl) && cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2
|
||
&& (!cl->reader->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check
|
||
&& chk_ctab(er->caid, &cl->reader->ctab) // Caid-check
|
||
&& (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->reader->ftab)) // Ident-check (not for csp: prid=0 always!)
|
||
&& chk_srvid(cl, er)) // Service-check
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
if(check_client(cl) && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3
|
||
&& (!cl->account->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check
|
||
&& chk_ctab(er->caid, &cl->ctab) // Caid-check
|
||
&& (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->ftab)) // Ident-check (not for csp: prid=0 always!)
|
||
&& chk_srvid(cl, er)) // Service-check
|
||
{
|
||
return 1;
|
||
}
|
||
free_ecm(er);
|
||
return 0;
|
||
}
|
||
|
||
static struct s_cacheex_matcher *is_cacheex_matcher_matching(ECM_REQUEST *from_er, ECM_REQUEST *to_er)
|
||
{
|
||
struct s_cacheex_matcher *entry = cfg.cacheex_matcher;
|
||
int8_t v_ok = (from_er && to_er) ? 2 : 1;
|
||
while(entry)
|
||
{
|
||
int8_t ok = 0;
|
||
if(from_er
|
||
&& (!entry->caid || entry->caid == from_er->caid)
|
||
&& (!entry->provid || entry->provid == from_er->prid)
|
||
&& (!entry->srvid || entry->srvid == from_er->srvid)
|
||
&& (!entry->chid || entry->chid == from_er->chid)
|
||
&& (!entry->pid || entry->pid == from_er->pid)
|
||
&& (!entry->ecmlen || entry->ecmlen == from_er->ecmlen))
|
||
{ ok++; }
|
||
|
||
if(to_er
|
||
&& (!entry->to_caid || entry->to_caid == to_er->caid)
|
||
&& (!entry->to_provid || entry->to_provid == to_er->prid)
|
||
&& (!entry->to_srvid || entry->to_srvid == to_er->srvid)
|
||
&& (!entry->to_chid || entry->to_chid == to_er->chid)
|
||
&& (!entry->to_pid || entry->to_pid == to_er->pid)
|
||
&& (!entry->to_ecmlen || entry->to_ecmlen == to_er->ecmlen))
|
||
{ ok++; }
|
||
|
||
if(ok == v_ok)
|
||
{
|
||
if(!from_er || !to_er || from_er->srvid == to_er->srvid)
|
||
{ return entry; }
|
||
}
|
||
entry = entry->next;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
bool cacheex_is_match_alias(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
return check_client(cl) && cl->account && cl->account->cacheex.mode == 1 && is_cacheex_matcher_matching(NULL, er);
|
||
}
|
||
|
||
#ifdef WITH_DEBUG
|
||
static void log_cacheex_cw(ECM_REQUEST *er, char *reason)
|
||
{
|
||
uint8_t *data;
|
||
uint8_t remotenodeid[8];
|
||
data = ll_last_element(er->csp_lastnodes);
|
||
if(data)
|
||
{ memcpy(remotenodeid, data, 8); }
|
||
else
|
||
{ memset(remotenodeid, 0 , 8); }
|
||
|
||
char buf_ecm[109];
|
||
format_ecm(er, buf_ecm, 109);
|
||
cs_log_dbg(D_CACHEEX,"got pushed ecm [%s]: %s - odd/even 0x%x - CSP cw: %s - pushed from %s, at hop %d, origin node-id %" PRIu64 "X",
|
||
reason, buf_ecm, er->ecm[0], (checkECMD5(er)?"NO":"YES"), er->from_csp ? "csp" : username((er->cacheex_src?er->cacheex_src:er->client)), ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0);
|
||
}
|
||
#endif
|
||
|
||
// check if sky_ger 64 bit CW has valid checksum bytes and therefore is probably invalid
|
||
uint8_t check_nds_cwex(ECM_REQUEST *er)
|
||
{
|
||
uint8_t k, csum;
|
||
uint8_t hit = 0;
|
||
uint8_t oe = checkCWpart(er->cw, 0) ? 0 : 8;
|
||
for(k = 0; k < 8; k += 4)
|
||
{
|
||
csum = ((er->cw[k + oe] + er->cw[k + oe + 1] + er->cw[k + oe + 2]) & 0xff);
|
||
if(er->cw[k + oe + 3] == csum)
|
||
{
|
||
hit++;
|
||
}
|
||
}
|
||
if(hit > 1)
|
||
{
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, int8_t csp)
|
||
{
|
||
if(er->rc >= E_NOTFOUND) { return 0; }
|
||
|
||
if(!cl)
|
||
{ return 0; }
|
||
if(!csp && cl->reader && cl->reader->cacheex.mode != 2) //from reader
|
||
{
|
||
cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl));
|
||
return 0;
|
||
}
|
||
if(!csp && !cl->reader && cl->account && cl->account->cacheex.mode != 3) //from user
|
||
{
|
||
cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl));
|
||
return 0;
|
||
}
|
||
if(!csp && !cl->reader && !cl->account) //not active!
|
||
{
|
||
cs_log_dbg(D_CACHEEX, "CACHEX received, but invalid client state %s", username(cl));
|
||
return 0;
|
||
}
|
||
|
||
if(!cfg.disablecrccws && ((cl->typ == 'c' && cl->account && !cl->account->disablecrccacheex) || ( cl->typ == 'p' && cl->reader && !cl->reader->disablecrccws)))
|
||
{
|
||
uint8_t selectedForIgnChecksum = chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for);
|
||
if(cl->typ == 'c')
|
||
{
|
||
selectedForIgnChecksum += chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for);
|
||
}
|
||
if(cl->typ == 'p')
|
||
{
|
||
selectedForIgnChecksum += chk_if_ignore_checksum(er, &cl->reader->disablecrccws_only_for);
|
||
}
|
||
if(!selectedForIgnChecksum)
|
||
{
|
||
uint8_t i, c;
|
||
for(i = 0; i < 16; i += 4)
|
||
{
|
||
c = ((er->cw[i] + er->cw[i + 1] + er->cw[i + 2]) & 0xff);
|
||
|
||
if(er->cw[i + 3] != c)
|
||
{
|
||
cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received cw with chksum error from %s", csp ? "csp" : username(cl));
|
||
cl->cwcacheexerr++;
|
||
if(cl->account)
|
||
{ cl->account->cwcacheexerr++; }
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef CS_CACHEEX_AIO
|
||
if(caid_is_videoguard(er->caid))
|
||
{
|
||
if(cl->typ == 'p' && chk_if_ignore_checksum(er, &cl->reader->disablecrccws_only_for) && !chk_srvid_disablecrccws_only_for_exception(er))
|
||
{
|
||
if(check_nds_cwex(er))
|
||
{
|
||
if(check_client(cl) && cl->reader && cl->reader->dropbadcws)
|
||
{
|
||
if (((D_CACHEEX) & cs_dblevel)) // avoid useless operations if debug is not enabled
|
||
{
|
||
uint8_t remotenodeid[8];
|
||
cacheex_get_srcnodeid(er, remotenodeid);
|
||
|
||
cs_log_dbg(D_CACHEEX, "Probably got pushed bad CW from cacheex reader: %s, caid %04X, srvid %04X - dropping CW, lg: %i, hop: %i, src-nodeid %" PRIu64 "X", cl->reader->label, er->caid, er->srvid, er->localgenerated, ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0);
|
||
}
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
if (((D_CACHEEX) & cs_dblevel)) // avoid useless operations if debug is not enabled
|
||
{
|
||
uint8_t remotenodeid[8];
|
||
cacheex_get_srcnodeid(er, remotenodeid);
|
||
|
||
cs_log_dbg(D_CACHEEX, "Probably got pushed bad CW from cacheex reader: %s, caid %04X, srvid %04X, lg: %i, hop: %i, src-nodeid %" PRIu64 "X", cl->reader->label, er->caid, er->srvid, er->localgenerated, ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if(cl->typ == 'c' && chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for) && !chk_srvid_disablecrccws_only_for_exception(er))
|
||
{
|
||
if(check_nds_cwex(er))
|
||
{
|
||
if (((D_CACHEEX) & cs_dblevel)) // avoid useless operations if debug is not enabled
|
||
{
|
||
uint8_t remotenodeid[8];
|
||
cacheex_get_srcnodeid(er, remotenodeid);
|
||
|
||
cs_log_dbg(D_CACHEEX, "Probably got bad CW from cacheex user: %s, caid %04X, srvid %04X, lg: %i, hop: %i, src-nodeid %" PRIu64 "X", username(cl), er->caid, er->srvid, er->localgenerated, ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if(chk_is_null_CW(er->cw))
|
||
{
|
||
cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl));
|
||
cl->cwcacheexerr++;
|
||
if(cl->account)
|
||
{ cl->account->cwcacheexerr++; }
|
||
return 0;
|
||
}
|
||
|
||
if(get_odd_even(er) == 0)
|
||
{
|
||
cs_log_dbg(D_CACHEEX, "push received ecm with null odd/even byte from %s", csp ? "csp" : username(cl));
|
||
cl->cwcacheexerr++;
|
||
if(cl->account)
|
||
{ cl->account->cwcacheexerr++; }
|
||
return 0;
|
||
}
|
||
|
||
if(!chk_halfCW(er, er->cw))
|
||
{
|
||
#ifdef WITH_DEBUG
|
||
if(cs_dblevel & D_CACHEEX)
|
||
{
|
||
log_cacheex_cw(er, "bad half cw");
|
||
}
|
||
#endif
|
||
cl->cwcacheexerr++;
|
||
if(cl->account)
|
||
{ cl->account->cwcacheexerr++; }
|
||
return 0;
|
||
}
|
||
|
||
if((csp && cfg.csp.block_fakecws) || (cl->reader && cl->reader->cacheex.block_fakecws)
|
||
|| (!cl->reader && cl->account && cl->account->cacheex.block_fakecws))
|
||
{
|
||
if(chk_is_fakecw(er->cw))
|
||
{
|
||
cs_log_dbg(D_CACHEEX, "push received fake cw from %s", csp ? "csp" : username(cl));
|
||
cl->cwcacheexerr++;
|
||
if(cl->account)
|
||
{ cl->account->cwcacheexerr++; }
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////
|
||
// CACHEEX 48/64 LEARNING (reuse oscam-ecm.c structures)
|
||
/////////////////////////////////////////////////////////////
|
||
|
||
if (!csp && !er->entropy_blocked)
|
||
{
|
||
/* Get learning entry from shared cw_detect_table */
|
||
extern cw_detect_entry cw_detect_table[];
|
||
cw_detect_entry *e = cw_detect_get(er);
|
||
uint8_t checksum_ok = cw_checksum_ok(er->cw);
|
||
|
||
/* Get CacheEX source nodeid */
|
||
uint64_t src_nodeid = 0;
|
||
if (er->csp_lastnodes && ll_count(er->csp_lastnodes) > 0)
|
||
{
|
||
uint8_t remotenodeid[8];
|
||
cacheex_get_srcnodeid(er, remotenodeid);
|
||
memcpy(&src_nodeid, remotenodeid, 8);
|
||
}
|
||
|
||
uint8_t whitelist = chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) ||
|
||
(cl->account && chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for));
|
||
|
||
/////////////////////////////////////////////////////////////
|
||
// CACHEEX LEARNING: ONLY FOR WHITELIST CAIDs IN UNKNOWN STATE
|
||
/////////////////////////////////////////////////////////////
|
||
|
||
if (whitelist && src_nodeid)
|
||
{
|
||
/* Learn 64bit (invalid checksum) */
|
||
if (!checksum_ok && !e->cacheex_64_finalized)
|
||
{
|
||
int finalized = cacheex_learn_add_nodeid(
|
||
e->cacheex_nodeid_64,
|
||
&e->cacheex_nodeid_64_count,
|
||
src_nodeid
|
||
);
|
||
|
||
if (finalized)
|
||
{
|
||
e->cacheex_64_finalized = 1;
|
||
e->is64 = 1;
|
||
cs_log("CACHEEX FINALIZED 64bit after %d unique nodes caid=%04X srvid=%04X",
|
||
CW_CACHEEX_NODEID_MAX, er->caid, er->srvid);
|
||
}
|
||
else
|
||
{
|
||
cs_log_dbg(D_CACHEEX, "CACHEEX LEARN 64bit (%d/%d nodes) caid=%04X srvid=%04X src_nodeid=%" PRIu64 "X",
|
||
e->cacheex_nodeid_64_count, CW_CACHEEX_NODEID_MAX, er->caid, er->srvid, src_nodeid);
|
||
}
|
||
}
|
||
|
||
/* Learn 48bit (valid checksum) */
|
||
if (checksum_ok && !e->cacheex_48_finalized)
|
||
{
|
||
int finalized = cacheex_learn_add_nodeid(
|
||
e->cacheex_nodeid_48,
|
||
&e->cacheex_nodeid_48_count,
|
||
src_nodeid
|
||
);
|
||
|
||
if (finalized)
|
||
{
|
||
e->cacheex_48_finalized = 1;
|
||
e->is48 = 1;
|
||
cs_log("CACHEEX FINALIZED 48bit after %d unique nodes caid=%04X srvid=%04X",
|
||
CW_CACHEEX_NODEID_MAX, er->caid, er->srvid);
|
||
}
|
||
else
|
||
{
|
||
cs_log_dbg(D_CACHEEX, "CACHEEX LEARN 48bit (%d/%d nodes) caid=%04X srvid=%04X src_nodeid=%" PRIu64 "X",
|
||
e->cacheex_nodeid_48_count, CW_CACHEEX_NODEID_MAX, er->caid, er->srvid, src_nodeid);
|
||
}
|
||
}
|
||
}
|
||
else if (!whitelist && checksum_ok)
|
||
{
|
||
/* STRICT MODE: only 48bit allowed, no learning */
|
||
cs_log_dbg(D_CACHEEX, "CACHEEX STRICT 48bit (CAID outside whitelist) caid=%04X srvid=%04X",
|
||
er->caid, er->srvid);
|
||
}
|
||
else if (!whitelist && !checksum_ok)
|
||
{
|
||
/* STRICT MODE: reject 64bit, no learning */
|
||
cs_log_dbg(D_CACHEEX, "CACHEEX STRICT REJECT 64bit (CAID outside whitelist) caid=%04X srvid=%04X",
|
||
er->caid, er->srvid);
|
||
cl->cwcacheexerr++;
|
||
if (cl->account) cl->account->cwcacheexerr++;
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
er->grp |= cl->grp; // ok for mode2 reader too: cl->reader->grp
|
||
er->rc = E_CACHEEX;
|
||
er->cacheex_src = cl;
|
||
er->selected_reader = cl->reader;
|
||
er->client = NULL; // No Owner! So no fallback!
|
||
|
||
if(check_client(cl))
|
||
{
|
||
cl->cwcacheexgot++;
|
||
if(cl->account)
|
||
{ cl->account->cwcacheexgot++; }
|
||
first_client->cwcacheexgot++;
|
||
#ifdef CS_CACHEEX_AIO
|
||
if(er->localgenerated)
|
||
{
|
||
cl->cwcacheexgotlg++;
|
||
if(cl->account)
|
||
cl->account->cwcacheexgotlg++;
|
||
first_client->cwcacheexgotlg++;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
cacheex_add_hitcache(cl, er); // we have to call it before add_cache, because in chk_process we could remove it!
|
||
add_cache(er);
|
||
#ifdef CS_CACHEEX_AIO
|
||
cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 1, er->localgenerated);
|
||
#else
|
||
cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 1);
|
||
#endif
|
||
|
||
cs_writelock(__func__, &ecm_pushed_deleted_lock);
|
||
er->next = ecm_pushed_deleted;
|
||
ecm_pushed_deleted = er;
|
||
cs_writeunlock(__func__, &ecm_pushed_deleted_lock);
|
||
|
||
return 1; // NO free, we have to wait cache push out stuff ends.
|
||
}
|
||
|
||
void cacheex_add_to_cache(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
er->from_cacheex = 1;
|
||
|
||
// Check maxhop limit before adding to cache
|
||
// Get the hop count from csp_lastnodes list
|
||
uint8_t hop = er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0;
|
||
int8_t max_hop;
|
||
#ifdef CS_CACHEEX_AIO
|
||
if(er->localgenerated)
|
||
{
|
||
max_hop = cacheex_maxhop_lg(cl, er);
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
max_hop = cacheex_maxhop(cl, er);
|
||
}
|
||
|
||
if(hop > max_hop)
|
||
{
|
||
debug_ecm(D_CACHEEX, "cacheex: reject ECM %04X@%06X/%04X hop %d > max_hop %d for reader %s",
|
||
er->caid, er->prid, er->srvid, hop, max_hop, username(cl));
|
||
free_push_in_ecm(er);
|
||
return;
|
||
}
|
||
|
||
if(!cacheex_add_to_cache_int(cl, er, 0))
|
||
{ free_push_in_ecm(er); }
|
||
}
|
||
|
||
void cacheex_add_to_cache_from_csp(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
// Check maxhop limit before adding to cache
|
||
// Get the hop count from csp_lastnodes list
|
||
uint8_t hop = er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0;
|
||
int8_t max_hop;
|
||
#ifdef CS_CACHEEX_AIO
|
||
if(er->localgenerated)
|
||
{
|
||
max_hop = cacheex_maxhop_lg(cl, er);
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
max_hop = cacheex_maxhop(cl, er);
|
||
}
|
||
|
||
if(hop > max_hop)
|
||
{
|
||
debug_ecm(D_CACHEEX, "cacheex: reject ECM %04X@%06X/%04X hop %d > max_hop %d for reader %s",
|
||
er->caid, er->prid, er->srvid, hop, max_hop, username(cl));
|
||
free_push_in_ecm(er);
|
||
return;
|
||
}
|
||
|
||
if(!cacheex_add_to_cache_int(cl, er, 1))
|
||
{ free_push_in_ecm(er); }
|
||
}
|
||
|
||
//Format:
|
||
//caid:prov:srvid:pid:chid:ecmlen=caid:prov:srvid:pid:chid:ecmlen[,validfrom,validto]
|
||
//validfrom: default=-2000
|
||
//validto: default=4000
|
||
//valid time if found in cache
|
||
static struct s_cacheex_matcher *cacheex_matcher_read_int(void)
|
||
{
|
||
FILE *fp = open_config_file(cs_cacheex_matcher);
|
||
if(!fp)
|
||
{ return NULL; }
|
||
|
||
char token[1024];
|
||
uint8_t type;
|
||
int32_t i, ret, count = 0;
|
||
struct s_cacheex_matcher *new_cacheex_matcher = NULL, *entry, *last = NULL;
|
||
uint32_t line = 0;
|
||
|
||
while(fgets(token, sizeof(token), fp))
|
||
{
|
||
line++;
|
||
if(cs_strlen(token) <= 1) { continue; }
|
||
if(token[0] == '#' || token[0] == '/') { continue; }
|
||
if(cs_strlen(token) > 100) { 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 = 'm';
|
||
uint32_t caid = 0, provid = 0, srvid = 0, pid = 0, chid = 0, ecmlen = 0;
|
||
uint32_t to_caid = 0, to_provid = 0, to_srvid = 0, to_pid = 0, to_chid = 0, to_ecmlen = 0;
|
||
int32_t valid_from = -2000, valid_to = 4000;
|
||
|
||
ret = sscanf(token, "%c:%4x:%6x:%4x:%4x:%4x:%4X=%4x:%6x:%4x:%4x:%4x:%4X,%4d,%4d",
|
||
&type,
|
||
&caid, &provid, &srvid, &pid, &chid, &ecmlen,
|
||
&to_caid, &to_provid, &to_srvid, &to_pid, &to_chid, &to_ecmlen,
|
||
&valid_from, &valid_to);
|
||
|
||
type = tolower(type);
|
||
|
||
if(ret < 7 || type != 'm')
|
||
{ continue; }
|
||
|
||
if(!cs_malloc(&entry, sizeof(struct s_cacheex_matcher)))
|
||
{
|
||
fclose(fp);
|
||
return new_cacheex_matcher;
|
||
}
|
||
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->to_caid = to_caid;
|
||
entry->to_provid = to_provid;
|
||
entry->to_srvid = to_srvid;
|
||
entry->to_pid = to_pid;
|
||
entry->to_chid = to_chid;
|
||
entry->to_ecmlen = to_ecmlen;
|
||
entry->valid_from = valid_from;
|
||
entry->valid_to = valid_to;
|
||
|
||
cs_log_dbg(D_TRACE, "cacheex-matcher: %c: %04X@%06X:%04X:%04X:%04X:%02X = %04X@%06X:%04X:%04X:%04X:%02X valid %d/%d",
|
||
entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen,
|
||
entry->to_caid, entry->to_provid, entry->to_srvid, entry->to_pid, entry->to_chid, entry->to_ecmlen,
|
||
entry->valid_from, entry->valid_to);
|
||
|
||
if(!new_cacheex_matcher)
|
||
{
|
||
new_cacheex_matcher = entry;
|
||
last = new_cacheex_matcher;
|
||
}
|
||
else
|
||
{
|
||
last->next = entry;
|
||
last = entry;
|
||
}
|
||
}
|
||
|
||
if(count)
|
||
{ cs_log("%d entries read from %s", count, cs_cacheex_matcher); }
|
||
|
||
fclose(fp);
|
||
|
||
return new_cacheex_matcher;
|
||
}
|
||
|
||
void cacheex_load_config_file(void)
|
||
{
|
||
struct s_cacheex_matcher *entry, *old_list;
|
||
|
||
old_list = cfg.cacheex_matcher;
|
||
cfg.cacheex_matcher = cacheex_matcher_read_int();
|
||
|
||
while(old_list)
|
||
{
|
||
entry = old_list->next;
|
||
NULLFREE(old_list);
|
||
old_list = entry;
|
||
}
|
||
}
|
||
|
||
CWCHECK get_cwcheck(ECM_REQUEST *er)
|
||
{
|
||
int32_t i;
|
||
int8_t mode = 0;
|
||
int16_t counter = 1;
|
||
|
||
for(i = 0; i < cfg.cacheex_cwcheck_tab.cwchecknum; i++)
|
||
{
|
||
CWCHECKTAB_DATA *d = &cfg.cacheex_cwcheck_tab.cwcheckdata[i];
|
||
|
||
if(i == 0 && d->caid <= 0)
|
||
{
|
||
mode = d->mode;
|
||
counter = d->counter;
|
||
continue; //check other, only valid for unset
|
||
}
|
||
|
||
if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1))
|
||
{
|
||
if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1)
|
||
{
|
||
if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1)
|
||
{
|
||
mode = d->mode;
|
||
counter = d->counter;
|
||
break;
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
//check for correct values
|
||
if(mode>2 || mode<0) mode=0;
|
||
if(counter<1) counter=1;
|
||
|
||
CWCHECK check_cw;
|
||
memset(&check_cw, 0, sizeof(CWCHECK));
|
||
check_cw.mode = mode;
|
||
check_cw.counter = counter;
|
||
|
||
return check_cw;
|
||
}
|
||
|
||
uint16_t get_cacheex_mode1_delay(ECM_REQUEST *er)
|
||
{
|
||
return caidvaluetab_get_value(&cfg.cacheex_mode1_delay_tab, er->caid, 0);
|
||
}
|
||
|
||
uint32_t get_cacheex_wait_time(ECM_REQUEST *er, struct s_client *cl)
|
||
{
|
||
int32_t i, dwtime = -1, awtime = -1;
|
||
|
||
for(i = 0; i < cfg.cacheex_wait_timetab.cevnum; i++)
|
||
{
|
||
CECSPVALUETAB_DATA *d = &cfg.cacheex_wait_timetab.cevdata[i];
|
||
|
||
if(i == 0 && d->caid <= 0)
|
||
{
|
||
dwtime = d->dwtime;
|
||
awtime = d->awtime;
|
||
continue; //check other, only valid for unset
|
||
}
|
||
|
||
if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1))
|
||
{
|
||
if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1)
|
||
{
|
||
if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1)
|
||
{
|
||
dwtime = d->dwtime;
|
||
awtime = d->awtime;
|
||
break;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
if(awtime > 0 && (dwtime <= 0 || awtime==dwtime) ) //if awtime==dwtime useless check hitcache
|
||
{
|
||
return awtime;
|
||
}
|
||
if(cl == NULL)
|
||
{
|
||
if(dwtime < 0)
|
||
{ dwtime = 0; }
|
||
return dwtime;
|
||
}
|
||
if(awtime > 0 || dwtime > 0)
|
||
{
|
||
//if found last in cache return dynwaittime else alwayswaittime
|
||
if(cacheex_check_hitcache(er,cl))
|
||
{ return dwtime >= awtime ? dwtime : awtime; }
|
||
else
|
||
{ return awtime > 0 ? awtime : 0; }
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
int32_t chk_csp_ctab(ECM_REQUEST *er, CECSPVALUETAB *tab)
|
||
{
|
||
if(!er->caid || !tab->cevnum)
|
||
{ return 1; } // nothing setup we add all
|
||
int32_t i;
|
||
for(i = 0; i < tab->cevnum; i++)
|
||
{
|
||
CECSPVALUETAB_DATA *d = &tab->cevdata[i];
|
||
if(d->caid > 0)
|
||
{
|
||
if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1))
|
||
{
|
||
if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1)
|
||
{
|
||
if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1)
|
||
{
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void cacheex_push_out(struct s_client *cl, ECM_REQUEST *er)
|
||
{
|
||
int32_t res = 0, stats = -1;
|
||
struct s_reader *reader = cl->reader;
|
||
struct s_module *module = get_module(cl);
|
||
|
||
// cc-nodeid-list-check
|
||
if(reader)
|
||
{
|
||
if(reader->ph.c_cache_push_chk && !reader->ph.c_cache_push_chk(cl, er))
|
||
return;
|
||
res = reader->ph.c_cache_push(cl, er);
|
||
#ifdef CS_CACHEEX_AIO
|
||
stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0, er->localgenerated);
|
||
#else
|
||
stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0);
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
if(module->c_cache_push_chk && !module->c_cache_push_chk(cl, er))
|
||
return;
|
||
res = module->c_cache_push(cl, er);
|
||
}
|
||
debug_ecm(D_CACHEEX, "pushed ECM %s to %s res %d stats %d", buf, username(cl), res, stats);
|
||
cl->cwcacheexpush++;
|
||
if(cl->account)
|
||
{ cl->account->cwcacheexpush++; }
|
||
first_client->cwcacheexpush++;
|
||
|
||
#ifdef CS_CACHEEX_AIO
|
||
if(er->localgenerated)
|
||
{
|
||
cl->cwcacheexpushlg++;
|
||
if(cl->account)
|
||
cl->account->cwcacheexpushlg++;
|
||
first_client->cwcacheexpushlg++;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
bool cacheex_check_queue_length(struct s_client *cl)
|
||
{
|
||
// Avoid full running queues:
|
||
if(ll_count(cl->joblist) <= 2000)
|
||
return 0;
|
||
|
||
cs_log_dbg(D_TRACE, "WARNING: job queue %s %s has more than 2000 jobs! count=%d, dropped!",
|
||
cl->typ == 'c' ? "client" : "reader", username(cl), ll_count(cl->joblist));
|
||
|
||
// Thread down???
|
||
SAFE_MUTEX_LOCK(&cl->thread_lock);
|
||
if(cl && !cl->kill && cl->thread && cl->thread_active)
|
||
{
|
||
// Just test for invalid thread id:
|
||
if(pthread_detach(cl->thread) == ESRCH)
|
||
{
|
||
cl->thread_active = 0;
|
||
cs_log_dbg(D_TRACE, "WARNING: %s %s thread died!", cl->typ == 'c' ? "client" : "reader", username(cl));
|
||
}
|
||
}
|
||
SAFE_MUTEX_UNLOCK(&cl->thread_lock);
|
||
return 1;
|
||
}
|
||
|
||
void cacheex_mode1_delay(ECM_REQUEST *er)
|
||
{
|
||
if(!er->cacheex_wait_time_expired && er->cacheex_mode1_delay
|
||
&& er->cacheex_reader_count > 0 && !er->stage && er->rc >= E_UNHANDLED)
|
||
{
|
||
cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex_mode1_delay timeout! ",
|
||
(check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid);
|
||
|
||
// setting stop_stage=1, we request only cacheex mode 1 readers. Others are requested at cacheex timeout!
|
||
request_cw_from_readers(er, 1);
|
||
}
|
||
}
|
||
|
||
void cacheex_timeout(ECM_REQUEST *er)
|
||
{
|
||
if(er->cacheex_wait_time_expired)
|
||
return;
|
||
er->cacheex_wait_time_expired = 1;
|
||
if(er->rc >= E_UNHANDLED)
|
||
{
|
||
cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex timeout! ",
|
||
(check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid);
|
||
|
||
#ifdef CS_CACHEEX_AIO
|
||
CACHE_HIT *result;
|
||
HIT_KEY search;
|
||
|
||
memset(&search, 0, sizeof(HIT_KEY));
|
||
search.caid = er->caid;
|
||
search.prid = er->prid;
|
||
search.srvid = er->srvid;
|
||
|
||
SAFE_RWLOCK_WRLOCK(&hitcache_lock);
|
||
|
||
result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
|
||
if(result)
|
||
{
|
||
if(cfg.waittime_block_start && (result->waittime_block <= cfg.waittime_block_start))
|
||
{
|
||
result->waittime_block++;
|
||
cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} waittime_block count: %u ",
|
||
(check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, result->waittime_block);
|
||
}
|
||
}
|
||
|
||
SAFE_RWLOCK_UNLOCK(&hitcache_lock);
|
||
#endif
|
||
// if check_cw mode=0, first try to get cw from cache without check counter!
|
||
CWCHECK check_cw = get_cwcheck(er);
|
||
if(!check_cw.mode)
|
||
{
|
||
struct ecm_request_t *ecm = NULL;
|
||
ecm = check_cache(er, er->client);
|
||
|
||
if(ecm) // found in cache
|
||
{
|
||
struct s_write_from_cache *wfc = NULL;
|
||
if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache)))
|
||
{
|
||
NULLFREE(ecm);
|
||
return;
|
||
}
|
||
wfc->er_new = er;
|
||
wfc->er_cache = ecm;
|
||
if(!add_job(er->client, ACTION_ECM_ANSWER_CACHE, wfc, sizeof(struct s_write_from_cache))) // write_ecm_answer_fromcache
|
||
{ NULLFREE(ecm); }
|
||
return;
|
||
}
|
||
}
|
||
|
||
// check if "normal" readers selected, if not send NOT FOUND!
|
||
// cacheex1-client (having always no "normal" reader),
|
||
// or not-cacheex-1 client with no normal readers available (or filtered by LB)
|
||
if((er->reader_count + er->fallback_reader_count - er->cacheex_reader_count) <= 0)
|
||
{
|
||
if(!cfg.wait_until_ctimeout)
|
||
{
|
||
er->rc = E_NOTFOUND;
|
||
er->selected_reader = NULL;
|
||
er->rcEx = 0;
|
||
cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex timeout: NO \"normal\" readers... not_found! ",
|
||
(check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid);
|
||
|
||
send_dcw(er->client, er);
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if(er->stage < 2)
|
||
{
|
||
debug_ecm(D_TRACE, "request for %s %s", username(er->client), buf);
|
||
request_cw_from_readers(er, 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef CS_CACHEEX_AIO
|
||
char* cxaio_ftab_to_buf(FTAB *lg_only_ftab)
|
||
{
|
||
int32_t i, k, l = 0, strncat_sz = 0;
|
||
char *ret;
|
||
char caid[5];
|
||
char provid[7];
|
||
char nprids[3];
|
||
|
||
// get size of return-val
|
||
for(i = 0; i < lg_only_ftab->nfilts; i++)
|
||
{
|
||
l += 4; // caid
|
||
l += 2; // nprid-counter
|
||
l += 6 * lg_only_ftab->filts[i].nprids; // prid/s
|
||
|
||
if(!lg_only_ftab->filts[i].nprids)
|
||
{
|
||
l += 6;
|
||
}
|
||
}
|
||
|
||
if(!cs_malloc(&ret, l * sizeof(char) + sizeof(char))) {
|
||
return "";
|
||
}
|
||
|
||
strncat_sz += l * sizeof(char) + sizeof(char);
|
||
|
||
for(i = 0; i < lg_only_ftab->nfilts; i++)
|
||
{
|
||
snprintf(caid, 5, "%04X", lg_only_ftab->filts[i].caid);
|
||
if (!cs_strncat(ret, caid, strncat_sz)) {
|
||
cs_log("FIXME!");
|
||
}
|
||
|
||
if(!lg_only_ftab->filts[i].nprids)
|
||
{
|
||
if (!cs_strncat(ret, "01", strncat_sz)) {
|
||
cs_log("FIXME2!");
|
||
}
|
||
snprintf(provid, 7, "000000");
|
||
if (!cs_strncat(ret, provid, strncat_sz)) {
|
||
cs_log("FIXME3!");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
snprintf(nprids, 3, "%02X", lg_only_ftab->filts[i].nprids);
|
||
if (!cs_strncat(ret, nprids, strncat_sz)) {
|
||
cs_log("FIXME4!");
|
||
}
|
||
}
|
||
|
||
for(k = 0; k < lg_only_ftab->filts[i].nprids; k++)
|
||
{
|
||
snprintf(provid, 7, "%06X", lg_only_ftab->filts[i].prids[k]);
|
||
if (!cs_strncat(ret, provid, strncat_sz)) {
|
||
cs_log("FIXME5!");
|
||
}
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
FTAB caidtab2ftab(CAIDTAB *ctab)
|
||
{
|
||
int i;
|
||
FTAB ftab;
|
||
memset(&ftab, 0, sizeof(ftab));
|
||
|
||
for(i=0; i<ctab->ctnum; i++)
|
||
{
|
||
FILTER d;
|
||
memset(&d, 0, sizeof(d));
|
||
d.caid = ctab->ctdata[i].caid;
|
||
d.prids[d.nprids] = NO_PROVID_VALUE;
|
||
d.nprids++;
|
||
ftab_add(&ftab, &d);
|
||
}
|
||
return ftab;
|
||
}
|
||
|
||
void caidtab2ftab_add(CAIDTAB *lgonly_ctab, FTAB *lgonly_tab)
|
||
{
|
||
int j, k, l, rc;
|
||
for(j = 0; j < lgonly_ctab->ctnum; j++)
|
||
{
|
||
CAIDTAB_DATA *d = &lgonly_ctab->ctdata[j];
|
||
if(d->caid)
|
||
{
|
||
rc = 0;
|
||
if(lgonly_tab->nfilts)
|
||
{
|
||
for(k = 0; (k < lgonly_tab->nfilts); k++)
|
||
{
|
||
if(lgonly_tab->filts[k].caid != 0 && lgonly_tab->filts[k].caid == d->caid)
|
||
{
|
||
for(l = 0; (l < lgonly_tab->filts[k].nprids); l++)
|
||
{
|
||
if(lgonly_tab->filts[k].prids[l] == NO_PROVID_VALUE)
|
||
{
|
||
rc = 1;
|
||
break;
|
||
}
|
||
}
|
||
if(!rc)
|
||
{
|
||
lgonly_tab->filts[k].nprids = 1;
|
||
lgonly_tab->filts[k].prids[0] = NO_PROVID_VALUE;
|
||
rc = 1;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if(!rc) // caid not found
|
||
{
|
||
FILTER df;
|
||
memset(&df, 0, sizeof(df));
|
||
df.caid = d->caid;
|
||
df.prids[0] = NO_PROVID_VALUE;
|
||
df.nprids++;
|
||
ftab_add(lgonly_tab, &df);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
#endif // CS_CACHEEX
|