revert breaks some stupid old compilers
[oscam.git] / oscam-cache.c
blob113613869edc499b96c63707196887cb770dd170
1 #define MODULE_LOG_PREFIX "cache"
3 #include "globals.h"
4 #include "module-cacheex.h"
5 #include "module-cw-cycle-check.h"
6 #include "oscam-cache.h"
7 #include "oscam-chk.h"
8 #include "oscam-client.h"
9 #include "oscam-ecm.h"
10 #include "oscam-garbage.h"
11 #include "oscam-lock.h"
12 #include "oscam-net.h"
13 #include "oscam-string.h"
14 #include "oscam-time.h"
15 #include "oscam-hashtable.h"
18 // CACHE functions **************************************************************+
19 struct s_pushclient {
20 struct s_client *cl;
21 struct s_pushclient *next_push;
24 typedef struct cw_t {
25 uchar cw[16];
26 uint8_t odd_even; //odd/even byte (0x80 0x81)
27 uint8_t cwc_cycletime;
28 uint8_t cwc_next_cw_cycle;
29 uint8_t got_bad_cwc; //used by cycle check
30 uint16_t caid; //first caid received
31 uint32_t prid; //first prid received
32 uint16_t srvid; //first srvid received
33 struct s_reader *selected_reader; //first answering: reader
34 struct s_client *cacheex_src; //first answering: cacheex client
36 uint64_t grp; //updated grp
37 uint8_t csp; //updated if answer from csp
38 uint8_t cacheex; //updated if answer from cacheex
39 uint8_t localcards; //updated if answer from local cards (or proxy using localcards option)
40 uint8_t proxy; //updated if answer from local reader
42 uint32_t count; //count of same cws receved
44 //for push out
45 pthread_rwlock_t pushout_client_lock;
46 struct s_pushclient *pushout_client; //list of clients that pushing cw
47 //end push out
49 node ht_node; //node for hash table
50 node ll_node; //node for linked list
51 } CW;
53 typedef struct cache_t {
54 hash_table ht_cw;
55 list ll_cw;
56 struct timeb upd_time; //updated time. Update time at each cw got
57 struct timeb first_recv_time; //time of first cw received
58 uint32_t csp_hash;
60 node ht_node; //node for hash table
61 node ll_node; //node for linked list
62 } ECMHASH;
65 static pthread_rwlock_t cache_lock;
66 static hash_table ht_cache;
67 static list ll_cache;
68 static int8_t cache_init_done = 0;
70 void init_cache(void){
71 init_hash_table(&ht_cache, &ll_cache);
72 if (pthread_rwlock_init(&cache_lock,NULL) != 0)
73 { cs_log("Error creating lock cache_lock!"); }
74 else
75 { cache_init_done = 1; }
78 void free_cache(void){
79 cleanup_cache(true);
80 cache_init_done = 0;
81 deinitialize_hash_table(&ht_cache);
82 pthread_rwlock_destroy(&cache_lock);
85 uint32_t cache_size(void){
86 if(!cache_init_done)
87 { return 0; }
89 return count_hash_table(&ht_cache);
92 static uint8_t count_sort(CW *a, CW *b){
93 if (a->count == b->count) return 0;
94 return (a->count > b->count) ? -1 : 1; //DESC order by count
97 uint8_t check_is_pushed(void *cwp, struct s_client *cl){
99 struct s_pushclient *cl_tmp;
100 CW* cw = (CW*)cwp;
101 bool pushed=false;
103 SAFE_RWLOCK_RDLOCK(&cw->pushout_client_lock);
104 for (cl_tmp = cw->pushout_client; cl_tmp; cl_tmp = cl_tmp->next_push) {
105 if(cl_tmp->cl==cl){
106 pushed=true;
107 break;
111 if(!pushed){
112 SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock);
113 SAFE_RWLOCK_WRLOCK(&cw->pushout_client_lock);
115 struct s_pushclient *new_push_client;
116 if(cs_malloc(&new_push_client, sizeof(struct s_pushclient))){
117 new_push_client->cl=cl;
119 new_push_client->next_push=cw->pushout_client;
120 cw->pushout_client=new_push_client;
123 SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock);
124 return 0;
125 }else{
126 SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock);
127 return 1;
131 uint8_t get_odd_even(ECM_REQUEST *er){
132 return (er->ecm[0] != 0x80 && er->ecm[0] != 0x81 ? 0 : er->ecm[0]);
136 CW *get_first_cw(ECMHASH *ecmhash, ECM_REQUEST *er){
137 if(!ecmhash) return NULL;
139 node *j;
140 CW *cw;
142 j = get_first_node_list(&ecmhash->ll_cw);
143 while (j) {
144 cw = get_data_from_node(j);
146 if(cw && cw->odd_even == get_odd_even(er) && !cw->got_bad_cwc)
147 return cw;
149 j = j->next;
152 return NULL;
155 static int compare_csp_hash(const void *arg, const void *obj){
156 uint32_t h = ((const ECMHASH*)obj)->csp_hash;
157 return memcmp(arg, &h, 4);
160 static int compare_cw(const void *arg, const void *obj){
161 return memcmp(arg, ((const CW*)obj)->cw, 16);
164 static bool cwcycle_check_cache(struct s_client *cl, ECM_REQUEST *er, CW *cw)
166 (void)cl; (void)er; (void)cw;
167 #ifdef CW_CYCLE_CHECK
168 if(cw->got_bad_cwc)
169 return 0;
170 uint8_t cwc_ct = cw->cwc_cycletime > 0 ? cw->cwc_cycletime : 0;
171 uint8_t cwc_ncwc = cw->cwc_next_cw_cycle < 2 ? cw->cwc_next_cw_cycle : 2;
172 if(checkcwcycle(cl, er, NULL, cw->cw, 0, cwc_ct, cwc_ncwc) != 0)
174 cs_log_dbg(D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [check_cache] cyclecheck passed ecm in INT. cache.", (cl ? cl->account->usr : "-"), er->caid, er->srvid);
175 }else{
176 cs_log_dbg(D_CWC, "cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> skip cache answer", (cl ? cl->account->usr : "-"), er->caid, er->srvid);
177 cw->got_bad_cwc = 1; // no need to check it again
178 return 0;
180 #endif
181 return 1;
185 * This function returns cw (mostly received) in cache for er, or NULL if not found.
186 * IMPORTANT:
187 * - If found, DON'T forget to free returned ecm, because it is a copy useful to get data
188 * - If found, and cacheex_src client of returned ecm is not NULL, and we want to access it,
189 * remember to check for its validity (client structure is still existent)
190 * E.g.: if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill)
191 * We don't want make this stuff here to avoid useless cpu time if outside function we would not access to it.
193 struct ecm_request_t *check_cache(ECM_REQUEST *er, struct s_client *cl)
195 if(!cache_init_done || !er->csp_hash) return NULL;
197 ECM_REQUEST *ecm = NULL;
198 ECMHASH *result;
199 CW *cw;
200 uint64_t grp = cl?cl->grp:0;
202 SAFE_RWLOCK_RDLOCK(&cache_lock);
204 result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t),&compare_csp_hash);
205 cw = get_first_cw(result, er);
206 if (!cw)
207 goto out_err;
210 cw->csp //csp have no grp!
212 !grp //csp client(no grp) searching for cache
217 cw->grp //ecm group --> only when readers/ex-clients answer (e_found) it
218 && (grp & cw->grp)
223 #ifdef CS_CACHEEX
225 //if preferlocalcards=2 for this ecm request, we can server ONLY cw from localcards readers until stage<3
226 if(er->preferlocalcards==2 && !cw->localcards && er->stage<3){
227 goto out_err;
230 CWCHECK check_cw = get_cwcheck(er);
231 if((!cw->proxy && !cw->localcards) //cw received from ONLY cacheex/csp peers
232 && check_cw.counter>1
233 && cw->count < check_cw.counter
234 && (check_cw.mode || !er->cacheex_wait_time_expired)
236 goto out_err;
238 #endif
240 if (!cwcycle_check_cache(cl, er, cw))
241 goto out_err;
243 if (cs_malloc(&ecm, sizeof(ECM_REQUEST))){
244 ecm->rc = E_FOUND;
245 ecm->rcEx = 0;
246 memcpy(ecm->cw, cw->cw, 16);
247 ecm->grp = cw->grp;
248 ecm->selected_reader = cw->selected_reader;
249 ecm->cwc_cycletime = cw->cwc_cycletime;
250 ecm->cwc_next_cw_cycle = cw->cwc_next_cw_cycle;
251 ecm->cacheex_src = cw->cacheex_src;
252 ecm->cw_count = cw->count;
256 out_err:
257 SAFE_RWLOCK_UNLOCK(&cache_lock);
258 return ecm;
261 static void cacheex_cache_add(ECM_REQUEST *er, ECMHASH *result, CW *cw, bool add_new_cw)
263 (void)er; (void)result; (void)cw; (void)add_new_cw;
264 #ifdef CS_CACHEEX
265 er->cw_cache = cw;
266 cacheex_cache_push(er);
268 //cacheex debug log lines and cw diff stuff
269 if(!check_client(er->cacheex_src))
270 return;
272 if(!add_new_cw)
274 debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src));
275 return;
278 debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src));
279 CW *cw_first = get_first_cw(result, er);
280 if(!cw_first)
281 return;
283 //compare er cw with mostly counted cached cw
284 if(memcmp(er->cw, cw_first->cw, sizeof(er->cw)) != 0)
286 er->cacheex_src->cwcacheexerrcw++;
287 if (er->cacheex_src->account)
288 er->cacheex_src->account->cwcacheexerrcw++;
290 if (((0x0200| 0x0800) & cs_dblevel)) //avoid useless operations if debug is not enabled
292 char cw1[16*3+2], cw2[16*3+2];
293 cs_hexdump(0, er->cw, 16, cw1, sizeof(cw1));
294 cs_hexdump(0, cw_first->cw, 16, cw2, sizeof(cw2));
296 char ip1[20]="", ip2[20]="";
297 if (check_client(er->cacheex_src))
298 cs_strncpy(ip1, cs_inet_ntoa(er->cacheex_src->ip), sizeof(ip1));
299 if (check_client(cw_first->cacheex_src))
300 cs_strncpy(ip2, cs_inet_ntoa(cw_first->cacheex_src->ip), sizeof(ip2));
301 else if (cw_first->selected_reader && check_client(cw_first->selected_reader->client))
302 cs_strncpy(ip2, cs_inet_ntoa(cw_first->selected_reader->client->ip), sizeof(ip2));
304 debug_ecm(D_CACHEEX| D_CSP, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s ", buf,
305 er->from_csp ? "csp" : username(er->cacheex_src), ip1,
306 check_client(cw_first->cacheex_src)?username(cw_first->cacheex_src):(cw_first->selected_reader?cw_first->selected_reader->label:"unknown/csp"), ip2,
307 cw1, cw2);
310 #endif
313 void add_cache(ECM_REQUEST *er){
314 if(!cache_init_done || !er->csp_hash) return;
316 ECMHASH *result = NULL;
317 CW *cw = NULL;
318 bool add_new_cw=false;
320 SAFE_RWLOCK_WRLOCK(&cache_lock);
322 //add csp_hash to cache
323 result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t), &compare_csp_hash);
324 if(!result){
325 if(cs_malloc(&result, sizeof(ECMHASH))){
326 result->csp_hash = er->csp_hash;
327 init_hash_table(&result->ht_cw, &result->ll_cw);
328 cs_ftime(&result->first_recv_time);
330 add_hash_table(&ht_cache, &result->ht_node, &ll_cache, &result->ll_node, result, &result->csp_hash, sizeof(uint32_t));
332 }else{
333 SAFE_RWLOCK_UNLOCK(&cache_lock);
334 cs_log("ERROR: NO added HASH to cache!!");
335 return;
339 cs_ftime(&result->upd_time); //need to be updated at each cw! We use it for deleting this hash when no more cws arrive inside max_cache_time!
342 //add cw to this csp hash
343 cw = find_hash_table(&result->ht_cw, er->cw, sizeof(er->cw), &compare_cw);
345 if(!cw){
347 if(count_hash_table(&result->ht_cw)>=10){ //max 10 different cws stored
348 SAFE_RWLOCK_UNLOCK(&cache_lock);
349 return;
352 while(1){
353 if(cs_malloc(&cw, sizeof(CW))){
354 memcpy(cw->cw, er->cw, sizeof(er->cw));
355 cw->odd_even = get_odd_even(er);
356 cw->cwc_cycletime = er->cwc_cycletime;
357 cw->cwc_next_cw_cycle = er->cwc_next_cw_cycle;
358 cw->count= 0;
359 cw->csp = 0;
360 cw->cacheex = 0;
361 cw->localcards=0;
362 cw->proxy=0;
363 cw->grp = 0;
364 cw->caid = er->caid;
365 cw->prid = er->prid;
366 cw->srvid = er->srvid;
367 cw->selected_reader=er->selected_reader;
368 cw->cacheex_src=er->cacheex_src;
369 cw->pushout_client = NULL;
371 while(1){
372 if (pthread_rwlock_init(&cw->pushout_client_lock, NULL) == 0)
373 break;
375 cs_log("Error creating lock pushout_client_lock!");
376 cs_sleepms(1);
380 add_hash_table(&result->ht_cw, &cw->ht_node, &result->ll_cw, &cw->ll_node, cw, cw->cw, sizeof(er->cw));
382 add_new_cw=true;
383 break;
386 cs_log("ERROR: NO added CW to cache!! Re-trying...");
387 cs_sleepms(1);
391 //update if answered from csp/cacheex/local_proxy
392 if(er->from_cacheex) cw->cacheex = 1;
393 if(er->from_csp) cw->csp = 1;
394 if(!er->cacheex_src){
395 if(is_localreader(er->selected_reader, er)) cw->localcards=1;
396 else cw->proxy = 1;
399 //always update group and counter
400 cw->grp |= er->grp;
401 cw->count++;
403 //sort cw_list by counter (DESC order)
404 if(cw->count>1)
405 sort_list(&result->ll_cw, count_sort);
407 SAFE_RWLOCK_UNLOCK(&cache_lock);
409 cacheex_cache_add(er, result, cw, add_new_cw);
412 void cleanup_cache(bool force){
413 ECMHASH *ecmhash;
414 CW *cw;
415 struct s_pushclient *pc, *nxt;
416 node *i,*i_next,*j,*j_next;
418 struct timeb now;
419 int64_t gone_first, gone_upd;
422 if(!cache_init_done)
423 { return; }
425 SAFE_RWLOCK_WRLOCK(&cache_lock);
427 i = get_first_node_list(&ll_cache);
428 while (i) {
429 i_next = i->next;
430 ecmhash = get_data_from_node(i);
432 if(!ecmhash){
433 i = i_next;
434 continue;
437 cs_ftime(&now);
438 gone_first = comp_timeb(&now, &ecmhash->first_recv_time);
439 gone_upd = comp_timeb(&now, &ecmhash->upd_time);
441 if(!force && gone_first<=(cfg.max_cache_time*1000)){ //not continue, useless check for nexts one!
442 break;
445 if(force || gone_upd>(cfg.max_cache_time*1000)){
447 j = get_first_node_list(&ecmhash->ll_cw);
448 while (j) {
449 j_next = j->next;
451 cw = get_data_from_node(j);
452 if(cw){
453 pthread_rwlock_destroy(&cw->pushout_client_lock);
454 pc = cw->pushout_client;
455 cw->pushout_client=NULL;
456 while (pc) {
457 nxt = pc->next_push;
458 NULLFREE(pc);
459 pc = nxt;
462 remove_elem_list(&ecmhash->ll_cw, &cw->ll_node);
463 remove_elem_hash_table(&ecmhash->ht_cw, &cw->ht_node);
464 NULLFREE(cw);
467 j = j_next;
470 deinitialize_hash_table(&ecmhash->ht_cw);
471 remove_elem_list(&ll_cache, &ecmhash->ll_node);
472 remove_elem_hash_table(&ht_cache, &ecmhash->ht_node);
473 NULLFREE(ecmhash);
476 i = i_next;
479 SAFE_RWLOCK_UNLOCK(&cache_lock);