revert breaks some stupid old compilers
[oscam.git] / module-cacheex.c
blob32e1c04862a7e01a770f70e432ed0d3931c3ebe4
1 #define MODULE_LOG_PREFIX "cacheex"
3 #include "globals.h"
5 #ifdef CS_CACHEEX
7 #include "cscrypt/md5.h"
8 #include "module-cacheex.h"
9 #include "module-cw-cycle-check.h"
10 #include "oscam-cache.h"
11 #include "oscam-chk.h"
12 #include "oscam-client.h"
13 #include "oscam-conf.h"
14 #include "oscam-ecm.h"
15 #include "oscam-hashtable.h"
16 #include "oscam-lock.h"
17 #include "oscam-net.h"
18 #include "oscam-string.h"
19 #include "oscam-time.h"
20 #include "oscam-work.h"
22 #define cs_cacheex_matcher "oscam.cacheex"
24 extern uint8_t cc_node_id[8];
25 extern uint8_t camd35_node_id[8];
27 uint8_t cacheex_peer_id[8];
29 extern CS_MUTEX_LOCK ecm_pushed_deleted_lock;
30 extern struct ecm_request_t *ecm_pushed_deleted;
31 extern CS_MUTEX_LOCK ecmcache_lock;
32 extern struct ecm_request_t *ecmcwcache;
34 // HIT CACHE functions **************************************************************
36 typedef struct hit_key_t {
37 uint16_t caid;
38 uint32_t prid;
39 uint16_t srvid;
40 } HIT_KEY;
42 typedef struct cache_hit_t {
43 HIT_KEY key;
44 struct timeb time;
45 struct timeb max_hitcache_time;
46 uint64_t grp;
47 uint64_t grp_last_max_hitcache_time;
48 node ht_node;
49 node ll_node;
50 } CACHE_HIT;
52 static pthread_rwlock_t hitcache_lock;
53 static hash_table ht_hitcache;
54 static list ll_hitcache;
55 static bool cacheex_running;
57 void cacheex_init_hitcache(void)
59 init_hash_table(&ht_hitcache, &ll_hitcache);
60 if (pthread_rwlock_init(&hitcache_lock,NULL) != 0)
61 cs_log("Error creating lock hitcache_lock!");
62 cacheex_running = true;
65 void cacheex_free_hitcache(void)
67 cacheex_running = false;
68 cacheex_cleanup_hitcache(true);
69 deinitialize_hash_table(&ht_hitcache);
70 pthread_rwlock_destroy(&hitcache_lock);
73 static int cacheex_compare_hitkey(const void *arg, const void *obj)
75 if(((const HIT_KEY*)arg)->caid==((const CACHE_HIT*)obj)->key.caid
76 && ((const HIT_KEY*)arg)->prid==((const CACHE_HIT*)obj)->key.prid
77 && ((const HIT_KEY*)arg)->srvid==((const CACHE_HIT*)obj)->key.srvid)
79 return 0;
81 return 1;
84 static int32_t cacheex_check_hitcache(ECM_REQUEST *er, struct s_client *cl)
86 CACHE_HIT *result;
87 HIT_KEY search;
88 memset(&search, 0, sizeof(HIT_KEY));
89 search.caid = er->caid;
90 search.prid = er->prid;
91 search.srvid = er->srvid;
92 SAFE_RWLOCK_RDLOCK(&hitcache_lock);
93 result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
94 if(result){
95 struct timeb now;
96 cs_ftime(&now);
97 int64_t gone = comp_timeb(&now, &result->time);
98 uint64_t grp = cl?cl->grp:0;
101 gone <= (cfg.max_hitcache_time*1000)
103 (!grp || !result->grp || (grp & result->grp))
106 SAFE_RWLOCK_UNLOCK(&hitcache_lock);
107 return 1;
110 SAFE_RWLOCK_UNLOCK(&hitcache_lock);
111 return 0;
114 static void cacheex_add_hitcache(struct s_client *cl, ECM_REQUEST *er)
116 if (!cfg.max_hitcache_time) //we don't want check/save hitcache
117 return;
118 if (!cfg.cacheex_wait_timetab.cevnum)
119 return;
120 uint32_t cacheex_wait_time = get_cacheex_wait_time(er,NULL);
121 if (!cacheex_wait_time)
122 return;
124 CACHE_HIT *result;
125 HIT_KEY search;
127 memset(&search, 0, sizeof(HIT_KEY));
128 search.caid = er->caid;
129 search.prid = er->prid;
130 search.srvid = er->srvid;
132 SAFE_RWLOCK_WRLOCK(&hitcache_lock);
134 result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
135 if(!result) { //not found, add it!
136 if(cs_malloc(&result, sizeof(CACHE_HIT)))
138 memset(result, 0, sizeof(CACHE_HIT));
139 result->key.caid = er->caid;
140 result->key.prid = er->prid;
141 result->key.srvid = er->srvid;
142 cs_ftime(&result->max_hitcache_time);
143 add_hash_table(&ht_hitcache, &result->ht_node, &ll_hitcache, &result->ll_node, result, &result->key, sizeof(HIT_KEY));
147 if(result)
149 if(cl){
150 result->grp |= cl->grp;
151 result->grp_last_max_hitcache_time |= cl->grp;
153 cs_ftime(&result->time); //always update time;
156 SAFE_RWLOCK_UNLOCK(&hitcache_lock);
159 static void cacheex_del_hitcache(struct s_client *cl, ECM_REQUEST *er)
161 HIT_KEY search;
162 CACHE_HIT *result;
164 memset(&search, 0, sizeof(HIT_KEY));
165 search.caid = er->caid;
166 search.prid = er->prid;
167 search.srvid = er->srvid;
169 if(cl && cl->grp)
171 result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
172 while(result)
174 result->grp &= ~cl->grp;
175 result->grp_last_max_hitcache_time &= ~cl->grp;
176 result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
180 SAFE_RWLOCK_WRLOCK(&hitcache_lock);
181 search_remove_elem_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey);
182 SAFE_RWLOCK_UNLOCK(&hitcache_lock);
185 void cacheex_cleanup_hitcache(bool force)
187 CACHE_HIT *cachehit;
188 node *i,*i_next;
189 struct timeb now;
190 int64_t gone, gone_max_hitcache_time;
191 int32_t timeout = (cfg.max_hitcache_time + (cfg.max_hitcache_time / 2))*1000; //1,5
192 int32_t clean_grp = (cfg.max_hitcache_time * 1000);
193 SAFE_RWLOCK_WRLOCK(&hitcache_lock);
194 i = get_first_node_list(&ll_hitcache);
195 while (i)
197 i_next = i->next;
198 cachehit = get_data_from_node(i);
200 if(!cachehit)
202 i = i_next;
203 continue;
206 cs_ftime(&now);
207 gone = comp_timeb(&now, &cachehit->time);
208 gone_max_hitcache_time = comp_timeb(&now, &cachehit->max_hitcache_time);
210 if(force || gone>timeout)
212 remove_elem_list(&ll_hitcache, &cachehit->ll_node);
213 remove_elem_hash_table(&ht_hitcache, &cachehit->ht_node);
214 NULLFREE(cachehit);
216 else if(gone_max_hitcache_time >= clean_grp){
217 cachehit->grp = cachehit->grp_last_max_hitcache_time;
218 cachehit->grp_last_max_hitcache_time = 0;
219 cs_ftime(&cachehit->max_hitcache_time);
221 i = i_next;
223 SAFE_RWLOCK_UNLOCK(&hitcache_lock);
226 static int32_t cacheex_ecm_hash_calc(uchar *buf, int32_t n)
228 int32_t i, h = 0;
229 for(i = 0; i < n; i++)
231 h = 31 * h + buf[i];
233 return h;
236 void cacheex_update_hash(ECM_REQUEST *er)
238 er->csp_hash = cacheex_ecm_hash_calc(er->ecm + 3, er->ecmlen - 3);
241 void cacheex_free_csp_lastnodes(ECM_REQUEST *er)
243 ll_destroy_free_data(&er->csp_lastnodes);
246 void cacheex_set_csp_lastnode(ECM_REQUEST *er)
248 er->csp_lastnodes = NULL;
251 void cacheex_set_cacheex_src(ECM_REQUEST *ecm, struct s_client *cl)
253 if(ecm->cacheex_src == cl)
254 ecm->cacheex_src = NULL;
257 void cacheex_init_cacheex_src(ECM_REQUEST *ecm, ECM_REQUEST *er)
259 if(!ecm->cacheex_src)
260 ecm->cacheex_src = er->cacheex_src;
263 static void *chkcache_process(void)
265 set_thread_name(__func__);
267 time_t timeout;
268 struct ecm_request_t *er, *ecm;
269 uint8_t add_hitcache_er;
270 struct s_reader *cl_rdr;
271 struct s_reader *rdr;
272 struct s_ecm_answer *ea;
273 struct s_client *cex_src=NULL;
274 struct s_write_from_cache *wfc=NULL;
276 while(cacheex_running)
278 cs_readlock(__func__, &ecmcache_lock);
279 for(er = ecmcwcache; er; er = er->next)
281 timeout = time(NULL)-((cfg.ctimeout+500)/1000+1);
282 if(er->tps.time < timeout)
283 { break; }
285 if(er->rc<E_UNHANDLED || er->readers_timeout_check) //already answered
286 { continue; }
288 //******** CHECK IF FOUND ECM IN CACHE
289 ecm = check_cache(er, er->client);
290 if(ecm) //found in cache
292 //check for add_hitcache
293 if(ecm->cacheex_src) //cw from cacheex
295 if((er->cacheex_wait_time && !er->cacheex_wait_time_expired) || !er->cacheex_wait_time) //only when no wait_time expires (or not wait_time)
298 //add_hitcache already called, but we check if we have to call it for these (er) caid|prid|srvid
299 if(ecm->prid!=er->prid || ecm->srvid!=er->srvid)
301 cex_src = ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill ? ecm->cacheex_src : NULL; //here we should be sure cex client has not been freed!
302 if(cex_src){ //add_hitcache only if client is really active
303 add_hitcache_er=1;
304 cl_rdr = cex_src->reader;
305 if(cl_rdr && cl_rdr->cacheex.mode == 2)
307 for(ea = er->matching_rdr; ea; ea = ea->next)
309 rdr = ea->reader;
310 if(cl_rdr == rdr && ((ea->status & REQUEST_ANSWERED) == REQUEST_ANSWERED))
312 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);
313 add_hitcache_er=0; //don't add hit cache, reader requested self
318 if(add_hitcache_er)
319 { cacheex_add_hitcache(cex_src, er); } //USE cacheex client (to get correct group) and ecm from requesting client (to get correct caid|prid|srvid)!!!
324 else
326 //add_hitcache already called, but we have to remove it because cacheex not coming before wait_time
327 if(ecm->prid==er->prid && ecm->srvid==er->srvid)
328 { cacheex_del_hitcache(er->client, ecm); }
331 //END check for add_hitcache
333 if(check_client(er->client))
335 wfc=NULL;
336 if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache)))
338 NULLFREE(ecm);
339 continue;
342 wfc->er_new=er;
343 wfc->er_cache=ecm;
345 if(!add_job(er->client, ACTION_ECM_ANSWER_CACHE, wfc, sizeof(struct s_write_from_cache))) //write_ecm_answer_fromcache
347 NULLFREE(ecm);
348 continue;
351 else
352 { NULLFREE(ecm); }
355 cs_readunlock(__func__, &ecmcache_lock);
357 cs_sleepms(10);
360 return NULL;
363 void checkcache_process_thread_start(void)
365 start_thread("chkcache_process", (void *) &chkcache_process, NULL, NULL, 1, 1);
368 void cacheex_init(void)
370 // Init random node id
371 get_random_bytes(cacheex_peer_id, 8);
372 #ifdef MODULE_CCCAM
373 memcpy(cacheex_peer_id, cc_node_id, 8);
374 #endif
375 #ifdef MODULE_CAMD35_TCP
376 memcpy(camd35_node_id, cacheex_peer_id, 8);
377 #endif
380 void cacheex_clear_account_stats(struct s_auth *account)
382 account->cwcacheexgot = 0;
383 account->cwcacheexpush = 0;
384 account->cwcacheexhit = 0;
387 void cacheex_clear_client_stats(struct s_client *client)
389 client->cwcacheexgot = 0;
390 client->cwcacheexpush = 0;
391 client->cwcacheexhit = 0;
394 int32_t cacheex_add_stats(struct s_client *cl, uint16_t caid, uint16_t srvid, uint32_t prid, uint8_t direction)
396 if(!cfg.cacheex_enable_stats)
397 { return -1; }
399 // create list if doesn't exist
400 if(!cl->ll_cacheex_stats)
401 { cl->ll_cacheex_stats = ll_create("ll_cacheex_stats"); }
403 time_t now = time((time_t *)0);
404 LL_ITER itr = ll_iter_create(cl->ll_cacheex_stats);
405 S_CACHEEX_STAT_ENTRY *cacheex_stats_entry;
407 // check for existing entry
408 while((cacheex_stats_entry = ll_iter_next(&itr)))
410 if(cacheex_stats_entry->cache_srvid == srvid &&
411 cacheex_stats_entry->cache_caid == caid &&
412 cacheex_stats_entry->cache_prid == prid &&
413 cacheex_stats_entry->cache_direction == direction)
415 // we already have this entry - just add count and time
416 cacheex_stats_entry->cache_count++;
417 cacheex_stats_entry->cache_last = now;
418 return cacheex_stats_entry->cache_count;
422 // if we land here we have to add a new entry
423 if(cs_malloc(&cacheex_stats_entry, sizeof(S_CACHEEX_STAT_ENTRY)))
425 cacheex_stats_entry->cache_caid = caid;
426 cacheex_stats_entry->cache_srvid = srvid;
427 cacheex_stats_entry->cache_prid = prid;
428 cacheex_stats_entry->cache_count = 1;
429 cacheex_stats_entry->cache_last = now;
430 cacheex_stats_entry->cache_direction = direction;
431 ll_iter_insert(&itr, cacheex_stats_entry);
432 return 1;
434 return 0;
438 int8_t cacheex_maxhop(struct s_client *cl)
440 int maxhop = 10;
441 if(cl->reader && cl->reader->cacheex.maxhop)
442 { maxhop = cl->reader->cacheex.maxhop; }
443 else if(cl->account && cl->account->cacheex.maxhop)
444 { maxhop = cl->account->cacheex.maxhop; }
445 return maxhop;
448 static void cacheex_cache_push_to_client(struct s_client *cl, ECM_REQUEST *er)
450 add_job(cl, ACTION_CACHE_PUSH_OUT, er, 0);
454 * Check for NULL ecmd5
456 static uint8_t checkECMD5(ECM_REQUEST *er)
458 int8_t i;
459 for(i = 0; i < CS_ECMSTORESIZE; i++)
460 if(er->ecmd5[i]) { return 1; }
461 return 0;
465 * cacheex modes:
467 * cacheex=1 CACHE PULL:
468 * Situation: oscam A reader1 has cacheex=1, oscam B account1 has cacheex=1
469 * oscam A gets a ECM request, reader1 send this request to oscam B, oscam B checks his cache
470 * a. not found in cache: return NOK
471 * a. found in cache: return OK+CW
472 * b. not found in cache, but found pending request: wait max cacheexwaittime and check again
473 * oscam B never requests new ECMs
475 * CW-flow: B->A
477 * cacheex=2 CACHE PUSH:
478 * Situation: oscam A reader1 has cacheex=2, oscam B account1 has cacheex=2
479 * if oscam B gets a CW, its pushed to oscam A
480 * reader has normal functionality and can request ECMs
482 * Problem: oscam B can only push if oscam A is connected
483 * Problem or feature?: oscam A reader can request ecms from oscam B
485 * CW-flow: B->A
488 void cacheex_cache_push(ECM_REQUEST *er)
490 if(er->rc >= E_NOTFOUND) { return; }
492 //cacheex=2 mode: push (server->remote)
493 struct s_client *cl;
494 cs_readlock(__func__, &clientlist_lock);
495 for(cl = first_client->next; cl; cl = cl->next)
497 if(check_client(cl) && er->cacheex_src != cl)
499 if(get_module(cl)->num == R_CSP) // always send to csp cl
501 if(!er->cacheex_src || cfg.csp.allow_reforward) { cacheex_cache_push_to_client(cl, er); } // but not if the origin was cacheex (might loop)
503 else if(cl->typ == 'c' && !cl->dup && cl->account && cl->account->cacheex.mode == 2) //send cache over user
505 if(get_module(cl)->c_cache_push // cache-push able
506 && (!er->grp || (cl->grp & er->grp)) //Group-check
507 /**** OUTGOING FILTER CHECK ***/
508 && (!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
509 && (!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
510 && (!cl->account->cacheex.drop_csp || checkECMD5(er)) //cacheex_drop_csp-check
511 && chk_ctab(er->caid, &cl->ctab) //Caid-check
512 && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->ftab)) //Ident-check (not for csp: prid=0 always!)
513 && chk_srvid(cl, er) //Service-check
514 && chk_csp_ctab(er, &cl->account->cacheex.filter_caidtab) //cacheex_ecm_filter
517 cacheex_cache_push_to_client(cl, er);
522 cs_readunlock(__func__, &clientlist_lock);
525 //cacheex=3 mode: reverse push (reader->server)
526 cs_readlock(__func__, &readerlist_lock);
527 cs_readlock(__func__, &clientlist_lock);
528 struct s_reader *rdr;
529 for(rdr = first_active_reader; rdr; rdr = rdr->next)
531 cl = rdr->client;
532 if(check_client(cl) && er->cacheex_src != cl && rdr->cacheex.mode == 3) //send cache over reader
534 if(rdr->ph.c_cache_push // cache-push able
535 && (!er->grp || (rdr->grp & er->grp)) //Group-check
536 /**** OUTGOING FILTER CHECK ***/
537 && (!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
538 && (!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
539 && (!rdr->cacheex.drop_csp || checkECMD5(er)) //cacheex_drop_csp-check
540 && chk_ctab(er->caid, &rdr->ctab) //Caid-check
541 && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &rdr->ftab)) //Ident-check (not for csp: prid=0 always!)
542 && chk_srvid(cl, er) //Service-check
543 && chk_csp_ctab(er, &rdr->cacheex.filter_caidtab) //cacheex_ecm_filter
546 cacheex_cache_push_to_client(cl, er);
550 cs_readunlock(__func__, &clientlist_lock);
551 cs_readunlock(__func__, &readerlist_lock);
555 /**** INCOMING FILTER CHECK ***/
556 uint8_t check_cacheex_filter(struct s_client *cl, ECM_REQUEST *er)
559 if(check_client(cl) && cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode==2
560 && (!cl->reader->cacheex.drop_csp || checkECMD5(er)) //cacheex_drop_csp-check
561 && chk_ctab(er->caid, &cl->reader->ctab) //Caid-check
562 && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->reader->ftab)) //Ident-check (not for csp: prid=0 always!)
563 && chk_srvid(cl, er) //Service-check
565 { return 1; }
567 if(check_client(cl) && cl->typ == 'c' && cl->account && cl->account->cacheex.mode==3
568 && (!cl->account->cacheex.drop_csp || checkECMD5(er)) //cacheex_drop_csp-check
569 && chk_ctab(er->caid, &cl->ctab) //Caid-check
570 && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->ftab)) //Ident-check (not for csp: prid=0 always!)
571 && chk_srvid(cl, er) //Service-check
573 { return 1; }
575 NULLFREE(er);
576 return 0;
581 static struct s_cacheex_matcher *is_cacheex_matcher_matching(ECM_REQUEST *from_er, ECM_REQUEST *to_er)
583 struct s_cacheex_matcher *entry = cfg.cacheex_matcher;
584 int8_t v_ok = (from_er && to_er) ? 2 : 1;
585 while(entry)
587 int8_t ok = 0;
588 if(from_er
589 && (!entry->caid || entry->caid == from_er->caid)
590 && (!entry->provid || entry->provid == from_er->prid)
591 && (!entry->srvid || entry->srvid == from_er->srvid)
592 && (!entry->chid || entry->chid == from_er->chid)
593 && (!entry->pid || entry->pid == from_er->pid)
594 && (!entry->ecmlen || entry->ecmlen == from_er->ecmlen))
595 { ok++; }
597 if(to_er
598 && (!entry->to_caid || entry->to_caid == to_er->caid)
599 && (!entry->to_provid || entry->to_provid == to_er->prid)
600 && (!entry->to_srvid || entry->to_srvid == to_er->srvid)
601 && (!entry->to_chid || entry->to_chid == to_er->chid)
602 && (!entry->to_pid || entry->to_pid == to_er->pid)
603 && (!entry->to_ecmlen || entry->to_ecmlen == to_er->ecmlen))
604 { ok++; }
606 if(ok == v_ok)
608 if(!from_er || !to_er || from_er->srvid == to_er->srvid)
609 { return entry; }
611 entry = entry->next;
613 return NULL;
616 bool cacheex_is_match_alias(struct s_client *cl, ECM_REQUEST *er)
618 return check_client(cl) && cl->account && cl->account->cacheex.mode == 1 && is_cacheex_matcher_matching(NULL, er);
621 static void log_cacheex_cw(ECM_REQUEST *er, char *reason)
623 uint8_t *data;
624 uint8_t remotenodeid[8];
625 data = ll_last_element(er->csp_lastnodes);
626 if(data)
627 { memcpy(remotenodeid, data, 8); }
628 else
629 { memset(remotenodeid, 0 , 8); }
631 char buf_ecm[109];
632 format_ecm(er, buf_ecm, 109);
633 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",
634 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);
637 static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, int8_t csp)
639 if(er->rc >= E_NOTFOUND) { return 0; }
641 if(!cl)
642 { return 0; }
643 if(!csp && cl->reader && cl->reader->cacheex.mode != 2) //from reader
645 cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl));
646 return 0;
648 if(!csp && !cl->reader && cl->account && cl->account->cacheex.mode != 3) //from user
650 cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl));
651 return 0;
653 if(!csp && !cl->reader && !cl->account) //not active!
655 cs_log_dbg(D_CACHEEX, "CACHEX received, but invalid client state %s", username(cl));
656 return 0;
659 uint8_t selectedForIgnChecksum = chk_if_ignore_checksum(er, cfg.disablecrccws, &cfg.disablecrccws_only_for);
660 if (cl->typ == 'c') {
661 selectedForIgnChecksum += chk_if_ignore_checksum (er, cl->account->disablecrccacheex, &cl->account->disablecrccacheex_only_for);
663 if (cl->typ == 'p') {
664 selectedForIgnChecksum += chk_if_ignore_checksum (er, cl->reader->disablecrccws, &cl->reader->disablecrccws_only_for);
667 uint8_t i, c;
668 if(cfg.disablecrccws == 0 || (cl->typ == 'c' && cl->account->disablecrccacheex == 0) || ( cl->typ == 'p' && cl->reader->disablecrccws == 0))
670 for(i = 0; i < 16; i += 4)
672 c = ((er->cw[i] + er->cw[i + 1] + er->cw[i + 2]) & 0xff);
674 if((i!=12) && selectedForIgnChecksum && (er->cw[i + 3] != c)){
675 break;
678 if(er->cw[i + 3] != c)
680 cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received cw with chksum error from %s", csp ? "csp" : username(cl));
681 cl->cwcacheexerr++;
682 if(cl->account)
683 { cl->account->cwcacheexerr++; }
684 return 0;
689 if(chk_is_null_CW(er->cw) && er->caid !=0x2600) // 0x2600 used by biss and constant cw could be indeed zero
691 cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl));
692 cl->cwcacheexerr++;
693 if(cl->account)
694 { cl->account->cwcacheexerr++; }
695 return 0;
698 if(get_odd_even(er)==0){
699 cs_log_dbg(D_CACHEEX, "push received ecm with null odd/even byte from %s", csp ? "csp" : username(cl));
700 cl->cwcacheexerr++;
701 if(cl->account)
702 { cl->account->cwcacheexerr++; }
703 return 0;
706 if(!chk_halfCW(er, er->cw)){
707 log_cacheex_cw(er, "bad half cw");
709 cl->cwcacheexerr++;
710 if(cl->account)
711 { cl->account->cwcacheexerr++; }
712 return 0;
715 if((csp && cfg.csp.block_fakecws) || (cl->reader && cl->reader->cacheex.block_fakecws)
716 || (!cl->reader && cl->account && cl->account->cacheex.block_fakecws))
718 if(chk_is_fakecw(er->cw))
720 cs_log_dbg(D_CACHEEX, "push received fake cw from %s", csp ? "csp" : username(cl));
721 cl->cwcacheexerr++;
722 if(cl->account)
723 { cl->account->cwcacheexerr++; }
724 return 0;
728 er->grp |= cl->grp; //ok for mode2 reader too: cl->reader->grp
729 er->rc = E_CACHEEX;
730 er->cacheex_src = cl;
731 er->selected_reader = cl->reader;
732 er->client = NULL; //No Owner! So no fallback!
734 if(check_client(cl))
736 cl->cwcacheexgot++;
737 if(cl->account)
738 { cl->account->cwcacheexgot++; }
739 first_client->cwcacheexgot++;
742 cacheex_add_hitcache(cl, er); //we have to call it before add_cache, because in chk_process we could remove it!
743 add_cache(er);
744 cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 1);
746 cs_writelock(__func__, &ecm_pushed_deleted_lock);
747 er->next = ecm_pushed_deleted;
748 ecm_pushed_deleted = er;
749 cs_writeunlock(__func__, &ecm_pushed_deleted_lock);
751 return 1; //NO free, we have to wait cache push out stuff ends.
755 void cacheex_add_to_cache(struct s_client *cl, ECM_REQUEST *er)
757 er->from_cacheex = 1;
758 if(!cacheex_add_to_cache_int(cl, er, 0))
759 { free_push_in_ecm(er); }
762 void cacheex_add_to_cache_from_csp(struct s_client *cl, ECM_REQUEST *er)
764 if(!cacheex_add_to_cache_int(cl, er, 1))
765 { free_push_in_ecm(er); }
769 //Format:
770 //caid:prov:srvid:pid:chid:ecmlen=caid:prov:srvid:pid:chid:ecmlen[,validfrom,validto]
771 //validfrom: default=-2000
772 //validto: default=4000
773 //valid time if found in cache
774 static struct s_cacheex_matcher *cacheex_matcher_read_int(void)
776 FILE *fp = open_config_file(cs_cacheex_matcher);
777 if(!fp)
778 { return NULL; }
780 char token[1024];
781 unsigned char type;
782 int32_t i, ret, count = 0;
783 struct s_cacheex_matcher *new_cacheex_matcher = NULL, *entry, *last = NULL;
784 uint32_t line = 0;
786 while(fgets(token, sizeof(token), fp))
788 line++;
789 if(strlen(token) <= 1) { continue; }
790 if(token[0] == '#' || token[0] == '/') { continue; }
791 if(strlen(token) > 100) { continue; }
793 for(i = 0; i < (int)strlen(token); i++)
795 if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':')
797 memmove(token + i + 2, token + i + 1, strlen(token) - i + 1);
798 token[i + 1] = '0';
800 if(token[i] == '#' || token[i] == '/')
802 token[i] = '\0';
803 break;
807 type = 'm';
808 uint32_t caid = 0, provid = 0, srvid = 0, pid = 0, chid = 0, ecmlen = 0;
809 uint32_t to_caid = 0, to_provid = 0, to_srvid = 0, to_pid = 0, to_chid = 0, to_ecmlen = 0;
810 int32_t valid_from = -2000, valid_to = 4000;
812 ret = sscanf(token, "%c:%4x:%6x:%4x:%4x:%4x:%4X=%4x:%6x:%4x:%4x:%4x:%4X,%4d,%4d",
813 &type,
814 &caid, &provid, &srvid, &pid, &chid, &ecmlen,
815 &to_caid, &to_provid, &to_srvid, &to_pid, &to_chid, &to_ecmlen,
816 &valid_from, &valid_to);
818 type = tolower(type);
820 if(ret < 7 || type != 'm')
821 { continue; }
823 if(!cs_malloc(&entry, sizeof(struct s_cacheex_matcher)))
825 fclose(fp);
826 return new_cacheex_matcher;
828 count++;
829 entry->line = line;
830 entry->type = type;
831 entry->caid = caid;
832 entry->provid = provid;
833 entry->srvid = srvid;
834 entry->pid = pid;
835 entry->chid = chid;
836 entry->ecmlen = ecmlen;
837 entry->to_caid = to_caid;
838 entry->to_provid = to_provid;
839 entry->to_srvid = to_srvid;
840 entry->to_pid = to_pid;
841 entry->to_chid = to_chid;
842 entry->to_ecmlen = to_ecmlen;
843 entry->valid_from = valid_from;
844 entry->valid_to = valid_to;
846 cs_log_dbg(D_TRACE, "cacheex-matcher: %c: %04X@%06X:%04X:%04X:%04X:%02X = %04X@%06X:%04X:%04X:%04X:%02X valid %d/%d",
847 entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen,
848 entry->to_caid, entry->to_provid, entry->to_srvid, entry->to_pid, entry->to_chid, entry->to_ecmlen,
849 entry->valid_from, entry->valid_to);
851 if(!new_cacheex_matcher)
853 new_cacheex_matcher = entry;
854 last = new_cacheex_matcher;
856 else
858 last->next = entry;
859 last = entry;
863 if(count)
864 { cs_log("%d entries read from %s", count, cs_cacheex_matcher); }
866 fclose(fp);
868 return new_cacheex_matcher;
871 void cacheex_load_config_file(void)
873 struct s_cacheex_matcher *entry, *old_list;
875 old_list = cfg.cacheex_matcher;
876 cfg.cacheex_matcher = cacheex_matcher_read_int();
878 while(old_list)
880 entry = old_list->next;
881 NULLFREE(old_list);
882 old_list = entry;
888 CWCHECK get_cwcheck(ECM_REQUEST *er){
889 int32_t i;
890 int8_t mode = 0;
891 int16_t counter = 1;
893 for(i = 0; i < cfg.cacheex_cwcheck_tab.cwchecknum; i++)
895 CWCHECKTAB_DATA *d = &cfg.cacheex_cwcheck_tab.cwcheckdata[i];
897 if(i == 0 && d->caid <= 0)
899 mode = d->mode;
900 counter = d->counter;
901 continue; //check other, only valid for unset
904 if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1))
906 if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1)
908 if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1)
910 mode = d->mode;
911 counter = d->counter;
912 break;
919 //check for correct values
920 if(mode>2 || mode<0) mode=0;
921 if(counter<1) counter=1;
923 CWCHECK check_cw;
924 memset(&check_cw, 0, sizeof(CWCHECK));
925 check_cw.mode = mode;
926 check_cw.counter = counter;
928 return check_cw;
933 uint16_t get_cacheex_mode1_delay(ECM_REQUEST *er)
935 return caidvaluetab_get_value(&cfg.cacheex_mode1_delay_tab, er->caid, 0);
940 uint32_t get_cacheex_wait_time(ECM_REQUEST *er, struct s_client *cl)
942 int32_t i, dwtime = -1, awtime = -1;
944 for(i = 0; i < cfg.cacheex_wait_timetab.cevnum; i++)
946 CECSPVALUETAB_DATA *d = &cfg.cacheex_wait_timetab.cevdata[i];
948 if(i == 0 && d->caid <= 0)
950 dwtime = d->dwtime;
951 awtime = d->awtime;
952 continue; //check other, only valid for unset
955 if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1))
957 if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1)
959 if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1)
961 dwtime = d->dwtime;
962 awtime = d->awtime;
963 break;
970 if(awtime > 0 && (dwtime <= 0 || awtime==dwtime) ) //if awtime==dwtime useless check hitcache
972 return awtime;
974 if(cl == NULL)
976 if(dwtime < 0)
977 { dwtime = 0; }
978 return dwtime;
980 if(awtime > 0 || dwtime > 0)
982 //if found last in cache return dynwaittime else alwayswaittime
983 if(cacheex_check_hitcache(er,cl))
984 { return dwtime >= awtime ? dwtime : awtime; }
985 else
986 { return awtime > 0 ? awtime : 0; }
988 return 0;
992 int32_t chk_csp_ctab(ECM_REQUEST *er, CECSPVALUETAB *tab)
994 if(!er->caid || !tab->cevnum)
995 { return 1; } // nothing setup we add all
996 int32_t i;
997 for(i = 0; i < tab->cevnum; i++)
999 CECSPVALUETAB_DATA *d = &tab->cevdata[i];
1000 if(d->caid > 0)
1002 if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1))
1004 if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1)
1006 if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1)
1008 return 1;
1014 return 0;
1017 void cacheex_push_out(struct s_client *cl, ECM_REQUEST *er) {
1018 int32_t res = 0, stats = -1;
1019 struct s_reader *reader = cl->reader;
1020 struct s_module *module = get_module(cl);
1021 // cc-nodeid-list-check
1022 if(reader)
1024 if(reader->ph.c_cache_push_chk && !reader->ph.c_cache_push_chk(cl, er))
1025 return;
1026 res = reader->ph.c_cache_push(cl, er);
1027 stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0);
1029 else
1031 if(module->c_cache_push_chk && !module->c_cache_push_chk(cl, er))
1032 return;
1033 res = module->c_cache_push(cl, er);
1035 debug_ecm(D_CACHEEX, "pushed ECM %s to %s res %d stats %d", buf, username(cl), res, stats);
1036 cl->cwcacheexpush++;
1037 if(cl->account)
1038 { cl->account->cwcacheexpush++; }
1039 first_client->cwcacheexpush++;
1042 bool cacheex_check_queue_length(struct s_client *cl)
1044 // Avoid full running queues:
1045 if(ll_count(cl->joblist) <= 2000)
1046 return 0;
1048 cs_log_dbg(D_TRACE, "WARNING: job queue %s %s has more than 2000 jobs! count=%d, dropped!",
1049 cl->typ == 'c' ? "client" : "reader",
1050 username(cl), ll_count(cl->joblist));
1051 // Thread down???
1052 SAFE_MUTEX_LOCK(&cl->thread_lock);
1053 if(cl && !cl->kill && cl->thread && cl->thread_active)
1055 // Just test for invalid thread id:
1056 if(pthread_detach(cl->thread) == ESRCH)
1058 cl->thread_active = 0;
1059 cs_log_dbg(D_TRACE, "WARNING: %s %s thread died!",
1060 cl->typ == 'c' ? "client" : "reader", username(cl));
1063 SAFE_MUTEX_UNLOCK(&cl->thread_lock);
1064 return 1;
1067 void cacheex_mode1_delay(ECM_REQUEST *er)
1069 if(!er->cacheex_wait_time_expired
1070 && er->cacheex_mode1_delay
1071 && er->cacheex_reader_count > 0
1072 && !er->stage
1073 && er->rc >= E_UNHANDLED)
1075 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);
1076 request_cw_from_readers(er, 1); // setting stop_stage=1, we request only cacheex mode 1 readers. Others are requested at cacheex timeout!
1080 void cacheex_timeout(ECM_REQUEST *er)
1082 if(er->cacheex_wait_time_expired)
1083 return;
1084 er->cacheex_wait_time_expired = 1;
1085 if(er->rc >= E_UNHANDLED)
1087 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);
1088 //if check_cw mode=0, first try to get cw from cache without check counter!
1089 CWCHECK check_cw = get_cwcheck(er);
1090 if(!check_cw.mode)
1092 struct ecm_request_t *ecm=NULL;
1093 ecm = check_cache(er, er->client);
1094 if(ecm) //found in cache
1096 struct s_write_from_cache *wfc=NULL;
1097 if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache)))
1099 NULLFREE(ecm);
1100 return;
1102 wfc->er_new=er;
1103 wfc->er_cache=ecm;
1104 if(!add_job(er->client, ACTION_ECM_ANSWER_CACHE, wfc, sizeof(struct s_write_from_cache))) //write_ecm_answer_fromcache
1105 { NULLFREE(ecm); }
1106 return;
1109 //check if "normal" readers selected, if not send NOT FOUND!
1110 //cacheex1-client (having always no "normal" reader), or not-cacheex-1 client with no normal readers available (or filtered by LB)
1111 if( (er->reader_count + er->fallback_reader_count - er->cacheex_reader_count) <= 0 )
1113 if(!cfg.wait_until_ctimeout){
1114 er->rc = E_NOTFOUND;
1115 er->selected_reader = NULL;
1116 er->rcEx = 0;
1117 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);
1118 send_dcw(er->client, er);
1119 return;
1122 else
1124 if(er->stage < 2){
1125 debug_ecm(D_TRACE, "request for %s %s", username(er->client), buf);
1126 request_cw_from_readers(er, 0);
1132 #endif