cwc: add support for the key state per section
This commit is contained in:
parent
bdb31c36a2
commit
bafbf389f8
3 changed files with 94 additions and 45 deletions
|
@ -56,6 +56,7 @@ typedef struct th_descrambler {
|
|||
void (*td_stop) (struct th_descrambler *d);
|
||||
void (*td_caid_change)(struct th_descrambler *d);
|
||||
int (*td_ecm_reset) (struct th_descrambler *d);
|
||||
void (*td_ecm_idle) (struct th_descrambler *d);
|
||||
|
||||
} th_descrambler_t;
|
||||
|
||||
|
@ -151,6 +152,7 @@ void descrambler_done ( void );
|
|||
void descrambler_service_start ( struct service *t );
|
||||
void descrambler_service_stop ( struct service *t );
|
||||
void descrambler_caid_changed ( struct service *t );
|
||||
int descrambler_resolved ( struct service *t, th_descrambler_t *ignore );
|
||||
void descrambler_keys ( th_descrambler_t *t, int type,
|
||||
const uint8_t *even, const uint8_t *odd );
|
||||
int descrambler_descramble ( struct service *t,
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
#define TVHEADEND_PROTOCOL_ID 0x6502
|
||||
#define CWC_KEEPALIVE_INTERVAL 30
|
||||
#define CWC_ES_PIDS 8
|
||||
#define CWC_MAX_NOKS 4
|
||||
#define CWC_MAX_NOKS 3
|
||||
|
||||
#define CWS_NETMSGSIZE 362
|
||||
#define CWS_FIRSTCMDNO 0xe0
|
||||
|
@ -98,6 +98,13 @@ static char *crypt_md5(const char *pw, const char *salt);
|
|||
typedef struct ecm_section {
|
||||
LIST_ENTRY(ecm_section) es_link;
|
||||
|
||||
enum {
|
||||
ES_UNKNOWN,
|
||||
ES_RESOLVED,
|
||||
ES_FORBIDDEN,
|
||||
ES_IDLE
|
||||
} es_keystate;
|
||||
|
||||
int es_section;
|
||||
int es_channel;
|
||||
|
||||
|
@ -702,20 +709,37 @@ static int
|
|||
cwc_ecm_reset(th_descrambler_t *th)
|
||||
{
|
||||
cwc_service_t *ct = (cwc_service_t *)th;
|
||||
ecm_pid_t *ep;
|
||||
ecm_section_t *es;
|
||||
|
||||
if (ct->cs_constcw)
|
||||
return 1; /* keys will not change */
|
||||
ct->td_keystate = DS_UNKNOWN;
|
||||
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
|
||||
LIST_FOREACH(es, &ep->ep_sections, es_link)
|
||||
es->es_keystate = ES_UNKNOWN;
|
||||
ct->ecm_state = ECM_RESET;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cwc_ecm_idle(th_descrambler_t *th)
|
||||
{
|
||||
cwc_service_t *ct = (cwc_service_t *)th;
|
||||
ecm_pid_t *ep;
|
||||
ecm_section_t *es;
|
||||
|
||||
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
|
||||
LIST_FOREACH(es, &ep->ep_sections, es_link)
|
||||
es->es_keystate = ES_IDLE;
|
||||
ct->ecm_state = ECM_RESET;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg,
|
||||
int len, int seq)
|
||||
{
|
||||
mpegts_service_t *t = (mpegts_service_t *)ct->td_service;
|
||||
th_descrambler_t *td;
|
||||
ecm_pid_t *ep;
|
||||
ecm_section_t *es2;
|
||||
char chaninfo[32];
|
||||
|
@ -733,24 +757,25 @@ handle_ecm_reply(cwc_service_t *ct, ecm_section_t *es, uint8_t *msg,
|
|||
if (es->es_nok < CWC_MAX_NOKS)
|
||||
es->es_nok++;
|
||||
|
||||
if(ct->td_keystate == DS_FORBIDDEN)
|
||||
if(es->es_keystate == ES_FORBIDDEN)
|
||||
return; // We already know it's bad
|
||||
|
||||
if (es->es_nok > 2) {
|
||||
if (es->es_nok >= CWC_MAX_NOKS) {
|
||||
tvhlog(LOG_DEBUG, "cwc",
|
||||
"Too many NOKs for service \"%s\"%s from %s",
|
||||
t->s_dvb_svcname, chaninfo, ct->td_nicename);
|
||||
goto forbid;
|
||||
}
|
||||
|
||||
LIST_FOREACH(td, &t->s_descramblers, td_service_link)
|
||||
if (td != (th_descrambler_t *)ct && td->td_keystate == DS_RESOLVED) {
|
||||
tvhlog(LOG_DEBUG, "cwc",
|
||||
"NOK from %s: Already has a key for service \"%s\", from %s",
|
||||
ct->td_nicename, t->s_dvb_svcname, td->td_nicename);
|
||||
es->es_nok = 3; /* do not send more ECM requests */
|
||||
if (descrambler_resolved((service_t *)t, (th_descrambler_t *)ct)) {
|
||||
tvhlog(LOG_DEBUG, "cwc",
|
||||
"NOK from %s: Already has a key for service \"%s\"",
|
||||
ct->td_nicename, t->s_dvb_svcname);
|
||||
es->es_nok = CWC_MAX_NOKS; /* do not send more ECM requests */
|
||||
es->es_keystate = ES_IDLE;
|
||||
if (ct->td_keystate == DS_UNKNOWN)
|
||||
ct->td_keystate = DS_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
tvhlog(LOG_DEBUG, "cwc", "Received NOK for service \"%s\"%s (seqno: %d "
|
||||
"Req delay: %"PRId64" ms)", t->s_dvb_svcname, chaninfo, seq, delay);
|
||||
|
@ -764,17 +789,24 @@ forbid:
|
|||
return;
|
||||
i++;
|
||||
}
|
||||
if (i && es->es_nok < 2) /* only first hit is allowed */
|
||||
if (i && es->es_nok < CWC_MAX_NOKS)
|
||||
return;
|
||||
|
||||
tvhlog(LOG_ERR, "cwc",
|
||||
"Can not descramble service \"%s\", access denied (seqno: %d "
|
||||
"Req delay: %"PRId64" ms) from %s",
|
||||
t->s_dvb_svcname, seq, delay, ct->td_nicename);
|
||||
|
||||
ct->td_keystate = DS_FORBIDDEN;
|
||||
ct->ecm_state = ECM_RESET;
|
||||
es->es_keystate = ES_FORBIDDEN;
|
||||
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
|
||||
LIST_FOREACH(es2, &ep->ep_sections, es_link)
|
||||
if (es2->es_keystate == ES_UNKNOWN ||
|
||||
es2->es_keystate == ES_RESOLVED)
|
||||
break;
|
||||
|
||||
if (ep == NULL) { /* !UNKNOWN && !RESOLVED */
|
||||
tvhlog(LOG_ERR, "cwc",
|
||||
"Can not descramble service \"%s\", access denied (seqno: %d "
|
||||
"Req delay: %"PRId64" ms) from %s",
|
||||
t->s_dvb_svcname, seq, delay, ct->td_nicename);
|
||||
ct->td_keystate = DS_FORBIDDEN;
|
||||
ct->ecm_state = ECM_RESET;
|
||||
}
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
@ -805,10 +837,12 @@ forbid:
|
|||
msg[3 + 10],msg[3 + 11],msg[3 + 12],msg[3 + 13],msg[3 + 14],
|
||||
msg[3 + 15], seq, delay);
|
||||
|
||||
if(ct->td_keystate != DS_RESOLVED)
|
||||
if(es->es_keystate != ES_RESOLVED)
|
||||
tvhlog(LOG_DEBUG, "cwc",
|
||||
"Obtained DES keys for service \"%s\" in %"PRId64" ms, from %s",
|
||||
t->s_dvb_svcname, delay, ct->td_nicename);
|
||||
es->es_keystate = ES_RESOLVED;
|
||||
es->es_resolved = 1;
|
||||
|
||||
descrambler_keys((th_descrambler_t *)ct, DESCRAMBLER_DES, msg + 3, msg + 3 + 8);
|
||||
} else {
|
||||
|
@ -827,18 +861,15 @@ forbid:
|
|||
msg[3 + 25],msg[3 + 26],msg[3 + 27],msg[3 + 28],msg[3 + 29],
|
||||
msg[3 + 30], msg[3 + 31], seq, delay);
|
||||
|
||||
if(ct->td_keystate != DS_RESOLVED)
|
||||
if(es->es_keystate != ES_RESOLVED)
|
||||
tvhlog(LOG_DEBUG, "cwc",
|
||||
"Obtained AES keys for service \"%s\" in %"PRId64" ms, from %s",
|
||||
t->s_dvb_svcname, delay, ct->td_nicename);
|
||||
es->es_keystate = ES_RESOLVED;
|
||||
es->es_resolved = 1;
|
||||
|
||||
descrambler_keys((th_descrambler_t *)ct, DESCRAMBLER_AES, msg + 3, msg + 3 + 16);
|
||||
}
|
||||
LIST_FOREACH(ep, &ct->cs_pids, ep_link)
|
||||
LIST_FOREACH(es2, &ep->ep_sections, es_link) {
|
||||
es2->es_resolved = 1;
|
||||
es2->es_pending = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -870,7 +901,7 @@ cwc_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
|
|||
if(es->es_seq == seq) {
|
||||
if (es->es_resolved) {
|
||||
mpegts_service_t *t = (mpegts_service_t *)ct->td_service;
|
||||
tvhlog(LOG_WARNING, "cwc",
|
||||
tvhlog(LOG_DEBUG, "cwc",
|
||||
"Ignore %sECM (PID %d) for service \"%s\" from %s (seq %i)",
|
||||
es->es_pending ? "duplicate " : "",
|
||||
ep->ep_pid, t->s_dvb_svcname, ct->td_nicename, es->es_seq);
|
||||
|
@ -1319,7 +1350,7 @@ cwc_emm(void *opaque, int pid, const uint8_t *data, int len)
|
|||
if (cwc->cwc_emmex) {
|
||||
if (cwc->cwc_update_id != ca_update_id) {
|
||||
int64_t delta = getmonoclock() - cwc->cwc_update_time;
|
||||
if (delta < 25000000UL) /* 25 seconds */
|
||||
if (delta < 25000000UL) /* 25 seconds */
|
||||
goto end_of_job;
|
||||
}
|
||||
cwc->cwc_update_time = getmonoclock();
|
||||
|
@ -1410,7 +1441,7 @@ cwc_emm_irdeto(cwc_t *cwc, struct cs_card_data *pcard, const uint8_t *data, int
|
|||
if (match) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (match)
|
||||
cwc_send_msg(cwc, data, len, 0, 1, 0, 0);
|
||||
}
|
||||
|
@ -1490,14 +1521,13 @@ int sort_nanos(uint8_t *dest, const uint8_t *src, int len)
|
|||
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;
|
||||
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];
|
||||
n = src[j];
|
||||
}
|
||||
j += l;
|
||||
}
|
||||
|
@ -1543,7 +1573,7 @@ cwc_emm_viaccess(cwc_t *cwc, struct cs_card_data *pcard, const uint8_t *data, in
|
|||
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);
|
||||
|
@ -1743,10 +1773,10 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
|
|||
ep->ep_last_section = 0;
|
||||
section = 0;
|
||||
}
|
||||
|
||||
|
||||
channel = pid;
|
||||
snprintf(chaninfo, sizeof(chaninfo), " (PID %d)", channel);
|
||||
|
||||
|
||||
LIST_FOREACH(es, &ep->ep_sections, es_link)
|
||||
if (es->es_section == section)
|
||||
break;
|
||||
|
@ -1755,29 +1785,32 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
|
|||
es->es_section = section;
|
||||
LIST_INSERT_HEAD(&ep->ep_sections, es, es_link);
|
||||
}
|
||||
|
||||
|
||||
if(es->es_ecmsize == len && !memcmp(es->es_ecm, data, len))
|
||||
break; /* key already sent */
|
||||
|
||||
|
||||
if(cwc->cwc_fd == -1) {
|
||||
// New key, but we are not connected (anymore), can not descramble
|
||||
ct->td_keystate = DS_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (es->es_keystate == ES_FORBIDDEN || es->es_keystate == ES_IDLE)
|
||||
break;
|
||||
|
||||
es->es_channel = channel;
|
||||
es->es_pending = 1;
|
||||
es->es_resolved = 0;
|
||||
|
||||
|
||||
memcpy(es->es_ecm, data, len);
|
||||
es->es_ecmsize = len;
|
||||
|
||||
|
||||
|
||||
if(ct->cs_channel >= 0 && channel != -1 &&
|
||||
ct->cs_channel != channel) {
|
||||
tvhlog(LOG_DEBUG, "cwc", "Filtering ECM (PID %d)", channel);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
es->es_seq = cwc_send_msg(cwc, data, len, sid, 1, caid, providerid);
|
||||
|
||||
tvhlog(LOG_DEBUG, "cwc",
|
||||
|
@ -1785,7 +1818,7 @@ cwc_table_input(void *opaque, int pid, const uint8_t *data, int len)
|
|||
chaninfo, section, ep->ep_last_section, t->s_dvb_svcname, es->es_seq);
|
||||
es->es_time = getmonoclock();
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
/* EMM */
|
||||
if (cwc->cwc_forward_emm)
|
||||
|
@ -2079,6 +2112,7 @@ cwc_service_start(service_t *t)
|
|||
td->td_service = t;
|
||||
td->td_stop = cwc_service_destroy;
|
||||
td->td_ecm_reset = cwc_ecm_reset;
|
||||
td->td_ecm_idle = cwc_ecm_idle;
|
||||
LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
|
||||
|
||||
LIST_INSERT_HEAD(&cwc->cwc_services, ct, cs_link);
|
||||
|
|
|
@ -162,6 +162,17 @@ descrambler_caid_changed ( service_t *t )
|
|||
}
|
||||
}
|
||||
|
||||
int
|
||||
descrambler_resolved( service_t *t, th_descrambler_t *ignore )
|
||||
{
|
||||
th_descrambler_t *td;
|
||||
|
||||
LIST_FOREACH(td, &t->s_descramblers, td_service_link)
|
||||
if (td != ignore && td->td_keystate == DS_RESOLVED)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
descrambler_keys ( th_descrambler_t *td, int type,
|
||||
const uint8_t *even, const uint8_t *odd )
|
||||
|
@ -191,6 +202,8 @@ descrambler_keys ( th_descrambler_t *td, int type,
|
|||
((mpegts_service_t *)td2->td_service)->s_dvb_svcname,
|
||||
td->td_nicename);
|
||||
td->td_keystate = DS_IDLE;
|
||||
if (td->td_ecm_idle)
|
||||
td->td_ecm_idle(td);
|
||||
goto fin;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue