descrambler: add time-based check for valid ECM key responses

This commit is contained in:
Jaroslav Kysela 2014-06-09 19:03:05 +02:00
parent 5eb3255186
commit 81dd4d1dd0
4 changed files with 84 additions and 33 deletions

View file

@ -52,6 +52,16 @@ typedef struct th_descrambler {
} th_descrambler_t;
typedef struct th_descrambler_runtime {
uint32_t dr_key:1;
uint32_t dr_key_first:1;
uint8_t dr_key_index;
time_t dr_key_start;
time_t dr_ecm_start;
time_t dr_ecm_key_time;
sbuf_t dr_buf;
} th_descrambler_runtime_t;
typedef void (*descrambler_section_callback_t)
(void *opaque, int pid, const uint8_t *section, int section_len);

View file

@ -784,7 +784,7 @@ capmt_set_filter(capmt_t *capmt, int adapter, sbuf_t *sb, int offset)
cf->adapter = adapter;
filter = &cf->dmx[filter_index];
filter->pid = pid;
memcpy(&filter->filter, sbuf_peek(sb, offset + 10), sizeof(filter->filter));
memcpy(&filter->filter, sbuf_peek(sb, offset + 8), sizeof(filter->filter));
filter->flags = 0;
/* ECM messages have the higher priority */
t = NULL;
@ -1433,12 +1433,11 @@ capmt_table_input(void *opaque, int pid, const uint8_t *data, int len)
if ((data[i] & f->mask[i]) != f->filter[i])
break;
}
if (i >= DMX_FILTER_SIZE && i <= len) {
if (i >= DMX_FILTER_SIZE && i <= len)
capmt_filter_data(capmt,
o->adapter, demux_index,
filter_index, data, len,
cf->dmx[filter_index].flags);
}
}
}

