1 /*
2  *  tvheadend, tsdebug code word interface
3  *  Copyright (C) 2014 Jaroslav Kysela
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <ctype.h>
20 #include "tvheadend.h"
21 #include "caclient.h"
22 #include "service.h"
23 #include "input.h"
24 
25 /**
26  *
27  */
28 typedef struct tsdebugcw_service {
29   th_descrambler_t;
30 
31   int        tdcw_type;
32   uint8_t    tdcw_key_even[16];   /* DES or AES key */
33   uint8_t    tdcw_key_odd [16];   /* DES or AES key */
34 
35 } tsdebugcw_service_t;
36 
37 typedef struct tsdebugcw_request {
38   TAILQ_ENTRY(tsdebugcw_request) link;
39   tsdebugcw_service_t *ct;
40 } tsdebugcw_request_t;
41 
42 pthread_mutex_t tsdebugcw_mutex;
43 TAILQ_HEAD(,tsdebugcw_request) tsdebugcw_requests;
44 
45 /*
46  *
47  */
48 static int
tsdebugcw_ecm_reset(th_descrambler_t * th)49 tsdebugcw_ecm_reset(th_descrambler_t *th)
50 {
51   return 1;
52 }
53 
54 /**
55  * s_stream_mutex is held
56  */
57 static void
tsdebugcw_service_destroy(th_descrambler_t * td)58 tsdebugcw_service_destroy(th_descrambler_t *td)
59 {
60   tsdebugcw_service_t *ct = (tsdebugcw_service_t *)td;
61   tsdebugcw_request_t *ctr, *ctrnext;
62 
63   pthread_mutex_lock(&tsdebugcw_mutex);
64   for (ctr = TAILQ_FIRST(&tsdebugcw_requests); ctr; ctr = ctrnext) {
65     ctrnext = TAILQ_NEXT(ctr, link);
66     if (ctr->ct == ct) {
67       TAILQ_REMOVE(&tsdebugcw_requests, ctr, link);
68       free(ctr);
69     }
70   }
71   pthread_mutex_unlock(&tsdebugcw_mutex);
72 
73   LIST_REMOVE(td, td_service_link);
74   free(ct->td_nicename);
75   free(ct);
76 }
77 
78 /**
79  * global_lock is held. Not that we care about that, but either way, it is.
80  */
81 void
tsdebugcw_service_start(service_t * t)82 tsdebugcw_service_start(service_t *t)
83 {
84   tsdebugcw_service_t *ct;
85   th_descrambler_t *td;
86   char buf[128];
87 
88   extern const idclass_t mpegts_service_class;
89   if (!idnode_is_instance(&t->s_id, &mpegts_service_class))
90     return;
91 
92   LIST_FOREACH(td, &t->s_descramblers, td_service_link)
93     if (td->td_stop == tsdebugcw_service_destroy)
94       break;
95   if (td)
96     return;
97 
98   ct                   = calloc(1, sizeof(tsdebugcw_service_t));
99   td                   = (th_descrambler_t *)ct;
100   snprintf(buf, sizeof(buf), "tsdebugcw");
101   td->td_nicename      = strdup(buf);
102   td->td_service       = t;
103   td->td_stop          = tsdebugcw_service_destroy;
104   td->td_ecm_reset     = tsdebugcw_ecm_reset;
105   LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
106 }
107 
108 /*
109  *
110  */
111 void
tsdebugcw_new_keys(service_t * t,int type,uint8_t * odd,uint8_t * even)112 tsdebugcw_new_keys(service_t *t, int type, uint8_t *odd, uint8_t *even)
113 {
114   static char empty[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
115   th_descrambler_t *td;
116   tsdebugcw_service_t *ct;
117   tsdebugcw_request_t *ctr;
118   int keylen = type == DESCRAMBLER_AES ? 16 : 8;
119 
120   LIST_FOREACH(td, &t->s_descramblers, td_service_link)
121     if (td->td_stop == tsdebugcw_service_destroy)
122       break;
123   if (!td)
124     return;
125   ct = (tsdebugcw_service_t *)td;
126   ct->tdcw_type = type;
127   if (memcmp(empty, odd, keylen))
128     memcpy(ct->tdcw_key_odd, odd, keylen);
129   if (memcmp(empty, even, keylen))
130     memcpy(ct->tdcw_key_even, even, keylen);
131   ctr = malloc(sizeof(*ctr));
132   ctr->ct = ct;
133   pthread_mutex_lock(&tsdebugcw_mutex);
134   TAILQ_INSERT_TAIL(&tsdebugcw_requests, ctr, link);
135   pthread_mutex_unlock(&tsdebugcw_mutex);
136 }
137 
138 /*
139  *
140  */
141 void
tsdebugcw_go(void)142 tsdebugcw_go(void)
143 {
144   tsdebugcw_request_t *ctr;
145   tsdebugcw_service_t *ct;
146 
147   while (1) {
148     pthread_mutex_lock(&tsdebugcw_mutex);
149     ctr = TAILQ_FIRST(&tsdebugcw_requests);
150     if (ctr)
151       TAILQ_REMOVE(&tsdebugcw_requests, ctr, link);
152     pthread_mutex_unlock(&tsdebugcw_mutex);
153     if (!ctr) break;
154     ct = ctr->ct;
155     descrambler_keys((th_descrambler_t *)ct, ct->tdcw_type,
156                      ct->tdcw_key_odd, ct->tdcw_key_even);
157     free(ctr);
158   }
159 }
160 
161 /*
162  *
163  */
164 void
tsdebugcw_init(void)165 tsdebugcw_init(void)
166 {
167   pthread_mutex_init(&tsdebugcw_mutex, NULL);
168   TAILQ_INIT(&tsdebugcw_requests);
169 }
170