TAILQ_HEAD(cwc_queue, cwc);
LIST_HEAD(cwc_transport_list, cwc_transport);
TAILQ_HEAD(cwc_message_queue, cwc_message);
+LIST_HEAD(ecm_channel_list, ecm_channel);
static struct cwc_queue cwcs;
static pthread_cond_t cwc_config_changed;
+/**
+ *
+ */
+typedef struct ecm_channel {
+ LIST_ENTRY(ecm_channel) ec_link;
+ int ec_failures;
+ int ec_channel;
+
+ uint16_t ec_seq;
+
+ size_t ec_ecmsize;
+ uint8_t ec_ecm[4096];
+
+} ecm_channel_t;
+
/**
*
LIST_ENTRY(cwc_transport) ct_link;
-
- /**
- * Sequence number generated on write (based on cwc_seq),
- * used to pair reply message (i.e when i CT_STATE_WAIT_REPLY)
- * with cwc_transport
- */
- uint16_t ct_seq;
-
-
- /**
- * Current ECM
- */
- uint8_t ct_ecm[4096];
- int ct_ecmsize;
-
- int ct_ecm_reply_pending; /* Waiting for a ECM reply */
+ struct ecm_channel_list ct_ecmchannels; // Status per ECM channel
+ int ct_okchannel;
/**
* Status of the key(s) in ct_keys
}
+/**
+ *
+ */
+static int
+channel_nok(cwc_transport_t *ct, ecm_channel_t *ec)
+{
+ ec->ec_failures++;
+
+ LIST_FOREACH(ec, &ct->ct_ecmchannels, ec_link)
+ if(ec->ec_failures < 2)
+ return 0;
+ return 1;
+}
+
+
/**
* Login command
*/
uint16_t seq = (msg[2] << 8) | msg[3];
th_transport_t *t;
int j;
+ ecm_channel_t *ec = NULL;
len -= 12;
msg += 12;
case 0x80:
case 0x81:
LIST_FOREACH(ct, &cwc->cwc_transports, ct_link) {
- if(ct->ct_seq == seq && ct->ct_ecm_reply_pending)
- break;
- }
-
- if(ct == NULL) {
- tvhlog(LOG_ERR, "cwc", "Got unexpected ECM reply");
- return 0;
+ LIST_FOREACH(ec, &ct->ct_ecmchannels, ec_link) {
+ if(ec->ec_seq == seq)
+ goto gotch;
+ }
}
+ tvhlog(LOG_ERR, "cwc", "Got unexpected ECM reply (seqno: %d)", seq);
+ return 0;
+ gotch:
t = ct->ct_transport;
- ct->ct_ecm_reply_pending = 0;
if(len < 19) {
+ if(ct->ct_okchannel == ec->ec_channel)
+ ct->ct_okchannel = -1;
+
if(ct->ct_keystate != CT_FORBIDDEN) {
+
+ if(ec->ec_channel != -1)
+ tvhlog(LOG_DEBUG, "cwc", "Received NOK on channel %d (seqno: %d)",
+ ec->ec_channel, seq);
+
+ if(ec->ec_channel != -1 && !channel_nok(ct, ec))
+ break;
+
tvhlog(LOG_ERR, "cwc",
- "Can not descramble service \"%s\", access denied",
- t->tht_svcname);
+ "Can not descramble service \"%s\", access denied (seqno: %d)",
+ t->tht_svcname, seq);
ct->ct_keystate = CT_FORBIDDEN;
}
- return 0;
- }
+ } else {
- tvhlog(LOG_DEBUG, "cwc",
- "Received ECM reply for service \"%s\" "
- "even: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x"
- " odd: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x ",
- t->tht_svcname,
- msg[3 + 0], msg[3 + 1], msg[3 + 2], msg[3 + 3], msg[3 + 4],
- msg[3 + 5], msg[3 + 6], msg[3 + 7], msg[3 + 8], msg[3 + 9],
- msg[3 + 10],msg[3 + 11],msg[3 + 12],msg[3 + 13],msg[3 + 14],
- msg[3 + 15]);
-
- if(ct->ct_keystate != CT_RESOLVED)
- tvhlog(LOG_INFO, "cwc",
- "Obtained key for for service \"%s\"",t->tht_svcname);
-
- ct->ct_keystate = CT_RESOLVED;
- pthread_mutex_lock(&t->tht_stream_mutex);
+ ct->ct_okchannel = ec->ec_channel;
- for(j = 0; j < 8; j++)
- if(msg[3 + j]) {
- set_even_control_word(ct->ct_keys, msg + 3);
- break;
- }
+ char chbuf[32];
- for(j = 0; j < 8; j++)
- if(msg[3 + 8 +j]) {
- set_odd_control_word(ct->ct_keys, msg + 3 + 8);
- break;
+ if(ec->ec_channel != -1) {
+ snprintf(chbuf, sizeof(chbuf), " on channel %d", ec->ec_channel);
+ } else {
+ chbuf[0] = 0;
}
- pthread_mutex_unlock(&t->tht_stream_mutex);
+ tvhlog(LOG_DEBUG, "cwc",
+ "Received ECM reply%s for service \"%s\" "
+ "even: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x"
+ " odd: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x (seqno: %d)",
+ chbuf,
+ t->tht_svcname,
+ msg[3 + 0], msg[3 + 1], msg[3 + 2], msg[3 + 3], msg[3 + 4],
+ msg[3 + 5], msg[3 + 6], msg[3 + 7], msg[3 + 8], msg[3 + 9],
+ msg[3 + 10],msg[3 + 11],msg[3 + 12],msg[3 + 13],msg[3 + 14],
+ msg[3 + 15], seq);
+
+ if(ct->ct_keystate != CT_RESOLVED)
+ tvhlog(LOG_INFO, "cwc",
+ "Obtained key for for service \"%s\"",t->tht_svcname);
+
+ ct->ct_keystate = CT_RESOLVED;
+ pthread_mutex_lock(&t->tht_stream_mutex);
+
+ for(j = 0; j < 8; j++)
+ if(msg[3 + j]) {
+ set_even_control_word(ct->ct_keys, msg + 3);
+ break;
+ }
+
+ for(j = 0; j < 8; j++)
+ if(msg[3 + 8 +j]) {
+ set_odd_control_word(ct->ct_keys, msg + 3 + 8);
+ break;
+ }
+
+ pthread_mutex_unlock(&t->tht_stream_mutex);
+ }
+
break;
+
default:
// EMM
break;
cwc_transport_t *ct = (cwc_transport_t *)td;
uint16_t sid = t->tht_dvb_service_id;
cwc_t *cwc = ct->ct_cwc;
+ int channel;
+ ecm_channel_t *ec;
if(len > 4096)
return;
case 0x81:
/* ECM */
- if(ct->ct_ecm_reply_pending)
- break;
+ if(cwc->cwc_caid >> 8 == 6) {
+ channel = data[6] << 8 | data[7];
+ } else {
+ channel = -1;
+ }
- if(ct->ct_ecmsize == len && !memcmp(ct->ct_ecm, data, len))
- break; /* key already sent */
+ LIST_FOREACH(ec, &ct->ct_ecmchannels, ec_link)
+ if(ec->ec_channel == channel)
+ break;
+
+ if(ec == NULL) {
+ ec = calloc(1, sizeof(ecm_channel_t));
+ LIST_INSERT_HEAD(&ct->ct_ecmchannels, ec, ec_link);
+ ec->ec_channel = channel;
+ }
+ if(ec->ec_ecmsize == len && !memcmp(ec->ec_ecm, data, len))
+ break; /* key already sent */
if(cwc->cwc_fd == -1) {
// New key, but we are not connected (anymore), can not descramble
break;
}
- tvhlog(LOG_DEBUG, "cwc", "Sending ECM for service %s", t->tht_svcname);
+ memcpy(ec->ec_ecm, data, len);
+ ec->ec_ecmsize = len;
- memcpy(ct->ct_ecm, data, len);
- ct->ct_ecmsize = len;
- ct->ct_seq = cwc_send_msg(cwc, data, len, sid, 1);
- ct->ct_ecm_reply_pending = 1;
+ if(ct->ct_okchannel != -1 && channel != -1 &&
+ ct->ct_okchannel != channel) {
+ tvhlog(LOG_DEBUG, "cwc", "Filtering ECM channel %d", channel);
+ return;
+ }
+
+ ec->ec_seq = cwc_send_msg(cwc, data, len, sid, 1);
+
+ if(channel != -1) {
+ tvhlog(LOG_DEBUG, "cwc",
+ "Sending ECM (channel %d) for service %s (seqno: %d)",
+ channel, t->tht_svcname, ec->ec_seq);
+ } else {
+ tvhlog(LOG_DEBUG, "cwc", "Sending ECM for service %s (seqno: %d)",
+ t->tht_svcname, ec->ec_seq);
+ }
break;
default:
cwc_transport_destroy(th_descrambler_t *td)
{
cwc_transport_t *ct = (cwc_transport_t *)td;
+ ecm_channel_t *ec;
+
+ while((ec = LIST_FIRST(&ct->ct_ecmchannels)) != NULL) {
+ LIST_REMOVE(ec, ec_link);
+ free(ec);
+ }
LIST_REMOVE(td, td_transport_link);
th_stream_t *st;
lock_assert(&global_lock);
-
TAILQ_FOREACH(cwc, &cwcs, cwc_link) {
if(cwc->cwc_caid == 0)
continue;
ct->ct_keys = get_key_struct();
ct->ct_cwc = cwc;
ct->ct_transport = t;
-
+ ct->ct_okchannel = -1;
+
td = &ct->ct_head;
td->td_stop = cwc_transport_destroy;
td->td_table = cwc_table_input;