]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
cwc: add support for the key state per section
authorJaroslav Kysela <perex@perex.cz>
Thu, 18 Sep 2014 20:18:49 +0000 (22:18 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 18 Sep 2014 20:18:49 +0000 (22:18 +0200)
src/descrambler.h
src/descrambler/cwc.c
src/descrambler/descrambler.c

index 12fb20d892bdb342bbd968a4818870eaaf295c75..54fbce8fe502b4ca9dac0a03cbdd1b15fc616ce9 100755 (executable)
@@ -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,
index 70ac300d7bd3b308a0d8cb9e26465b353ee7d6ca..f14599c3e8340c1b7aae004fcfc654dc223f3620 100755 (executable)
@@ -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);
index 602f51725eff9b49fc8e37bb01a3cc03ef3745f5..b0827f1b152e928f2bf7acb4b979a1782afcda63 100755 (executable)
@@ -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;
     }