#define MODULE_LOG_PREFIX "cccam" #include "globals.h" #include "oscam-array.h" #if defined(CS_CACHEEX) && defined(MODULE_CCCAM) #include "module-cacheex.h" #include "module-cccam-data.h" #include "module-cccam-cacheex.h" #include "oscam-cache.h" #include "oscam-client.h" #include "oscam-ecm.h" #include "oscam-string.h" #include "oscam-chk.h" #include "oscam-reader.h" #ifdef CS_CACHEEX_AIO #include "oscam-chk.h" #include "oscam-config.h" #endif #define CSP_HASH_SWAP(n) (((((uint32_t)(n) & 0xFF)) << 24) | \ ((((uint32_t)(n) & 0xFF00)) << 8) | \ ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ ((((uint32_t)(n) & 0xFF000000)) >> 24)) extern int32_t cc_cli_connect(struct s_client *cl); extern int32_t cc_cmd_send(struct s_client *cl, uint8_t *buf, int32_t len, cc_msg_type_t cmd); #ifdef CS_CACHEEX_AIO void cc_cacheex_feature_trigger_in(struct s_client *cl, uint8_t *buf) { int32_t feature = 0; int i = 0; uint8_t filter_count; uint8_t j, k, l, rc; feature = buf[1] | (buf[0] << 8); FTAB *lgonly_tab; // check client & cacheex-mode if( !check_client(cl) || !( (cl->typ == 'c' && cl->account->cacheex.mode > 0) || (cl->typ == 'p' && cl->reader->cacheex.mode > 0) ) ) { return; } switch(feature) { // set localgenerated only case 1: if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) { if(cfg.cacheex_lg_only_remote_settings || cl->account->cacheex.lg_only_remote_settings) cl->account->cacheex.localgenerated_only = buf[4]; else if(buf[4]) cl->account->cacheex.localgenerated_only = buf[4]; } else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) { if(cfg.cacheex_lg_only_remote_settings || cl->reader->cacheex.lg_only_remote_settings) cl->reader->cacheex.localgenerated_only = buf[4]; else if(buf[4]) cl->reader->cacheex.localgenerated_only = buf[4]; } break; // set localgenerated only caidtab case 2: filter_count = buf[i+4]; i += 5; memset(&lgonly_tab, 0, sizeof(lgonly_tab)); if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) { lgonly_tab = &cl->account->cacheex.lg_only_tab; } else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) { lgonly_tab = &cl->reader->cacheex.lg_only_tab; } else { return; } // remotesettings enabled - replace local settings if(cfg.cacheex_lg_only_remote_settings || ( (cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.lg_only_remote_settings) || (cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.lg_only_remote_settings) ) ) { ftab_clear(lgonly_tab); for(j = 0; j < filter_count; j++) { FILTER d; memset(&d, 0, sizeof(d)); d.caid = b2i(2, buf + i); i += 2; d.nprids = 1; d.prids[0] = NO_PROVID_VALUE; ftab_add(lgonly_tab, &d); } } // remotesettings disabled - write additional remote-caids received else { for(j = 0; j < filter_count; j++) { FILTER d; memset(&d, 0, sizeof(d)); d.caid = b2i(2, buf + i); i += 2; d.nprids = 1; d.prids[0] = NO_PROVID_VALUE; if(!chk_lg_only_cp(d.caid, d.prids[0], lgonly_tab)) { cs_log_dbg(D_CACHEEX, "%04X:%06X not found in local settings - adding them", d.caid, d.prids[0]); for(l = rc = 0; (!rc) && (l < lgonly_tab->nfilts); l++) { if(lgonly_tab->filts[l].caid == d.caid) { rc = 1; if(lgonly_tab->filts[l].nprids+1 <= CS_MAXPROV) { lgonly_tab->filts[l].prids[lgonly_tab->filts[l].nprids] = d.prids[0]; lgonly_tab->filts[l].nprids++; } else { cs_log_dbg(D_CACHEEX, "error: cacheex_lg_only_tab -> max. number(%i) of providers reached", CS_MAXPROV); } } } if(!rc) { ftab_add(lgonly_tab, &d); } } } } break; // set cacheex_ecm_filter - extended case 4: filter_count = buf[i+4]; i += 5; CECSPVALUETAB *filter; memset(&filter, 0, sizeof(filter)); if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.allow_filter) { filter = &cl->account->cacheex.filter_caidtab; } else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.allow_filter) { filter = &cl->reader->cacheex.filter_caidtab; } else { return; } cecspvaluetab_clear(filter); for(j = 0; j < filter_count; j++) { int32_t caid = -1, cmask = -1, provid = -1, srvid = -1; CECSPVALUETAB_DATA d; memset(&d, 0, sizeof(d)); caid = b2i(2, buf + i); if(caid == 0xFFFF) caid = -1; i += 2; cmask = b2i(2, buf + i); if(cmask == 0xFFFF) cmask = -1; i += 2; provid = b2i(3, buf + i); if(provid == 0xFFFFFF) provid = -1; i += 3; srvid = b2i(2, buf + i); if(srvid == 0xFFFF) srvid = -1; i += 2; if(caid > 0) { d.caid = caid; d.cmask = cmask; d.prid = provid; d.srvid = srvid; cecspvaluetab_add(filter, &d); } } break; // no push after case 8: ; CAIDVALUETAB *ctab; memset(&ctab, 0, sizeof(ctab)); if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) { ctab = &cl->account->cacheex.cacheex_nopushafter_tab; } else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) { ctab = &cl->reader->cacheex.cacheex_nopushafter_tab; } else { return; } filter_count = buf[i+4]; i += 5; caidvaluetab_clear(ctab); for(j = 0; j < filter_count; j++) { uint16_t caid = 0, value = 0; CAIDVALUETAB_DATA d; memset(&d, 0, sizeof(d)); caid = b2i(2, buf + i); if(caid == 0xFFFF) caid = -1; i += 2; value = b2i(2, buf + i); if(value == 0xFFFF) value = -1; i += 2; if(caid > 0) { d.caid = caid; d.value = value; caidvaluetab_add(ctab, &d); } } break; // max_hop case 16: if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.allow_maxhop) { cl->account->cacheex.maxhop = buf[4]; cl->account->cacheex.maxhop_lg = buf[5]; } else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.allow_maxhop) { cl->reader->cacheex.maxhop = buf[4]; cl->reader->cacheex.maxhop_lg = buf[5]; } break; // aio-version case 32: ; uint16_t payload_size = b2i(2, buf + i + 2); if(cl->typ == 'c' && cl->account->cacheex.mode > 0) { char *ofs = (char *)buf + i + 4; memset(cl->account->cacheex.aio_version, 0, sizeof(cl->account->cacheex.aio_version)); if(payload_size > 0) { size_t str_len = strnlen(ofs, payload_size); if(str_len >= sizeof(cl->account->cacheex.aio_version)) str_len = sizeof(cl->account->cacheex.aio_version) - 1; memcpy(cl->account->cacheex.aio_version, ofs, str_len); // sanitize: remove non-printable characters size_t x; for(x = 0; x < str_len; x++) { if(cl->account->cacheex.aio_version[x] < 0x20 || cl->account->cacheex.aio_version[x] > 0x7E) { cl->account->cacheex.aio_version[x] = '\0'; break; } } } } else if(cl->typ == 'p' && cl->reader->cacheex.mode > 0) { char *ofs = (char *)buf + i + 4; memset(cl->reader->cacheex.aio_version, 0, sizeof(cl->reader->cacheex.aio_version)); if(payload_size > 0) { size_t str_len = strnlen(ofs, payload_size); if(str_len >= sizeof(cl->reader->cacheex.aio_version)) str_len = sizeof(cl->reader->cacheex.aio_version) - 1; memcpy(cl->reader->cacheex.aio_version, ofs, str_len); // sanitize: remove non-printable characters size_t x; for(x = 0; x < str_len; x++) { if(cl->reader->cacheex.aio_version[x] < 0x20 || cl->reader->cacheex.aio_version[x] > 0x7E) { cl->reader->cacheex.aio_version[x] = '\0'; break; } } } } break; // lg_only_tab caid:prov1[,provN][;caid:prov] case 64: ; memset(&lgonly_tab, 0, sizeof(lgonly_tab)); if(cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1)) { lgonly_tab = &cl->account->cacheex.lg_only_tab; } else if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) { lgonly_tab = &cl->reader->cacheex.lg_only_tab; } else { return; } filter_count = buf[i+4]; i += 5; // remotesettings enabled - replace local settings if(cfg.cacheex_lg_only_remote_settings || ( (cl->typ == 'c' && (cl->account->cacheex.mode == 2 || cl->account->cacheex.mode == 1) && cl->account->cacheex.lg_only_remote_settings) || (cl->typ == 'p' && cl->reader->cacheex.mode == 3 && cl->reader->cacheex.lg_only_remote_settings) || !lgonly_tab->nfilts ) ) { ftab_clear(lgonly_tab); for(j = 0; j < filter_count; j++) { FILTER d; memset(&d, 0, sizeof(d)); d.caid = b2i(2, buf + i); i += 2; d.nprids = b2i(1, buf + i); i += 1; for(k=0; k < d.nprids; k++) { d.prids[k] = b2i(3, buf + i); i += 3; } ftab_add(lgonly_tab, &d); } } // remotesettings disabled - write additional remote-caid/provids received else { for(j = 0; j < filter_count; j++) { FILTER d; memset(&d, 0, sizeof(d)); d.caid = b2i(2, buf + i); i += 2; d.nprids = b2i(1, buf + i); i += 1; for(k=0; k < d.nprids; k++) { d.prids[k] = b2i(3, buf + i); i += 3; if(!chk_ident_filter(d.caid, d.prids[k], lgonly_tab)) { cs_log_dbg(D_CACHEEX, "%04X:%06X not found in local settings - adding them", d.caid, d.prids[k]); for(l = rc = 0; (!rc) && (l < lgonly_tab->nfilts); l++) { if(lgonly_tab->filts[l].caid == d.caid) { rc = 1; if(lgonly_tab->filts[l].nprids+1 <= CS_MAXPROV) { lgonly_tab->filts[l].prids[lgonly_tab->filts[l].nprids] = d.prids[k]; lgonly_tab->filts[l].nprids++; } else { cs_log_dbg(D_CACHEEX, "error: cacheex_lg_only_tab -> max. number of providers reached"); } } } if(!rc) { ftab_add(lgonly_tab, &d); } } } } } break; default: return; } } void cc_cacheex_feature_trigger(struct s_client *cl, int32_t feature, uint8_t mode) { // size: (feature-bitfield & mask: 2) + payload-size: 2 + feature-payload :x uint16_t size = 2 + 2; int i = 0; uint8_t j; uint8_t payload[MAX_ECM_SIZE-size]; memset(payload, 0, sizeof(payload)); // check client & cacheex-mode if(!check_client(cl)) { return; } switch(feature) { FTAB *lgonly_tab; // set localgenerated only case 1: // bitfield i2b_buf(2, feature, payload + i); i += 2; // payload-size i2b_buf(2, 1, payload + i); i += 2; size += 1; // set payload if(mode == 2) { if(cl->reader->cacheex.localgenerated_only_in) payload[i] = cl->reader->cacheex.localgenerated_only_in; else payload[i] = cfg.cacheex_localgenerated_only_in; } else if(mode == 3) { if(cl->account->cacheex.localgenerated_only_in) payload[i] = cl->account->cacheex.localgenerated_only_in; else payload[i] = cfg.cacheex_localgenerated_only_in; } break; // set localgenerated only caidtab; cx-aio < 9.2.6-04 case 2: ; if(mode == 2) { lgonly_tab = &cl->reader->cacheex.lg_only_in_tab; if(!lgonly_tab->nfilts) lgonly_tab = &cfg.cacheex_lg_only_in_tab; } else if(mode == 3) { lgonly_tab = &cl->account->cacheex.lg_only_in_tab; if(!lgonly_tab->nfilts) lgonly_tab = &cfg.cacheex_lg_only_in_tab; } else { return; } size += (lgonly_tab->nfilts * 2 + 1); if(size < 32) size = 32; // bitfield i2b_buf(2, feature, payload + i); i += 2; // payload-size if((lgonly_tab->nfilts * 2 + 1) > (int)sizeof(payload)) { cs_log_dbg(D_CACHEEX, "ERROR: too much localgenerated only caidtab-entries (max. 255)"); return; } i2b_buf(2, (lgonly_tab->nfilts * 2 + 1), payload + i); // n * caid + ctnum i += 2; // set payload if(lgonly_tab->nfilts > 255) { cs_log_dbg(D_CACHEEX, "ERROR: too much localgenerated only caidtab-entries (max. 255)"); return; } payload[i] = lgonly_tab->nfilts; i += 1; for(j = 0; j < lgonly_tab->nfilts; j++) { FILTER *d = &lgonly_tab->filts[j]; if(d->caid) { i2b_buf(2, d->caid, payload + i); i += 2; } else { continue; } } break; // cacchex_ecm_filter extendend case 4: ; CECSPVALUETAB *filter; if(mode == 2) { filter = &cl->reader->cacheex.filter_caidtab; // if not set, use global settings if(cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) filter = &cfg.cacheex_filter_caidtab; // if aio, use global aio settings if(cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && cl->cacheex_aio_checked && (cl->reader->cacheex.feature_bitfield & 4)) filter = &cfg.cacheex_filter_caidtab_aio; } else if(mode == 3) { filter = &cl->account->cacheex.filter_caidtab; // if not set, use global settings if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) filter = &cfg.cacheex_filter_caidtab; // if aio, use global aio settings if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && cl->cacheex_aio_checked && (cl->account->cacheex.feature_bitfield & 4)) filter = &cfg.cacheex_filter_caidtab_aio; } else { return; } size += (filter->cevnum * 9 + 1); // bitfield i2b_buf(2, feature, payload + i); i += 2; // payload-size if((filter->cevnum * 9 + 1) > (int)sizeof(payload)) { cs_log_dbg(D_CACHEEX, "ERROR: to much cacheex_ecm_filter-entries (max. 63), only 30 default cccam-filters sent"); return; } i2b_buf(2, (filter->cevnum * 9 + 1), payload + i); // n * (caid2,mask2,provid3,srvid2) + ctnum1 i += 2; // set payload payload[i] = filter->cevnum; i += 1; for(j = 0; j < filter->cevnum; j++) { CECSPVALUETAB_DATA *d = &filter->cevdata[j]; if(d->caid) { i2b_buf(2, d->caid, payload + i); i += 2; } if(d->cmask) { i2b_buf(2, d->cmask, payload + i); } i += 2; if(d->prid) { i2b_buf(3, d->prid, payload + i); } i += 3; if(d->srvid) { i2b_buf(2, d->srvid, payload + i); } i += 2; } break; // no push after case 8: ; CAIDVALUETAB *ctab; if(mode == 2) { ctab = &cl->reader->cacheex.cacheex_nopushafter_tab; if(!ctab->cvnum) ctab = &cfg.cacheex_nopushafter_tab; } else if(mode == 3) { ctab = &cl->account->cacheex.cacheex_nopushafter_tab; if(!ctab->cvnum) ctab = &cfg.cacheex_nopushafter_tab; } else { return; } size += (ctab->cvnum * 4 + 1); // bitfield i2b_buf(2, feature, payload + i); i += 2; // payload-size if((ctab->cvnum * 4 + 1) > (int)sizeof(payload)) { cs_log_dbg(D_CACHEEX, "ERROR: to much no push after caidtvalueab-entries (max. 255)"); return; } i2b_buf(2, (ctab->cvnum * 4 + 1), payload + i); // n * (caid2+value2) + cvnum i += 2; // set payload if(ctab->cvnum > 255) { cs_log_dbg(D_CACHEEX, "ERROR: to much no push after caidtvalueab-entries (max. 255)"); return; } payload[i] = ctab->cvnum; i += 1; for(j = 0; j < ctab->cvnum; j++) { CAIDVALUETAB_DATA *d = &ctab->cvdata[j]; if(d->caid) { i2b_buf(2, d->caid, payload + i); i += 2; i2b_buf(2, d->value, payload + i); i += 2; } else { continue; } } break; // max hop case 16: // bitfield i2b_buf(2, feature, payload + i); i += 2; // payload-size i2b_buf(2, 2, payload + i); i += 2; size += 2; // set payload if(mode == 2) { if(cl->reader->cacheex.maxhop) payload[i] = cl->reader->cacheex.maxhop; else payload[i] = 0; i += 1; if(cl->reader->cacheex.maxhop_lg) payload[i] = cl->reader->cacheex.maxhop_lg; else payload[i] = 0; } else if(mode == 3) { if(cl->account->cacheex.maxhop) payload[i] = cl->account->cacheex.maxhop; else payload[i] = 0; i += 1; if(cl->account->cacheex.maxhop_lg) payload[i] = cl->account->cacheex.maxhop_lg; else payload[i] = 0; } break; // aio-version case 32: ; uint8_t token[CS_AIO_VERSION_LEN]; memset(token, 0, sizeof(token)); // bitfield i2b_buf(2, feature, payload + i); i += 2; // payload-size i2b_buf(2, sizeof(token), payload + i); i += 2; size +=sizeof(token); // set payload snprintf((char *)token, sizeof(token), "%s", CS_AIO_VERSION); uint8_t *ofs = payload + i; memcpy(ofs, token, sizeof(token)); break; // lg_only_tab case 64: ; // bitfield i2b_buf(2, feature, payload + i); i += 2; if(mode == 2) { lgonly_tab = &cl->reader->cacheex.lg_only_in_tab; if(!lgonly_tab->nfilts) lgonly_tab = &cfg.cacheex_lg_only_in_tab; } else if(mode == 3) { lgonly_tab = &cl->account->cacheex.lg_only_in_tab; if(!lgonly_tab->nfilts) lgonly_tab = &cfg.cacheex_lg_only_in_tab; } else { return; } char *cx_aio_ftab; cx_aio_ftab = cxaio_ftab_to_buf(lgonly_tab); if(cs_strlen(cx_aio_ftab) > 0 && cx_aio_ftab[0] != '\0') { size += cs_strlen(cx_aio_ftab) * sizeof(char); // payload-size i2b_buf(2, cs_strlen(cx_aio_ftab), payload + i); i += 2; // filter counter payload[i] = lgonly_tab->nfilts; i += 1; for(j=0; jtyp == 'c' && (cl->account->cacheex.mode == 2 ||cl->account->cacheex.mode == 1)) { cl->account->cacheex.feature_bitfield = field; // flag 32 => aio-version if(cl->account->cacheex.feature_bitfield & 32) { cc_cacheex_feature_trigger(cl, 32, 2); } } if(cl->typ == 'p' && cl->reader->cacheex.mode == 3) { cl->reader->cacheex.feature_bitfield = field; // flag 32 => aio-version if(cl->reader->cacheex.feature_bitfield & 32) { cc_cacheex_feature_trigger(cl, 32, 3); } } if(cl->typ == 'c' && cl->account->cacheex.mode == 3) { struct s_auth *acc = cl->account; if(acc) { acc->cacheex.feature_bitfield = field; // process feature-specific actions based on feature_bitfield received // flag 1 => set localgenerated only flag if(acc->cacheex.feature_bitfield & 1) { cc_cacheex_feature_trigger(cl, 1, 3); } // flag 2 => set localgenerated only caids flag if(acc->cacheex.feature_bitfield & 2 && !(acc->cacheex.feature_bitfield & 64)) { cc_cacheex_feature_trigger(cl, 2, 3); } // flag 4 => set cacheex_ecm_filter (extended) if(acc->cacheex.feature_bitfield & 4) { cc_cacheex_feature_trigger(cl, 4, 3); } // flag 8 => np push after caids if(acc->cacheex.feature_bitfield & 8) { cc_cacheex_feature_trigger(cl, 8, 3); } // flag 16 => maxhop if(acc->cacheex.feature_bitfield & 16) { cc_cacheex_feature_trigger(cl, 16, 3); } // flag 32 => aio-version if(acc->cacheex.feature_bitfield & 32) { cc_cacheex_feature_trigger(cl, 32, 3); } // flag 64 => lg_only_tab if(acc->cacheex.feature_bitfield & 64) { cc_cacheex_feature_trigger(cl, 64, 3); } } else { cs_log_dbg(D_CACHEEX, "feature_bitfield save failed - cl, %s", username(cl)); } } else if(cl->typ == 'p' && (cl->reader->cacheex.mode == 2 || cl->reader->cacheex.mode == 1)) { struct s_reader *rdr = cl->reader; if(rdr) { rdr->cacheex.feature_bitfield = field; // process feature-specific actions // flag 1 => set localgenerated_only; cause of rdr->cacheex.localgenerated_only_in is set if(rdr->cacheex.feature_bitfield & 1) { cc_cacheex_feature_trigger(cl, 1, 2); } // flag 2 => set localgenerated_only_caidtab; cause of rdr->cacheex.localgenerated_only_in_caidtab is set if(rdr->cacheex.feature_bitfield & 2 && !(rdr->cacheex.feature_bitfield & 64)) { cc_cacheex_feature_trigger(cl, 2, 2); } // flag 4 => set cacchex_ecm_filter extendend if(rdr->cacheex.feature_bitfield & 4) { cc_cacheex_feature_trigger(cl, 4, 2); } // flag 8 => np push after caids if(rdr->cacheex.feature_bitfield & 8) { cc_cacheex_feature_trigger(cl, 8, 2); } // flag 16 => maxhop if(rdr->cacheex.feature_bitfield & 16) { cc_cacheex_feature_trigger(cl, 16, 2); } // flag 32 => aio-version if(rdr->cacheex.feature_bitfield & 32) { cc_cacheex_feature_trigger(cl, 32, 2); } // flag 64 => lg_only_tab if(rdr->cacheex.feature_bitfield & 64) { cc_cacheex_feature_trigger(cl, 64, 2); } } else { cs_log_dbg(D_CACHEEX, "feature_bitfield save failed - rdr, %s", username(cl)); } } } void cc_cacheex_feature_request_reply(struct s_client *cl) { int32_t size = 2; uint8_t rbuf[size]; i2b_buf(2, CACHEEX_FEATURES, rbuf); cc_cmd_send(cl, rbuf, size, MSG_CACHE_FEATURE_EXCHANGE_REPLY); } void cc_cacheex_feature_request(struct s_client *cl) { int32_t size = 2; uint8_t rbuf[2]; i2b_buf(2, CACHEEX_FEATURES, rbuf); cc_cmd_send(cl, rbuf, size, MSG_CACHE_FEATURE_EXCHANGE); } #endif void cc_cacheex_filter_out(struct s_client *cl) { struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; int i = 0, j; CECSPVALUETAB *filter; int32_t size = 482; // minimal size, keep it <= 512 for max UDP packet size without fragmentation uint8_t buf[482]; memset(buf, 0, sizeof(buf)); if(rdr && (rdr->cacheex.mode == 2 #ifdef CS_CACHEEX_AIO || rdr->cacheex.mode == 1 #endif )) // mode == 2 send filters from rdr { filter = &rdr->cacheex.filter_caidtab; #ifdef CS_CACHEEX_AIO // if not set, use global settings if(rdr->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) filter = &cfg.cacheex_filter_caidtab; #endif } else if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3) // mode == 3 send filters from acc { filter = &cl->account->cacheex.filter_caidtab; #ifdef CS_CACHEEX_AIO // if not set, use global settings if(cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0) filter = &cfg.cacheex_filter_caidtab; #endif } else { return; } i2b_buf(2, filter->cevnum, buf + i); i += 2; int32_t max_filters = 30; for(j = 0; j < max_filters; j++) { if(filter->cevnum > j) { CECSPVALUETAB_DATA *d = &filter->cevdata[j]; i2b_buf(4, d->caid, buf + i); } i += 4; } for(j = 0; j < max_filters; j++) { if(filter->cevnum > j) { CECSPVALUETAB_DATA *d = &filter->cevdata[j]; i2b_buf(4, d->cmask, buf + i); } i += 4; } for(j = 0; j < max_filters; j++) { if(filter->cevnum > j) { CECSPVALUETAB_DATA *d = &filter->cevdata[j]; i2b_buf(4, d->prid, buf + i); } i += 4; } for(j = 0; j < max_filters; j++) { if(filter->cevnum > j) { CECSPVALUETAB_DATA *d = &filter->cevdata[j]; i2b_buf(4, d->srvid, buf + i); } i += 4; } cs_log_dbg(D_CACHEEX, "cacheex: sending push filter request to %s", username(cl)); cc_cmd_send(cl, buf, size, MSG_CACHE_FILTER); } void cc_cacheex_filter_in(struct s_client *cl, uint8_t *buf) { struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; int i = 0, j; int32_t caid, cmask, provid, srvid; CECSPVALUETAB *filter; // mode == 2 write filters to acc if(cl->typ == 'c' && cl->account && (cl->account->cacheex.mode == 2 #ifdef CS_CACHEEX_AIO || cl->account->cacheex.mode == 1 #endif ) && cl->account->cacheex.allow_filter == 1) { filter = &cl->account->cacheex.filter_caidtab; } else if(rdr && rdr->cacheex.mode == 3 && rdr->cacheex.allow_filter == 1) // mode == 3 write filters to rdr { filter = &rdr->cacheex.filter_caidtab; } else { return; } cecspvaluetab_clear(filter); i += 2; int32_t max_filters = 30; for(j = 0; j < max_filters; j++) { caid = b2i(4, buf + i); if(caid > 0) { CECSPVALUETAB_DATA d; memset(&d, 0, sizeof(d)); d.caid = b2i(4, buf + i); cecspvaluetab_add(filter, &d); } i += 4; } for(j = 0; j < max_filters; j++) { cmask = b2i(4, buf + i); if(j < filter->cevnum) { CECSPVALUETAB_DATA *d = &filter->cevdata[j]; d->cmask = cmask; } i += 4; } for(j = 0; j < max_filters; j++) { provid = b2i(4, buf + i); if(j < filter->cevnum) { CECSPVALUETAB_DATA *d = &filter->cevdata[j]; d->prid = provid; } i += 4; } for(j = 0; j < max_filters; j++) { srvid = b2i(4, buf + i); if(j < filter->cevnum) { CECSPVALUETAB_DATA *d = &filter->cevdata[j]; d->srvid = srvid; } i += 4; } cs_log_dbg(D_CACHEEX, "cacheex: received push filter request from %s", username(cl)); } static int32_t cc_cacheex_push_chk(struct s_client *cl, struct ecm_request_t *er) { struct cc_data *cc = cl->cc; if(chk_is_null_nodeid(cc->peer_node_id)) { cs_log_dbg(D_CACHEEX, "cacheex: NO peer_node_id got yet, skip!"); return 0; } if( ll_count(er->csp_lastnodes) >= cacheex_maxhop(cl) // check max 10 nodes to push #ifdef CS_CACHEEX_AIO && (!er->localgenerated || (er->localgenerated && (ll_count(er->csp_lastnodes) >= cacheex_maxhop_lg(cl)))) // check maxhop_lg if cw is lg-flagged #endif ) { #ifdef CS_CACHEEX_AIO cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes(non-lg) or reached %d nodes(lg), no push", cacheex_maxhop(cl), cacheex_maxhop_lg(cl)); #else cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes, no push", cacheex_maxhop(cl)); #endif return 0; } uint8_t *remote_node = cc->peer_node_id; // search existing peer nodes LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); uint8_t *node; while((node = ll_li_next(li))) { cs_log_dbg(D_CACHEEX, "cacheex: check node %" PRIu64 "X == %" PRIu64 "X ?", cacheex_node_id(node), cacheex_node_id(remote_node)); if(memcmp(node, remote_node, 8) == 0) { break; } } ll_li_destroy(li); // node found, so we got it from there, do not push if(node) { cs_log_dbg(D_CACHEEX, "cacheex: node %" PRIu64 "X found in list => skip push!", cacheex_node_id(node)); return 0; } if(!cl->cc) { if(cl->reader && !cl->reader->tcp_connected) { cc_cli_connect(cl); } } if(!cc || !cl->udp_fd) { cs_log_dbg(D_CACHEEX, "cacheex: not connected %s -> no push", username(cl)); return 0; } // check if cw is already pushed if(check_is_pushed(er->cw_cache, cl)) { return 0; } return 1; } static int32_t cc_cacheex_push_out(struct s_client *cl, struct ecm_request_t *er) { int8_t rc = (er->rc < E_NOTFOUND) ? E_FOUND : er->rc; if(rc != E_FOUND && rc != E_UNHANDLED) { return -1; // Maybe later we could support other rcs } if(cl->reader) { if(!cl->reader->tcp_connected) { cc_cli_connect(cl); } } struct cc_data *cc = cl->cc; if(!cc || !cl->udp_fd) { cs_log_dbg(D_CACHEEX, "cacheex: not connected %s -> no push", username(cl)); return (-1); } uint32_t size = sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw) + sizeof(uint8_t) + #ifdef CS_CACHEEX_AIO (ll_count(er->csp_lastnodes) + 1) * 8 + sizeof(uint8_t); #else (ll_count(er->csp_lastnodes) + 1) * 8; #endif uint8_t *buf; if(!cs_malloc(&buf, size + 20)) // camd35_send() adds +20 { return -1; } // build ecm message //buf[0] = er->caid >> 8; //buf[1] = er->caid & 0xff; //buf[2] = er->prid >> 24; //buf[3] = er->prid >> 16; //buf[4] = er->prid >> 8; //buf[5] = er->prid & 0xff; //buf[10] = er->srvid >> 8; //buf[11] = er->srvid & 0xff; buf[12] = (sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw)) & 0xff; buf[13] = (sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw)) >> 8; //buf[12] = 0; //buf[13] = 0; buf[14] = rc; i2b_buf(2, er->caid, buf + 0); i2b_buf(4, er->prid, buf + 2); i2b_buf(2, er->srvid, buf + 10); if(er->cwc_cycletime && er->cwc_next_cw_cycle < 2) { buf[18] = er->cwc_cycletime; // contains cwc stage3 cycletime if(er->cwc_next_cw_cycle == 1) { buf[18] = (buf[18] | 0x80); // set bit 8 to high } if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) { cl->account->cwc_info++; } else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) { cl->cwc_info++; } cs_log_dbg(D_CWC, "CWC (CE) push to %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); } buf[19] = er->ecm[0] != 0x80 && er->ecm[0] != 0x81 ? 0 : er->ecm[0]; uint8_t *ofs = buf + 20; // write oscam ecmd5 memcpy(ofs, er->ecmd5, sizeof(er->ecmd5)); // 16 ofs += sizeof(er->ecmd5); // write csp hashcode i2b_buf(4, CSP_HASH_SWAP(er->csp_hash), ofs); ofs += 4; // write cw memcpy(ofs, er->cw, sizeof(er->cw)); // 16 ofs += sizeof(er->cw); // write node count *ofs = ll_count(er->csp_lastnodes) + 1; ofs++; // write own node memcpy(ofs, cc->node_id, 8); ofs += 8; // write other nodes LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); uint8_t *node; while((node = ll_li_next(li))) { memcpy(ofs, node, 8); ofs += 8; } ll_li_destroy(li); #ifdef CS_CACHEEX_AIO // add localgenerated cw-flag if(er->localgenerated) { *ofs = 1; } else { *ofs = 0xFF; } #endif int32_t res = cc_cmd_send(cl, buf, size + 20, MSG_CACHE_PUSH); if(res > 0) // cache-ex is pushing out, so no receive but last_g should be updated otherwise disconnect! { if(cl->reader) { cl->reader->last_s = cl->reader->last_g = time((time_t *)0); // correct } if(cl) { cl->last = time(NULL); } } NULLFREE(buf); return res; } void cc_cacheex_push_in(struct s_client *cl, uint8_t *buf) { struct cc_data *cc = cl->cc; ECM_REQUEST *er; if(!cc) { return; } if(cl->reader) { cl->reader->last_s = cl->reader->last_g = time((time_t *)0); } if(cl) { cl->last = time(NULL); } int8_t rc = buf[14]; if(rc != E_FOUND && rc != E_UNHANDLED) // Maybe later we could support other rcs { return; } uint16_t size = buf[12] | (buf[13] << 8); if(size != sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw)) { cs_log_dbg(D_CACHEEX, "cacheex: %s received old cash-push format! data ignored!", username(cl)); return; } if(!(er = get_ecmtask())) { return; } er->caid = b2i(2, buf + 0); er->prid = b2i(4, buf + 2); er->srvid = b2i(2, buf + 10); er->ecm[0] = buf[19] != 0x80 && buf[19] != 0x81 ? 0 : buf[19]; // odd/even byte, usefull to send it over CSP and to check cw for swapping er->rc = rc; er->ecmlen = 0; if(buf[18]) { if(buf[18] & (0x01 << 7)) { er->cwc_cycletime = (buf[18] & 0x7F); // remove bit 8 to get cycletime er->cwc_next_cw_cycle = 1; } else { er->cwc_cycletime = buf[18]; er->cwc_next_cw_cycle = 0; } } #ifndef CS_CACHEEX_AIO if (er->cwc_cycletime && er->cwc_next_cw_cycle < 2) { if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) { cl->account->cwc_info++; } else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) { cl->cwc_info++; } cs_log_dbg(D_CWC, "CWC (CE) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); } #endif uint8_t *ofs = buf + 20; // Read ecmd5 memcpy(er->ecmd5, ofs, sizeof(er->ecmd5)); // 16 ofs += sizeof(er->ecmd5); if(!check_cacheex_filter(cl, er)) { return; } #ifdef CS_CACHEEX_AIO // check cacheex_ecm_filter if(check_client(cl) && cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 && ( (cl->reader->cacheex.filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cl->reader->cacheex.filter_caidtab)) // reader cacheex_ecm_filter not matching if set || (cl->reader->cacheex.filter_caidtab.cevnum == 0 && (cl->reader->cacheex.feature_bitfield & 4) && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab_aio)) // global cacheex_ecm_filter_aio not matching if set || (cl->reader->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab)) // global cacheex_ecm_filter not matching if set ) ) { cs_log_dbg(D_CACHEEX, "cacheex: received cache not matching cacheex_ecm_filter => pushing filter again"); cc_cacheex_filter_out(cl); // get cache != cacheex_ecm_filter, send filter again - remote restarted if(cl->reader->cacheex.feature_bitfield & 4) cc_cacheex_feature_trigger(cl, 4, 2); free_push_in_ecm(er); return; } if(check_client(cl) && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 && ( (cl->account->cacheex.filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cl->account->cacheex.filter_caidtab)) // account cacheex_ecm_filter not matching if set || (cl->account->cacheex.filter_caidtab.cevnum == 0 && (cl->account->cacheex.feature_bitfield & 4) && cfg.cacheex_filter_caidtab_aio.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab_aio)) // global cacheex_ecm_filter_aio not matching if set || (cl->account->cacheex.filter_caidtab.cevnum == 0 && cfg.cacheex_filter_caidtab_aio.cevnum == 0 && cfg.cacheex_filter_caidtab.cevnum > 0 && !chk_csp_ctab(er, &cfg.cacheex_filter_caidtab)) // global cacheex_ecm_filter not matching if set ) ) { cs_log_dbg(D_CACHEEX, "cacheex: received cache not matching cacheex_ecm_filter => pushing filter again"); cc_cacheex_filter_out(cl); // get cache != cacheex_ecm_filter, send filter again - remote restarted if(cl->account->cacheex.feature_bitfield & 4) cc_cacheex_feature_trigger(cl, 4, 3); free_push_in_ecm(er); return; } #endif // Read csp_hash er->csp_hash = CSP_HASH_SWAP(b2i(4, ofs)); ofs += 4; // Read cw memcpy(er->cw, ofs, sizeof(er->cw)); // 16 ofs += sizeof(er->cw); // Read lastnode count uint8_t count = *ofs; ofs++; #ifndef CS_CACHEEX_AIO // check max nodes if(count > cacheex_maxhop(cl)) { cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", (int32_t)count, cacheex_maxhop(cl), username(cl)); NULLFREE(er); return; } #endif cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes %s", (int32_t)count, username(cl)); // Read lastnodes uint8_t *data; if (er) { er->csp_lastnodes = ll_create("csp_lastnodes"); } while(count) { if(!cs_malloc(&data, 8)) { break; } memcpy(data, ofs, 8); ofs += 8; ll_append(er->csp_lastnodes, data); count--; cs_log_dbg(D_CACHEEX, "cacheex: received node %" PRIu64 "X %s", cacheex_node_id(data), username(cl)); } #ifdef CS_CACHEEX_AIO if(b2i(1, ofs) == 1) { er->localgenerated = 1; cs_log_dbg(D_CACHEEX, "cacheex: received ECM with localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); //check max nodes for lg flagged cw: if(ll_count(er->csp_lastnodes) > cacheex_maxhop_lg(cl)) { cs_log_dbg(D_CACHEEX, "cacheex: received (lg) %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop_lg(cl), username(cl)); free_push_in_ecm(er); return; } } // without localgenerated flag else { //check max nodes: if(ll_count(er->csp_lastnodes) > cacheex_maxhop(cl)) { cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop(cl), username(cl)); free_push_in_ecm(er); return; } if( (cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 && !chk_srvid_localgenerated_only_exception(er) // cx2 && ( // !aio (cl->cacheex_aio_checked && !cl->reader->cacheex.feature_bitfield && ( !cfg.cacheex_lg_only_in_aio_only && !cl->reader->cacheex.lg_only_in_aio_only && (cfg.cacheex_localgenerated_only_in || cl->reader->cacheex.localgenerated_only_in || ((cl->reader->cacheex.feature_bitfield & 64) && (chk_lg_only(er, &cl->reader->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab))) || ( !(cl->reader->cacheex.feature_bitfield & 64) && (chk_ctab_ex(er->caid, &cl->reader->cacheex.localgenerated_only_in_caidtab) || chk_ctab_ex(er->caid, &cfg.cacheex_localgenerated_only_in_caidtab)))) ) ) || // aio (cl->cacheex_aio_checked && cl->reader->cacheex.feature_bitfield && ( cfg.cacheex_localgenerated_only_in || cl->reader->cacheex.localgenerated_only_in || ((cl->reader->cacheex.feature_bitfield & 64) && (chk_lg_only(er, &cl->reader->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab))) || ( !(cl->reader->cacheex.feature_bitfield & 64) && (chk_ctab_ex(er->caid, &cl->reader->cacheex.localgenerated_only_in_caidtab) || chk_ctab_ex(er->caid, &cfg.cacheex_localgenerated_only_in_caidtab))) ) ) ) ) || (cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 && !chk_srvid_localgenerated_only_exception(er) // cx3 && ( // !aio (cl->cacheex_aio_checked && !cl->account->cacheex.feature_bitfield && ( !cfg.cacheex_lg_only_in_aio_only && !cl->account->cacheex.lg_only_in_aio_only && (cfg.cacheex_localgenerated_only_in || cl->account->cacheex.localgenerated_only_in || ((cl->account->cacheex.feature_bitfield & 64) && (chk_lg_only(er, &cl->account->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab))) || ( !(cl->account->cacheex.feature_bitfield & 64) && (chk_ctab_ex(er->caid, &cl->account->cacheex.localgenerated_only_in_caidtab) || chk_ctab_ex(er->caid, &cfg.cacheex_localgenerated_only_in_caidtab)))) ) ) || // aio (cl->cacheex_aio_checked && cl->account->cacheex.feature_bitfield && ( cfg.cacheex_localgenerated_only_in || cl->account->cacheex.localgenerated_only_in || ((cl->account->cacheex.feature_bitfield & 64) && (chk_lg_only(er, &cl->account->cacheex.lg_only_in_tab) || chk_lg_only(er, &cfg.cacheex_lg_only_in_tab))) || ( !(cl->account->cacheex.feature_bitfield & 64) && (chk_ctab_ex(er->caid, &cl->account->cacheex.localgenerated_only_in_caidtab) || chk_ctab_ex(er->caid, &cfg.cacheex_localgenerated_only_in_caidtab))) ) ) ) ) ) { cs_log_dbg(D_CACHEEX, "cacheex: drop ECM without localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); free_push_in_ecm(er); return; } } #endif // for compatibility: add peer node if no node received if(!ll_count(er->csp_lastnodes)) { if(!cs_malloc(&data, 8)) { return; } memcpy(data, cc->peer_node_id, 8); ll_append(er->csp_lastnodes, data); cs_log_dbg(D_CACHEEX, "cacheex: added missing remote node id %" PRIu64 "X", cacheex_node_id(data)); } #ifdef CS_CACHEEX_AIO if (er->cwc_cycletime && er->cwc_next_cw_cycle < 2) { if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) { cl->account->cwc_info++; } else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) { cl->cwc_info++; } cs_log_dbg(D_CWC, "CWC (CE) received from %s cycletime: %isek - nextcwcycle: CW%i for %04X@%06X:%04X", username(cl), er->cwc_cycletime, er->cwc_next_cw_cycle, er->caid, er->prid, er->srvid); } #endif cacheex_add_to_cache(cl, er); } void cc_cacheex_module_init(struct s_module *ph) { ph->c_cache_push = cc_cacheex_push_out; ph->c_cache_push_chk = cc_cacheex_push_chk; } #endif