diff --git a/src/cwc.c b/src/cwc.c index 601f2cb7..689e0218 100644 --- a/src/cwc.c +++ b/src/cwc.c @@ -57,6 +57,7 @@ typedef enum { CARD_IRDETO, CARD_CONAX, CARD_SECA, + CARD_VIACCESS, CARD_UNKNOWN } card_type_t; @@ -223,6 +224,22 @@ typedef struct cwc { /* Emm forwarding */ int cwc_forward_emm; + /* Emm duplicate cache */ + struct { +#define EMM_CACHE_SIZE (1<<5) +#define EMM_CACHE_MASK (EMM_CACHE_SIZE-1) + uint32_t cache[EMM_CACHE_SIZE]; + uint32_t w; + uint32_t n; + } cwc_emm_cache; + + /* Viaccess EMM assemble state */ + struct { + int shared_toggle; + int shared_len; + uint8_t * shared_emm; + } cwc_viaccess_emm; + /* Card type */ card_type_t cwc_card_type; @@ -255,6 +272,8 @@ static void cwc_detecs_card_type(cwc_t *cwc); void cwc_emm_conax(cwc_t *cwc, uint8_t *data, int len); void cwc_emm_irdeto(cwc_t *cwc, uint8_t *data, int len); void cwc_emm_seca(cwc_t *cwc, uint8_t *data, int len); +void cwc_emm_viaccess(cwc_t *cwc, uint8_t *data, int len); + /** * @@ -634,6 +653,11 @@ cwc_detecs_card_type(cwc_t *cwc) tvhlog(LOG_INFO, "cwc", "%s: irdeto card", cwc->cwc_hostname); break; + case 0x05: + cwc->cwc_card_type = CARD_VIACCESS; + tvhlog(LOG_INFO, "cwc", "%s: viaccess card", + cwc->cwc_hostname); + break; case 0x0b: cwc->cwc_card_type = CARD_CONAX; tvhlog(LOG_INFO, "cwc", "%s: conax card", @@ -1102,6 +1126,29 @@ verify_provider(cwc_t *cwc, uint32_t providerid) return 0; } +/** + * + */ +static void +cwc_emm_cache_insert(cwc_t *cwc, uint32_t crc) +{ + /* evict the oldest entry */ + cwc->cwc_emm_cache.cache[cwc->cwc_emm_cache.w] = crc; + cwc->cwc_emm_cache.w = (cwc->cwc_emm_cache.w+1)&EMM_CACHE_MASK; + if (cwc->cwc_emm_cache.n < EMM_CACHE_SIZE) + cwc->cwc_emm_cache.n++; +} + +static int +cwc_emm_cache_lookup(cwc_t *cwc, uint32_t crc) +{ + int i; + for (i=0; icwc_emm_cache.n; i++) + if (cwc->cwc_emm_cache.cache[i] == crc) + return 1; + return 0; +} + /** * @@ -1125,6 +1172,9 @@ cwc_emm(uint8_t *data, int len) case CARD_SECA: cwc_emm_seca(cwc, data, len); break; + case CARD_VIACCESS: + cwc_emm_viaccess(cwc, data, len); + break; case CARD_UNKNOWN: break; } @@ -1213,6 +1263,179 @@ cwc_emm_seca(cwc_t *cwc, uint8_t *data, int len) cwc_send_msg(cwc, data, len, 0, 1); } +/** + * viaccess emm handler + * inspired by opensasc-ng, https://opensvn.csie.org/traccgi/opensascng/ + */ +static +uint8_t * nano_start(uint8_t * data) +{ + switch(data[0]) { + case 0x88: return &data[8]; + case 0x8e: return &data[7]; + case 0x8c: + case 0x8d: return &data[3]; + case 0x80: + case 0x81: return &data[4]; + } + return NULL; +} + +static +uint8_t * nano_checknano90fromnano(uint8_t * data) +{ + if(data && data[0]==0x90 && data[1]==0x03) return data; + return 0; +} + +static +uint8_t * nano_checknano90(uint8_t * data) +{ + return nano_checknano90fromnano(nano_start(data)); +} + +static +int sort_nanos(uint8_t *dest, const uint8_t *src, int len) +{ + int w = 0, c = -1; + + while (1) { + int j, n; + n = 0x100; + for (j = 0; j < len;) { + int l = src[j + 1] + 2; + if (src[j] == c) { + if (w + l > len) { + return -1; + } + memcpy(dest + w, src + j, l); + w += l; + } + else if (src[j] > c && src[j] < n) { + n = src[j]; + } + j += l; + } + if (n == 0x100) { + break; + } + c = n; + } + return 0; +} + +static int via_provider_id(uint8_t * data) +{ + const uint8_t * tmp; + tmp = nano_checknano90(data); + if (!tmp) return 0; + return (tmp[2] << 16) | (tmp[3] << 8) | (tmp[4]&0xf0); +} + + +void +cwc_emm_viaccess(cwc_t *cwc, uint8_t *data, int mlen) +{ + /* Get SCT len */ + int len = 3 + ((data[1] & 0x0f) << 8) + data[2]; + + switch (data[0]) + { + case 0x8c: + case 0x8d: + { + int match = 0; + int i; + uint32_t id; + + /* Match provider id */ + id = via_provider_id(data); + if (!id) break; + + for(i = 0; i < cwc->cwc_num_providers; i++) + if(cwc->cwc_providers[i].id == id) { + match = 1; + break; + } + if (!match) break; + + if (data[0] != cwc->cwc_viaccess_emm.shared_toggle) { + cwc->cwc_viaccess_emm.shared_len = 0; + free(cwc->cwc_viaccess_emm.shared_emm); + cwc->cwc_viaccess_emm.shared_emm = (uint8_t*)malloc(len); + if (cwc->cwc_viaccess_emm.shared_emm) { + cwc->cwc_viaccess_emm.shared_len = len; + memcpy(cwc->cwc_viaccess_emm.shared_emm, data, len); + } + cwc->cwc_viaccess_emm.shared_toggle = data[0]; + } + } + break; + case 0x8e: + if (cwc->cwc_viaccess_emm.shared_emm) { + int match = 0; + int i; + /* Match SA and provider in shared */ + for(i = 0; i < cwc->cwc_num_providers; i++) { + if(memcmp(&data[3],&cwc->cwc_providers[i].sa[4], 3)) continue; + if((data[6]&2)) continue; + if(via_provider_id(cwc->cwc_viaccess_emm.shared_emm) != cwc->cwc_providers[i].id) continue; + match = 1; + break; + } + if (!match) break; + + uint8_t * tmp = alloca(len + cwc->cwc_viaccess_emm.shared_len); + uint8_t * ass = nano_start(data); + len -= (ass - data); + if((data[6] & 2) == 0) { + int addrlen = len - 8; + len=0; + tmp[len++] = 0x9e; + tmp[len++] = addrlen; + memcpy(&tmp[len], &ass[0], addrlen); len += addrlen; + tmp[len++] = 0xf0; + tmp[len++] = 0x08; + memcpy(&tmp[len],&ass[addrlen],8); len += 8; + } else { + memcpy(tmp, ass, len); + } + ass = nano_start(cwc->cwc_viaccess_emm.shared_emm); + int l = cwc->cwc_viaccess_emm.shared_len - (ass - cwc->cwc_viaccess_emm.shared_emm); + memcpy(&tmp[len], ass, l); len += l; + + ass = (uint8_t*) alloca(len+7); + if(ass) { + uint32_t crc; + + memcpy(ass, data, 7); + if (sort_nanos(ass + 7, tmp, len)) { + return; + } + + /* Set SCT len */ + len += 4; + ass[1] = (len>>8) | 0x70; + ass[2] = len & 0xff; + len += 3; + + crc = crc32(ass, len, 0xffffffff); + if (!cwc_emm_cache_lookup(cwc, crc)) { + tvhlog(LOG_DEBUG, "cwc", + "Send EMM " + "%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x" + "...%02x.%02x.%02x.%02x", + ass[0], ass[1], ass[2], ass[3], + ass[4], ass[5], ass[6], ass[7], + ass[len-4], ass[len-3], ass[len-2], ass[len-1]); + cwc_send_msg(cwc, ass, len, 0, 1); + cwc_emm_cache_insert(cwc, crc); + } + } + } + break; + } +} /** *