View file

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tvheadend.h"
#include "descrambler.h"
#include "cwc.h"
#include "capmt.h"
@ -132,28 +133,32 @@ descrambler_done ( void )
void
descrambler_service_start ( service_t *t )
{
t->s_descramble_key = 0;
th_descrambler_runtime_t *dr;
#if ENABLE_CWC
cwc_service_start(t);
#endif
#if ENABLE_CAPMT
capmt_service_start(t);
#endif
t->s_descramble_buf = calloc(1, sizeof(sbuf_t));
sbuf_init(t->s_descramble_buf);
t->s_descramble = dr = calloc(1, sizeof(th_descrambler_runtime_t));
sbuf_init(&dr->dr_buf);
dr->dr_key_index = 0xff;
}
void
descrambler_service_stop ( service_t *t )
{
th_descrambler_t *td;
th_descrambler_runtime_t *dr = t->s_descramble;
while ((td = LIST_FIRST(&t->s_descramblers)) != NULL)
td->td_stop(td);
if (t->s_descramble_buf) {
sbuf_free(t->s_descramble_buf);
t->s_descramble_buf = NULL;
if (dr) {
sbuf_free(&dr->dr_buf);
free(dr);
}
t->s_descramble = NULL;
}
void
@ -171,33 +176,56 @@ void
descrambler_keys ( th_descrambler_t *td,
const uint8_t *even, const uint8_t *odd )
{
int i;
th_descrambler_runtime_t *dr = td->td_service->s_descramble;
int i, j = 0;
for (i = 0; i < 8; i++)
if (even[i]) {
j++;
tvhcsa_set_key_even(td->td_csa, even);
break;
}
for (i = 0; i < 8; i++)
if (odd[i]) {
j++;
tvhcsa_set_key_odd(td->td_csa, odd);
break;
}
if (j == 0) {
tvhlog(LOG_DEBUG, "descrambler", "Empty keys received for service \"%s\"",
((mpegts_service_t *)td->td_service)->s_dvb_svcname);
return;
}
if (td->td_keystate != DS_RESOLVED)
tvhlog(LOG_DEBUG, "descrambler", "Obtained key for service \"%s\"",
((mpegts_service_t *)td->td_service)->s_dvb_svcname);
dr->dr_ecm_key_time = dispatch_clock;
td->td_keystate = DS_RESOLVED;
}
static inline void
key_update( th_descrambler_runtime_t *dr, uint8_t key )
{
/* set the even (0) or odd (0x40) key index */
dr->dr_key_index = key & 0x40;
dr->dr_key_start = dispatch_clock;
}
int
descrambler_descramble ( service_t *t,
elementary_stream_t *st,
const uint8_t *tsb )
{
th_descrambler_t *td;
th_descrambler_runtime_t *dr = t->s_descramble;
int count, failed, off, size;
uint8_t *tsb2;
if (dr == NULL)
return -1;
count = failed = 0;
LIST_FOREACH(td, &t->s_descramblers, td_service_link) {
count++;
@ -207,34 +235,47 @@ descrambler_descramble ( service_t *t,
}
if (td->td_keystate != DS_RESOLVED)
continue;
if (t->s_descramble_buf) {
for (off = 0, size = t->s_descramble_buf->sb_ptr; off < size; off += 188)
if (dr->dr_buf.sb_ptr > 0) {
for (off = 0, size = dr->dr_buf.sb_ptr; off < size; off += 188) {
tsb2 = dr->dr_buf.sb_data + off;
if ((tsb2[3] & 0x80) != 0x00 && dr->dr_key_index != (tsb2[3] & 0x40)) {
if (dr->dr_ecm_key_time < dr->dr_key_start) {
sbuf_free(&dr->dr_buf);
goto forbid;
}
key_update(dr, tsb[3]);
}
tvhcsa_descramble(td->td_csa,
(mpegts_service_t *)td->td_service,
st, t->s_descramble_buf->sb_data + off);
sbuf_free(t->s_descramble_buf);
free(t->s_descramble_buf);
t->s_descramble_buf = NULL;
st, tsb2);
}
sbuf_free(&dr->dr_buf);
}
if ((tsb[3] & 0x80) != 0x00 && dr->dr_key_index != (tsb[3] & 0x40)) {
if (dr->dr_ecm_key_time < dr->dr_key_start) {
forbid:
td->td_keystate = DS_FORBIDDEN;
failed++;
continue;
}
key_update(dr, tsb[3]);
}
tvhcsa_descramble(td->td_csa,
(mpegts_service_t *)td->td_service,
st, tsb);
return 1;
}
if (t->s_descramble_key && count != failed) {
/*
* Fill a temporary buffer until the keys are known to make
* streaming faster.
*/
if (t->s_descramble_buf == NULL) {
t->s_descramble_buf = calloc(1, sizeof(sbuf_t));
if (t->s_descramble_buf)
sbuf_init(t->s_descramble_buf);
}
if (t->s_descramble_buf) {
if (t->s_descramble_buf->sb_ptr >= 3000 * 188)
sbuf_cut(t->s_descramble_buf, 300 * 188);
sbuf_append(t->s_descramble_buf, tsb, 188);
if (dr->dr_ecm_start) { /* ECM sent */
if ((tsb[3] & 0x80) != 0x00 && dr->dr_key_start == 0)
key_update(dr, tsb[3]);
if (count != failed) {
/*
* Fill a temporary buffer until the keys are known to make
* streaming faster.
*/
if (dr->dr_buf.sb_ptr >= 3000 * 188)
sbuf_cut(&dr->dr_buf, 300 * 188);
sbuf_append(&dr->dr_buf, tsb, 188);
}
}
return count && count == failed ? -1 : count;
@ -246,6 +287,7 @@ descrambler_table_callback
{
descrambler_table_t *dt = mt->mt_opaque;
descrambler_section_t *ds;
th_descrambler_runtime_t *dr;
pthread_mutex_lock(&mt->mt_mux->mm_descrambler_lock);
TAILQ_FOREACH(ds, &dt->sections, link)
@ -263,7 +305,8 @@ descrambler_table_callback
if ((mt->mt_flags & MT_FAST) != 0) { /* ECM */
if (mt->mt_service) {
/* The keys are requested from this moment */
mt->mt_service->s_descramble_key |= 1;
dr = mt->mt_service->s_descramble;
dr->dr_ecm_start = dispatch_clock;
}
}
}

View file

@ -401,8 +401,7 @@ typedef struct service {
struct th_descrambler_list s_descramblers;
uint16_t s_scrambled_seen;
uint16_t s_descramble_key;
sbuf_t *s_descramble_buf;
th_descrambler_runtime_t *s_descramble;
/**
* List of all and filtered components.